/* * OMAP850 SPI 100k controller driver * Author: Fabrice Crohas * from original omap1_mcspi driver * * Copyright (C) 2005, 2006 Nokia Corporation * Author: Samuel Ortiz and * Juha Yrj�l� * * 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 #include #include #include #include #include #include #include #include #include #include #include #define VERBOSE #define OMAP1_SPI100K_MAX_FREQ 48000000 /* per-channel banks, 0x14 bytes each, first is: */ #define OMAP1_SPI100K_TX0 0x0A #define OMAP1_SPI100K_RX0 0x0C /* per-register bitmasks: */ #define OMAP1_SPI100K_CHSTAT_RXS (1 << 0) #define OMAP1_SPI100K_CHSTAT_TXS (1 << 0) #define OMAP1_SPI100K_CHSTAT_EOT (1 << 2) /* 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; }; 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 inline void spi100k_write_reg(struct spi_master *master, int idx, u32 val) { struct omap1_spi100k *spi100k = spi_master_get_devdata(master); __raw_writel(val, spi100k->base + idx); } static inline u32 spi100k_read_reg(struct spi_master *master, int idx) { struct omap1_spi100k *spi100k = spi_master_get_devdata(master); return __raw_readl(spi100k->base + idx); } static void omap1_spi100k_set_enable(const struct spi_device *spi, int enable) { u32 l; //l = enable ? OMAP1_SPI100K_CHCTRL_EN : 0; //spi100k_write_cs_reg(spi, OMAP1_SPI100K_CHCTRL0, l); } static int spi100k_wait_for_trigger_bit(void __iomem *reg, unsigned long bit) { unsigned long timeout; /* timeout = jiffies + msecs_to_jiffies(1000); while (!(__raw_readl(reg) & bit)) { if (time_after(jiffies, timeout)) return -1; cpu_relax(); } */ return 0; } 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; u32 l; void __iomem *base = cs->base; void __iomem *tx_reg; void __iomem *rx_reg; void __iomem *chstat_reg_tx; void __iomem *chstat_reg_rx; int word_len; spi100k = spi_master_get_devdata(spi->master); count = xfer->len; c = count; word_len = cs->word_len; /* We store the pre-calculated register addresses on stack to speed * up the transfer loop. */ tx_reg = base + OMAP1_SPI100K_TX0; rx_reg = base + OMAP1_SPI100K_RX0; chstat_reg_tx = base + 0x6; chstat_reg_rx = base + 0x0; // Dump registers dev_dbg(&spi->dev, "read 0x0= %04x\n",__raw_readl(spi100k->base + 0x0) ); dev_dbg(&spi->dev, "read 0x2= %04x\n",__raw_readl(spi100k->base + 0x2) ); dev_dbg(&spi->dev, "read 0x4= %04x\n",__raw_readl(spi100k->base + 0x4) ); dev_dbg(&spi->dev, "read 0x6= %04x\n",__raw_readl(spi100k->base + 0x6) ); dev_dbg(&spi->dev, "read 0x8= %04x\n",__raw_readl(spi100k->base + 0x8) ); dev_dbg(&spi->dev, "read 0xa= %04x\n",__raw_readl(spi100k->base + 0xa) ); dev_dbg(&spi->dev, "read 0xc= %04x\n",__raw_readl(spi100k->base + 0xc) ); dev_dbg(&spi->dev, "read 0xe= %04x\n",__raw_readl(spi100k->base + 0xe) ); if (word_len <= 8) { u8 *rx; const u8 *tx; rx = xfer->rx_buf; tx = xfer->tx_buf; do { c -= 1; if (tx != NULL) { if (spi100k_wait_for_trigger_bit(chstat_reg_tx, OMAP1_SPI100K_CHSTAT_TXS) < 0) { dev_err(&spi->dev, "TXS timed out\n"); goto out; } #ifdef VERBOSE dev_dbg(&spi->dev, "write-%d %02x\n", word_len, *tx); #endif __raw_writel(*tx++, tx_reg); } if (rx != NULL) { if (spi100k_wait_for_trigger_bit(chstat_reg_rx, OMAP1_SPI100K_CHSTAT_RXS) < 0) { dev_err(&spi->dev, "RXS timed out\n"); goto out; } *rx++ = __raw_readl(rx_reg); #ifdef VERBOSE dev_dbg(&spi->dev, "read-%d %02x\n", word_len, *(rx - 1)); #endif } } while (c); } else if (word_len <= 16) { u16 *rx; const u16 *tx; rx = xfer->rx_buf; tx = xfer->tx_buf; do { c -= 2; if (tx != NULL) { if (spi100k_wait_for_trigger_bit(chstat_reg_tx, OMAP1_SPI100K_CHSTAT_TXS) < 0) { dev_err(&spi->dev, "TXS timed out\n"); goto out; } #ifdef VERBOSE dev_dbg(&spi->dev, "write-%d %04x\n", word_len, *tx); #endif __raw_writel(*tx++, tx_reg); } if (rx != NULL) { if (spi100k_wait_for_trigger_bit(chstat_reg_rx, OMAP1_SPI100K_CHSTAT_RXS) < 0) { dev_err(&spi->dev, "RXS timed out\n"); goto out; } *rx++ = __raw_readl(rx_reg); #ifdef VERBOSE dev_dbg(&spi->dev, "read-%d %04x\n", word_len, *(rx - 1)); #endif } } while (c); } else if (word_len <= 32) { u32 *rx; const u32 *tx; rx = xfer->rx_buf; tx = xfer->tx_buf; do { c -= 4; if (tx != NULL) { if (spi100k_wait_for_trigger_bit(chstat_reg_tx, OMAP1_SPI100K_CHSTAT_TXS) < 0) { dev_err(&spi->dev, "TXS timed out\n"); goto out; } #ifdef VERBOSE dev_dbg(&spi->dev, "write-%d %04x\n", word_len, *tx); #endif __raw_writel(*tx++, tx_reg); } if (rx != NULL) { if (spi100k_wait_for_trigger_bit(chstat_reg_rx, OMAP1_SPI100K_CHSTAT_RXS) < 0) { dev_err(&spi->dev, "RXS timed out\n"); goto out; } *rx++ = __raw_readl(rx_reg); #ifdef VERBOSE dev_dbg(&spi->dev, "read-%d %04x\n", word_len, *(rx - 1)); #endif } } while (c); } /* for TX_ONLY mode, be sure all words have shifted out */ if (xfer->rx_buf == NULL) { if (spi100k_wait_for_trigger_bit(chstat_reg_tx, OMAP1_SPI100K_CHSTAT_TXS) < 0) { dev_err(&spi->dev, "TXS timed out\n"); } else if (spi100k_wait_for_trigger_bit(chstat_reg_tx, OMAP1_SPI100K_CHSTAT_EOT) < 0) dev_err(&spi->dev, "EOT timed out\n"); } out: 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_cs *cs = spi->controller_state; struct omap1_spi100k *spi100k; u32 l = 0, div = 0; u8 word_len = spi->bits_per_word; /* spi100k = spi_master_get_devdata(spi->master); if (t != NULL && t->bits_per_word) word_len = t->bits_per_word; cs->word_len = word_len; if (spi->max_speed_hz) { while (div <= 15 && (OMAP1_SPI100K_MAX_FREQ / (1 << div)) > spi->max_speed_hz) div++; } else div = 15; l = spi100k_read_cs_reg(spi, OMAP1_SPI100K_CHCONF0); */ /* standard 4-wire master mode: SCK, MOSI/out, MISO/in, nCS * REVISIT: this controller could support SPI_3WIRE mode. */ /* l &= ~(OMAP1_SPI100K_CHCONF_IS|OMAP1_SPI100K_CHCONF_DPE1); l |= OMAP1_SPI100K_CHCONF_DPE0; */ /* wordlength */ /* l &= ~OMAP1_SPI100K_CHCONF_WL_MASK; l |= (word_len - 1) << 7; */ /* set chipselect polarity; manage with FORCE */ /* if (!(spi->mode & SPI_CS_HIGH)) l |= OMAP1_SPI100K_CHCONF_EPOL; /* active-low; normal */ // else // l &= ~OMAP1_SPI100K_CHCONF_EPOL; /* set clock divisor */ /* l &= ~OMAP1_SPI100K_CHCONF_CLKD_MASK; l |= div << 2; */ /* set SPI mode 0..3 */ /* if (spi->mode & SPI_CPOL) l |= OMAP1_SPI100K_CHCONF_POL; else l &= ~OMAP1_SPI100K_CHCONF_POL; if (spi->mode & SPI_CPHA) l |= OMAP1_SPI100K_CHCONF_PHA; else l &= ~OMAP1_SPI100K_CHCONF_PHA; spi100k_write_cs_reg(spi, OMAP1_SPI100K_CHCONF0, l); dev_dbg(&spi->dev, "setup: speed %d, sample %s edge, clk %s\n", OMAP1_SPI100K_MAX_FREQ / (1 << div), (spi->mode & SPI_CPHA) ? "trailing" : "leading", (spi->mode & SPI_CPOL) ? "inverted" : "normal"); */ 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->mode & ~MODEBITS) { dev_dbg(&spi->dev, "setup: unsupported mode bits %x\n", spi->mode & ~MODEBITS); return -EINVAL; } if (spi->bits_per_word == 0) spi->bits_per_word = 8; else 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; } clk_enable(spi100k->ick); clk_enable(spi100k->fck); ret = omap1_spi100k_setup_transfer(spi, NULL); clk_disable(spi100k->fck); clk_disable(spi100k->ick); return ret; } static void omap1_spi100k_cleanup(struct spi_device *spi) { struct omap1_spi100k *spi100k; spi100k = spi_master_get_devdata(spi->master); } static void omap1_spi100k_work(struct work_struct *work) { struct omap1_spi100k *spi100k; 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; int status = 0; u32 chconf; 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(spi, 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(spi, 1); cs_active = 1; } //chconf = spi100k_read_cs_reg(spi, OMAP1_SPI100K_CHCONF0); //chconf &= ~OMAP1_SPI100K_CHCONF_TRM_MASK; //if (t->tx_buf == NULL) // chconf |= OMAP1_SPI100K_CHCONF_TRM_RX_ONLY; //else if (t->rx_buf == NULL) // chconf |= OMAP1_SPI100K_CHCONF_TRM_TX_ONLY; //spi100k_write_cs_reg(spi, OMAP1_SPI100K_CHCONF0, chconf); if (t->len) { unsigned count; /* RX_ONLY mode needs dummy data in TX reg */ if (t->tx_buf == NULL) __raw_writel(0, cs->base + OMAP1_SPI100K_TX0); 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(spi, 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(spi, 0); omap1_spi100k_set_enable(spi, 0); m->status = status; m->complete(m->context); spin_lock_irq(&spi100k->lock); } clk_disable(spi100k->fck); clk_disable(spi100k->ick); spin_unlock_irq(&spi100k->lock); } 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; /* 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) { struct spi_master *master = spi100k->master; u32 tmp; clk_enable(spi100k->ick); clk_enable(spi100k->fck); //spi100k_write_reg(master, OMAP1_SPI100K_SYSCONFIG, // OMAP1_SPI100K_SYSCONFIG_SOFTRESET); //do { // tmp = spi100k_read_reg(master, OMAP1_SPI100K_SYSSTATUS); //} while (!(tmp & OMAP1_SPI100K_SYSSTATUS_RESETDONE)); //spi100k_write_reg(master, OMAP1_SPI100K_SYSCONFIG, /* (3 << 8) | (2 << 3) | */ // OMAP1_SPI100K_SYSCONFIG_AUTOIDLE); //omap1_spi100k_set_master_mode(master); clk_disable(spi100k->fck); clk_disable(spi100k->ick); return 0; } static int __init omap1_spi100k_probe(struct platform_device *pdev) { struct spi_master *master; struct omap1_spi100k *spi100k; struct resource *r; int status = 0, i; unsigned num_chipselect; 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 = num_chipselect; 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->dev.bus_id)) { status = -EBUSY; goto err1; } spi100k->base = (void __iomem *) io_p2v(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, "spi100k_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, "spi100k_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; 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); 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; } static struct platform_driver omap1_spi100k_driver = { .driver = { .name = "omap1_spi100k", .owner = THIS_MODULE, }, .remove = __exit_p(omap1_spi100k_remove), }; static int __init omap1_spi100k_init(void) { omap1_spi100k_wq = create_singlethread_workqueue( omap1_spi100k_driver.driver.name); if (omap1_spi100k_wq == NULL) return -1; return platform_driver_probe(&omap1_spi100k_driver, omap1_spi100k_probe); } subsys_initcall(omap1_spi100k_init); static void __exit omap1_spi100k_exit(void) { platform_driver_unregister(&omap1_spi100k_driver); destroy_workqueue(omap1_spi100k_wq); } module_exit(omap1_spi100k_exit); MODULE_LICENSE("GPL");