[PATCH v2 0/5] SPI OF bindings and mpc5200-spi driver - Kernel

This is a discussion on [PATCH v2 0/5] SPI OF bindings and mpc5200-spi driver - Kernel ; I think I've addressed all the comments I've received. This series adds OpenFirmware device tree bindings for SPI devices. SPI master drivers which make use of of_spi.c can retrieve the set of spi devices from the device tree and automatically ...

+ Reply to Thread
Results 1 to 8 of 8

Thread: [PATCH v2 0/5] SPI OF bindings and mpc5200-spi driver

  1. [PATCH v2 0/5] SPI OF bindings and mpc5200-spi driver

    I think I've addressed all the comments I've received. This series
    adds OpenFirmware device tree bindings for SPI devices. SPI master
    drivers which make use of of_spi.c can retrieve the set of spi
    devices from the device tree and automatically create them.

    This series depends on previously posted patch:
    "spi: Change modalias from a pointer to a character array"

    Documentation/powerpc/booting-without-of.txt | 60 +++
    drivers/of/Kconfig | 6 +
    drivers/of/Makefile | 1 +
    drivers/of/of_spi.c | 88 ++++
    drivers/spi/Kconfig | 8 +
    drivers/spi/Makefile | 1 +
    drivers/spi/mpc52xx_spi.c | 595 ++++++++++++++++++++++++++
    drivers/spi/spi.c | 141 +++++--
    include/linux/of_spi.h | 18 +
    include/linux/spi/mpc52xx_spi.h | 10 +
    include/linux/spi/spi.h | 12 +-
    11 files changed, 895 insertions(+), 45 deletions(-)

    --
    Grant Likely, B.Sc. P.Eng.
    Secret Lab Technologies Ltd.
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  2. [PATCH v2 2/5] spi: split up spi_new_device() to allow two stage registration.

    From: Grant Likely

    spi_new_device() allocates and registers an spi device all in one swoop.
    If the driver needs to add extra data to the spi_device before it is
    registered, then this causes problems.

    This patch splits the allocation and registration portions of code out
    of spi_new_device() and creates three new functions; spi_alloc_device(),
    spi_register_device(), and spi_device_release(). spi_new_device() is
    modified to use the new functions for allocation and registration.
    None of the existing users of spi_new_device() should be affected by
    this change.

    Drivers using the new API can forego the use of an spi_board_info
    structure to describe the device layout and populate data into the
    spi_device structure directly.

    This change is in preparation for adding an OF device tree parser to
    generate spi_devices based on data in the device tree.

    Signed-off-by: Grant Likely
    ---

    drivers/spi/spi.c | 139 ++++++++++++++++++++++++++++++++---------------
    include/linux/spi/spi.h | 10 +++
    2 files changed, 105 insertions(+), 44 deletions(-)

    diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
    index 32b7a42..e64add0 100644
    --- a/drivers/spi/spi.c
    +++ b/drivers/spi/spi.c
    @@ -178,6 +178,96 @@ struct boardinfo {
    static LIST_HEAD(board_list);
    static DEFINE_MUTEX(board_lock);

    +/**
    + * spi_alloc_device - Allocate a new SPI device
    + * @master: Controller to which device is connected
    + * Context: can sleep
    + *
    + * Allows a driver to allocate and initialize and spi_device without
    + * registering it immediately. This allows a driver to directly
    + * fill the spi_device with device parameters before calling
    + * spi_add_device() on it.
    + *
    + * Caller is responsible to call spi_add_device() on the returned
    + * spi_device structure to add it to the SPI master. If the caller
    + * needs to discard the spi_device without adding it, then it should
    + * call spi_dev_put() on it.
    + *
    + * Returns a pointer to the new device, or NULL.
    + */
    +struct spi_device *spi_alloc_device(struct spi_master *master)
    +{
    + struct spi_device *spi;
    + struct device *dev = master->dev.parent;
    +
    + if (!spi_master_get(master))
    + return NULL;
    +
    + spi = kzalloc(sizeof *spi, GFP_KERNEL);
    + if (!spi) {
    + dev_err(dev, "cannot alloc spi_device\n");
    + spi_master_put(master);
    + return NULL;
    + }
    +
    + spi->master = master;
    + spi->dev.parent = dev;
    + spi->dev.bus = &spi_bus_type;
    + spi->dev.release = spidev_release;
    + device_initialize(&spi->dev);
    + return spi;
    +}
    +EXPORT_SYMBOL_GPL(spi_alloc_device);
    +
    +/**
    + * spi_add_device - Add an spi_device allocated with spi_alloc_device
    + * @spi: spi_device to register
    + *
    + * Companion function to spi_alloc_device. Devices allocated with
    + * spi_alloc_device can be added onto the spi bus with this function.
    + *
    + * Returns 0 on success; non-zero on failure
    + */
    +int spi_add_device(struct spi_device *spi)
    +{
    + struct device *dev = spi->master->dev.parent;
    + int status;
    +
    + /* Chipselects are numbered 0..max; validate. */
    + if (spi->chip_select >= spi->master->num_chipselect) {
    + dev_err(dev, "cs%d > max %d\n",
    + spi->chip_select,
    + spi->master->num_chipselect);
    + return -EINVAL;
    + }
    +
    + /* Set the bus ID string */
    + snprintf(spi->dev.bus_id, sizeof spi->dev.bus_id,
    + "%s.%u", spi->master->dev.bus_id,
    + spi->chip_select);
    +
    + /* drivers may modify this initial i/o setup */
    + status = spi->master->setup(spi);
    + if (status < 0) {
    + dev_err(dev, "can't %s %s, status %d\n",
    + "setup", spi->dev.bus_id, status);
    + return status;
    + }
    +
    + /* driver core catches callers that misbehave by defining
    + * devices that already exist.
    + */
    + status = device_add(&spi->dev);
    + if (status < 0) {
    + dev_err(dev, "can't %s %s, status %d\n",
    + "add", spi->dev.bus_id, status);
    + return status;
    + }
    +
    + dev_dbg(dev, "registered child %s\n", spi->dev.bus_id);
    + return 0;
    +}
    +EXPORT_SYMBOL_GPL(spi_add_device);

    /**
    * spi_new_device - instantiate one new SPI device
    @@ -197,7 +287,6 @@ struct spi_device *spi_new_device(struct spi_master *master,
    struct spi_board_info *chip)
    {
    struct spi_device *proxy;
    - struct device *dev = master->dev.parent;
    int status;

    /* NOTE: caller did any chip->bus_num checks necessary.
    @@ -207,66 +296,28 @@ struct spi_device *spi_new_device(struct spi_master *master,
    * suggests syslogged diagnostics are best here (ugh).
    */

    - /* Chipselects are numbered 0..max; validate. */
    - if (chip->chip_select >= master->num_chipselect) {
    - dev_err(dev, "cs%d > max %d\n",
    - chip->chip_select,
    - master->num_chipselect);
    - return NULL;
    - }
    -
    - if (!spi_master_get(master))
    + proxy = spi_alloc_device(master);
    + if (!proxy)
    return NULL;

    WARN_ON(strlen(chip->modalias) >= sizeof(proxy->modalias));

    - proxy = kzalloc(sizeof *proxy, GFP_KERNEL);
    - if (!proxy) {
    - dev_err(dev, "can't alloc dev for cs%d\n",
    - chip->chip_select);
    - goto fail;
    - }
    - proxy->master = master;
    proxy->chip_select = chip->chip_select;
    proxy->max_speed_hz = chip->max_speed_hz;
    proxy->mode = chip->mode;
    proxy->irq = chip->irq;
    strlcpy(proxy->modalias, chip->modalias, sizeof(proxy->modalias));
    -
    - snprintf(proxy->dev.bus_id, sizeof proxy->dev.bus_id,
    - "%s.%u", master->dev.bus_id,
    - chip->chip_select);
    - proxy->dev.parent = dev;
    - proxy->dev.bus = &spi_bus_type;
    proxy->dev.platform_data = (void *) chip->platform_data;
    proxy->controller_data = chip->controller_data;
    proxy->controller_state = NULL;
    - proxy->dev.release = spidev_release;

    - /* drivers may modify this initial i/o setup */
    - status = master->setup(proxy);
    + status = spi_add_device(proxy);
    if (status < 0) {
    - dev_err(dev, "can't %s %s, status %d\n",
    - "setup", proxy->dev.bus_id, status);
    - goto fail;
    + spi_dev_put(proxy);
    + return NULL;
    }

    - /* driver core catches callers that misbehave by defining
    - * devices that already exist.
    - */
    - status = device_register(&proxy->dev);
    - if (status < 0) {
    - dev_err(dev, "can't %s %s, status %d\n",
    - "add", proxy->dev.bus_id, status);
    - goto fail;
    - }
    - dev_dbg(dev, "registered child %s\n", proxy->dev.bus_id);
    return proxy;
    -
    -fail:
    - spi_master_put(master);
    - kfree(proxy);
    - return NULL;
    }
    EXPORT_SYMBOL_GPL(spi_new_device);

    diff --git a/include/linux/spi/spi.h b/include/linux/spi/spi.h
    index b55910b..d45967e 100644
    --- a/include/linux/spi/spi.h
    +++ b/include/linux/spi/spi.h
    @@ -778,8 +778,18 @@ spi_register_board_info(struct spi_board_info const *info, unsigned n)
    * use spi_new_device() to describe each device. You can also call
    * spi_unregister_device() to start making that device vanish, but
    * normally that would be handled by spi_unregister_master().
    + *
    + * You can also use spi_alloc_device() and spi_add_device() to
    + * for a two stage registration of an SPI device. This gives the caller
    + * some more control over the spi_device structure before it is registered
    */
    extern struct spi_device *
    +spi_alloc_device(struct spi_master *master);
    +
    +extern int
    +spi_add_device(struct spi_device *spi);
    +
    +extern struct spi_device *
    spi_new_device(struct spi_master *, struct spi_board_info *);

    static inline void

    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  3. [PATCH v2 5/5] powerpc/mpc5200: Add mpc5200-spi (non-PSC) device driver

    From: Grant Likely

    Adds support for the dedicated SPI device on the Freescale MPC5200(b)
    SoC.

    Signed-off-by: Grant Likely
    ---

    drivers/spi/Kconfig | 8 +
    drivers/spi/Makefile | 1
    drivers/spi/mpc52xx_spi.c | 595 +++++++++++++++++++++++++++++++++++++++
    include/linux/spi/mpc52xx_spi.h | 10 +
    4 files changed, 614 insertions(+), 0 deletions(-)

    diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
    index 66ec5d8..01860ac 100644
    --- a/drivers/spi/Kconfig
    +++ b/drivers/spi/Kconfig
    @@ -116,6 +116,14 @@ config SPI_LM70_LLP
    which interfaces to an LM70 temperature sensor using
    a parallel port.

    +config SPI_MPC52xx
    + tristate "Freescale MPC52xx SPI (non-PSC) controller support"
    + depends on PPC_MPC52xx && SPI
    + select SPI_MASTER_OF
    + help
    + This drivers supports the MPC52xx SPI controller in master SPI
    + mode.
    +
    config SPI_MPC52xx_PSC
    tristate "Freescale MPC52xx PSC SPI controller"
    depends on SPI_MASTER && PPC_MPC52xx && EXPERIMENTAL
    diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
    index 7fca043..340b878 100644
    --- a/drivers/spi/Makefile
    +++ b/drivers/spi/Makefile
    @@ -22,6 +22,7 @@ obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o
    obj-$(CONFIG_SPI_OMAP_UWIRE) += omap_uwire.o
    obj-$(CONFIG_SPI_OMAP24XX) += omap2_mcspi.o
    obj-$(CONFIG_SPI_MPC52xx_PSC) += mpc52xx_psc_spi.o
    +obj-$(CONFIG_SPI_MPC52xx) += mpc52xx_spi.o
    obj-$(CONFIG_SPI_MPC83xx) += spi_mpc83xx.o
    obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o
    obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o
    diff --git a/drivers/spi/mpc52xx_spi.c b/drivers/spi/mpc52xx_spi.c
    new file mode 100644
    index 0000000..453690f
    --- /dev/null
    +++ b/drivers/spi/mpc52xx_spi.c
    @@ -0,0 +1,595 @@
    +/*
    + * MPC52xx SPI master driver.
    + * Copyright (C) 2008 Secret Lab Technologies Ltd.
    + *
    + * This is the driver for the MPC5200's dedicated SPI device (not for a
    + * PSC in SPI mode)
    + */
    +
    +#include
    +#include
    +#include
    +#include
    +#include
    +#include
    +#include
    +#include
    +#include
    +#include
    +#include
    +#include
    +
    +MODULE_AUTHOR("Grant Likely ");
    +MODULE_DESCRIPTION("MPC52xx SPI (non-PSC) Driver");
    +MODULE_LICENSE("GPL");
    +
    +/* Register offsets */
    +#define SPI_CTRL1 0x00
    +#define SPI_CTRL1_SPIE (1 << 7)
    +#define SPI_CTRL1_SPE (1 << 6)
    +#define SPI_CTRL1_MSTR (1 << 4)
    +#define SPI_CTRL1_CPOL (1 << 3)
    +#define SPI_CTRL1_CPHA (1 << 2)
    +#define SPI_CTRL1_SSOE (1 << 1)
    +#define SPI_CTRL1_LSBFE (1 << 0)
    +
    +#define SPI_CTRL2 0x01
    +#define SPI_BRR 0x04
    +
    +#define SPI_STATUS 0x05
    +#define SPI_STATUS_SPIF (1 << 7)
    +#define SPI_STATUS_WCOL (1 << 6)
    +#define SPI_STATUS_MODF (1 << 4)
    +
    +#define SPI_DATA 0x09
    +#define SPI_PORTDATA 0x0d
    +#define SPI_DATADIR 0x10
    +
    +/* FSM state return values */
    +#define FSM_STOP 0
    +#define FSM_POLL 1
    +#define FSM_CONTINUE 2
    +
    +/* Driver internal data */
    +struct mpc52xx_spi {
    + struct spi_master *master;
    + u32 sysclk;
    + void __iomem *regs;
    + int irq0; /* MODF irq */
    + int irq1; /* SPIF irq */
    + int ipb_freq;
    +
    + /* Statistics */
    + int msg_count;
    + int wcol_count;
    + int wcol_ticks;
    + u32 wcol_tx_timestamp;
    + int modf_count;
    + int byte_count;
    +
    + /* Hooks for platform modification of behaviour */
    + void (*premessage)(struct spi_message *m, void *context);
    + void *premessage_context;
    +
    + struct list_head queue; /* queue of pending messages */
    + spinlock_t lock;
    + struct work_struct work;
    +
    +
    + /* Details of current transfer (length, and buffer pointers) */
    + struct spi_message *message; /* current message */
    + struct spi_transfer *transfer; /* current transfer */
    + int (*state)(int irq, struct mpc52xx_spi *ms, u8 status, u8 data);
    + int len;
    + int timestamp;
    + u8 *rx_buf;
    + const u8 *tx_buf;
    + int cs_change;
    +};
    +
    +/*
    + * CS control function
    + */
    +static void mpc52xx_spi_chipsel(struct mpc52xx_spi *ms, int value)
    +{
    + if (value)
    + writeb(0, ms->regs + SPI_PORTDATA); /* Assert SS pin */
    + else
    + writeb(0x08, ms->regs + SPI_PORTDATA); /* Deassert SS pin */
    +}
    +
    +/*
    + * Start a new transfer. This is called both by the idle state
    + * for the first transfer in a message, and by the wait state when the
    + * previous transfer in a message is complete.
    + */
    +static void mpc52xx_spi_start_transfer(struct mpc52xx_spi *ms)
    +{
    + ms->rx_buf = ms->transfer->rx_buf;
    + ms->tx_buf = ms->transfer->tx_buf;
    + ms->len = ms->transfer->len;
    +
    + /* Activate the chip select */
    + if (ms->cs_change)
    + mpc52xx_spi_chipsel(ms, 1);
    + ms->cs_change = ms->transfer->cs_change;
    +
    + /* Write out the first byte */
    + ms->wcol_tx_timestamp = get_tbl();
    + if (ms->tx_buf)
    + writeb(*ms->tx_buf++, ms->regs + SPI_DATA);
    + else
    + writeb(0, ms->regs + SPI_DATA);
    +}
    +
    +/* Forward declaration of state handlers */
    +static int mpc52xx_spi_fsmstate_transfer(int irq, struct mpc52xx_spi *ms,
    + u8 status, u8 data);
    +static int mpc52xx_spi_fsmstate_wait(int irq, struct mpc52xx_spi *ms,
    + u8 status, u8 data);
    +
    +/*
    + * IDLE state
    + *
    + * No transfers are in progress; if another transfer is pending then retrieve
    + * it and kick it off. Otherwise, stop processing the state machine
    + */
    +static int
    +mpc52xx_spi_fsmstate_idle(int irq, struct mpc52xx_spi *ms, u8 status, u8 data)
    +{
    + struct spi_message *m;
    + struct spi_device *spi;
    + int spr, sppr;
    + u8 ctrl1;
    +
    + if (status && (irq != NO_IRQ))
    + dev_err(&ms->master->dev, "spurious irq, status=0x%.2x\n",
    + status);
    +
    + /* Check if there is another transfer waiting */
    + if (list_empty(&ms->queue))
    + return FSM_STOP;
    +
    + /* Get the next message */
    + spin_lock(&ms->lock);
    +
    + /* Call the pre-message hook with a pointer to the next
    + * message. The pre-message hook may enqueue a new message for
    + * changing the chip select value to the head of the queue */
    + m = list_first_entry(&ms->queue, struct spi_message, queue);
    + if (ms->premessage)
    + ms->premessage(m, ms->premessage_context);
    +
    + /* reget the head of the queue (the premessage hook may have enqueued
    + * something before it.) and drop the spinlock */
    + ms->message = list_first_entry(&ms->queue, struct spi_message, queue);
    + list_del_init(&ms->message->queue);
    + spin_unlock(&ms->lock);
    +
    + /* Setup the controller parameters */
    + ctrl1 = SPI_CTRL1_SPIE | SPI_CTRL1_SPE | SPI_CTRL1_MSTR;
    + spi = ms->message->spi;
    + if (spi->mode & SPI_CPHA)
    + ctrl1 |= SPI_CTRL1_CPHA;
    + if (spi->mode & SPI_CPOL)
    + ctrl1 |= SPI_CTRL1_CPOL;
    + if (spi->mode & SPI_LSB_FIRST)
    + ctrl1 |= SPI_CTRL1_LSBFE;
    + writeb(ctrl1, ms->regs + SPI_CTRL1);
    +
    + /* Setup the controller speed */
    + /* minimum divider is '2'. Also, add '1' to force rounding up. */
    + sppr = ((ms->ipb_freq / ms->message->spi->max_speed_hz) + 1) >> 1;
    + spr = 0;
    + if (sppr < 1)
    + sppr = 1;
    + while (((sppr - 1) & ~0x7) != 0) {
    + sppr = (sppr + 1) >> 1; /* add '1' to force rounding up */
    + spr++;
    + }
    + sppr--; /* sppr quantity in register is offset by 1 */
    + if (spr > 7) {
    + /* Don't overrun limits of SPI baudrate register */
    + spr = 7;
    + sppr = 7;
    + }
    + writeb(sppr << 4 | spr, ms->regs + SPI_BRR); /* Set speed */
    +
    + ms->cs_change = 1;
    + ms->transfer = container_of(ms->message->transfers.next,
    + struct spi_transfer, transfer_list);
    +
    + mpc52xx_spi_start_transfer(ms);
    + ms->state = mpc52xx_spi_fsmstate_transfer;
    +
    +#if defined(VERBOSE_DEBUG)
    + dev_info(&ms->master->dev, "msg:%p, max_speed:%i, brr:%.2x\n",
    + ms->message, ms->message->spi->max_speed_hz,
    + readb(ms->regs + SPI_BRR));
    +#endif
    +
    + return FSM_CONTINUE;
    +}
    +
    +/*
    + * TRANSFER state
    + *
    + * In the middle of a transfer. If the SPI core has completed processing
    + * a byte, then read out the received data and write out the next byte
    + * (unless this transfer is finished; in which case go on to the wait
    + * state)
    + */
    +static int mpc52xx_spi_fsmstate_transfer(int irq, struct mpc52xx_spi *ms,
    + u8 status, u8 data)
    +{
    + if (!status)
    + return ms->irq0 == NO_IRQ ? FSM_POLL : FSM_STOP;
    +
    + if (status & SPI_STATUS_WCOL) {
    + /* The SPI device is stoopid. At slower speeds, it may raise
    + * the SPIF flag before the state machine is actually finished.
    + * which causes a collision (internal to the state machine
    + * only). The manual recommends inserting a delay between
    + * receving the interrupt and sending the next byte, but
    + * it can also be worked around simply by retrying the
    + * transfer which is what we do here. */
    + ms->wcol_count++;
    + ms->wcol_ticks += get_tbl() - ms->wcol_tx_timestamp;
    + ms->wcol_tx_timestamp = get_tbl();
    + data = 0;
    + if (ms->tx_buf)
    + data = *(ms->tx_buf-1);
    + writeb(data, ms->regs + SPI_DATA); /* try again */
    + return FSM_CONTINUE;
    + } else if (status & SPI_STATUS_MODF) {
    + ms->modf_count++;
    + dev_err(&ms->master->dev, "mod fault\n");
    + mpc52xx_spi_chipsel(ms, 0);
    + ms->message->status = -EIO;
    + if (ms->message->complete)
    + ms->message->complete(ms->message->context);
    + ms->state = mpc52xx_spi_fsmstate_idle;
    + return FSM_CONTINUE;
    + }
    +
    + /* Read data out of the spi device */
    + ms->byte_count++;
    + if (ms->rx_buf)
    + *ms->rx_buf++ = data;
    +
    + /* Is the transfer complete? */
    + ms->len--;
    + if (ms->len == 0) {
    + ms->timestamp = get_tbl();
    + ms->timestamp += ms->transfer->delay_usecs * tb_ticks_per_usec;
    + ms->state = mpc52xx_spi_fsmstate_wait;
    + return FSM_CONTINUE;
    + }
    +
    + /* Write out the next byte */
    + ms->wcol_tx_timestamp = get_tbl();
    + if (ms->tx_buf)
    + writeb(*ms->tx_buf++, ms->regs + SPI_DATA);
    + else
    + writeb(0, ms->regs + SPI_DATA);
    +
    + return FSM_CONTINUE;
    +}
    +
    +/*
    + * WAIT state
    + *
    + * A transfer has completed; need to wait for the delay period to complete
    + * before starting the next transfer
    + */
    +static int
    +mpc52xx_spi_fsmstate_wait(int irq, struct mpc52xx_spi *ms, u8 status, u8 data)
    +{
    + if (status && irq != NO_IRQ)
    + dev_err(&ms->master->dev, "spurious irq, status=0x%.2x\n",
    + status);
    +
    + if (((int)get_tbl()) - ms->timestamp < 0)
    + return FSM_POLL;
    +
    + ms->message->actual_length += ms->transfer->len;
    +
    + /* Check if there is another transfer in this message. If there
    + * aren't then deactivate CS, notify sender, and drop back to idle
    + * to start the next message. */
    + if (ms->transfer->transfer_list.next == &ms->message->transfers) {
    + ms->msg_count++;
    + mpc52xx_spi_chipsel(ms, 0);
    + ms->message->status = 0;
    + if (ms->message->complete)
    + ms->message->complete(ms->message->context);
    + ms->state = mpc52xx_spi_fsmstate_idle;
    + return FSM_CONTINUE;
    + }
    +
    + /* There is another transfer; kick it off */
    +
    + if (ms->cs_change)
    + mpc52xx_spi_chipsel(ms, 0);
    +
    + ms->transfer = container_of(ms->transfer->transfer_list.next,
    + struct spi_transfer, transfer_list);
    + mpc52xx_spi_start_transfer(ms);
    + ms->state = mpc52xx_spi_fsmstate_transfer;
    + return FSM_CONTINUE;
    +}
    +
    +/*
    + * IRQ handler
    + */
    +static irqreturn_t mpc52xx_spi_irq(int irq, void *_ms)
    +{
    + struct mpc52xx_spi *ms = _ms;
    + int rc = FSM_CONTINUE;
    + u8 status, data;
    +
    + while (rc == FSM_CONTINUE) {
    + /* Interrupt cleared by read of STATUS followed by
    + * read of DATA registers*/
    + status = readb(ms->regs + SPI_STATUS);
    + data = readb(ms->regs + SPI_DATA); /* clear status */
    + rc = ms->state(irq, ms, status, data);
    + }
    +
    + if (rc == FSM_POLL)
    + schedule_work(&ms->work);
    +
    + return IRQ_HANDLED;
    +}
    +
    +/*
    + * Workqueue method of running the state machine
    + */
    +static void mpc52xx_spi_wq(struct work_struct *work)
    +{
    + struct mpc52xx_spi *ms = container_of(work, struct mpc52xx_spi, work);
    + mpc52xx_spi_irq(NO_IRQ, ms);
    +}
    +
    +/*
    + * spi_master callbacks
    + */
    +
    +static int mpc52xx_spi_setup(struct spi_device *spi)
    +{
    + return 0;
    +}
    +
    +static int mpc52xx_spi_transfer(struct spi_device *spi, struct spi_message *m)
    +{
    + struct mpc52xx_spi *ms = spi_master_get_devdata(spi->master);
    + unsigned long flags;
    +
    + m->actual_length = 0;
    + m->status = -EINPROGRESS;
    +
    + spin_lock_irqsave(&ms->lock, flags);
    + list_add_tail(&m->queue, &ms->queue);
    + spin_unlock_irqrestore(&ms->lock, flags);
    + schedule_work(&ms->work);
    +
    + return 0;
    +}
    +
    +/*
    + * Hook to modify premessage hook
    + */
    +void mpc52xx_spi_set_premessage_hook(struct spi_master *master,
    + void (*hook)(struct spi_message *m,
    + void *context),
    + void *hook_context)
    +{
    + struct mpc52xx_spi *ms = spi_master_get_devdata(master);
    + ms->premessage = hook;
    + ms->premessage_context = hook_context;
    +}
    +EXPORT_SYMBOL(mpc52xx_spi_set_premessage_hook);
    +
    +/*
    + * SysFS files
    + */
    +static int
    +*mpc52xx_spi_sysfs_get_counter(struct mpc52xx_spi *ms, const char *name)
    +{
    + if (strcmp(name, "msg_count") == 0)
    + return &ms->msg_count;
    + if (strcmp(name, "byte_count") == 0)
    + return &ms->byte_count;
    + if (strcmp(name, "wcol_count") == 0)
    + return &ms->wcol_count;
    + if (strcmp(name, "wcol_ticks") == 0)
    + return &ms->wcol_ticks;
    + if (strcmp(name, "modf_count") == 0)
    + return &ms->modf_count;
    + return NULL;
    +}
    +
    +static ssize_t mpc52xx_spi_show_count(struct device *dev,
    + struct device_attribute *attr,
    + char *buf)
    +{
    + struct spi_master *master = container_of(dev, struct spi_master, dev);
    + struct mpc52xx_spi *ms = spi_master_get_devdata(master);
    + int *counter;
    +
    + counter = mpc52xx_spi_sysfs_get_counter(ms, attr->attr.name);
    + if (!counter)
    + return sprintf(buf, "error\n");
    + return sprintf(buf, "%d\n", *counter);
    +}
    +
    +static ssize_t mpc52xx_spi_set_count(struct device *dev,
    + struct device_attribute *attr,
    + const char *buf, size_t count)
    +{
    + struct spi_master *master = container_of(dev, struct spi_master, dev);
    + struct mpc52xx_spi *ms = spi_master_get_devdata(master);
    + int *counter;
    + int value = simple_strtoul(buf, NULL, 0);
    +
    + counter = mpc52xx_spi_sysfs_get_counter(ms, attr->attr.name);
    + if (counter)
    + *counter = value;
    + return count;
    +}
    +
    +DEVICE_ATTR(msg_count, 0644, mpc52xx_spi_show_count, mpc52xx_spi_set_count);
    +DEVICE_ATTR(byte_count, 0644, mpc52xx_spi_show_count, mpc52xx_spi_set_count);
    +DEVICE_ATTR(wcol_count, 0644, mpc52xx_spi_show_count, mpc52xx_spi_set_count);
    +DEVICE_ATTR(wcol_ticks, 0644, mpc52xx_spi_show_count, mpc52xx_spi_set_count);
    +DEVICE_ATTR(modf_count, 0644, mpc52xx_spi_show_count, mpc52xx_spi_set_count);
    +
    +/*
    + * OF Platform Bus Binding
    + */
    +static int __devinit mpc52xx_spi_of_probe(struct of_device *op,
    + const struct of_device_id *match)
    +{
    + struct spi_master *master;
    + struct mpc52xx_spi *ms;
    + void __iomem *regs;
    + const u32 *prop;
    + int rc, len;
    +
    + /* MMIO registers */
    + dev_dbg(&op->dev, "probing mpc5200 SPI device\n");
    + regs = of_iomap(op->node, 0);
    + if (!regs)
    + return -ENODEV;
    +
    + /* initialize the device */
    + writeb(SPI_CTRL1_SPIE | SPI_CTRL1_SPE | SPI_CTRL1_MSTR, regs+SPI_CTRL1);
    + writeb(0x0, regs + SPI_CTRL2);
    + writeb(0xe, regs + SPI_DATADIR); /* Set output pins */
    + writeb(0x8, regs + SPI_PORTDATA); /* Deassert /SS signal */
    +
    + /* Clear the status register and re-read it to check for a MODF
    + * failure. This driver cannot currently handle multiple masters
    + * on the SPI bus. This fault will also occur if the SPI signals
    + * are not connected to any pins (port_config setting) */
    + readb(regs + SPI_STATUS);
    + readb(regs + SPI_DATA);
    + if (readb(regs + SPI_STATUS) & SPI_STATUS_MODF) {
    + dev_err(&op->dev, "mode fault; is port_config correct?\n");
    + return -EIO;
    + }
    +
    + dev_dbg(&op->dev, "allocating spi_master struct\n");
    + master = spi_alloc_master(&op->dev, sizeof *ms);
    + if (!master)
    + return -ENOMEM;
    + master->bus_num = -1;
    + master->num_chipselect = 1;
    + prop = of_get_property(op->node, "num-slaves", &len);
    + if (prop && len >= sizeof(*prop))
    + master->num_chipselect = *prop;
    +
    + master->setup = mpc52xx_spi_setup;
    + master->transfer = mpc52xx_spi_transfer;
    + dev_set_drvdata(&op->dev, master);
    +
    + ms = spi_master_get_devdata(master);
    + ms->master = master;
    + ms->regs = regs;
    + ms->irq0 = irq_of_parse_and_map(op->node, 0);
    + ms->irq1 = irq_of_parse_and_map(op->node, 1);
    + ms->state = mpc52xx_spi_fsmstate_idle;
    + ms->ipb_freq = mpc52xx_find_ipb_freq(op->node);
    + spin_lock_init(&ms->lock);
    + INIT_LIST_HEAD(&ms->queue);
    + INIT_WORK(&ms->work, mpc52xx_spi_wq);
    +
    + dev_dbg(&op->dev, "registering spi_master struct\n");
    + rc = spi_register_master(master);
    + if (rc < 0)
    + goto err_register;
    +
    + /* Decide if interrupts can be used */
    + if ((ms->irq0 != NO_IRQ) && (ms->irq1 != NO_IRQ)) {
    + rc = request_irq(ms->irq0, mpc52xx_spi_irq, IRQF_SAMPLE_RANDOM,
    + "mpc5200-spi-modf", ms);
    + rc |= request_irq(ms->irq1, mpc52xx_spi_irq, IRQF_SAMPLE_RANDOM,
    + "mpc5200-spi-spiF", ms);
    + if (rc) {
    + free_irq(ms->irq0, ms);
    + free_irq(ms->irq1, ms);
    + ms->irq0 = ms->irq1 = NO_IRQ;
    + dev_info(&op->dev, "using polled mode\n");
    + }
    + } else {
    + /* operate in polled mode */
    + ms->irq0 = ms->irq1 = NO_IRQ;
    + dev_info(&op->dev, "using polled mode\n");
    + }
    +
    + /* Create SysFS files */
    + rc = device_create_file(&ms->master->dev, &dev_attr_msg_count);
    + rc |= device_create_file(&ms->master->dev, &dev_attr_byte_count);
    + rc |= device_create_file(&ms->master->dev, &dev_attr_wcol_count);
    + rc |= device_create_file(&ms->master->dev, &dev_attr_wcol_ticks);
    + rc |= device_create_file(&ms->master->dev, &dev_attr_modf_count);
    + if (rc)
    + dev_info(&ms->master->dev, "error creating sysfs files\n");
    +
    + dev_info(&ms->master->dev, "registered MPC5200 SPI bus\n");
    +
    + of_register_spi_devices(master, op->node);
    +
    + return rc;
    +
    + err_register:
    + dev_err(&ms->master->dev, "initialization failed\n");
    + spi_master_put(master);
    + return rc;
    +}
    +
    +static void __devexit mpc52xx_spi_of_remove(struct of_device *op)
    +{
    + struct spi_master *master = dev_get_drvdata(&op->dev);
    + struct mpc52xx_spi *ms = spi_master_get_devdata(master);
    +
    + device_remove_file(&ms->master->dev, &dev_attr_msg_count);
    + device_remove_file(&ms->master->dev, &dev_attr_byte_count);
    + device_remove_file(&ms->master->dev, &dev_attr_wcol_count);
    + device_remove_file(&ms->master->dev, &dev_attr_wcol_ticks);
    + device_remove_file(&ms->master->dev, &dev_attr_modf_count);
    +
    + free_irq(ms->irq0, ms);
    + free_irq(ms->irq1, ms);
    +
    + spi_unregister_master(master);
    + spi_master_put(master);
    + iounmap(ms->regs);
    +}
    +
    +static struct of_device_id mpc52xx_spi_of_match[] __devinitdata = {
    + { .compatible = "fsl,mpc5200-spi", },
    + {}
    +};
    +MODULE_DEVICE_TABLE(of, mpc52xx_psc_spi_of_match);
    +
    +static struct of_platform_driver mpc52xx_spi_of_driver = {
    + .owner = THIS_MODULE,
    + .name = "mpc52xx-spi",
    + .match_table = mpc52xx_spi_of_match,
    + .probe = mpc52xx_spi_of_probe,
    + .remove = __exit_p(mpc52xx_spi_of_remove),
    +};
    +
    +static int __init mpc52xx_spi_init(void)
    +{
    + return of_register_platform_driver(&mpc52xx_spi_of_driver);
    +}
    +module_init(mpc52xx_spi_init);
    +
    +static void __exit mpc52xx_spi_exit(void)
    +{
    + of_unregister_platform_driver(&mpc52xx_spi_of_driver);
    +}
    +module_exit(mpc52xx_spi_exit);
    +
    diff --git a/include/linux/spi/mpc52xx_spi.h b/include/linux/spi/mpc52xx_spi.h
    new file mode 100644
    index 0000000..d1004cf
    --- /dev/null
    +++ b/include/linux/spi/mpc52xx_spi.h
    @@ -0,0 +1,10 @@
    +
    +#ifndef INCLUDE_MPC5200_SPI_H
    +#define INCLUDE_MPC5200_SPI_H
    +
    +extern void mpc52xx_spi_set_premessage_hook(struct spi_master *master,
    + void (*hook)(struct spi_message *m,
    + void *context),
    + void *hook_context);
    +
    +#endif

    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  4. [PATCH v2 4/5] spi: Add OF binding support for SPI busses

    From: Grant Likely

    This patch adds support for populating an SPI bus based on data in the
    OF device tree. This is useful for powerpc platforms which use the
    device tree instead of discrete code for describing platform layout.

    Signed-off-by: Grant Likely
    ---

    drivers/of/Kconfig | 6 +++
    drivers/of/Makefile | 1 +
    drivers/of/of_spi.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++
    include/linux/of_spi.h | 18 ++++++++++
    4 files changed, 113 insertions(+), 0 deletions(-)

    diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
    index 3a7a11a..edd6e92 100644
    --- a/drivers/of/Kconfig
    +++ b/drivers/of/Kconfig
    @@ -13,3 +13,9 @@ config OF_I2C
    depends on PPC_OF && I2C
    help
    OpenFirmware I2C accessors
    +
    +config OF_SPI
    + def_tristate SPI
    + depends on OF && PPC_OF && SPI
    + help
    + OpenFirmware SPI accessors
    diff --git a/drivers/of/Makefile b/drivers/of/Makefile
    index 548772e..4c3c6f8 100644
    --- a/drivers/of/Makefile
    +++ b/drivers/of/Makefile
    @@ -2,3 +2,4 @@ obj-y = base.o
    obj-$(CONFIG_OF_DEVICE) += device.o platform.o
    obj-$(CONFIG_OF_GPIO) += gpio.o
    obj-$(CONFIG_OF_I2C) += of_i2c.o
    +obj-$(CONFIG_OF_SPI) += of_spi.o
    diff --git a/drivers/of/of_spi.c b/drivers/of/of_spi.c
    new file mode 100644
    index 0000000..ed0c807
    --- /dev/null
    +++ b/drivers/of/of_spi.c
    @@ -0,0 +1,88 @@
    +/*
    + * SPI OF support routines
    + * Copyright (C) 2008 Secret Lab Technologies Ltd.
    + *
    + * Support routines for deriving SPI device attachments from the device
    + * tree.
    + */
    +
    +#include
    +#include
    +#include
    +#include
    +
    +/**
    + * of_register_spi_devices - Register child devices onto the SPI bus
    + * @master: Pointer to spi_master device
    + * @np: parent node of SPI device nodes
    + *
    + * Registers an spi_device for each child node of 'np' which has a 'reg'
    + * property.
    + */
    +void of_register_spi_devices(struct spi_master *master, struct device_node *np)
    +{
    + struct spi_device *spi;
    + struct device_node *nc;
    + const u32 *prop;
    + const char *sprop;
    + int rc;
    + int len;
    +
    + for_each_child_of_node(np, nc) {
    + /* Alloc an spi_device */
    + spi = spi_alloc_device(master);
    + if (!spi) {
    + dev_err(&master->dev, "spi_device alloc error for %s\n",
    + nc->full_name);
    + continue;
    + }
    +
    + /* Device address */
    + prop = of_get_property(nc, "reg", &len);
    + if (!prop || len < sizeof(*prop)) {
    + dev_err(&master->dev, "%s has no 'reg' property\n",
    + nc->full_name);
    + continue;
    + }
    + spi->chip_select = *prop;
    +
    + /* Mode (clock phase/polarity/etc.) */
    + if (of_find_property(nc, "spi,cpha", NULL))
    + spi->mode |= SPI_CPHA;
    + if (of_find_property(nc, "spi,cpol", NULL))
    + spi->mode |= SPI_CPOL;
    +
    + /* Device speed */
    + prop = of_get_property(nc, "max-speed", &len);
    + if (!prop || len < sizeof(*prop)) {
    + dev_err(&master->dev, "%s has no 'max-speed' property\n",
    + nc->full_name);
    + continue;
    + }
    + spi->max_speed_hz = *prop;
    +
    + /* IRQ */
    + spi->irq = irq_of_parse_and_map(nc, 0);
    +
    + /* Select device driver */
    + sprop = of_get_property(nc, "linux,modalias", &len);
    + if (sprop && len > 0)
    + strncpy(spi->modalias, sprop, KOBJ_NAME_LEN);
    + else
    + strncpy(spi->modalias, "spidev", KOBJ_NAME_LEN);
    +
    + /* Store a pointer to the node in the device structure */
    + of_node_get(nc);
    + spi->dev.archdata.of_node = nc;
    +
    + /* Register the new device */
    + rc = spi_add_device(spi);
    + if (rc) {
    + dev_err(&master->dev, "spi_device register error %s\n",
    + nc->full_name);
    + spi_dev_put(spi);
    + }
    +
    + }
    +}
    +EXPORT_SYMBOL(of_register_spi_devices);
    diff --git a/include/linux/of_spi.h b/include/linux/of_spi.h
    new file mode 100644
    index 0000000..5f71ee8
    --- /dev/null
    +++ b/include/linux/of_spi.h
    @@ -0,0 +1,18 @@
    +/*
    + * OpenFirmware SPI support routines
    + * Copyright (C) 2008 Secret Lab Technologies Ltd.
    + *
    + * Support routines for deriving SPI device attachments from the device
    + * tree.
    + */
    +
    +#ifndef __LINUX_OF_SPI_H
    +#define __LINUX_OF_SPI_H
    +
    +#include
    +#include
    +
    +extern void of_register_spi_devices(struct spi_master *master,
    + struct device_node *np);
    +
    +#endif /* __LINUX_OF_SPI */

    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  5. Re: [PATCH v2 4/5] spi: Add OF binding support for SPI busses

    On 7/2/08, Grant Likely wrote:
    > From: Grant Likely
    >
    > This patch adds support for populating an SPI bus based on data in the
    > OF device tree. This is useful for powerpc platforms which use the
    > device tree instead of discrete code for describing platform layout.
    >
    > Signed-off-by: Grant Likely
    > ---
    >
    > drivers/of/Kconfig | 6 +++
    > drivers/of/Makefile | 1 +
    > drivers/of/of_spi.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++
    > include/linux/of_spi.h | 18 ++++++++++
    > 4 files changed, 113 insertions(+), 0 deletions(-)
    >
    > diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
    > index 3a7a11a..edd6e92 100644
    > --- a/drivers/of/Kconfig
    > +++ b/drivers/of/Kconfig
    > @@ -13,3 +13,9 @@ config OF_I2C
    > depends on PPC_OF && I2C
    > help
    > OpenFirmware I2C accessors
    > +
    > +config OF_SPI
    > + def_tristate SPI
    > + depends on OF && PPC_OF && SPI
    > + help
    > + OpenFirmware SPI accessors
    > diff --git a/drivers/of/Makefile b/drivers/of/Makefile
    > index 548772e..4c3c6f8 100644
    > --- a/drivers/of/Makefile
    > +++ b/drivers/of/Makefile
    > @@ -2,3 +2,4 @@ obj-y = base.o
    > obj-$(CONFIG_OF_DEVICE) += device.o platform.o
    > obj-$(CONFIG_OF_GPIO) += gpio.o
    > obj-$(CONFIG_OF_I2C) += of_i2c.o
    > +obj-$(CONFIG_OF_SPI) += of_spi.o
    > diff --git a/drivers/of/of_spi.c b/drivers/of/of_spi.c
    > new file mode 100644
    > index 0000000..ed0c807
    > --- /dev/null
    > +++ b/drivers/of/of_spi.c
    > @@ -0,0 +1,88 @@
    > +/*
    > + * SPI OF support routines
    > + * Copyright (C) 2008 Secret Lab Technologies Ltd.
    > + *
    > + * Support routines for deriving SPI device attachments from the device
    > + * tree.
    > + */
    > +
    > +#include
    > +#include
    > +#include
    > +#include
    > +
    > +/**
    > + * of_register_spi_devices - Register child devices onto the SPI bus
    > + * @master: Pointer to spi_master device
    > + * @np: parent node of SPI device nodes
    > + *
    > + * Registers an spi_device for each child node of 'np' which has a 'reg'
    > + * property.
    > + */
    > +void of_register_spi_devices(struct spi_master *master, struct device_node *np)
    > +{
    > + struct spi_device *spi;
    > + struct device_node *nc;
    > + const u32 *prop;
    > + const char *sprop;
    > + int rc;
    > + int len;
    > +
    > + for_each_child_of_node(np, nc) {
    > + /* Alloc an spi_device */
    > + spi = spi_alloc_device(master);
    > + if (!spi) {
    > + dev_err(&master->dev, "spi_device alloc error for %s\n",
    > + nc->full_name);
    > + continue;
    > + }
    > +
    > + /* Device address */
    > + prop = of_get_property(nc, "reg", &len);
    > + if (!prop || len < sizeof(*prop)) {
    > + dev_err(&master->dev, "%s has no 'reg' property\n",
    > + nc->full_name);
    > + continue;
    > + }
    > + spi->chip_select = *prop;
    > +
    > + /* Mode (clock phase/polarity/etc.) */
    > + if (of_find_property(nc, "spi,cpha", NULL))
    > + spi->mode |= SPI_CPHA;
    > + if (of_find_property(nc, "spi,cpol", NULL))
    > + spi->mode |= SPI_CPOL;
    > +
    > + /* Device speed */
    > + prop = of_get_property(nc, "max-speed", &len);
    > + if (!prop || len < sizeof(*prop)) {
    > + dev_err(&master->dev, "%s has no 'max-speed' property\n",
    > + nc->full_name);
    > + continue;
    > + }
    > + spi->max_speed_hz = *prop;
    > +
    > + /* IRQ */
    > + spi->irq = irq_of_parse_and_map(nc, 0);
    > +
    > + /* Select device driver */
    > + sprop = of_get_property(nc, "linux,modalias", &len);
    > + if (sprop && len > 0)
    > + strncpy(spi->modalias, sprop, KOBJ_NAME_LEN);
    > + else
    > + strncpy(spi->modalias, "spidev", KOBJ_NAME_LEN);


    You're missing a request_module("%s", info.type) to make sure the
    module is loaded.

    It might make sense to share code with of_find_i2c_driver() so we have
    a common way of guessing module names.

    > +
    > + /* Store a pointer to the node in the device structure */
    > + of_node_get(nc);
    > + spi->dev.archdata.of_node = nc;
    > +
    > + /* Register the new device */
    > + rc = spi_add_device(spi);
    > + if (rc) {
    > + dev_err(&master->dev, "spi_device register error %s\n",
    > + nc->full_name);
    > + spi_dev_put(spi);
    > + }
    > +
    > + }
    > +}
    > +EXPORT_SYMBOL(of_register_spi_devices);
    > diff --git a/include/linux/of_spi.h b/include/linux/of_spi.h
    > new file mode 100644
    > index 0000000..5f71ee8
    > --- /dev/null
    > +++ b/include/linux/of_spi.h
    > @@ -0,0 +1,18 @@
    > +/*
    > + * OpenFirmware SPI support routines
    > + * Copyright (C) 2008 Secret Lab Technologies Ltd.
    > + *
    > + * Support routines for deriving SPI device attachments from the device
    > + * tree.
    > + */
    > +
    > +#ifndef __LINUX_OF_SPI_H
    > +#define __LINUX_OF_SPI_H
    > +
    > +#include
    > +#include
    > +
    > +extern void of_register_spi_devices(struct spi_master *master,
    > + struct device_node *np);
    > +
    > +#endif /* __LINUX_OF_SPI */
    >
    >



    --
    Jon Smirl
    jonsmirl@gmail.com
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  6. RE: [PATCH v2 4/5] spi: Add OF binding support for SPI busses



    > -----Original Message-----
    > From: linuxppc-dev-bounces+b11801=freescale.com@ozlabs.org
    > [mailto:linuxppc-dev-bounces+b11801=freescale.com@ozlabs.org]
    > On Behalf Of Grant Likely
    > Sent: 2008?7?3? 9:03
    > To: linuxppc-dev@ozlabs.org;
    > spi-devel-general@lists.sourceforge.net; linux-kernel@vger.kernel.org
    > Cc: david-b@pacbell.net; fabrizio.garetto@gmail.com
    > Subject: [PATCH v2 4/5] spi: Add OF binding support for SPI busses
    >
    > From: Grant Likely
    >
    > This patch adds support for populating an SPI bus based on data in the
    > OF device tree. This is useful for powerpc platforms which use the
    > device tree instead of discrete code for describing platform layout.
    >
    > Signed-off-by: Grant Likely
    > ---
    >
    > drivers/of/Kconfig | 6 +++
    > drivers/of/Makefile | 1 +
    > drivers/of/of_spi.c | 88
    > ++++++++++++++++++++++++++++++++++++++++++++++++
    > include/linux/of_spi.h | 18 ++++++++++
    > 4 files changed, 113 insertions(+), 0 deletions(-)
    >
    > diff --git a/drivers/of/Kconfig b/drivers/of/Kconfig
    > index 3a7a11a..edd6e92 100644
    > --- a/drivers/of/Kconfig
    > +++ b/drivers/of/Kconfig
    > @@ -13,3 +13,9 @@ config OF_I2C
    > depends on PPC_OF && I2C
    > help
    > OpenFirmware I2C accessors
    > +
    > +config OF_SPI
    > + def_tristate SPI
    > + depends on OF && PPC_OF && SPI
    > + help
    > + OpenFirmware SPI accessors
    > diff --git a/drivers/of/Makefile b/drivers/of/Makefile
    > index 548772e..4c3c6f8 100644
    > --- a/drivers/of/Makefile
    > +++ b/drivers/of/Makefile
    > @@ -2,3 +2,4 @@ obj-y = base.o
    > obj-$(CONFIG_OF_DEVICE) += device.o platform.o
    > obj-$(CONFIG_OF_GPIO) += gpio.o
    > obj-$(CONFIG_OF_I2C) += of_i2c.o
    > +obj-$(CONFIG_OF_SPI) += of_spi.o
    > diff --git a/drivers/of/of_spi.c b/drivers/of/of_spi.c
    > new file mode 100644
    > index 0000000..ed0c807
    > --- /dev/null
    > +++ b/drivers/of/of_spi.c
    > @@ -0,0 +1,88 @@
    > +/*
    > + * SPI OF support routines
    > + * Copyright (C) 2008 Secret Lab Technologies Ltd.
    > + *
    > + * Support routines for deriving SPI device attachments from
    > the device
    > + * tree.
    > + */
    > +
    > +#include
    > +#include
    > +#include
    > +#include
    > +
    > +/**
    > + * of_register_spi_devices - Register child devices onto the SPI bus
    > + * @master: Pointer to spi_master device
    > + * @np: parent node of SPI device nodes
    > + *
    > + * Registers an spi_device for each child node of 'np' which
    > has a 'reg'
    > + * property.
    > + */
    > +void of_register_spi_devices(struct spi_master *master,
    > struct device_node *np)
    > +{
    > + struct spi_device *spi;
    > + struct device_node *nc;
    > + const u32 *prop;
    > + const char *sprop;
    > + int rc;
    > + int len;
    > +
    > + for_each_child_of_node(np, nc) {
    > + /* Alloc an spi_device */
    > + spi = spi_alloc_device(master);
    > + if (!spi) {
    > + dev_err(&master->dev, "spi_device alloc
    > error for %s\n",
    > + nc->full_name);
    > + continue;
    > + }
    > +
    > + /* Device address */
    > + prop = of_get_property(nc, "reg", &len);
    > + if (!prop || len < sizeof(*prop)) {
    > + dev_err(&master->dev, "%s has no 'reg'
    > property\n",
    > + nc->full_name);
    > + continue;
    > + }
    > + spi->chip_select = *prop;
    > +
    > + /* Mode (clock phase/polarity/etc.) */
    > + if (of_find_property(nc, "spi,cpha", NULL))
    > + spi->mode |= SPI_CPHA;
    > + if (of_find_property(nc, "spi,cpol", NULL))
    > + spi->mode |= SPI_CPOL;


    so becuase in function spi_alloc_deive, spi is allocated by kzalloc,
    how about writing as follows:
    /* Mode (clock phase/polarity/etc.) */
    prop = of_get_property(nc, "spi,cpha", NULL))
    if (prop)
    spi->mode |= *prop;
    prop = of_get_property(nc, "spi,cpol", NULL))
    if (prop)
    spi->mode |= *prop;

    > +
    > + /* Device speed */
    > + prop = of_get_property(nc, "max-speed", &len);
    > + if (!prop || len < sizeof(*prop)) {
    > + dev_err(&master->dev, "%s has no
    > 'max-speed' property\n",
    > + nc->full_name);
    > + continue;
    > + }
    > + spi->max_speed_hz = *prop;
    > +
    > + /* IRQ */
    > + spi->irq = irq_of_parse_and_map(nc, 0);
    > +
    > + /* Select device driver */
    > + sprop = of_get_property(nc, "linux,modalias", &len);
    > + if (sprop && len > 0)
    > + strncpy(spi->modalias, sprop, KOBJ_NAME_LEN);
    > + else
    > + strncpy(spi->modalias, "spidev", KOBJ_NAME_LEN);
    > +

    how about writing as follows:

    if (sprop && len > 0)
    strncpy(spi->modalias, sprop, KOBJ_NAME_LEN -
    1);
    else
    strncpy(spi->modalias, "spidev", KOBJ_NAME_LEN -
    1);
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  7. Re: [PATCH v2 4/5] spi: Add OF binding support for SPI busses

    On Fri, Jul 04, 2008 at 11:54:57AM +0800, Chen Gong wrote:
    > Grant Likely wrote:
    > > + /* Mode (clock phase/polarity/etc.) */
    > > + if (of_find_property(nc, "spi,cpha", NULL))
    > > + spi->mode |= SPI_CPHA;
    > > + if (of_find_property(nc, "spi,cpol", NULL))
    > > + spi->mode |= SPI_CPOL;

    >
    > so becuase in function spi_alloc_deive, spi is allocated by kzalloc,
    > how about writing as follows:
    > /* Mode (clock phase/polarity/etc.) */
    > prop = of_get_property(nc, "spi,cpha", NULL))
    > if (prop)
    > spi->mode |= *prop;
    > prop = of_get_property(nc, "spi,cpol", NULL))
    > if (prop)
    > spi->mode |= *prop;


    spi,cpha and spi,cpol are defined as empty properties. The presence of
    the property in the node means I need to set the flag in the spi_device.

    > > + /* Select device driver */
    > > + sprop = of_get_property(nc, "linux,modalias", &len);
    > > + if (sprop && len > 0)
    > > + strncpy(spi->modalias, sprop, KOBJ_NAME_LEN);
    > > + else
    > > + strncpy(spi->modalias, "spidev", KOBJ_NAME_LEN);
    > > +

    > how about writing as follows:
    >
    > if (sprop && len > 0)
    > strncpy(spi->modalias, sprop, KOBJ_NAME_LEN -
    > 1);
    > else
    > strncpy(spi->modalias, "spidev", KOBJ_NAME_LEN -
    > 1);


    Actually, neither are very good. What it should really be is:

    if (sprop && len > 0)
    strlcpy(spi->modalias, sprop, sizeof (spi->modalias));
    else
    strlcpy(spi->modalias, "spidev", sizeof (spi->modalias));

    This ensures that the string is always null terminated and always the
    right size.

    .... But the whole argument is a bit moot. The fact that I even defined
    a "linux,modalias" property is a great big hairy hack that I would
    never want my mother to see. Instead, I need a method to bind to an SPI
    driver based on the compatible list. Jon Smirl has done some work
    in this regard for i2c, but I haven't looked at it very deeply, and so
    do not at all understand it (yet).

    Thanks for the comments.
    g.
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

  8. Re: [PATCH v2 4/5] spi: Add OF binding support for SPI busses

    On Wed, Jul 02, 2008 at 11:02:23PM -0400, Jon Smirl wrote:
    > On 7/2/08, Grant Likely wrote:
    > > From: Grant Likely
    > >
    > > This patch adds support for populating an SPI bus based on data in the
    > > OF device tree. This is useful for powerpc platforms which use the
    > > device tree instead of discrete code for describing platform layout.
    > >
    > > Signed-off-by: Grant Likely
    > > ---
    > > + /* Select device driver */
    > > + sprop = of_get_property(nc, "linux,modalias", &len);
    > > + if (sprop && len > 0)
    > > + strncpy(spi->modalias, sprop, KOBJ_NAME_LEN);
    > > + else
    > > + strncpy(spi->modalias, "spidev", KOBJ_NAME_LEN);

    >
    > You're missing a request_module("%s", info.type) to make sure the
    > module is loaded.
    >
    > It might make sense to share code with of_find_i2c_driver() so we have
    > a common way of guessing module names.


    You're right. I've refactored the i2c code to make it usable by SPI
    also. I'll post the new patch series this evening.

    g.
    --
    To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
    the body of a message to majordomo@vger.kernel.org
    More majordomo info at http://vger.kernel.org/majordomo-info.html
    Please read the FAQ at http://www.tux.org/lkml/

+ Reply to Thread