Re: [spi-devel-general] [PATCH 1/3] [SPI] [OMAP] Add OMAP spi100k driver

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Mon, Dec 7, 2009 at 1:48 PM, Cory Maccarrone <darkstar6262@xxxxxxxxx> wrote:
> This change adds the OMAP SPI 100k driver created by
> Fabrice Crohas <fcrohas@xxxxxxxxx>.  This SPI bus is found on
> OMAP7xx-series smartphones, and for many, the touchscreen is
> attached to this bus.
>
> The lion's share of the work was done by Fabrice on this driver --
> I am merely porting it from the Linwizard project on his behalf.
>
> Signed-off-by: Cory Maccarrone <darkstar6262@xxxxxxxxx>
> ---
>  drivers/spi/Kconfig         |    6 +
>  drivers/spi/Makefile        |    1 +
>  drivers/spi/omap_spi_100k.c |  642 +++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 649 insertions(+), 0 deletions(-)
>  create mode 100644 drivers/spi/omap_spi_100k.c
>
> diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
> index 4b6f7cb..7ef9b12 100644
> --- a/drivers/spi/Kconfig
> +++ b/drivers/spi/Kconfig
> @@ -164,6 +164,12 @@ config SPI_OMAP24XX
>          SPI master controller for OMAP24xx/OMAP34xx Multichannel SPI
>          (McSPI) modules.
>
> +config SPI_OMAP_100K
> +       tristate "OMAP SPI 100K"
> +       depends on SPI_MASTER && (ARCH_OMAP850 || ARCH_OMAP730)
> +       help
> +         OMAP SPI 100K master controller for omap7xx boards.
> +
>  config SPI_ORION
>        tristate "Orion SPI master (EXPERIMENTAL)"
>        depends on PLAT_ORION && EXPERIMENTAL
> diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
> index 21a1182..55f670d 100644
> --- a/drivers/spi/Makefile
> +++ b/drivers/spi/Makefile
> @@ -22,6 +22,7 @@ obj-$(CONFIG_SPI_LM70_LLP)            += spi_lm70llp.o
>  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_OMAP_100K)            += omap_spi_100k.o
>  obj-$(CONFIG_SPI_ORION)                        += orion_spi.o
>  obj-$(CONFIG_SPI_PL022)                        += amba-pl022.o
>  obj-$(CONFIG_SPI_MPC52xx_PSC)          += mpc52xx_psc_spi.o
> diff --git a/drivers/spi/omap_spi_100k.c b/drivers/spi/omap_spi_100k.c
> new file mode 100644
> index 0000000..d0ebfa8
> --- /dev/null
> +++ b/drivers/spi/omap_spi_100k.c
> @@ -0,0 +1,642 @@
> +/*
> + * OMAP7xx SPI 100k controller driver
> + * Author: Fabrice Crohas <fcrohas@xxxxxxxxx>
> + * from original omap1_mcspi driver
> + *
> + * Copyright (C) 2005, 2006 Nokia Corporation
> + * Author:      Samuel Ortiz <samuel.ortiz@xxxxxxxxx> and
> + *              Juha Yrj�l� <juha.yrjola@xxxxxxxxx>
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
> + * GNU General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public License
> + * along with this program; if not, write to the Free Software
> + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
> + *
> + */
> +#include <linux/kernel.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/module.h>
> +#include <linux/device.h>
> +#include <linux/delay.h>
> +#include <linux/platform_device.h>
> +#include <linux/err.h>
> +#include <linux/clk.h>
> +#include <linux/io.h>
> +#include <linux/gpio.h>
> +
> +#include <linux/spi/spi.h>
> +
> +#include <plat/clock.h>
> +
> +#define OMAP1_SPI100K_MAX_FREQ          48000000
> +
> +#define ICR_SPITAS      (OMAP7XX_ICR_BASE + 0x12)
> +
> +#define SPI_SETUP1      0x00
> +#define SPI_SETUP2      0x02
> +#define SPI_CTRL        0x04
> +#define SPI_STATUS      0x06
> +#define SPI_TX_LSB      0x08
> +#define SPI_TX_MSB      0x0a
> +#define SPI_RX_LSB      0x0c
> +#define SPI_RX_MSB      0x0e
> +
> +#define SPI_SETUP1_INT_READ_ENABLE      (1UL << 5)
> +#define SPI_SETUP1_INT_WRITE_ENABLE     (1UL << 4)
> +#define SPI_SETUP1_CLOCK_DIVISOR(x)     ((x) << 1)
> +#define SPI_SETUP1_CLOCK_ENABLE         (1UL << 0)
> +
> +#define SPI_SETUP2_ACTIVE_EDGE_FALLING  (0UL << 0)
> +#define SPI_SETUP2_ACTIVE_EDGE_RISING   (1UL << 0)
> +#define SPI_SETUP2_NEGATIVE_LEVEL       (0UL << 5)
> +#define SPI_SETUP2_POSITIVE_LEVEL       (1UL << 5)
> +#define SPI_SETUP2_LEVEL_TRIGGER        (0UL << 10)
> +#define SPI_SETUP2_EDGE_TRIGGER         (1UL << 10)
> +
> +#define SPI_CTRL_SEN(x)                 ((x) << 7)
> +#define SPI_CTRL_WORD_SIZE(x)           (((x) - 1) << 2)
> +#define SPI_CTRL_WR                     (1UL << 1)
> +#define SPI_CTRL_RD                     (1UL << 0)
> +
> +#define SPI_STATUS_WE                   (1UL << 1)
> +#define SPI_STATUS_RD                   (1UL << 0)
> +
> +#define WRITE 0
> +#define READ  1
> +
> +
> +/* use PIO for small transfers, avoiding DMA setup/teardown overhead and
> + * cache operations; better heuristics consider wordsize and bitrate.
> + */
> +#define DMA_MIN_BYTES                   8
> +
> +struct omap1_spi100k {
> +       struct work_struct      work;
> +       /* lock protects queue and registers */
> +       spinlock_t              lock;
> +       struct list_head        msg_queue;
> +       struct spi_master       *master;
> +       struct clk              *ick;
> +       struct clk              *fck;
> +       /* Virtual base address of the controller */
> +       void __iomem            *base;
> +       unsigned long           base_addr;
> +};
> +
> +struct omap1_spi100k_cs {
> +       void __iomem            *base;
> +       int                     word_len;
> +};
> +
> +static struct workqueue_struct *omap1_spi100k_wq;
> +
> +#define MOD_REG_BIT(val, mask, set) do { \
> +       if (set) \
> +               val |= mask; \
> +       else \
> +               val &= ~mask; \
> +} while (0)
> +
> +static void spi100k_enable_clock(struct spi_master *master)
> +{
> +       unsigned int val;
> +       struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
> +
> +       /* enable SPI */
> +       val = omap_readw(spi100k->base_addr + SPI_SETUP1);
> +       val |= SPI_SETUP1_CLOCK_ENABLE;
> +       omap_writew(val, spi100k->base_addr + SPI_SETUP1);
> +}
> +
> +static void spi100k_disable_clock(struct spi_master *master)
> +{
> +       unsigned int val;
> +       struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
> +
> +       /* disable SPI */
> +       val = omap_readw(spi100k->base_addr + SPI_SETUP1);
> +       val &= ~SPI_SETUP1_CLOCK_ENABLE;
> +       omap_writew(val, spi100k->base_addr + SPI_SETUP1);
> +}
> +
> +static void spi100k_write_data(struct spi_master *master, int len, int data)
> +{
> +       struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
> +
> +       /* write 16-bit word */
> +       spi100k_enable_clock(master);
> +       omap_writew( data , spi100k->base_addr + SPI_TX_MSB);
> +
> +       omap_writew(SPI_CTRL_SEN(0) |
> +                   SPI_CTRL_WORD_SIZE(len) |
> +                   SPI_CTRL_WR,
> +                   spi100k->base_addr + SPI_CTRL);
> +
> +       /* Wait for bit ack send change */
> +       while((omap_readw(spi100k->base_addr + SPI_STATUS) & SPI_STATUS_WE) != SPI_STATUS_WE);
> +       udelay(1000);
> +
> +       spi100k_disable_clock(master);
> +}
> +
> +static int spi100k_read_data(struct spi_master *master, int len)
> +{
> +       int dataH,dataL;
> +       struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
> +
> +       spi100k_enable_clock(master);
> +       omap_writew(SPI_CTRL_SEN(0) |
> +                   SPI_CTRL_WORD_SIZE(len) |
> +                   SPI_CTRL_RD,
> +                   spi100k->base_addr + SPI_CTRL);
> +
> +       while((omap_readw(spi100k->base_addr + SPI_STATUS) & SPI_STATUS_RD) != SPI_STATUS_RD);
> +       udelay(1000);
> +
> +       dataL = omap_readw(spi100k->base_addr + SPI_RX_LSB);
> +       dataH = omap_readw(spi100k->base_addr + SPI_RX_MSB);
> +       spi100k_disable_clock(master);
> +
> +       return dataL;
> +}
> +
> +static void spi100k_open(struct spi_master *master)
> +{
> +       /* get control of SPI */
> +       struct omap1_spi100k *spi100k = spi_master_get_devdata(master);
> +
> +       omap_writew(SPI_SETUP1_INT_READ_ENABLE |
> +                   SPI_SETUP1_INT_WRITE_ENABLE |
> +                   SPI_SETUP1_CLOCK_DIVISOR(0), spi100k->base_addr + SPI_SETUP1);
> +
> +       /* configure clock and interrupts */
> +       omap_writew(SPI_SETUP2_ACTIVE_EDGE_FALLING |
> +                   SPI_SETUP2_NEGATIVE_LEVEL |
> +                   SPI_SETUP2_LEVEL_TRIGGER, spi100k->base_addr + SPI_SETUP2);
> +
> +}
> +
> +static void spi100k_close(struct spi_master *master)
> +{
> +}
setting master->cleanup = NULL shud do

