diff options
Diffstat (limited to 'target/linux/lantiq/files/drivers/spi/spi-falcon.c')
-rw-r--r-- | target/linux/lantiq/files/drivers/spi/spi-falcon.c | 483 |
1 files changed, 0 insertions, 483 deletions
diff --git a/target/linux/lantiq/files/drivers/spi/spi-falcon.c b/target/linux/lantiq/files/drivers/spi/spi-falcon.c deleted file mode 100644 index 447bbaaa3..000000000 --- a/target/linux/lantiq/files/drivers/spi/spi-falcon.c +++ /dev/null @@ -1,483 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 as published - * by the Free Software Foundation. - * - * Copyright (C) 2010 Thomas Langer <thomas.langer@lantiq.com> - */ - -#include <linux/module.h> -#include <linux/device.h> -#include <linux/platform_device.h> -#include <linux/spi/spi.h> -#include <linux/delay.h> -#include <linux/workqueue.h> - -#include <lantiq_soc.h> - -#define DRV_NAME "falcon_spi" - -#define FALCON_SPI_XFER_BEGIN (1 << 0) -#define FALCON_SPI_XFER_END (1 << 1) - -/* Bus Read Configuration Register0 */ -#define LTQ_BUSRCON0 0x00000010 -/* Bus Write Configuration Register0 */ -#define LTQ_BUSWCON0 0x00000018 -/* Serial Flash Configuration Register */ -#define LTQ_SFCON 0x00000080 -/* Serial Flash Time Register */ -#define LTQ_SFTIME 0x00000084 -/* Serial Flash Status Register */ -#define LTQ_SFSTAT 0x00000088 -/* Serial Flash Command Register */ -#define LTQ_SFCMD 0x0000008C -/* Serial Flash Address Register */ -#define LTQ_SFADDR 0x00000090 -/* Serial Flash Data Register */ -#define LTQ_SFDATA 0x00000094 -/* Serial Flash I/O Control Register */ -#define LTQ_SFIO 0x00000098 -/* EBU Clock Control Register */ -#define LTQ_EBUCC 0x000000C4 - -/* Dummy Phase Length */ -#define SFCMD_DUMLEN_OFFSET 16 -#define SFCMD_DUMLEN_MASK 0x000F0000 -/* Chip Select */ -#define SFCMD_CS_OFFSET 24 -#define SFCMD_CS_MASK 0x07000000 -/* field offset */ -#define SFCMD_ALEN_OFFSET 20 -#define SFCMD_ALEN_MASK 0x00700000 -/* SCK Rise-edge Position */ -#define SFTIME_SCKR_POS_OFFSET 8 -#define SFTIME_SCKR_POS_MASK 0x00000F00 -/* SCK Period */ -#define SFTIME_SCK_PER_OFFSET 0 -#define SFTIME_SCK_PER_MASK 0x0000000F -/* SCK Fall-edge Position */ -#define SFTIME_SCKF_POS_OFFSET 12 -#define SFTIME_SCKF_POS_MASK 0x0000F000 -/* Device Size */ -#define SFCON_DEV_SIZE_A23_0 0x03000000 -#define SFCON_DEV_SIZE_MASK 0x0F000000 -/* Read Data Position */ -#define SFTIME_RD_POS_MASK 0x000F0000 -/* Data Output */ -#define SFIO_UNUSED_WD_MASK 0x0000000F -/* Command Opcode mask */ -#define SFCMD_OPC_MASK 0x000000FF -/* dlen bytes of data to write */ -#define SFCMD_DIR_WRITE 0x00000100 -/* Data Length offset */ -#define SFCMD_DLEN_OFFSET 9 -/* Command Error */ -#define SFSTAT_CMD_ERR 0x20000000 -/* Access Command Pending */ -#define SFSTAT_CMD_PEND 0x00400000 -/* Frequency set to 100MHz. */ -#define EBUCC_EBUDIV_SELF100 0x00000001 -/* Serial Flash */ -#define BUSRCON0_AGEN_SERIAL_FLASH 0xF0000000 -/* 8-bit multiplexed */ -#define BUSRCON0_PORTW_8_BIT_MUX 0x00000000 -/* Serial Flash */ -#define BUSWCON0_AGEN_SERIAL_FLASH 0xF0000000 -/* Chip Select after opcode */ -#define SFCMD_KEEP_CS_KEEP_SELECTED 0x00008000 - -struct falcon_spi { - u32 sfcmd; /* for caching of opcode, direction, ... */ - struct spi_master *master; -}; - -int -falcon_spi_xfer(struct spi_device *spi, - struct spi_transfer *t, - unsigned long flags) -{ - struct device *dev = &spi->dev; - struct falcon_spi *priv = spi_master_get_devdata(spi->master); - const u8 *txp = t->tx_buf; - u8 *rxp = t->rx_buf; - unsigned int bytelen = ((8 * t->len + 7) / 8); - unsigned int len, alen, dumlen; - u32 val; - enum { - state_init, - state_command_prepare, - state_write, - state_read, - state_disable_cs, - state_end - } state = state_init; - - do { - switch (state) { - case state_init: /* detect phase of upper layer sequence */ - { - /* initial write ? */ - if (flags & FALCON_SPI_XFER_BEGIN) { - if (!txp) { - dev_err(dev, - "BEGIN without tx data!\n"); - return -1; - } - /* - * Prepare the parts of the sfcmd register, - * which should not - * change during a sequence! - * Only exception are the length fields, - * especially alen and dumlen. - */ - - priv->sfcmd = ((spi->chip_select - << SFCMD_CS_OFFSET) - & SFCMD_CS_MASK); - priv->sfcmd |= SFCMD_KEEP_CS_KEEP_SELECTED; - priv->sfcmd |= *txp; - txp++; - bytelen--; - if (bytelen) { - /* - * more data: - * maybe address and/or dummy - */ - state = state_command_prepare; - break; - } else { - dev_dbg(dev, "write cmd %02X\n", - priv->sfcmd & SFCMD_OPC_MASK); - } - } - /* continued write ? */ - if (txp && bytelen) { - state = state_write; - break; - } - /* read data? */ - if (rxp && bytelen) { - state = state_read; - break; - } - /* end of sequence? */ - if (flags & FALCON_SPI_XFER_END) - state = state_disable_cs; - else - state = state_end; - break; - } - /* collect tx data for address and dummy phase */ - case state_command_prepare: - { - /* txp is valid, already checked */ - val = 0; - alen = 0; - dumlen = 0; - while (bytelen > 0) { - if (alen < 3) { - val = (val<<8)|(*txp++); - alen++; - } else if ((dumlen < 15) && (*txp == 0)) { - /* - * assume dummy bytes are set to 0 - * from upper layer - */ - dumlen++; - txp++; - } else - break; - bytelen--; - } - priv->sfcmd &= ~(SFCMD_ALEN_MASK | SFCMD_DUMLEN_MASK); - priv->sfcmd |= (alen << SFCMD_ALEN_OFFSET) | - (dumlen << SFCMD_DUMLEN_OFFSET); - if (alen > 0) - ltq_ebu_w32(val, LTQ_SFADDR); - - dev_dbg(dev, "write cmd %02X, alen=%d " - "(addr=%06X) dumlen=%d\n", - priv->sfcmd & SFCMD_OPC_MASK, - alen, val, dumlen); - - if (bytelen > 0) { - /* continue with write */ - state = state_write; - } else if (flags & FALCON_SPI_XFER_END) { - /* end of sequence? */ - state = state_disable_cs; - } else { - /* - * go to end and expect another - * call (read or write) - */ - state = state_end; - } - break; - } - case state_write: - { - /* txp still valid */ - priv->sfcmd |= SFCMD_DIR_WRITE; - len = 0; - val = 0; - do { - if (bytelen--) - val |= (*txp++) << (8 * len++); - if ((flags & FALCON_SPI_XFER_END) - && (bytelen == 0)) { - priv->sfcmd &= - ~SFCMD_KEEP_CS_KEEP_SELECTED; - } - if ((len == 4) || (bytelen == 0)) { - ltq_ebu_w32(val, LTQ_SFDATA); - ltq_ebu_w32(priv->sfcmd - | (len<<SFCMD_DLEN_OFFSET), - LTQ_SFCMD); - len = 0; - val = 0; - priv->sfcmd &= ~(SFCMD_ALEN_MASK - | SFCMD_DUMLEN_MASK); - } - } while (bytelen); - state = state_end; - break; - } - case state_read: - { - /* read data */ - priv->sfcmd &= ~SFCMD_DIR_WRITE; - do { - if ((flags & FALCON_SPI_XFER_END) - && (bytelen <= 4)) { - priv->sfcmd &= - ~SFCMD_KEEP_CS_KEEP_SELECTED; - } - len = (bytelen > 4) ? 4 : bytelen; - bytelen -= len; - ltq_ebu_w32(priv->sfcmd - |(len<<SFCMD_DLEN_OFFSET), LTQ_SFCMD); - priv->sfcmd &= ~(SFCMD_ALEN_MASK - | SFCMD_DUMLEN_MASK); - do { - val = ltq_ebu_r32(LTQ_SFSTAT); - if (val & SFSTAT_CMD_ERR) { - /* reset error status */ - dev_err(dev, "SFSTAT: CMD_ERR " - "(%x)\n", val); - ltq_ebu_w32(SFSTAT_CMD_ERR, - LTQ_SFSTAT); - return -1; - } - } while (val & SFSTAT_CMD_PEND); - val = ltq_ebu_r32(LTQ_SFDATA); - do { - *rxp = (val & 0xFF); - rxp++; - val >>= 8; - len--; - } while (len); - } while (bytelen); - state = state_end; - break; - } - case state_disable_cs: - { - priv->sfcmd &= ~SFCMD_KEEP_CS_KEEP_SELECTED; - ltq_ebu_w32(priv->sfcmd | (0 << SFCMD_DLEN_OFFSET), - LTQ_SFCMD); - val = ltq_ebu_r32(LTQ_SFSTAT); - if (val & SFSTAT_CMD_ERR) { - /* reset error status */ - dev_err(dev, "SFSTAT: CMD_ERR (%x)\n", val); - ltq_ebu_w32(SFSTAT_CMD_ERR, LTQ_SFSTAT); - return -1; - } - state = state_end; - break; - } - case state_end: - break; - } - } while (state != state_end); - - return 0; -} - -static int -falcon_spi_setup(struct spi_device *spi) -{ - struct device *dev = &spi->dev; - const u32 ebuclk = 100000000; - unsigned int i; - unsigned long flags; - - dev_dbg(dev, "setup\n"); - - if (spi->master->bus_num > 0 || spi->chip_select > 0) - return -ENODEV; - - spin_lock_irqsave(&ebu_lock, flags); - - if (ebuclk < spi->max_speed_hz) { - /* set EBU clock to 100 MHz */ - ltq_sys1_w32_mask(0, EBUCC_EBUDIV_SELF100, LTQ_EBUCC); - i = 1; /* divider */ - } else { - /* set EBU clock to 50 MHz */ - ltq_sys1_w32_mask(EBUCC_EBUDIV_SELF100, 0, LTQ_EBUCC); - - /* search for suitable divider */ - for (i = 1; i < 7; i++) { - if (ebuclk / i <= spi->max_speed_hz) - break; - } - } - - /* setup period of serial clock */ - ltq_ebu_w32_mask(SFTIME_SCKF_POS_MASK - | SFTIME_SCKR_POS_MASK - | SFTIME_SCK_PER_MASK, - (i << SFTIME_SCKR_POS_OFFSET) - | (i << (SFTIME_SCK_PER_OFFSET + 1)), - LTQ_SFTIME); - - /* - * set some bits of unused_wd, to not trigger HOLD/WP - * signals on non QUAD flashes - */ - ltq_ebu_w32((SFIO_UNUSED_WD_MASK & (0x8 | 0x4)), LTQ_SFIO); - - ltq_ebu_w32(BUSRCON0_AGEN_SERIAL_FLASH | BUSRCON0_PORTW_8_BIT_MUX, - LTQ_BUSRCON0); - ltq_ebu_w32(BUSWCON0_AGEN_SERIAL_FLASH, LTQ_BUSWCON0); - /* set address wrap around to maximum for 24-bit addresses */ - ltq_ebu_w32_mask(SFCON_DEV_SIZE_MASK, SFCON_DEV_SIZE_A23_0, LTQ_SFCON); - - spin_unlock_irqrestore(&ebu_lock, flags); - - return 0; -} - -static int -falcon_spi_transfer(struct spi_device *spi, struct spi_message *m) -{ - struct falcon_spi *priv = spi_master_get_devdata(spi->master); - struct spi_transfer *t; - unsigned long spi_flags; - unsigned long flags; - int ret = 0; - - priv->sfcmd = 0; - m->actual_length = 0; - - spi_flags = FALCON_SPI_XFER_BEGIN; - list_for_each_entry(t, &m->transfers, transfer_list) { - if (list_is_last(&t->transfer_list, &m->transfers)) - spi_flags |= FALCON_SPI_XFER_END; - - spin_lock_irqsave(&ebu_lock, flags); - ret = falcon_spi_xfer(spi, t, spi_flags); - spin_unlock_irqrestore(&ebu_lock, flags); - - if (ret) - break; - - m->actual_length += t->len; - - if (t->delay_usecs || t->cs_change) - BUG(); - - spi_flags = 0; - } - - m->status = ret; - m->complete(m->context); - - return 0; -} - -static void -falcon_spi_cleanup(struct spi_device *spi) -{ - struct device *dev = &spi->dev; - - dev_dbg(dev, "cleanup\n"); -} - -static int __devinit -falcon_spi_probe(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct falcon_spi *priv; - struct spi_master *master; - int ret; - - dev_dbg(dev, "probing\n"); - - master = spi_alloc_master(&pdev->dev, sizeof(*priv)); - if (!master) { - dev_err(dev, "no memory for spi_master\n"); - return -ENOMEM; - } - - priv = spi_master_get_devdata(master); - priv->master = master; - - master->mode_bits = SPI_MODE_3; - master->num_chipselect = 1; - master->bus_num = 0; - - master->setup = falcon_spi_setup; - master->transfer = falcon_spi_transfer; - master->cleanup = falcon_spi_cleanup; - - platform_set_drvdata(pdev, priv); - - ret = spi_register_master(master); - if (ret) - spi_master_put(master); - - return ret; -} - -static int __devexit -falcon_spi_remove(struct platform_device *pdev) -{ - struct device *dev = &pdev->dev; - struct falcon_spi *priv = platform_get_drvdata(pdev); - - dev_dbg(dev, "removed\n"); - - spi_unregister_master(priv->master); - - return 0; -} - -static struct platform_driver falcon_spi_driver = { - .probe = falcon_spi_probe, - .remove = __devexit_p(falcon_spi_remove), - .driver = { - .name = DRV_NAME, - .owner = THIS_MODULE - } -}; - -static int __init -falcon_spi_init(void) -{ - return platform_driver_register(&falcon_spi_driver); -} - -static void __exit -falcon_spi_exit(void) -{ - platform_driver_unregister(&falcon_spi_driver); -} - -module_init(falcon_spi_init); -module_exit(falcon_spi_exit); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("Lantiq Falcon SPI controller driver"); |