RFC: SPI patch

Rodrigo Rubira Branco (BSDaemon) rodrigo at kernelhacking.com
Tue Jan 15 13:49:27 CST 2008


Hello,

Sorry my delay in answer your mail, I'm really busy (like everyone I
think...).

The patch looks ok, but it's a good idea to not touch the ppc/ tree, just
the powerpc one...

Also, try to not use unsigned int and uint, choose one ;)

You missed an undef debug in your code.

Just as a hint, I have not really checked it, but careful with this kind of
operation:

> +	hw->mapbase = resource.start;
> +	hw->mapsize = resource.end - resource.start + 1;

Since resource.start can be bigger then resource.end, and because the type
are unsigned you get a big number instead of a small one, passing the checks
of -lt sizeof(struct...)

In your comments XXXX I agree with you in the returning of an -EINVAL error.

Please, split your patch in at least two, the first one directly related to
spi and the other one, not related but needed and send it as [PATCH 1/2] and
[PATCH 2/2] respectively...



cya, congrats and good luck with your first patch....


Rodrigo (BSDaemon).

--
http://www.kernelhacking.com/rodrigo

Kernel Hacking: If i really know, i can hack

GPG KeyID: 1FCEDEA1


--------- Mensagem Original --------
De: gary.jennejohn at freenet.de
Para: kernel-mentors at selenic.com <kernel-mentors at selenic.com>
Assunto: RFC: SPI patch
Data: 14/01/08 13:52