> +static void omap1_spi100k_force_cs(struct omap1_spi100k *spi100k, int enable)
> +{
> +       if (enable)
> +               omap_writew(0x05fc, spi100k->base_addr + SPI_CTRL);
> +       else
> +               omap_writew(0x05fd, spi100k->base_addr + SPI_CTRL);
> +}
> +
> +static void omap1_spi100k_set_enable(struct omap1_spi100k *spi100k, int enable)
> +{
> +}
how about dropping it altogether?

> +static unsigned
> +omap1_spi100k_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer)
> +{
> +       struct omap1_spi100k    *spi100k;
> +       struct omap1_spi100k_cs *cs = spi->controller_state;
> +       unsigned int            count, c;
> +       int                     word_len;
> +
> +       spi100k = spi_master_get_devdata(spi->master);
> +       count = xfer->len;
> +       c = count;
> +       word_len = cs->word_len;
> +
> +       /* RX_ONLY mode needs dummy data in TX reg */
> +       if (xfer->tx_buf == NULL)
> +               spi100k_write_data(spi->master,word_len, 0);
> +
> +       if (word_len <= 8) {
> +               u8              *rx;
> +               const u8        *tx;
> +
> +               rx = xfer->rx_buf;
> +               tx = xfer->tx_buf;
> +               do {
> +                       c-=1;
> +                       if (xfer->tx_buf != NULL)
> +                               spi100k_write_data(spi->master,word_len, *tx);
> +                       if (xfer->rx_buf != NULL)
> +                               *rx = spi100k_read_data(spi->master,word_len);
> +               } while(c);
> +       } else if (word_len <= 16) {
> +               u16             *rx;
> +               const u16       *tx;
> +
> +               rx = xfer->rx_buf;
> +               tx = xfer->tx_buf;
> +               do {
> +                       c-=2;
> +                       if (xfer->tx_buf != NULL)
> +                               spi100k_write_data(spi->master,word_len, *tx++);
> +                       if (xfer->rx_buf != NULL)
> +                               *rx++ = spi100k_read_data(spi->master,word_len);
> +               } while(c);
> +       } else if (word_len <= 32) {
> +               u32             *rx;
> +               const u32       *tx;
> +
> +               rx = xfer->rx_buf;
> +               tx = xfer->tx_buf;
> +               do {
> +                       c-=4;
> +                       if (xfer->tx_buf != NULL)
> +                               spi100k_write_data(spi->master,word_len, *tx);
> +                       if (xfer->rx_buf != NULL)
> +                               *rx = spi100k_read_data(spi->master,word_len);
> +               } while(c);
> +       }
> +       return count - c;
> +}
> +
> +/* called only when no transfer is active to this device */
> +static int omap1_spi100k_setup_transfer(struct spi_device *spi,
> +               struct spi_transfer *t)
> +{
> +       struct omap1_spi100k *spi100k = spi_master_get_devdata(spi->master);
> +       struct omap1_spi100k_cs *cs = spi->controller_state;
> +       u8 word_len = spi->bits_per_word;
> +
> +       if (t != NULL && t->bits_per_word)
> +               word_len = t->bits_per_word;
> +       if (!word_len)
> +               word_len = 8;
> +
> +       if (spi->bits_per_word > 32)
> +               return -EINVAL;
> +       cs->word_len = word_len;
> +
> +       /* SPI init before transfer */
> +       omap_writew(0x3e , spi100k->base_addr + SPI_SETUP1);
> +       omap_writew(0x00 , spi100k->base_addr + SPI_STATUS);
> +       omap_writew(0x3e , spi100k->base_addr + SPI_CTRL);
> +
> +       return 0;
> +}
> +
> +/* the spi->mode bits understood by this driver: */
> +#define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH)
> +
> +static int omap1_spi100k_setup(struct spi_device *spi)
> +{
> +       int                     ret;
> +       struct omap1_spi100k    *spi100k;
> +       struct omap1_spi100k_cs *cs = spi->controller_state;
> +
> +       if (spi->bits_per_word < 4 || spi->bits_per_word > 32) {
> +                dev_dbg(&spi->dev, "setup: unsupported %d bit words\n",
> +                       spi->bits_per_word);
> +                return -EINVAL;
> +       }
> +
> +       spi100k = spi_master_get_devdata(spi->master);
> +
> +       if (!cs) {
> +               cs = kzalloc(sizeof *cs, GFP_KERNEL);
> +               if (!cs)
> +                       return -ENOMEM;
> +               cs->base = spi100k->base + spi->chip_select * 0x14;
> +               spi->controller_state = cs;
> +       }
> +
> +       spi100k_open(spi->master);
> +
> +       clk_enable(spi100k->ick);
> +       clk_enable(spi100k->fck);
> +
> +       ret = omap1_spi100k_setup_transfer(spi, NULL);
> +
> +       clk_disable(spi100k->ick);
> +       clk_disable(spi100k->fck);
> +
> +       return ret;
> +}
> +
> +static void omap1_spi100k_cleanup(struct spi_device *spi)
> +{
> +       spi100k_close(spi->master);
> +}
no need for cleanup callback if u have nothing to do in _close.

