RFC: SPI patch

Rodrigo Rubira Branco (BSDaemon) rodrigo at kernelhacking.com
Tue Jan 15 15:59:30 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