* [PATCH v3] spi: Add PPC4xx SPI driver
@ 2008-10-26 5:48 Stefan Roese
[not found] ` <1225000124-29366-1-git-send-email-sr-ynQEQJNshbs@public.gmane.org>
0 siblings, 1 reply; 6+ messages in thread
From: Stefan Roese @ 2008-10-26 5:48 UTC (permalink / raw)
To: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f
Cc: linuxppc-dev-mnsaURCQ41sdnm+yROfE0A
This adds a SPI driver for the SPI controller found in the IBM/AMCC
4xx PowerPC's.
Signed-off-by: Stefan Roese <sr-ynQEQJNshbs@public.gmane.org>
Signed-off-by: Wolfgang Ocker <weo-pMF0Ia4UJXeoYr4blSSd5g@public.gmane.org>
---
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@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@0 {
reg = <0>;
...
};
touchscreen@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..6f94acc
--- /dev/null
+++ b/drivers/spi/spi_ppc4xx.c
@@ -0,0 +1,594 @@
+/*
+ * SPI_PPC4XX SPI controller driver.
+ *
+ * Copyright (C) 2007 Gary Jennejohn <garyj-ynQEQJNshbs@public.gmane.org>
+ * Copyright 2008 Stefan Roese <sr-ynQEQJNshbs@public.gmane.org>, 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-Y5A6D6n0/KfQXOPxS62xeg@public.gmane.org>
+ *
+ * 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-native.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;
+
+ volatile 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,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
-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH v3] spi: Add PPC4xx SPI driver
[not found] ` <1225000124-29366-1-git-send-email-sr-ynQEQJNshbs@public.gmane.org>
@ 2008-10-29 14:53 ` Josh Boyer
[not found] ` <20081029145305.GA3676-JVa43G0qJ78RlTYtMZUE4cJ8OCA1ciLf@public.gmane.org>
0 siblings, 1 reply; 6+ messages in thread
From: Josh Boyer @ 2008-10-29 14:53 UTC (permalink / raw)
To: Stefan Roese
Cc: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
linuxppc-dev-mnsaURCQ41sdnm+yROfE0A
On Sun, Oct 26, 2008 at 06:48:44AM +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-ynQEQJNshbs@public.gmane.org>
>Signed-off-by: Wolfgang Ocker <weo-pMF0Ia4UJXeoYr4blSSd5g@public.gmane.org>
>---
>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@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@0 {
> reg = <0>;
> ...
> };
>
> touchscreen@1 {
> reg = <1>;
> ...
> };
> }
>
>Special thanks to Wolfgang Ocker and Anton Vorontsov for their input.
Looks pretty good. Just a few minor comments/questions below.
Also, do you have a patch for a DTS file that gives an example of how to
instantiate the SPI stuff in the device tree?
>diff --git a/drivers/spi/spi_ppc4xx.c b/drivers/spi/spi_ppc4xx.c
>new file mode 100644
>index 0000000..6f94acc
>--- /dev/null
>+++ b/drivers/spi/spi_ppc4xx.c
>+#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-native.h>
Should maybe just be <asm/dcr.h>
>+#include <asm/dcr-regs.h>
>+
<snip>
>+/* 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;
>+
>+ volatile struct spi_ppc4xx_regs __iomem *regs; /* pointer to the registers */
volatile?
>+ struct spi_master *master;
>+ struct device *dev;
>+};
<snip>
>+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);
Maybe iowrite8? Same comment elsewhere.
>+ wait_for_completion(&hw->done);
>+
>+ return hw->count;
>+}
>+
<snip>
>+static struct of_device_id spi_ppc4xx_of_match[] = {
>+ { .compatible = "ibm,spi", },
>+ {},
>+};
I'm wondering if that is too generic of a match. In theory,
IBM could have another SPI controller that isn't for 4xx.
Maybe "ibm,spi-4xx" ?
josh
-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v3] spi: Add PPC4xx SPI driver
[not found] ` <20081029145305.GA3676-JVa43G0qJ78RlTYtMZUE4cJ8OCA1ciLf@public.gmane.org>
@ 2008-10-30 23:24 ` Jason Hanna
[not found] ` <8f9e78f00810301624k5925a52ah924960f869388233-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2008-10-31 8:31 ` Stefan Roese
1 sibling, 1 reply; 6+ messages in thread
From: Jason Hanna @ 2008-10-30 23:24 UTC (permalink / raw)
To: Josh Boyer
Cc: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f, Stefan Roese,
linuxppc-dev-mnsaURCQ41sdnm+yROfE0A
On Wed, Oct 29, 2008 at 10:53 AM, Josh Boyer <jwboyer-23VcF4HTsmIX0ybBhKVfKdBPR1lH4CV8@public.gmane.org> wrote:
> Also, do you have a patch for a DTS file that gives an example of how to
> instantiate the SPI stuff in the device tree?
I'll second that request. I've updated the device tree for my Kilauea
(405EX) board but am not sure I have everything correct.
Also, any pointers to sample/test code incorporating a spi protocol
driver would be incredibly helpful. I'm very new to device driver
programming and don't really know what I'm doing yet. I seem to be
getting the spi_ppc4xx and spi_bitbang modules loaded, but am unsure
how to verify proper initialization and what next steps I need to
follow in order to develop and associate a protocol driver.
I'd like to start quite simply - some test routines that would drive
data over the SPI connector on my Kilauea, using a spare GPIO pin as a
chip select. I've got a logic analyzer from which to verify the
results.
I've been reading through all the documentation I can find but am a
little stuck. A concrete example with a SPI protocol driver would be
quite useful.
Thanks,
-jmh
-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v3] spi: Add PPC4xx SPI driver
[not found] ` <20081029145305.GA3676-JVa43G0qJ78RlTYtMZUE4cJ8OCA1ciLf@public.gmane.org>
2008-10-30 23:24 ` Jason Hanna
@ 2008-10-31 8:31 ` Stefan Roese
[not found] ` <200810310931.19565.sr-ynQEQJNshbs@public.gmane.org>
1 sibling, 1 reply; 6+ messages in thread
From: Stefan Roese @ 2008-10-31 8:31 UTC (permalink / raw)
To: Josh Boyer
Cc: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
linuxppc-dev-mnsaURCQ41sdnm+yROfE0A
On Wednesday 29 October 2008, Josh Boyer wrote:
> Looks pretty good. Just a few minor comments/questions below.
Thanks. I also added some comments below.
> Also, do you have a patch for a DTS file that gives an example of how to
> instantiate the SPI stuff in the device tree?
OK, I'll add a DTS patch to the next version.
> >diff --git a/drivers/spi/spi_ppc4xx.c b/drivers/spi/spi_ppc4xx.c
> >new file mode 100644
> >index 0000000..6f94acc
> >--- /dev/null
> >+++ b/drivers/spi/spi_ppc4xx.c
> >+#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-native.h>
>
> Should maybe just be <asm/dcr.h>
Fixed.
> >+#include <asm/dcr-regs.h>
> >+
>
> <snip>
>
> >+/* 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;
> >+
> >+ volatile struct spi_ppc4xx_regs __iomem *regs; /* pointer to the
> > registers */
>
> volatile?
Fixed.
> >+ struct spi_master *master;
> >+ struct device *dev;
> >+};
>
> <snip>
>
> >+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);
>
> Maybe iowrite8? Same comment elsewhere.
Why? We use the in_/out_xxx() accessor function for all other 4xx driver as
well.
> >+ wait_for_completion(&hw->done);
> >+
> >+ return hw->count;
> >+}
> >+
>
> <snip>
>
> >+static struct of_device_id spi_ppc4xx_of_match[] = {
> >+ { .compatible = "ibm,spi", },
> >+ {},
> >+};
>
> I'm wondering if that is too generic of a match. In theory,
> IBM could have another SPI controller that isn't for 4xx.
> Maybe "ibm,spi-4xx" ?
Right. I was doing it the same way as already done before, e.g. "ibm,iic". For
the gpio driver we already switched to "ibm,ppc4xx-gpio". So how
about "ibm,ppc4xx-spi"?
Thanks.
Best regards,
Stefan
-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v3] spi: Add PPC4xx SPI driver
[not found] ` <200810310931.19565.sr-ynQEQJNshbs@public.gmane.org>
@ 2008-10-31 10:34 ` Josh Boyer
0 siblings, 0 replies; 6+ messages in thread
From: Josh Boyer @ 2008-10-31 10:34 UTC (permalink / raw)
To: Stefan Roese
Cc: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f,
linuxppc-dev-mnsaURCQ41sdnm+yROfE0A
On Fri, 31 Oct 2008 09:31:19 +0100
Stefan Roese <sr-ynQEQJNshbs@public.gmane.org> wrote:
> On Wednesday 29 October 2008, Josh Boyer wrote:
> > Looks pretty good. Just a few minor comments/questions below.
>
> Thanks. I also added some comments below.
>
> > Also, do you have a patch for a DTS file that gives an example of how to
> > instantiate the SPI stuff in the device tree?
>
> OK, I'll add a DTS patch to the next version.
Ok thanks.
> > >+ /* 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);
> >
> > Maybe iowrite8? Same comment elsewhere.
>
> Why? We use the in_/out_xxx() accessor function for all other 4xx driver as
> well.
Yeah. We used to have all of 4xx in arch/ppc too. ;)
Seriously though, it doesn't really bother me too much. The
io{read,write} functions do have a bit better clarity as to endian-ness
though.
> > >+ wait_for_completion(&hw->done);
> > >+
> > >+ return hw->count;
> > >+}
> > >+
> >
> > <snip>
> >
> > >+static struct of_device_id spi_ppc4xx_of_match[] = {
> > >+ { .compatible = "ibm,spi", },
> > >+ {},
> > >+};
> >
> > I'm wondering if that is too generic of a match. In theory,
> > IBM could have another SPI controller that isn't for 4xx.
> > Maybe "ibm,spi-4xx" ?
>
> Right. I was doing it the same way as already done before, e.g. "ibm,iic". For
> the gpio driver we already switched to "ibm,ppc4xx-gpio". So how
> about "ibm,ppc4xx-spi"?
Sounds fine to me.
josh
-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH v3] spi: Add PPC4xx SPI driver
[not found] ` <8f9e78f00810301624k5925a52ah924960f869388233-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
@ 2008-11-21 3:17 ` David Brownell
0 siblings, 0 replies; 6+ messages in thread
From: David Brownell @ 2008-11-21 3:17 UTC (permalink / raw)
To: Jason Hanna
Cc: spi-devel-general-5NWGOfrQmneRv+LV9MX5uipxlwaOVQ5f, Stefan Roese,
Josh Boyer, linuxppc-dev-mnsaURCQ41sdnm+yROfE0A
On Thursday 30 October 2008, Jason Hanna wrote:
> Also, any pointers to sample/test code incorporating a spi protocol
> driver would be incredibly helpful.
Look at Documentation/spi/*.c ... for user mode code
hooking up through "spidev".
> I'm very new to device driver
> programming and don't really know what I'm doing yet. I seem to be
> getting the spi_ppc4xx and spi_bitbang modules loaded, but am unsure
> how to verify proper initialization and what next steps I need to
> follow in order to develop and associate a protocol driver.
Documentation/spi/spi-summary should help.
And sticking a 'scope on the outputs (as you
said you plan) is a decent place to start,
if you don't have something to talk to yet.
- dave
-------------------------------------------------------------------------
This SF.Net email is sponsored by the Moblin Your Move Developer's challenge
Build the coolest Linux based applications with Moblin SDK & win great prizes
Grand prize is a trip for two to an Open Source event anywhere in the world
http://moblin-contest.org/redirect.php?banner_id=100&url=/
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2008-11-21 3:17 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-10-26 5:48 [PATCH v3] spi: Add PPC4xx SPI driver Stefan Roese
[not found] ` <1225000124-29366-1-git-send-email-sr-ynQEQJNshbs@public.gmane.org>
2008-10-29 14:53 ` Josh Boyer
[not found] ` <20081029145305.GA3676-JVa43G0qJ78RlTYtMZUE4cJ8OCA1ciLf@public.gmane.org>
2008-10-30 23:24 ` Jason Hanna
[not found] ` <8f9e78f00810301624k5925a52ah924960f869388233-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2008-11-21 3:17 ` David Brownell
2008-10-31 8:31 ` Stefan Roese
[not found] ` <200810310931.19565.sr-ynQEQJNshbs@public.gmane.org>
2008-10-31 10:34 ` Josh Boyer
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).