> +static void omap1_spi100k_work(struct work_struct *work)
> +{
> +       struct omap1_spi100k    *spi100k;
> +       int status = 0;
> +
> +       spi100k = container_of(work, struct omap1_spi100k, work);
> +       spin_lock_irq(&spi100k->lock);
> +
> +       clk_enable(spi100k->ick);
> +       clk_enable(spi100k->fck);
> +
> +       /* We only enable one channel at a time -- the one whose message is
> +        * at the head of the queue -- although this controller would gladly
> +        * arbitrate among multiple channels.  This corresponds to "single
> +        * channel" master mode.  As a side effect, we need to manage the
> +        * chipselect with the FORCE bit ... CS != channel enable.
> +        */
> +        while (!list_empty(&spi100k->msg_queue)) {
> +               struct spi_message              *m;
> +               struct spi_device               *spi;
> +               struct spi_transfer             *t = NULL;
> +               int                             cs_active = 0;
> +               struct omap1_spi100k_cs         *cs;
> +               int                             par_override = 0;
> +
> +               m = container_of(spi100k->msg_queue.next, struct spi_message,
> +                                queue);
> +
> +               list_del_init(&m->queue);
> +               spin_unlock_irq(&spi100k->lock);
> +
> +               spi = m->spi;
> +               cs = spi->controller_state;
> +
> +               omap1_spi100k_set_enable(spi100k, 1);
> +               list_for_each_entry(t, &m->transfers, transfer_list) {
> +                       if (t->tx_buf == NULL && t->rx_buf == NULL && t->len) {
> +                               status = -EINVAL;
> +                               break;
> +                       }
> +                       if (par_override || t->speed_hz || t->bits_per_word) {
> +                               par_override = 1;
> +                               status = omap1_spi100k_setup_transfer(spi, t);
> +                               if (status < 0)
> +                                       break;
> +                               if (!t->speed_hz && !t->bits_per_word)
> +                                       par_override = 0;
> +                       }
> +
> +                       if (!cs_active) {
> +                               omap1_spi100k_force_cs(spi100k, 1);
> +                               cs_active = 1;
> +                       }
> +
> +                       if (t->len) {
> +                               unsigned count;
> +
> +                               /* RX_ONLY mode needs dummy data in TX reg */
> +                               if (t->tx_buf == NULL)
> +                                       spi100k_write_data(spi->master, 8, 0);
> +
> +                               count = omap1_spi100k_txrx_pio(spi, t);
> +                               m->actual_length += count;
> +
> +                               if (count != t->len) {
> +                                       status = -EIO;
> +                                       break;
> +                               }
> +                       }
> +
> +                       if (t->delay_usecs)
> +                               udelay(t->delay_usecs);
> +
> +                       /* ignore the "leave it on after last xfer" hint */
> +
> +                       if (t->cs_change) {
> +                               omap1_spi100k_force_cs(spi100k, 0);
> +                               cs_active = 0;
> +                       }
> +               }
> +
> +               /* Restore defaults if they were overriden */
> +               if (par_override) {
> +                       par_override = 0;
> +                       status = omap1_spi100k_setup_transfer(spi, NULL);
> +               }
> +
> +               if (cs_active)
> +                       omap1_spi100k_force_cs(spi100k, 0);
> +
> +               omap1_spi100k_set_enable(spi100k, 0);
> +
> +               m->status = status;
> +               m->complete(m->context);
> +
> +               spin_lock_irq(&spi100k->lock);
> +       }
> +
> +       clk_disable(spi100k->ick);
> +       clk_disable(spi100k->fck);
> +       spin_unlock_irq(&spi100k->lock);
> +
> +       if (status < 0) {
> +               printk(KERN_WARNING "spi transfer failed with %d\n", status);
> +       }
no need of {}
> +}
> +
> +static int omap1_spi100k_transfer(struct spi_device *spi, struct spi_message *m)
> +{
> +       struct omap1_spi100k    *spi100k;
> +       unsigned long           flags;
> +       struct spi_transfer     *t;
> +
> +       m->actual_length = 0;
> +       m->status = 0;
 the status shud default to -EINPROGRESS, not success.

> +       /* reject invalid messages and transfers */
> +       if (list_empty(&m->transfers) || !m->complete)
> +               return -EINVAL;
> +
> +       list_for_each_entry(t, &m->transfers, transfer_list) {
> +               const void      *tx_buf = t->tx_buf;
> +               void            *rx_buf = t->rx_buf;
> +               unsigned        len = t->len;
> +
> +               if (t->speed_hz > OMAP1_SPI100K_MAX_FREQ
> +                               || (len && !(rx_buf || tx_buf))
> +                               || (t->bits_per_word &&
> +                                       (  t->bits_per_word < 4
> +                                       || t->bits_per_word > 32))) {
> +                       dev_dbg(&spi->dev, "transfer: %d Hz, %d %s%s, %d bpw\n",
> +                                       t->speed_hz,
> +                                       len,
> +                                       tx_buf ? "tx" : "",
> +                                       rx_buf ? "rx" : "",
> +                                       t->bits_per_word);
> +                       return -EINVAL;
> +               }
> +
> +               if (t->speed_hz && t->speed_hz < OMAP1_SPI100K_MAX_FREQ/(1<<16)) {
> +                       dev_dbg(&spi->dev, "%d Hz max exceeds %d\n",
> +                                       t->speed_hz,
> +                                       OMAP1_SPI100K_MAX_FREQ/(1<<16));
> +                       return -EINVAL;
> +               }
> +
> +       }
> +
> +       spi100k = spi_master_get_devdata(spi->master);
> +
> +       spin_lock_irqsave(&spi100k->lock, flags);
> +       list_add_tail(&m->queue, &spi100k->msg_queue);
> +       queue_work(omap1_spi100k_wq, &spi100k->work);
> +       spin_unlock_irqrestore(&spi100k->lock, flags);
> +
> +       return 0;
> +}
> +
> +static int __init omap1_spi100k_reset(struct omap1_spi100k *spi100k)
> +{
> +       return 0;
> +}
> +
> +static int __devinit omap1_spi100k_probe(struct platform_device *pdev)
> +{
> +       struct spi_master       *master;
> +       struct omap1_spi100k    *spi100k;
> +       struct resource         *r;
> +       int                     status = 0;
> +
> +       if ( !pdev->id)
> +               return -EINVAL;
> +
> +       master = spi_alloc_master(&pdev->dev, sizeof *spi100k);
> +       if (master == NULL) {
> +               dev_dbg(&pdev->dev, "master allocation failed\n");
> +               return -ENOMEM;
> +       }
> +
> +       if (pdev->id != -1)
> +              master->bus_num = pdev->id;
> +
> +       master->setup = omap1_spi100k_setup;
> +       master->transfer = omap1_spi100k_transfer;
> +       master->cleanup = omap1_spi100k_cleanup;
> +       master->num_chipselect =2;
> +       master->mode_bits = MODEBITS;
> +
> +       dev_set_drvdata(&pdev->dev, master);
> +
> +       spi100k = spi_master_get_devdata(master);
> +       spi100k->master = master;
> +
> +       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       if (r == NULL) {
> +                status = -ENODEV;
> +                goto err1;
> +       }
> +       if (!request_mem_region(r->start, (r->end - r->start) + 1,
> +                       pdev->name)) {
> +              status = -EBUSY;
> +              goto err1;
> +       }
> +
> +       spi100k->base = (void __iomem *) OMAP2_L3_IO_ADDRESS(r->start);
> +       spi100k->base_addr = (unsigned long)r->start;
> +       INIT_WORK(&spi100k->work, omap1_spi100k_work);
> +
> +       spin_lock_init(&spi100k->lock);
> +       INIT_LIST_HEAD(&spi100k->msg_queue);
> +       spi100k->ick = clk_get(&pdev->dev, "ick");
> +       if (IS_ERR(spi100k->ick)) {
> +               dev_dbg(&pdev->dev, "can't get spi100k_ick\n");
> +               status = PTR_ERR(spi100k->ick);
> +               goto err1a;
> +       }
> +
> +       spi100k->fck = clk_get(&pdev->dev, "fck");
> +       if (IS_ERR(spi100k->fck)) {
> +               dev_dbg(&pdev->dev, "can't get spi100k_fck\n");
> +               status = PTR_ERR(spi100k->fck);
> +               goto err2;
> +       }
> +
> +       if (omap1_spi100k_reset(spi100k) < 0)
> +               goto err3;
> +
> +       status = spi_register_master(master);
> +       if (status < 0)
> +               goto err3;
> +
> +       printk(KERN_INFO "spi Registered\n");
> +       return status;
> +
> +err3:
> +       clk_put(spi100k->fck);
> +err2:
> +       clk_put(spi100k->ick);
> +err1a:
> +       release_mem_region(r->start, (r->end - r->start) + 1);
> +err1:
> +       spi_master_put(master);
> +       printk(KERN_INFO "Error: spi\n");
> +       return status;
> +}
> +
> +static int __exit omap1_spi100k_remove(struct platform_device *pdev)
> +{
> +       struct spi_master       *master;
> +       struct omap1_spi100k    *spi100k;
> +       struct resource         *r;
> +
> +       master = dev_get_drvdata(&pdev->dev);
> +       spi100k = spi_master_get_devdata(master);
> +
> +       clk_put(spi100k->fck);
> +       clk_put(spi100k->ick);
> +
> +       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       release_mem_region(r->start, (r->end - r->start) + 1);
> +
> +       spi_unregister_master(master);
> +
> +       return 0;
> +}
In remove you might want to wait for mssg queue to be flushed?

