[PATCH v4] spi: Add PPC4xx SPI driver

Josh Boyer jwboyer at linux.vnet.ibm.com
Wed Nov 5 00:10:01 EST 2008


On Fri, Oct 31, 2008 at 12:07:46PM +0100, Stefan Roese wrote:
>This adds a SPI driver for the SPI controller found in the IBM/AMCC
>4xx PowerPC's.
>
>Signed-off-by: Stefan Roese <sr at denx.de>
>Signed-off-by: Wolfgang Ocker <weo at reccoware.de>

Acked-by: Josh Boyer <jwboyer at linux.vnet.ibm.com>

>---
>Changes in v4:
>- Added fixes suggested by Josh Boyer
>- Changed compatible property from "ibm,spi" to "ibm,ppc4xx-spi"
>
>Changes in v3:
>- When the device is removed the GPIOs are released. The memory
>  for the GPIO array is freed.
>
>Changes in v2:
>- Now the gpios property is correctly decoded and the
>  resulting gpio numbers are used as the devices chip
>  selects.
>
>  So we can describe the SPI node like this:
>
>        SPI0: spi at ef600900 {
>                #address-cells = <1>;
>                #size-cells = <0>;
>                ...
>
>                /* two GPIOs, representing two chip selects: 0 and 1 */
>                gpios = <&GPIO0 5 0 &GPIO1 16 0>;
>
>                mmc-slot at 0 {
>                        reg = <0>;
>                        ...
>                };
>
>                touchscreen at 1 {
>                        reg = <1>;
>                        ...
>                };
>        }
>
>Special thanks to Wolfgang Ocker and Anton Vorontsov for their input.
>
>
> drivers/spi/Kconfig      |    7 +
> drivers/spi/Makefile     |    1 +
> drivers/spi/spi_ppc4xx.c |  594 ++++++++++++++++++++++++++++++++++++++++++++++
> 3 files changed, 602 insertions(+), 0 deletions(-)
> create mode 100644 drivers/spi/spi_ppc4xx.c
>
>diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
>index b9d0efb..69d5fee 100644
>--- a/drivers/spi/Kconfig
>+++ b/drivers/spi/Kconfig
>@@ -155,6 +155,13 @@ config SPI_ORION
> 	help
> 	  This enables using the SPI master controller on the Orion chips.
> 
>+config SPI_PPC4xx
>+	tristate "PPC4xx SPI Controller"
>+	depends on 4xx && SPI_MASTER
>+	select SPI_BITBANG
>+	help
>+	  This selects a driver for the PPC4xx SPI Controller.
>+
> config SPI_PXA2XX
> 	tristate "PXA2xx SSP SPI master"
> 	depends on ARCH_PXA && EXPERIMENTAL
>diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
>index ccf18de..a2e5816 100644
>--- a/drivers/spi/Makefile
>+++ b/drivers/spi/Makefile
>@@ -24,6 +24,7 @@ obj-$(CONFIG_SPI_OMAP24XX)		+= omap2_mcspi.o
> obj-$(CONFIG_SPI_ORION)			+= orion_spi.o
> obj-$(CONFIG_SPI_MPC52xx_PSC)		+= mpc52xx_psc_spi.o
> obj-$(CONFIG_SPI_MPC83xx)		+= spi_mpc83xx.o
>+obj-$(CONFIG_SPI_PPC4xx)		+= spi_ppc4xx.o
> obj-$(CONFIG_SPI_S3C24XX_GPIO)		+= spi_s3c24xx_gpio.o
> obj-$(CONFIG_SPI_S3C24XX)		+= spi_s3c24xx.o
> obj-$(CONFIG_SPI_TXX9)			+= spi_txx9.o
>diff --git a/drivers/spi/spi_ppc4xx.c b/drivers/spi/spi_ppc4xx.c
>new file mode 100644
>index 0000000..cfdb57d
>--- /dev/null
>+++ b/drivers/spi/spi_ppc4xx.c
>@@ -0,0 +1,594 @@
>+/*
>+ * SPI_PPC4XX SPI controller driver.
>+ *
>+ * Copyright (C) 2007 Gary Jennejohn <garyj at denx.de>
>+ * Copyright 2008 Stefan Roese <sr at denx.de>, DENX Software Engineering
>+ *
>+ * Based in part on drivers/spi/spi_s3c24xx.c
>+ *
>+ * Copyright (c) 2006 Ben Dooks
>+ * Copyright (c) 2006 Simtec Electronics
>+ *	Ben Dooks <ben at simtec.co.uk>
>+ *
>+ * 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.
>+ */
>+
>+#include <linux/module.h>
>+#include <linux/init.h>
>+#include <linux/sched.h>
>+#include <linux/errno.h>
>+#include <linux/wait.h>
>+#include <linux/of_platform.h>
>+#include <linux/of_spi.h>
>+#include <linux/of_gpio.h>
>+#include <linux/interrupt.h>
>+#include <linux/delay.h>
>+
>+#include <linux/gpio.h>
>+#include <linux/spi/spi.h>
>+#include <linux/spi/spi_bitbang.h>
>+
>+#include <asm/io.h>
>+#include <asm/dcr.h>
>+#include <asm/dcr-regs.h>
>+
>+/* bits in mode register - bit 0 ist MSb */
>+/* data latched on leading edge of clock, else trailing edge */
>+#define SPI_PPC4XX_MODE_SCP	(0x80 >> 3)
>+/* port enabled */
>+#define SPI_PPC4XX_MODE_SPE	(0x80 >> 4)
>+/* MSB first, else LSB first */
>+#define SPI_PPC4XX_MODE_RD	(0x80 >> 5)
>+/* clock invert - idle clock = 1, active clock = 0; else reversed */
>+#define SPI_PPC4XX_MODE_CI	(0x80 >> 6)
>+/* loopback enable */
>+#define SPI_PPC4XX_MODE_IL	(0x80 >> 7)
>+/* bits in control register */
>+/* starts a transfer when set */
>+#define SPI_PPC4XX_CR_STR	(0x80 >> 7)
>+/* bits in status register */
>+/* port is busy with a transfer */
>+#define SPI_PPC4XX_SR_BSY	(0x80 >> 6)
>+/* RxD ready */
>+#define SPI_PPC4XX_SR_RBR	(0x80 >> 7)
>+
>+/* the spi->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)
>+
>+#define DRIVER_NAME	"spi_ppc4xx_of"
>+
>+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
>+	 * bit 0 is the MSb!
>+	 */
>+	u8 cdm;
>+};
>+
>+/* SPI Controller driver's private data. */
>+struct ppc4xx_spi {
>+	/* bitbang has to be first */
>+	struct spi_bitbang bitbang;
>+	struct completion done;
>+
>+	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;
>+
>+	int *gpios;
>+	unsigned int num_gpios;
>+
>+	struct spi_ppc4xx_regs __iomem *regs; /* pointer to the registers */
>+	struct spi_master *master;
>+	struct device *dev;
>+};
>+
>+/* 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(&spi->dev, "txrx: tx %p, rx %p, len %d\n",
>+		t->tx_buf, t->rx_buf, t->len);
>+
>+	hw = spi_master_get_devdata(spi->master);
>+
>+	hw->tx = t->tx_buf;
>+	hw->rx = t->rx_buf;
>+	hw->len = t->len;
>+	hw->count = 0;
>+
>+	/* send the first byte */
>+	data = hw->tx ? hw->tx[0] : 0;
>+	out_8(&hw->regs->txd, data);
>+	out_8(&hw->regs->cr, SPI_PPC4XX_CR_STR);
>+	wait_for_completion(&hw->done);
>+
>+	return hw->count;
>+}
>+
>+static int spi_ppc4xx_setupxfer(struct spi_device *spi, struct spi_transfer *t)
>+{
>+	struct ppc4xx_spi *hw = spi_master_get_devdata(spi->master);
>+	struct spi_ppc4xx_cs *cs = spi->controller_state;
>+	unsigned char cdm = 0;
>+	int scr;
>+
>+	if (cs == NULL) {
>+		cs = kzalloc(sizeof(*cs), GFP_KERNEL);
>+		if (!cs)
>+			return -ENOMEM;
>+		spi->controller_state = cs;
>+	}
>+
>+	/*
>+	 * Allow platform reduce the interrupt load on the CPU during SPI
>+	 * transfers. We do not target maximum performance, but rather allow
>+	 * platform to limit SPI bus frequency and interrupt rate.
>+	 */
>+	cs->bpw = t ? t->bits_per_word : spi->bits_per_word;
>+	cs->speed_hz = t ? min(t->speed_hz, spi->max_speed_hz) :
>+		spi->max_speed_hz;
>+
>+	if (cs->bpw != 8) {
>+		dev_err(&spi->dev, "invalid bits-per-word (%d)\n", cs->bpw);
>+		return -EINVAL;
>+	}
>+
>+	if (cs->speed_hz == 0) {
>+		dev_err(&spi->dev, "invalid speed_hz (must be non-zero)\n");
>+		return -EINVAL;
>+	}
>+
>+	/* set the clock */
>+	/* opb_freq was already divided by 4 */
>+	scr = (hw->opb_freq / cs->speed_hz) - 1;
>+
>+	if (scr > 0)
>+		cdm = min(scr, 0xff);
>+
>+	dev_dbg(&spi->dev, "setting pre-scaler to %d (hz %d)\n", cdm,
>+		cs->speed_hz);
>+
>+	if (in_8(&hw->regs->cdm) != cdm)
>+		out_8(&hw->regs->cdm, cdm);
>+
>+	spin_lock(&hw->bitbang.lock);
>+	if (!hw->bitbang.busy) {
>+		hw->bitbang.chipselect(spi, BITBANG_CS_INACTIVE);
>+		/* need to ndelay here? */
>+	}
>+	spin_unlock(&hw->bitbang.lock);
>+
>+	return 0;
>+}
>+
>+static int spi_ppc4xx_setup(struct spi_device *spi)
>+{
>+	struct ppc4xx_spi *hw = spi_master_get_devdata(spi->master);
>+	int ret;
>+	struct spi_ppc4xx_cs *cs = spi->controller_state;
>+	unsigned char mode;
>+
>+	if (!spi->bits_per_word)
>+		spi->bits_per_word = 8;
>+
>+	if (spi->mode & ~MODEBITS) {
>+		dev_dbg(&spi->dev, "setup: unsupported mode bits %x\n",
>+			spi->mode & ~MODEBITS);
>+		return -EINVAL;
>+	}
>+
>+	if (cs == NULL) {
>+		cs = kzalloc(sizeof *cs, GFP_KERNEL);
>+		if (!cs)
>+			return -ENOMEM;
>+		spi->controller_state = cs;
>+	}
>+
>+	ret = spi_ppc4xx_setupxfer(spi, NULL);
>+	if (ret < 0) {
>+		dev_err(&spi->dev, "setupxfer returned %d\n", ret);
>+		return ret;
>+	}
>+
>+	/*
>+	 * We set all bits of the SPI0_MODE register, so,
>+	 * no need to read-modify-write
>+	 */
>+	mode = SPI_PPC4XX_MODE_SPE;
>+
>+	switch (spi->mode & (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->mode & SPI_LSB_FIRST) {
>+		/* this assumes that bit 7 is the LSb! */
>+		mode |= SPI_PPC4XX_MODE_RD;
>+	}
>+
>+	/* write new configration */
>+	out_8(&hw->regs->mode, mode);
>+
>+	dev_dbg(&spi->dev, "%s: mode %d, %u bpw, %d hz\n",
>+		__FUNCTION__, spi->mode, spi->bits_per_word,
>+		spi->max_speed_hz);
>+
>+	return 0;
>+}
>+
>+static void spi_ppc4xx_chipsel(struct spi_device *spi, int value)
>+{
>+	struct ppc4xx_spi *hw = spi_master_get_devdata(spi->master);
>+	unsigned int cspol = spi->mode & SPI_CS_HIGH ? 1 : 0;
>+	unsigned int cs = spi->chip_select;
>+
>+	if (!hw->num_gpios)
>+		return;
>+
>+	if (cs >= hw->num_gpios)
>+		return;
>+
>+	if (value != BITBANG_CS_INACTIVE && value != BITBANG_CS_ACTIVE)
>+		return;
>+
>+	if (value == BITBANG_CS_INACTIVE)
>+		cspol = !cspol;
>+
>+	gpio_set_value(hw->gpios[cs], cspol);
>+}
>+
>+static irqreturn_t spi_ppc4xx_int(int irq, void *dev_id)
>+{
>+	struct ppc4xx_spi *hw;
>+	u8 status;
>+	u8 data;
>+	unsigned int count;
>+
>+	hw = (struct ppc4xx_spi *)dev_id;
>+
>+	status = in_8(&hw->regs->sr);
>+	if (!status)
>+		return IRQ_NONE;
>+
>+	/* should never happen but check anyway */
>+	if (status & SPI_PPC4XX_SR_BSY) {
>+		u8 lstatus;
>+		int cnt = 0;
>+
>+		dev_dbg(hw->dev, "got interrupt but spi still busy?\n");
>+		do {
>+			ndelay(10);
>+			lstatus = in_8(&hw->regs->sr);
>+		} while (++cnt < 100 && lstatus & SPI_PPC4XX_SR_BSY);
>+
>+		if (cnt >= 100) {
>+			dev_err(hw->dev, "busywait: too many loops!\n");
>+			complete(&hw->done);
>+			return IRQ_HANDLED;
>+		} else {
>+			/* status is always 1 (RBR) here */
>+			status = in_8(&hw->regs->sr);
>+			dev_dbg(hw->dev, "loops %d status %x\n", cnt, status);
>+		}
>+	}
>+
>+	count = hw->count;
>+	hw->count++;
>+
>+	if (status & SPI_PPC4XX_SR_RBR) {
>+		/* Data Ready */
>+		data = in_8(&hw->regs->rxd);
>+		if (hw->rx)
>+			hw->rx[count] = data;
>+	}
>+
>+	count++;
>+
>+	if (count < hw->len) {
>+		data = hw->tx ? hw->tx[count] : 0;
>+		out_8(&hw->regs->txd, data);
>+		out_8(&hw->regs->cr, SPI_PPC4XX_CR_STR);
>+	} else {
>+		complete(&hw->done);
>+	}
>+
>+	return IRQ_HANDLED;
>+}
>+
>+static void spi_ppc4xx_cleanup(struct spi_device *spi)
>+{
>+	kfree(spi->controller_state);
>+}
>+
>+static void spi_ppc4xx_enable(struct ppc4xx_spi *hw)
>+{
>+	/*
>+	 * On all 4xx PPC's the SPI bus is shared/multiplexed with
>+	 * the 2nd I2C bus. We need to enable the the SPI bus before
>+	 * using it.
>+	 */
>+
>+	/* need to clear bit 14 to enable SPC */
>+	dcri_clrset(SDR0, SDR0_PFC1, 0x80000000 >> 14, 0);
>+}
>+
>+static unsigned int of_num_gpios(struct device_node *node)
>+{
>+	unsigned int num = 0;
>+
>+	while (gpio_is_valid(of_get_gpio(node, num)))
>+		num++;
>+	return num;
>+}
>+
>+static void free_gpios(struct ppc4xx_spi *hw)
>+{
>+	if (hw->num_gpios) {
>+		int i;
>+		for (i = 0; i < hw->num_gpios; i++)
>+			gpio_free(hw->gpios[i]);
>+
>+		kfree(hw->gpios);
>+		hw->gpios = NULL;
>+	}
>+}
>+
>+/*
>+ * of_device layer stuff...
>+ */
>+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->node;
>+	struct device *dev = &op->dev;
>+	struct device_node *opbnp;
>+	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(struct ppc4xx_spi));
>+
>+	hw->master = spi_master_get(master);
>+	hw->dev = dev;
>+
>+	init_completion(&hw->done);
>+
>+	hw->num_gpios = of_num_gpios(np);
>+	if (hw->num_gpios) {
>+		int i;
>+
>+		hw->gpios = kzalloc(sizeof(int) * hw->num_gpios,
>+				    GFP_KERNEL);
>+		if (!hw->gpios) {
>+			ret = -ENOMEM;
>+			goto free_master;
>+		}
>+
>+		for (i = 0; i < hw->num_gpios; i++) {
>+			int gpio = of_get_gpio(np, i);
>+			if (gpio < 0) {
>+				dev_err(dev, "Invalid gpio spec %d\n", i);
>+				ret = gpio;
>+				goto free_gpios;
>+			}
>+
>+			ret = gpio_request(gpio, np->name);
>+			if (ret < 0) {
>+				dev_err(dev, "gpio %d already in use\n", i);
>+				ret = gpio;
>+				goto free_gpios;
>+			}
>+
>+			gpio_direction_output(gpio, 0);
>+			hw->gpios[i] = gpio;
>+		}
>+	}
>+
>+	/* Setup the state for the bitbang driver */
>+	bbp = &hw->bitbang;
>+	bbp->master = hw->master;
>+	bbp->setup_transfer = spi_ppc4xx_setupxfer;
>+	bbp->chipselect = spi_ppc4xx_chipsel;
>+	bbp->txrx_bufs = spi_ppc4xx_txrx;
>+	bbp->use_dma = 0;
>+	bbp->master->setup = spi_ppc4xx_setup;
>+	bbp->master->cleanup = spi_ppc4xx_cleanup;
>+	/* only one SPI controller */
>+	bbp->master->bus_num = 0;
>+	if (bbp->master->num_chipselect == 0) {
>+		/* this many pins in al GPIO controllers */
>+		bbp->master->num_chipselect = hw->num_gpios;
>+	}
>+
>+	/* Get the clock for the OPB */
>+	opbnp = of_find_compatible_node(NULL, NULL, "ibm,opb");
>+	if (opbnp == NULL) {
>+		dev_err(dev, "OPB: cannot find node\n");
>+		ret = -ENODEV;
>+		goto free_gpios;
>+	}
>+	/* Get the clock (Hz) for the OPB */
>+	clk = of_get_property(opbnp, "clock-frequency", NULL);
>+	if (clk == NULL) {
>+		dev_err(dev, "OPB: no clock-frequency property set\n");
>+		of_node_put(opbnp);
>+		ret = -ENODEV;
>+		goto free_gpios;
>+	}
>+	hw->opb_freq = *clk;
>+	hw->opb_freq >>= 2;
>+	of_node_put(opbnp);
>+
>+	ret = of_address_to_resource(np, 0, &resource);
>+	if (ret) {
>+		dev_err(dev, "error while parsing device node resource\n");
>+		goto free_gpios;
>+	}
>+	hw->mapbase = resource.start;
>+	hw->mapsize = resource.end - resource.start + 1;
>+
>+	/* Sanity check */
>+	if (hw->mapsize < sizeof(struct spi_ppc4xx_regs)) {
>+		dev_err(dev, "too small to map registers\n");
>+		ret = -EINVAL;
>+		goto free_gpios;
>+	}
>+
>+	/* Request IRQ */
>+	hw->irqnum = irq_of_parse_and_map(np, 0);
>+	ret = request_irq(hw->irqnum, spi_ppc4xx_int,
>+			  IRQF_DISABLED, "spi_ppc4xx_of", (void *)hw);
>+	if (ret) {
>+		dev_err(dev, "unable to allocate interrupt\n");
>+		goto free_gpios;
>+	}
>+
>+	if (!request_mem_region(hw->mapbase, hw->mapsize, DRIVER_NAME)) {
>+		dev_err(dev, "resource unavailable\n");
>+		ret = -EBUSY;
>+		goto request_mem_error;
>+	}
>+
>+	hw->regs = ioremap(hw->mapbase, sizeof(struct spi_ppc4xx_regs));
>+
>+	if (!hw->regs) {
>+		dev_err(dev, "unable to memory map registers\n");
>+		ret = -ENXIO;
>+		goto map_io_error;
>+	}
>+
>+	spi_ppc4xx_enable(hw);
>+
>+	/* Finally register our spi controller */
>+	dev->dma_mask = 0;
>+	ret = spi_bitbang_start(bbp);
>+	if (ret) {
>+		dev_err(dev, "failed to register SPI master\n");
>+		goto unmap_regs;
>+	}
>+
>+	dev_info(dev, "driver initialized\n");
>+	of_register_spi_devices(master, np);
>+
>+	return 0;
>+
>+unmap_regs:
>+	iounmap(hw->regs);
>+map_io_error:
>+	release_mem_region(hw->mapbase, hw->mapsize);
>+request_mem_error:
>+	free_irq(hw->irqnum, hw);
>+free_gpios:
>+	free_gpios(hw);
>+free_master:
>+	dev_set_drvdata(dev, NULL);
>+	spi_master_put(master);
>+
>+	dev_err(dev, "initialization failed\n");
>+	return ret;
>+}
>+
>+static int __exit spi_ppc4xx_of_remove(struct of_device *op)
>+{
>+	struct spi_master *master = dev_get_drvdata(&op->dev);
>+	struct ppc4xx_spi *hw = spi_master_get_devdata(master);
>+
>+	spi_bitbang_stop(&hw->bitbang);
>+	dev_set_drvdata(&op->dev, NULL);
>+	release_mem_region(hw->mapbase, hw->mapsize);
>+	free_irq(hw->irqnum, hw);
>+	iounmap(hw->regs);
>+	free_gpios(hw);
>+	return 0;
>+}
>+
>+static struct of_device_id spi_ppc4xx_of_match[] = {
>+	{ .compatible = "ibm,ppc4xx-spi", },
>+	{},
>+};
>+
>+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(&spi_ppc4xx_of_driver);
>+}
>+
>+static void __exit spi_ppc4xx_exit(void)
>+{
>+	of_unregister_platform_driver(&spi_ppc4xx_of_driver);
>+}
>+
>+module_init(spi_ppc4xx_init);
>+module_exit(spi_ppc4xx_exit);
>+
>+MODULE_AUTHOR("Gary Jennejohn & Stefan Roese");
>+MODULE_DESCRIPTION("Simple PPC4xx SPI Driver");
>+MODULE_LICENSE("GPL");
>-- 
>1.6.0.3
>
>_______________________________________________
>Linuxppc-dev mailing list
>Linuxppc-dev at ozlabs.org
>https://ozlabs.org/mailman/listinfo/linuxppc-dev



More information about the Linuxppc-dev mailing list