>
> Hello Mentors,
>
> [PATCH] SPI: add a driver for the 440EPx
>
> Thsi is the first driver/patch which I've ever sent upstream, so I
> thought I'd pass the intended patch past you guys.
>
> I'm wondering whether I should split it up into several patches.
> All the changes are required for the driver to work, but they aren't
> all directly related to SPI.
>
> Anyway, it passes checkpatch.pl with no errors and boots on a sequoia
> board.
>
> TIA for any feedback.
>
> From: Gary Jennejohn &lt;garyj at denx.de&gt;
>
> commit d8af1d29fdbd0a6614c01582ea09115b7c51e773
> Author: Gary Jennejohn &lt;garyj at denx.de&gt;
> Date:   Thu Jan 10 13:25:48 2008 +0100
>
>     Add a driver for the SPI controller in the 440EPx. The driver works
>     with both arch/ppc and arch/powerpc.
>
>     Add hardware resources for SPI and GPIO (GPIO for powerpc only).
>
>     Block use of IIC1 when the SPI controller is used because they share
>     a configuration register.
>
>     Signed-off-by: Gary Jennejohn &lt;garyj at denx.de&gt;
> ---
>
> diff --git a/arch/powerpc/boot/dts/sequoia.dts
b/arch/powerpc/boot/dts/sequoia.dts
> index 27837ed..3316231 100644
> --- a/arch/powerpc/boot/dts/sequoia.dts
> +++ b/arch/powerpc/boot/dts/sequoia.dts
> @@ -294,6 +294,29 @@
>  				interrupts = &lt;7 4&gt;;
>  			};
>
> +			SPI0: spi at ef600900 {
> +				device_type = &quot;spi&quot;;
> +				compatible = &quot;ibm,spi-440epx&quot;, &quot;ibm,spi&quot;;
> +				reg = &lt;ef600900 7&gt;;
> +				interrupt-parent = &lt;&amp;UIC0&gt;;
> +				interrupts = &lt;8 4&gt;;
> +			};
> +
> +
> +			GPIO0: gpio at ef600b00 {
> +				compatible = &quot;ibm,gpio-440epx&quot;, &quot;ibm,gpio&quot;;
> +				#address-cells = &lt;0&gt;;
> +				#size-cells = &lt;0&gt;;
> +				reg = &lt;ef600b00 44&gt;;
> +			};
> +
> +			GPIO1: gpio at ef600c00 {
> +				compatible = &quot;ibm,gpio-440epx&quot;, &quot;ibm,gpio&quot;;
> +				#address-cells = &lt;0&gt;;
> +				#size-cells = &lt;0&gt;;
> +				reg = &lt;ef600c00 44&gt;;
> +			};
> +
>  			ZMII0: emac-zmii at ef600d00 {
>  				device_type = &quot;zmii-interface&quot;;
>  				compatible = &quot;ibm,zmii-440epx&quot;, &quot;ibm,zmii&quot;;
> diff --git a/arch/ppc/platforms/4xx/ppc440epx.c
b/arch/ppc/platforms/4xx/ppc440epx.c
> index 597bc79..a8e1566 100644
> --- a/arch/ppc/platforms/4xx/ppc440epx.c
> +++ b/arch/ppc/platforms/4xx/ppc440epx.c
> @@ -69,9 +69,12 @@ static struct ocp_func_iic_data ppc440ep
>  	.fast_mode	= 0,		/* Use standad mode (100Khz) */
>  };
>
> +/* either IIC1 or SPI! */
> +#if !defined(CONFIG_SPI_PPC4xx) &amp;&amp;
!defined(CONFIG_SPI_PPC4xx_MODULE)
>  static struct ocp_func_iic_data ppc440epx_iic1_def = {
>  	.fast_mode	= 0,		/* Use standad mode (100Khz) */
>  };
> +#endif
>  OCP_SYSFS_IIC_DATA()
>
>  struct ocp_def core_ocp[] = {
> @@ -119,6 +122,8 @@ struct ocp_def core_ocp[] = {
>  	  .additions	= &amp;ppc440epx_iic0_def,
>  	  .show		= &amp;ocp_show_iic_data
>  	},
> +/* either IIC1 or SPI! */
> +#if !defined(CONFIG_SPI_PPC4xx) &amp;&amp;
!defined(CONFIG_SPI_PPC4xx_MODULE)
>  	{ .vendor	= OCP_VENDOR_IBM,
>  	  .function	= OCP_FUNC_IIC,
>  	  .index	= 1,
> @@ -128,6 +133,7 @@ struct ocp_def core_ocp[] = {
>  	  .additions	= &amp;ppc440epx_iic1_def,
>  	  .show		= &amp;ocp_show_iic_data
>  	},
> +#endif
>  	{ .vendor	= OCP_VENDOR_IBM,
>  	  .function	= OCP_FUNC_GPIO,
>  	  .index	= 0,
> @@ -241,6 +247,19 @@ static struct resource ehci_usb_resource
>  	},
>  };
>
> +static struct resource spi_resources[] = {
> +	[0] = {
> +		.start	= 0x0EF600900,
> +		.end	= 0x0EF600906,
> +		.flags	= IORESOURCE_MEM,
> +	},
> +	[1] = {
> +		.start	= 8,
> +		.end	= 8,
> +		.flags	= IORESOURCE_IRQ,
> +	},
> +};
> +
>  static u64 dma_mask = 0xffffffffULL;
>
>  static struct platform_device ohci_usb_device = {
> @@ -276,10 +295,19 @@ static struct platform_device ehci_usb_d
>  	}
>  };
>
> +static struct platform_device ppc4xx_spi_device = {
> +	/* this must agree with driver.name  used in the SPI driver! */
> +	.name		= &quot;spi_ppc4xx&quot;,
> +	.id		= 0,
> +	.num_resources	= ARRAY_SIZE(spi_resources),
> +	.resource	= spi_resources,
> +};
> +
>  static struct platform_device *ppc440epx_devs[] __initdata = {
>  	&amp;ohci_usb_device,
>  	&amp;ehci_usb_device,
>  	&amp;usb_gadget_device,
> +	&amp;ppc4xx_spi_device,
>  };
>
>  static int __init ppc440epx_platform_add_devices(void)
> diff --git a/drivers/i2c/busses/i2c-ibm_of.c
b/drivers/i2c/busses/i2c-ibm_of.c
> index 5fc6594..f2ea0ef 100644
> --- a/drivers/i2c/busses/i2c-ibm_of.c
> +++ b/drivers/i2c/busses/i2c-ibm_of.c
> @@ -1,5 +1,5 @@
>  /*
> - * drivers/i2c/busses/i2c-ibm_iic.c
> + * drivers/i2c/busses/i2c-ibm_of.c
>   *
>   * Support for the IIC peripheral on IBM PPC 4xx
>   *
> @@ -703,6 +703,15 @@ static int __devinit iic_probe (struct o
>  	}
>  	dev-&gt;paddr = res.start;
>
> +#if (defined(CONFIG_SPI_PPC4xx) || defined(CONFIG_SPI_PPC4xx_MODULE))
> +	&amp;&amp; defined(CONFIG_440EPX)
> +	/*
> +	 * Hack, but on the 440EPx we can have either IIC1 or SPI.
> +	 */
> +	if (dev-&gt;paddr == 0x1ef600800ULL)
> +		goto fail1;
> +#endif
> +
>  	if (!request_mem_region(dev-&gt;paddr, sizeof(struct iic_regs),
>  				&quot;ibm_iic&quot;)) {
>  		ret = -EBUSY;
> diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
> index b2c8d53..088b3af 100644
> --- a/drivers/spi/Kconfig
> +++ b/drivers/spi/Kconfig
> @@ -160,6 +160,13 @@ config SPI_OMAP24XX
>  	  SPI master controller for OMAP24xx Multichannel SPI
>  	  (McSPI) modules.
>
> +config SPI_PPC4xx
> +	tristate &quot;PPC4xx SPI Controller&quot;
> +	depends on 440EPX &amp;&amp; SPI_MASTER
> +	select SPI_BITBANG
> +	help
> +	  This selects a driver for the 440EPx SPI Controller.
> +
>  config SPI_PXA2XX
>  	tristate &quot;PXA2xx SSP SPI master&quot;
>  	depends on SPI_MASTER &amp;&amp; ARCH_PXA &amp;&amp; EXPERIMENTAL
> diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
> index dd1b39a..dfd6720 100644
> --- a/drivers/spi/Makefile
> +++ b/drivers/spi/Makefile
> @@ -28,6 +28,7 @@ obj-$(CONFIG_SPI_S3C24XX_GPIO)		+= spi_s
>  obj-$(CONFIG_SPI_S3C24XX)		+= spi_s3c24xx.o
>  obj-$(CONFIG_SPI_TXX9)			+= spi_txx9.o
>  obj-$(CONFIG_SPI_XILINX)		+= xilinx_spi.o
> +obj-$(CONFIG_SPI_PPC4xx)		+= spi_ppc4xx.o
>  # 	... add above this line ...
>
>  # SPI protocol drivers (device/link on bus)
> diff --git a/drivers/spi/spi_ppc4xx.c b/drivers/spi/spi_ppc4xx.c
> new file mode 100644
> index 0000000..d5235c6
> --- /dev/null
> +++ b/drivers/spi/spi_ppc4xx.c
> @@ -0,0 +1,880 @@
> +/*
> + * SPI_PPC4XX SPI controller driver.
> + * Copyright (C) 2008 Gary Jenneohn &lt;garyj at denx.de&gt;
> + *
> + * Based in part on drivers/spi/spi_s3c24xx.c
> + *
> + * Copyright (c) 2006 Ben Dooks
> + * Copyright (c) 2006 Simtec Electronics
> + *	Ben Dooks &lt;ben at simtec.co.uk&gt;
> + *
> + * This program is free software; you can redistribute  it and/or modify
it
> + * under the terms of the GNU General Public License version 2 as
published
> + * by the Free Software Foundation.
> + */
> +
> +#undef DEBUG
> +
> +#include &lt;linux/module.h&gt;
> +#include &lt;linux/init.h&gt;
> +#include &lt;linux/sched.h&gt;
> +#include &lt;linux/errno.h&gt;
> +#include &lt;linux/wait.h&gt;
> +#ifdef CONFIG_PPC_OF
> +#include &lt;asm/of_platform.h&gt;
> +#else
> +#include &lt;asm/ocp.h&gt; /* deprecated */
> +#include &lt;linux/platform_device.h&gt;
> +#endif
> +#include &lt;linux/interrupt.h&gt;
> +#include &lt;linux/delay.h&gt;
> +
> +#include &lt;linux/spi/spi.h&gt;
> +#include &lt;linux/spi/spi_bitbang.h&gt;
> +
> +#include &lt;asm/io.h&gt;
> +#include &lt;asm/dcr-native.h&gt;
> +
> +/* Attention! These may be 440EPx specific. */
> +struct spi_ppc4xx_regs {
> +	u8	mode;
> +	u8	rxd;
> +	u8	txd;
> +	u8	cr;
> +	u8	sr;
> +	u8	dummy;
> +	/*
> +	 * Clock divisor modulus register
> +	 * This uses the follwing formula:
> +	 *    SCPClkOut = OPBCLK/(4(CDM + 1))
> +	 * or
> +	 *    CDM = (OPBCLK/4*SCPClkOut) - 1
> +	 */
> +	u8	cdm;
> +};
> +
> +/* bits in mode register - bit 0 ist MSb */
> +/* data latched on leading edge of clock, else trailing edge */
> +#define SPI_PPC4XX_MODE_SCP	(0x80 &gt;&gt; 3)
> +/* port enabled */
> +#define SPI_PPC4XX_MODE_SPE	(0x80 &gt;&gt; 4)
> +/* MSB first, else LSB first */
> +#define SPI_PPC4XX_MODE_RD	(0x80 &gt;&gt; 5)
> +/* clock invert - idle clock = 1, active clock = 0; else reversed */
> +#define SPI_PPC4XX_MODE_CI	(0x80 &gt;&gt; 6)
> +/* loopback enable */
> +#define SPI_PPC4XX_MODE_IL	(0x80 &gt;&gt; 7)
> +/* bits in control register */
> +/* starts a transfer when set */
> +#define SPI_PPC4XX_CR_STR	(0x80 &gt;&gt; 7)
> +/* bits in status register */
> +/* port is busy with a transfer */
> +#define SPI_PPC4XX_SR_BSY	(0x80 &gt;&gt; 6)
> +/* RxD ready */
> +#define SPI_PPC4XX_SR_RBR	(0x80 &gt;&gt; 7)
> +
> +/* the spi-&gt;mode bits understood by this driver */
> +#define MODEBITS (SPI_CPHA | SPI_CPOL | SPI_CS_HIGH | SPI_LSB_FIRST)
> +
> +/* clock settings (SCP and CI) for various SPI modes */
> +#define SPI_CLK_MODE0		SPI_PPC4XX_MODE_SCP
> +#define SPI_CLK_MODE1		0
> +#define SPI_CLK_MODE2		SPI_PPC4XX_MODE_CI
> +#define SPI_CLK_MODE3		(SPI_PPC4XX_MODE_SCP | SPI_PPC4XX_MODE_CI)
> +
> +#ifdef CONFIG_PPC_OF
> +#define DRIVER_NAME		&quot;spi_ppc4xx_of&quot;
> +static struct device_node *gpio0np;
> +#else
> +#define DRIVER_NAME		&quot;spi_ppc4xx&quot;
> +#ifndef GPIO0_BASE
> +#define GPIO0_BASE		0x1EF600B00ULL
> +#define GPIO1_BASE		0x1EF600C00ULL
> +#define GPIOx_SIZE		0x44
> +#endif
> +#endif
> +
> +#ifndef SDR0_CFGADDR
> +#define SDR0_CFGADDR		0x00E
> +#define SDR0_CFGDATA		0x00F
> +#endif
> +#define SDR0_PFC1		0x4101
> +
> +/*
> + * Offsets - these don't seem to be in any standard header,
> + * but these values may be true only for 440EPx.
> + */
> +#define GPIOx_TCR		0x04
> +#define GPIOx_OSRL		0x08
> +#define GPIOx_OSRH		0x0C
> +#define GPIOx_TSRL		0x10
> +#define GPIOx_TSRH		0x14
> +#define GPIOx_ODR		0x18
> +
> +/* may be true only for 440EPx */
> +/* number of pins per GPIO controller */
> +#define GPIO_NUMPINS		32
> +/* total number of pins in all GPIO controllers */
> +#define GPIO_TOTPINS		64
> +
> +struct ppc4xx_spi {
> +	/* bitbang has to be first */
> +	struct spi_bitbang	bitbang;
> +	struct completion	done;
> +
> +	void __iomem		*gpio0;
> +	void __iomem		*gpio1;
> +	u64			mapbase;
> +	u64			mapsize;
> +	int			irqnum;
> +	/* need this to set the SPI clock */
> +	unsigned int		opb_freq;
> +
> +	/* for transfers */
> +	int			len;
> +	int			count;
> +	/* data buffers */
> +	const unsigned char	*tx;
> +	unsigned char		*rx;
> +	/* pointer to the registers */
> +	struct spi_ppc4xx_regs __iomem *regs;
> +	struct spi_master	*master;
> +	struct device		*dev;
> +
> +	/* flags to record whether a GPIO pin has been set up as an output */
> +	u8			gpio0_flags[GPIO_NUMPINS];
> +	u8			gpio1_flags[GPIO_NUMPINS];
> +};
> +
> +/* need this so we can set the clock in the chipselect routine */
> +struct spi_ppc4xx_cs {
> +	/* speed in hertz */
> +	u32	speed_hz;
> +	/* bits per word - must be 8! */
> +	u8	bpw;
> +};
> +
> +static int spi_ppc4xx_txrx(struct spi_device *spi, struct spi_transfer
*t)
> +{
> +	struct ppc4xx_spi *hw;
> +	u8 data;
> +
> +	dev_dbg(&amp;spi-&gt;dev, &quot;txrx: tx %p, rx %p, len %dn&quot;,
> +		t-&gt;tx_buf, t-&gt;rx_buf, t-&gt;len);
> +
> +	hw = spi_master_get_devdata(spi-&gt;master);
> +
> +	hw-&gt;tx = t-&gt;tx_buf;
> +	hw-&gt;rx = t-&gt;rx_buf;
> +	hw-&gt;len = t-&gt;len;
> +	hw-&gt;count = 0;
> +
> +	/* send the first byte */
> +	data = hw-&gt;tx ? hw-&gt;tx[0] : 0;
> +	out_8(&amp;hw-&gt;regs-&gt;txd, data);
> +	out_8(&amp;hw-&gt;regs-&gt;cr, SPI_PPC4XX_CR_STR);
> +	wait_for_completion(&amp;hw-&gt;done);
> +
> +	return hw-&gt;count;
> +}
> +
> +static int spi_ppc4xx_setupxfer(struct spi_device *spi, struct
spi_transfer *t)
> +{
> +	struct ppc4xx_spi *hw;
> +	struct spi_ppc4xx_cs *cs = spi-&gt;controller_state;
> +
> +	hw = spi_master_get_devdata(spi-&gt;master);
> +
> +	cs-&gt;bpw = t ? t-&gt;bits_per_word : spi-&gt;bits_per_word;
> +	cs-&gt;speed_hz  = t ? t-&gt;speed_hz : spi-&gt;max_speed_hz;
> +
> +	if (cs-&gt;bpw != 8) {
> +		dev_err(&amp;spi-&gt;dev, &quot;invalid bits-per-word (%d)n&quot;,
cs-&gt;bpw);
> +		return -EINVAL;
> +	}
> +
> +	spin_lock(&amp;hw-&gt;bitbang.lock);
> +	if (!hw-&gt;bitbang.busy)
> +		hw-&gt;bitbang.chipselect(spi, BITBANG_CS_INACTIVE);
> +	spin_unlock(&amp;hw-&gt;bitbang.lock);
> +
> +	return 0;
> +}
> +
> +static int spi_ppc4xx_setup(struct spi_device *spi)
> +{
> +	int ret;
> +	struct spi_ppc4xx_cs *cs = spi-&gt;controller_state;
> +
> +	if (!spi-&gt;bits_per_word)
> +		spi-&gt;bits_per_word = 8;
> +
> +	if (spi-&gt;mode &amp; ~MODEBITS) {
> +		dev_dbg(&amp;spi-&gt;dev, &quot;setup: unsupported mode bits %xn&quot;,
> +			spi-&gt;mode &amp; ~MODEBITS);
> +		return -EINVAL;
> +	}
> +
> +	if (cs == NULL) {
> +		cs = kzalloc(sizeof(*cs), GFP_KERNEL);
> +		if (!cs)
> +			return -ENOMEM;
> +		spi-&gt;controller_state = cs;
> +	}
> +	cs-&gt;speed_hz = spi-&gt;max_speed_hz;
> +	cs-&gt;bpw = spi-&gt;bits_per_word;
> +
> +	ret = spi_ppc4xx_setupxfer(spi, NULL);
> +	if (ret &lt; 0) {
> +		dev_err(&amp;spi-&gt;dev, &quot;setupxfer returned %dn&quot;, ret);
> +		return ret;
> +	}
> +
> +	dev_dbg(&amp;spi-&gt;dev, &quot;%s: mode %d, %u bpw, %d hzn&quot;,
> +		__FUNCTION__, spi-&gt;mode, spi-&gt;bits_per_word,
> +		spi-&gt;max_speed_hz);
> +
> +	return 0;
> +}
> +
> +/*
> + * The next two routines are very 440EPx specific!
> + * They should really be in a platform-specific file or be handled by
> + * generic GPIO handlers. They can go away once gnereic GPIO support is
> + * available for 4xx.
> + * NOTE: chipsel must be zero-based.
> + */
> +
> +/*
> + * Check whether a GPIO pin (used as chip select) is set up as an output.
> + * If not, set it up and note it in the appropriate flags array.
> + *
> + * NOTE: this assumes that the specified pin is really not needed for one
> + * of the alternate configurations and just blasts it.
> + */
> +static void spi_ppc4xx_gpio_check(struct ppc4xx_spi *hw, u8 pin)
> +{
> +	u8 *flags;
> +	void __iomem *gpio = hw-&gt;gpio0;
> +	/*
> +	 * Most of this code lifted from u-boot.
> +	 * Thanks go to Stefan Roese.
> +	 */
> +	u32 mask;
> +	u32 mask2;
> +	u32 val;
> +	u32 offs = 0;
> +	u32 offs2 = 0;
> +	int pin2 = pin &lt;&lt; 1;
> +
> +	flags = hw-&gt;gpio0_flags;
> +	if (pin &gt;= GPIO_NUMPINS) {
> +		offs = 0x100;
> +		pin -= GPIO_NUMPINS;
> +		flags = hw-&gt;gpio1_flags;
> +	}
> +	/* already set up for output */
> +	if (flags[pin])
> +		return;
> +
> +	if (pin &gt;= GPIO_NUMPINS/2) {
> +		/* point at OSRH/TSRH */
> +		offs2 = 0x04;
> +		pin2 = (pin - GPIO_NUMPINS/2) &lt;&lt; 1;
> +	}
> +
> +	mask = 0x80000000 &gt;&gt; pin;
> +	mask2 = 0xc0000000 &gt;&gt; (pin2 &lt;&lt; 1);
> +
> +	/* first set TCR to 0 */
> +	val = in_be32(gpio + GPIOx_TCR + offs) &amp; ~mask;
> +	out_be32(gpio + GPIOx_TCR + offs, val);
> +
> +	/* always want to set up the pin for output */
> +	/* clear GPIOx_OSRL/H */
> +	val = in_be32(gpio + GPIOx_OSRL + offs + offs2) &amp; ~mask2;
> +	out_be32(gpio + GPIOx_OSRL + offs + offs2, val);
> +	/* clear GPIOx_TSRL/H */
> +	val = in_be32(gpio + GPIOx_TSRL + offs + offs2) &amp; ~mask2;
> +	out_be32(gpio + GPIOx_TSRL + offs + offs2, val);
> +	/* clear GPIOx_ODR */
> +	val = in_be32(gpio + GPIOx_ODR + offs) &amp; ~mask;
> +	out_be32(gpio + GPIOx_ODR + offs, val);
> +
> +	/* now configure TCR to drive output if selected */
> +	val = in_be32(gpio + GPIOx_TCR + offs) | mask;
> +	out_be32(gpio + GPIOx_TCR + offs, val);
> +
> +	flags[pin] = 1;
> +}
> +
> +/*
> + * Set the chip select as a GPIO.
> + */
> +static void spi_ppc4xx_gpio(struct ppc4xx_spi *hw, u8 chipsel, u8 pol)
> +{
> +	void __iomem *gpio = hw-&gt;gpio0;
> +	u32 val;
> +
> +	/* check whether the pin is set up for output */
> +	spi_ppc4xx_gpio_check(hw, chipsel);
> +
> +	if (chipsel &gt;= GPIO_NUMPINS) {
> +		gpio = hw-&gt;gpio1;
> +		chipsel -= GPIO_NUMPINS;
> +	}
> +
> +	val = in_be32(gpio);
> +	if (pol)
> +		val |= (0x80000000 &gt;&gt; chipsel);
> +	else
> +		val &amp;= ~(0x80000000 &gt;&gt; chipsel);
> +	out_be32(gpio, val);
> +}
> +
> +static void spi_ppc4xx_chipsel(struct spi_device *spi, int value)
> +{
> +	struct ppc4xx_spi *hw;
> +	struct spi_ppc4xx_cs *cs = spi-&gt;controller_state;
> +	unsigned int cspol = spi-&gt;mode &amp; SPI_CS_HIGH ? 1 : 0;
> +	unsigned char mode;
> +	unsigned char cdm, oldcdm;
> +	int scr;
> +
> +	hw = spi_master_get_devdata(spi-&gt;master);
> +
> +	/* disable the controller here? */
> +
> +	switch (value) {
> +	case BITBANG_CS_INACTIVE:
> +		spi_ppc4xx_gpio(hw, spi-&gt;chip_select, cspol^1);
> +		break;
> +
> +	case BITBANG_CS_ACTIVE:
> +		mode = in_8(&amp;hw-&gt;regs-&gt;mode);
> +
> +		/* clear the CLK bits */
> +		mode &amp;= ~SPI_CLK_MODE3;
> +		switch (spi-&gt;mode &amp; (SPI_CPHA|SPI_CPOL)) {
> +		case SPI_MODE_0:
> +			mode |= SPI_CLK_MODE0;
> +			break;
> +		case SPI_MODE_1:
> +			mode |= SPI_CLK_MODE1;
> +			break;
> +		case SPI_MODE_2:
> +			mode |= SPI_CLK_MODE2;
> +			break;
> +		case SPI_MODE_3:
> +			mode |= SPI_CLK_MODE3;
> +			break;
> +		}
> +
> +		if (spi-&gt;mode &amp; SPI_LSB_FIRST)
> +			mode |= SPI_PPC4XX_MODE_RD;
> +		else
> +			mode &amp;= ~SPI_PPC4XX_MODE_RD;
> +
> +		mode |= SPI_PPC4XX_MODE_SPE;
> +
> +		/* set the clock */
> +		cdm = 0;
> +		/* opb_freq was already divided by 4 */
> +		scr = (hw-&gt;opb_freq/cs-&gt;speed_hz) - 1;
> +
> +		if (scr &lt;= 0)
> +			goto cdm_done;
> +
> +		cdm = scr &amp; 0xff;
> +
> +cdm_done:
> +		dev_dbg(&amp;spi-&gt;dev, &quot;setting pre-scaler to %d (hz
%d)n&quot;, cdm,
> +			cs-&gt;speed_hz);
> +		oldcdm = in_8(&amp;hw-&gt;regs-&gt;cdm);
> +		if (oldcdm != cdm)
> +			out_8(&amp;hw-&gt;regs-&gt;cdm, cdm);
> +		/* write new configration */
> +		out_8(&amp;hw-&gt;regs-&gt;mode, mode);
> +
> +		spi_ppc4xx_gpio(hw, spi-&gt;chip_select, cspol);
> +
> +		break;
> +	}
> +}
> +
> +static irqreturn_t spi_ppc4xx_int(int irq, void *dev_id)
> +{
> +	struct ppc4xx_spi *hw;
> +	u8 status;
> +	u8 data;
> +	unsigned int count;
> +
> +	hw = dev_id;
> +
> +	if (irq != hw-&gt;irqnum) {
> +		printk(KERN_WARNING
> +		       &quot;spi_ppc4xx_int : &quot;
> +		       &quot;Received wrong int %d. Waiting for %dn&quot;, irq,
> +			hw-&gt;irqnum);
> +		return IRQ_NONE;
> +	}
> +
> +	status = in_8(&amp;hw-&gt;regs-&gt;sr);
> +
> +	/* should never happen but check anyway */
> +	if (status &amp; SPI_PPC4XX_SR_BSY) {
> +		dev_dbg(hw-&gt;dev, &quot;got interrupt but spi still busy?n&quot;);
> +		complete(&amp;hw-&gt;done);
> +		return IRQ_HANDLED;
> +	}
> +
> +	count = hw-&gt;count;
> +	hw-&gt;count++;
> +
> +	if (status &amp; SPI_PPC4XX_SR_RBR) {
> +		/* Data Ready */
> +		data = in_8(&amp;hw-&gt;regs-&gt;rxd);
> +		if (hw-&gt;rx)
> +			hw-&gt;rx[count] = data;
> +	}
> +
> +	count++;
> +
> +	if (count &lt; hw-&gt;len) {
> +		data = hw-&gt;tx ? hw-&gt;tx[count] : 0;
> +		out_8(&amp;hw-&gt;regs-&gt;txd, data);
> +		out_8(&amp;hw-&gt;regs-&gt;cr, SPI_PPC4XX_CR_STR);
> +	} else {
> +		complete(&amp;hw-&gt;done);
> +	}
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static void spi_ppc4xx_cleanup(struct spi_device *spi)
> +{
> +	kfree(spi-&gt;controller_state);
> +}
> +
> +static void spi_ppc4xx_enable(struct ppc4xx_spi *hw)
> +{
> +	unsigned long pfc1;
> +	uint osrh, tsrh;
> +
> +	mtdcr(SDR0_CFGADDR, SDR0_PFC1);
> +	pfc1 = mfdcr(SDR0_CFGDATA);
> +	/* need to clear bit 14 to enable SPC */
> +	pfc1 &amp;= ~(1 &lt;&lt; 17);
> +	mtdcr(SDR0_CFGADDR, SDR0_PFC1);
> +	mtdcr(SDR0_CFGDATA, pfc1);
> +
> +	/* enable SPCDO */
> +	osrh = in_be32((void *)((u32)hw-&gt;gpio0 + GPIOx_OSRH));
> +	tsrh = in_be32((void *)((u32)hw-&gt;gpio0 + GPIOx_TSRH));
> +	/* need to set bits 14:15 in OSRH to 0x01 */
> +	osrh &amp;= ~(0x03 &lt;&lt; 16);
> +	osrh |= (0x01 &lt;&lt; 16);
> +	out_be32((void *)((u32)hw-&gt;gpio0 + GPIOx_OSRH), osrh);
> +
> +	/* need to set bits 14:15 in TSRH to 0x01 */
> +	tsrh &amp;= ~(0x03 &lt;&lt; 16);
> +	tsrh |= (0x01 &lt;&lt; 16);
> +	out_be32((void *)((u32)hw-&gt;gpio0 + GPIOx_TSRH), tsrh);
> +}
> +
> +#ifdef CONFIG_PPC_OF
> +static int __init spi_ppc4xx_of_probe(struct of_device *op,
> +	const struct of_device_id *match)
> +{
> +	struct ppc4xx_spi *hw;
> +	struct spi_master *master;
> +	struct spi_bitbang *bbp;
> +	struct resource resource;
> +	struct device_node *np = op-&gt;node;
> +	struct device *dev = &amp;op-&gt;dev;
> +	struct device_node *gpionp;
> +	const u32 *regaddr;
> +	u64 addr64, size64;
> +	int ret;
> +	const unsigned int *clk;
> +
> +	master = spi_alloc_master(dev, sizeof(*hw));
> +	if (master == NULL)
> +		return -ENOMEM;
> +	dev_set_drvdata(dev, master);
> +	hw = spi_master_get_devdata(master);
> +	memset(hw, 0, sizeof(*hw));
> +
> +	hw-&gt;master = spi_master_get(master);
> +	hw-&gt;dev = dev;
> +
> +	init_completion(&amp;hw-&gt;done);
> +
> +	/* setup the state for the bitbang driver */
> +	bbp = &amp;hw-&gt;bitbang;
> +	bbp-&gt;master = hw-&gt;master;
> +	bbp-&gt;setup_transfer = spi_ppc4xx_setupxfer;
> +	bbp-&gt;chipselect = spi_ppc4xx_chipsel;
> +	bbp-&gt;txrx_bufs = spi_ppc4xx_txrx;
> +	bbp-&gt;use_dma = 0;
> +	bbp-&gt;master-&gt;setup = spi_ppc4xx_setup;
> +	bbp-&gt;master-&gt;cleanup = spi_ppc4xx_cleanup;
> +	/* only one SPI controller */
> +	bbp-&gt;master-&gt;bus_num = 0;
> +	if (bbp-&gt;master-&gt;num_chipselect == 0)
> +		bbp-&gt;master-&gt;num_chipselect = GPIO_TOTPINS;
> +
> +	dev_dbg(dev, &quot;bitbang at %pn&quot;, bbp);
> +
> +	/* get the clock for the OPB */
> +	gpionp = of_find_compatible_node(NULL, NULL, &quot;ibm,opb&quot;);
> +	if (gpionp == NULL) {
> +		dev_warn(dev, &quot;OPB: cannot find noden&quot;);
> +		ret = -ENODEV;
> +		goto free_master;
> +	}
> +	/* get the clock (Hz) for the OPB */
> +	clk = of_get_property(gpionp, &quot;clock-frequency&quot;, NULL);
> +	if (clk == NULL) {
> +		dev_warn(dev, &quot;OPB: no clock-frequency property setn&quot;);
> +		of_node_put(gpionp);
> +		ret = -ENODEV;
> +		goto free_master;
> +	}
> +	hw-&gt;opb_freq = *clk;
> +	hw-&gt;opb_freq &gt;&gt;= 2;
> +	of_node_put(gpionp);
> +
> +	ret = of_address_to_resource(np, 0, &amp;resource);
> +	if (ret) {
> +		printk(KERN_ERR &quot;Error while parsing device node resourcen&quot;);
> +		goto free_master;
> +	}
> +	hw-&gt;mapbase = resource.start;
> +	hw-&gt;mapsize = resource.end - resource.start + 1;
> +
> +	/* sanity check */
> +	if (hw-&gt;mapsize &lt; sizeof(struct spi_ppc4xx_regs)) {
> +		dev_warn(dev, &quot;too small to map registersn&quot;);
> +		/* XXXX */
> +		ret = -EINVAL;
> +		goto free_master;
> +	}
> +
> +	hw-&gt;irqnum = irq_of_parse_and_map(np, 0);
> +	ret = request_irq(hw-&gt;irqnum, spi_ppc4xx_int,
> +			  IRQF_DISABLED, &quot;spi_ppc4xx_of&quot;, (void *)hw);
> +	if (ret) {
> +		dev_warn(dev, &quot;unable to allocate interruptn&quot;);
> +		goto free_master;
> +	}
> +
> +	if (!request_mem_region(hw-&gt;mapbase, hw-&gt;mapsize, DRIVER_NAME)) {
> +		dev_warn(dev, &quot;resource unavailablen&quot;);
> +		ret = -EBUSY;
> +		goto request_mem_error;
> +	}
> +
> +	hw-&gt;regs = ioremap(hw-&gt;mapbase, sizeof(struct spi_ppc4xx_regs));
> +
> +	if (!hw-&gt;regs) {
> +		dev_warn(dev, &quot;unable to memory map registersn&quot;);
> +		ret = -ENXIO;
> +		goto map_io_error;
> +	}
> +
> +	/* since there's no GPIO driver we have to do it ourselves */
> +	/* find the entry for GPIO0 */
> +	gpionp = of_find_compatible_node(NULL, NULL, &quot;ibm,gpio&quot;);
> +	if (gpionp == NULL) {
> +		dev_warn(dev, &quot;unable to find node for GPIO0n&quot;);
> +		ret = -ENODEV;
> +		goto unmap_regs;
> +	}
> +	regaddr = of_get_address(gpionp, 0, &amp;size64, NULL);
> +	if (regaddr == NULL) {
> +		dev_warn(dev, &quot;unable to get address for GPIO0n&quot;);
> +		of_node_put(gpionp);
> +		ret = -ENODEV;
> +		goto unmap_regs;
> +	}
> +	addr64 = of_translate_address(gpionp, regaddr);
> +
> +	hw-&gt;gpio0 = ioremap(addr64, size64);
> +	if (!hw-&gt;gpio0) {
> +		dev_warn(dev, &quot;unable to memory map gpio0n&quot;);
> +		of_node_put(gpionp);
> +		ret = -ENOMEM;
> +		goto unmap_regs;
> +	}
> +	gpio0np = gpionp;
> +
> +	/* find the entry for GPIO1 */
> +	/* note that this will of_node_put gpio0np! */
> +	gpionp = of_find_compatible_node(gpio0np, NULL, &quot;ibm,gpio&quot;);
> +	if (gpionp == NULL) {
> +		dev_warn(dev, &quot;unable to find node for GPIO1n&quot;);
> +		ret = -ENODEV;
> +		goto unmap_gpio0;
> +	}
> +	regaddr = of_get_address(gpionp, 0, &amp;size64, NULL);
> +	if (regaddr == NULL) {
> +		dev_warn(dev, &quot;unable to get address for GPIO1n&quot;);
> +		of_node_put(gpionp);
> +		ret = -ENODEV;
> +		goto unmap_gpio0;
> +	}
> +	addr64 = of_translate_address(gpionp, regaddr);
> +
> +	/* possibly needed for CS */
> +	hw-&gt;gpio1 = ioremap(addr64, size64);
> +	if (!hw-&gt;gpio1) {
> +		dev_warn(dev, &quot;unable to memory map gpio1n&quot;);
> +		of_node_put(gpionp);
> +		ret = -ENOMEM;
> +		goto unmap_gpio0;
> +	}
> +	of_node_put(gpionp);
> +
> +	spi_ppc4xx_enable(hw);
> +
> +	/* register our spi controller */
> +	ret = spi_bitbang_start(bbp);
> +	if (ret) {
> +		dev_err(dev, &quot;Failed to register SPI mastern&quot;);
> +		goto unmap_gpio0;
> +	}
> +
> +	printk(KERN_INFO &quot;SPI: SPI_PPC4XX OF SPI drivern&quot;);
> +
> +	return 0;
> +
> +unmap_gpio0:
> +	iounmap(hw-&gt;gpio0);
> +unmap_regs:
> +	iounmap(hw-&gt;regs);
> +map_io_error:
> +	release_mem_region(hw-&gt;mapbase, hw-&gt;mapsize);
> +request_mem_error:
> +	free_irq(hw-&gt;irqnum, hw);
> +free_master:
> +	dev_set_drvdata(dev, NULL);
> +	spi_master_put(master);
> +
> +	dev_err(dev, &quot;initialization failedn&quot;);
> +	return ret;
> +}
> +
> +static int __exit spi_ppc4xx_of_remove(struct of_device *op)
> +{
> +	struct spi_master *master = dev_get_drvdata(&amp;op-&gt;dev);
> +	struct ppc4xx_spi *hw;
> +
> +	hw = spi_master_get_devdata(master);
> +	spi_bitbang_stop(&amp;hw-&gt;bitbang);
> +	spi_unregister_master(hw-&gt;master);
> +	dev_set_drvdata(&amp;op-&gt;dev, NULL);
> +	release_mem_region(hw-&gt;mapbase, hw-&gt;mapsize);
> +	free_irq(hw-&gt;irqnum, hw);
> +	iounmap(hw-&gt;regs);
> +	iounmap(hw-&gt;gpio0);
> +	iounmap(hw-&gt;gpio1);
> +
> +	return 0;
> +}
> +
> +static struct of_device_id spi_ppc4xx_of_match[] = {
> +	{ .type = &quot;spi&quot;, .compatible = &quot;ibm,spi&quot;, },
> +	{},
> +};
> +
> +MODULE_DEVICE_TABLE(of, spi_ppc4xx_of_match);
> +
> +static struct of_platform_driver spi_ppc4xx_of_driver = {
> +	.owner = THIS_MODULE,
> +	.name = DRIVER_NAME,
> +	.match_table = spi_ppc4xx_of_match,
> +	.probe = spi_ppc4xx_of_probe,
> +	.remove = __exit_p(spi_ppc4xx_of_remove),
> +	.driver = {
> +		.name = DRIVER_NAME,
> +		.owner = THIS_MODULE,
> +	},
> +};
> +
> +static int __init spi_ppc4xx_init(void)
> +{
> +	return of_register_platform_driver(&amp;spi_ppc4xx_of_driver);
> +}
> +
> +static void __exit spi_ppc4xx_exit(void)
> +{
> +	of_unregister_platform_driver(&amp;spi_ppc4xx_of_driver);
> +}
> +
> +#else /* not OF-type probe */
> +
> +static int __devinit spi_ppc4xx_probe(struct platform_device *pdev)
> +{
> +	struct ppc4xx_spi *hw;
> +	struct spi_master *master;
> +	struct spi_bitbang *bbp;
> +	struct device *dev = &amp;pdev-&gt;dev;
> +	struct resource *mem_io, *irqres;
> +	int ret = -ENODEV;
> +
> +	master = spi_alloc_master(dev, sizeof(*hw));
> +	if (master == NULL)
> +		return -ENOMEM;
> +
> +	dev_set_drvdata(dev, master);
> +	hw = spi_master_get_devdata(master);
> +	memset(hw, 0, sizeof(*hw));
> +
> +	hw-&gt;master = spi_master_get(master);
> +	hw-&gt;dev = dev;
> +
> +	platform_set_drvdata(pdev, hw);
> +	init_completion(&amp;hw-&gt;done);
> +
> +	/* setup the state for the bitbang driver */
> +	bbp = &amp;hw-&gt;bitbang;
> +	bbp-&gt;master = hw-&gt;master;
> +	bbp-&gt;setup_transfer = spi_ppc4xx_setupxfer;
> +	bbp-&gt;chipselect = spi_ppc4xx_chipsel;
> +	bbp-&gt;txrx_bufs = spi_ppc4xx_txrx;
> +	bbp-&gt;use_dma = 0;
> +	bbp-&gt;master-&gt;setup = spi_ppc4xx_setup;
> +	bbp-&gt;master-&gt;cleanup = spi_ppc4xx_cleanup;
> +	/* only one SPI controller */
> +	bbp-&gt;master-&gt;bus_num = 0;
> +	if (bbp-&gt;master-&gt;num_chipselect == 0)
> +		bbp-&gt;master-&gt;num_chipselect = GPIO_TOTPINS;
> +
> +	dev_dbg(dev, &quot;bitbang at %pn&quot;, bbp);
> +
> +	/* get the clock (Hz) for the OPB. Set in sequoia_setup_arch() */
> +	hw-&gt;opb_freq = ocp_sys_info.opb_bus_freq &gt;&gt; 2;
> +
> +	/* needed to set SCPD0 */
> +	hw-&gt;gpio0 = ioremap(GPIO0_BASE, GPIOx_SIZE);
> +	if (hw-&gt;gpio0 == NULL) {
> +		printk(KERN_ERR DRIVER_NAME &quot; unable to ioremap GPIO0n&quot;);
> +		ret = -ENOMEM;
> +		goto free_master;
> +	}
> +	/* possibly needed for CS */
> +	hw-&gt;gpio1 = ioremap(GPIO1_BASE, GPIOx_SIZE);
> +	if (hw-&gt;gpio1 == NULL) {
> +		printk(KERN_ERR DRIVER_NAME &quot; unable to ioremap GPIO1n&quot;);
> +		iounmap(hw-&gt;gpio0);
> +		ret = -ENOMEM;
> +		goto free_master;
> +	}
> +
> +	mem_io = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	irqres = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> +
> +	if ((!mem_io) | (!irqres))
> +		goto out_err;
> +	/* save value for release_mem_region use */
> +	hw-&gt;mapbase = mem_io-&gt;start;
> +	hw-&gt;mapsize = mem_io-&gt;end - mem_io-&gt;start + 1;
> +	/* sanity check */
> +	if (hw-&gt;mapsize &lt; sizeof(struct spi_ppc4xx_regs)) {
> +		dev_warn(dev, &quot;too small to map registersn&quot;);
> +		/* XXXX */
> +		ret = -EINVAL;
> +		goto out_err;
> +	}
> +
> +	hw-&gt;irqnum = irqres-&gt;start;
> +	ret = request_irq(hw-&gt;irqnum, spi_ppc4xx_int,
> +			  IRQF_DISABLED, &quot;spi_ppc4xx&quot;, (void *)hw);
> +	if (ret)
> +		goto out_err;
> +
> +	if (!request_mem_region(hw-&gt;mapbase, hw-&gt;mapsize, DRIVER_NAME)) {
> +		printk(KERN_ERR DRIVER_NAME &quot; - resource unavailablen&quot;);
> +		ret = -EBUSY;
> +		goto request_mem_error;
> +	}
> +
> +	hw-&gt;regs = ioremap(hw-&gt;mapbase, sizeof(struct spi_ppc4xx_regs));
> +
> +	if (!hw-&gt;regs) {
> +		printk(KERN_ERR DRIVER_NAME &quot; - failed to map spi regsn&quot;);
> +		ret = -ENXIO;
> +		goto map_io_error;
> +	}
> +
> +	spi_ppc4xx_enable(hw);
> +
> +	/* register our spi controller */
> +	ret = spi_bitbang_start(bbp);
> +	if (ret) {
> +		dev_err(dev, &quot;Failed to register SPI mastern&quot;);
> +		goto map_io_error;
> +	}
> +
> +	return 0;
> +
> +map_io_error:
> +	release_mem_region(hw-&gt;mapbase, hw-&gt;mapsize);
> +request_mem_error:
> +	free_irq(hw-&gt;irqnum, hw);
> +out_err:
> +	iounmap(hw-&gt;gpio0);
> +	iounmap(hw-&gt;gpio1);
> +free_master:
> +	platform_set_drvdata(pdev, NULL);
> +	dev_set_drvdata(dev, NULL);
> +	spi_master_put(master);
> +
> +	printk(KERN_ERR &quot;SPI: SPI_PPC4XX SPI init FAILED !!!n&quot;);
> +	return ret;
> +}
> +
> +static int __devexit spi_ppc4xx_remove(struct platform_device *dev)
> +{
> +	struct ppc4xx_spi *hw;
> +
> +	hw = platform_get_drvdata(dev);
> +	spi_bitbang_stop(&amp;hw-&gt;bitbang);
> +	spi_unregister_master(hw-&gt;master);
> +	dev_set_drvdata(&amp;dev-&gt;dev, NULL);
> +	platform_set_drvdata(dev, NULL);
> +	release_mem_region(hw-&gt;mapbase, hw-&gt;mapsize);
> +	free_irq(hw-&gt;irqnum, hw);
> +	iounmap(hw-&gt;regs);
> +	iounmap(hw-&gt;gpio0);
> +	iounmap(hw-&gt;gpio1);
> +
> +	return 0;
> +}
> +
> +static struct platform_driver spi_ppc4xx_platform_driver = {
> +	.probe = spi_ppc4xx_probe,
> +	.remove = __exit_p(spi_ppc4xx_remove),
> +	.driver = {
> +		   .name = &quot;spi_ppc4xx&quot;,
> +		   .owner = THIS_MODULE,
> +	},
> +};
> +
> +static int __init spi_ppc4xx_init(void)
> +{
> +	printk(KERN_INFO &quot;SPI: SPI_PPC4XX SPI drivern&quot;);
> +	return platform_driver_register(&amp;spi_ppc4xx_platform_driver);
> +}
> +
> +static void __exit spi_ppc4xx_exit(void)
> +{
> +	platform_driver_unregister(&amp;spi_ppc4xx_platform_driver);
> +}
> +#endif /* CONFIG_PPC_OF */
> +
> +module_init(spi_ppc4xx_init);
> +module_exit(spi_ppc4xx_exit);
> +
> +MODULE_LICENSE(&quot;GPL&quot;);
> --
> Gary Jennejohn
> *********************************************************************
> DENX Software Engineering GmbH,     MD: Wolfgang Denk &amp; Detlev Zundel
> HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany
> Phone: +49-8142-66989-0 Fax: +49-8142-66989-80  Email: office at denx.de
> *********************************************************************
> _______________________________________________
> Kernel-mentors mailing list
> Kernel-mentors at selenic.com
> http://selenic.com/mailman/listinfo/kernel-mentors
>
>
>
>
>
>

________________________________________________
Message sent using UebiMiau 2.7.2



More information about the Kernel-mentors mailing list