> +static struct platform_driver omap1_spi100k_driver = {
> +       .driver = {
> +               .name           = "omap1_spi100k",
> +               .owner          = THIS_MODULE,
> +       },
> +       .probe          = omap1_spi100k_probe,
> +       .remove         = __exit_p(omap1_spi100k_remove),
> +};
> +
> +
> +static int __init omap1_spi100k_init(void)
> +{
> +       int ret;
> +
> +       printk(KERN_INFO "In spi init\n");
> +       omap1_spi100k_wq = create_singlethread_workqueue(
> +                       omap1_spi100k_driver.driver.name);
> +
> +       if (omap1_spi100k_wq == NULL) {
> +               printk(KERN_INFO "Error: spi init\n");
> +               return -1;
> +       }
> +
> +       ret = platform_driver_register(&omap1_spi100k_driver);
> +       if (ret)
> +               return ret;
> +
> +       return 0;
> +}
> +
> +static void __exit omap1_spi100k_exit(void)
> +{
> +       platform_driver_unregister(&omap1_spi100k_driver);
> +
> +       destroy_workqueue(omap1_spi100k_wq);
> +}
> +
> +module_init(omap1_spi100k_init);
> +module_exit(omap1_spi100k_exit);
> +
> +MODULE_DESCRIPTION("OMAP7xx SPI 100k controller driver");
> +MODULE_AUTHOR("Fabrice Crohas <fcrohas@xxxxxxxxx>");
> +MODULE_LICENSE("GPL");
> +
> --
> 1.6.3.3
>
>
>
> ------------------------------------------------------------------------------
> Join us December 9, 2009 for the Red Hat Virtual Experience,
> a free event focused on virtualization and cloud computing.
> Attend in-depth sessions from your desk. Your couch. Anywhere.
> http://p.sf.net/sfu/redhat-sfdev2dev
> _______________________________________________
> spi-devel-general mailing list
> spi-devel-general@xxxxxxxxxxxxxxxxxxxxx
> https://lists.sourceforge.net/lists/listinfo/spi-devel-general
>
>
��.n��������+%������w��{.n�����{�������ܨ}���Ơz�j:+v�����w����ޙ��&�)ߡ�a����z�ޗ���ݢj��w�f


[Index of Archives]     [Linux Arm (vger)]     [ARM Kernel]     [ARM MSM]     [Linux Tegra]     [Linux WPAN Networking]     [Linux Wireless Networking]     [Maemo Users]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite Trails]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux