From: Greg Kroah-Hartman Date: Mon, 20 May 2019 08:47:13 +0000 (+0200) Subject: staging: kpc2000: move the spi driver out of its subdirectory X-Git-Url: http://git.maquefel.me/?a=commitdiff_plain;h=f900d81cb960097524be3705b050b60aa5959a83;p=linux.git staging: kpc2000: move the spi driver out of its subdirectory There is no need for a subdirectory for just a single .c file. So move it out of kpc_spi/ and rename it to the module name that we want the file to build to, saving one more linking stage. Cc: Matt Sickler Signed-off-by: Greg Kroah-Hartman --- diff --git a/drivers/staging/kpc2000/Makefile b/drivers/staging/kpc2000/Makefile index 1e48e9df13294..99b1699a8b70a 100644 --- a/drivers/staging/kpc2000/Makefile +++ b/drivers/staging/kpc2000/Makefile @@ -2,5 +2,5 @@ obj-$(CONFIG_KPC2000) += kpc2000/ obj-$(CONFIG_KPC2000_I2C) += kpc_i2c/ -obj-$(CONFIG_KPC2000_SPI) += kpc_spi/ +obj-$(CONFIG_KPC2000_SPI) += kpc2000_spi.o obj-$(CONFIG_KPC2000_DMA) += kpc_dma/ diff --git a/drivers/staging/kpc2000/kpc2000_spi.c b/drivers/staging/kpc2000/kpc2000_spi.c new file mode 100644 index 0000000000000..075ae4fafa7dd --- /dev/null +++ b/drivers/staging/kpc2000/kpc2000_spi.c @@ -0,0 +1,548 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * KP2000 SPI controller driver + * + * Copyright (C) 2014-2018 Daktronics + * Author: Matt Sickler + * Very loosely based on spi-omap2-mcspi.c + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "kpc.h" + +static struct mtd_partition p2kr0_spi0_parts[] = { + { .name = "SLOT_0", .size = 7798784, .offset = 0, }, + { .name = "SLOT_1", .size = 7798784, .offset = MTDPART_OFS_NXTBLK}, + { .name = "SLOT_2", .size = 7798784, .offset = MTDPART_OFS_NXTBLK}, + { .name = "SLOT_3", .size = 7798784, .offset = MTDPART_OFS_NXTBLK}, + { .name = "CS0_EXTRA", .size = MTDPART_SIZ_FULL, .offset = MTDPART_OFS_NXTBLK}, +}; +static struct mtd_partition p2kr0_spi1_parts[] = { + { .name = "SLOT_4", .size = 7798784, .offset = 0, }, + { .name = "SLOT_5", .size = 7798784, .offset = MTDPART_OFS_NXTBLK}, + { .name = "SLOT_6", .size = 7798784, .offset = MTDPART_OFS_NXTBLK}, + { .name = "SLOT_7", .size = 7798784, .offset = MTDPART_OFS_NXTBLK}, + { .name = "CS1_EXTRA", .size = MTDPART_SIZ_FULL, .offset = MTDPART_OFS_NXTBLK}, +}; + +static struct flash_platform_data p2kr0_spi0_pdata = { + .name = "SPI0", + .nr_parts = ARRAY_SIZE(p2kr0_spi0_parts), + .parts = p2kr0_spi0_parts, +}; +static struct flash_platform_data p2kr0_spi1_pdata = { + .name = "SPI1", + .nr_parts = ARRAY_SIZE(p2kr0_spi1_parts), + .parts = p2kr0_spi1_parts, +}; + +static struct spi_board_info p2kr0_board_info[] = { + { + .modalias = "n25q256a11", + .bus_num = 1, + .chip_select = 0, + .mode = SPI_MODE_0, + .platform_data = &p2kr0_spi0_pdata + }, + { + .modalias = "n25q256a11", + .bus_num = 1, + .chip_select = 1, + .mode = SPI_MODE_0, + .platform_data = &p2kr0_spi1_pdata + }, +}; + +/*************** + * SPI Defines * + ***************/ +#define KP_SPI_REG_CONFIG 0x0 /* 0x00 */ +#define KP_SPI_REG_STATUS 0x1 /* 0x08 */ +#define KP_SPI_REG_FFCTRL 0x2 /* 0x10 */ +#define KP_SPI_REG_TXDATA 0x3 /* 0x18 */ +#define KP_SPI_REG_RXDATA 0x4 /* 0x20 */ + +#define KP_SPI_CLK 48000000 +#define KP_SPI_MAX_FIFODEPTH 64 +#define KP_SPI_MAX_FIFOWCNT 0xFFFF + +#define KP_SPI_REG_CONFIG_TRM_TXRX 0 +#define KP_SPI_REG_CONFIG_TRM_RX 1 +#define KP_SPI_REG_CONFIG_TRM_TX 2 + +#define KP_SPI_REG_STATUS_RXS 0x01 +#define KP_SPI_REG_STATUS_TXS 0x02 +#define KP_SPI_REG_STATUS_EOT 0x04 +#define KP_SPI_REG_STATUS_TXFFE 0x10 +#define KP_SPI_REG_STATUS_TXFFF 0x20 +#define KP_SPI_REG_STATUS_RXFFE 0x40 +#define KP_SPI_REG_STATUS_RXFFF 0x80 + + + +/****************** + * SPI Structures * + ******************/ +struct kp_spi { + struct spi_master *master; + u64 __iomem *base; + unsigned long phys; + struct device *dev; + int fifo_depth; + unsigned int pin_dir:1; +}; + + +struct kp_spi_controller_state { + void __iomem *base; + unsigned long phys; + unsigned char chip_select; + int word_len; + s64 conf_cache; +}; + + +union kp_spi_config { + /* use this to access individual elements */ + struct __attribute__((packed)) spi_config_bitfield { + unsigned int pha : 1; /* spim_clk Phase */ + unsigned int pol : 1; /* spim_clk Polarity */ + unsigned int epol : 1; /* spim_csx Polarity */ + unsigned int dpe : 1; /* Transmission Enable */ + unsigned int wl : 5; /* Word Length */ + unsigned int : 3; + unsigned int trm : 2; /* TxRx Mode */ + unsigned int cs : 4; /* Chip Select */ + unsigned int wcnt : 7; /* Word Count */ + unsigned int ffen : 1; /* FIFO Enable */ + unsigned int spi_en : 1; /* SPI Enable */ + unsigned int : 5; + } bitfield; + /* use this to grab the whole register */ + u32 reg; +}; + + + +union kp_spi_status { + struct __attribute__((packed)) spi_status_bitfield { + unsigned int rx : 1; /* Rx Status */ + unsigned int tx : 1; /* Tx Status */ + unsigned int eo : 1; /* End of Transfer */ + unsigned int : 1; + unsigned int txffe : 1; /* Tx FIFO Empty */ + unsigned int txfff : 1; /* Tx FIFO Full */ + unsigned int rxffe : 1; /* Rx FIFO Empty */ + unsigned int rxfff : 1; /* Rx FIFO Full */ + unsigned int : 24; + } bitfield; + u32 reg; +}; + + + +union kp_spi_ffctrl { + struct __attribute__((packed)) spi_ffctrl_bitfield { + unsigned int ffstart : 1; /* FIFO Start */ + unsigned int : 31; + } bitfield; + u32 reg; +}; + + + +/*************** + * SPI Helpers * + ***************/ +static inline int +kp_spi_bytes_per_word(int word_len) +{ + if (word_len <= 8){ + return 1; + } + else if (word_len <= 16) { + return 2; + } + else { /* word_len <= 32 */ + return 4; + } +} + +static inline u64 +kp_spi_read_reg(struct kp_spi_controller_state *cs, int idx) +{ + u64 __iomem *addr = cs->base; + u64 val; + + addr += idx; + if ((idx == KP_SPI_REG_CONFIG) && (cs->conf_cache >= 0)){ + return cs->conf_cache; + } + val = readq((void*)addr); + return val; +} + +static inline void +kp_spi_write_reg(struct kp_spi_controller_state *cs, int idx, u64 val) +{ + u64 __iomem *addr = cs->base; + addr += idx; + writeq(val, (void*)addr); + if (idx == KP_SPI_REG_CONFIG) + cs->conf_cache = val; +} + +static int +kp_spi_wait_for_reg_bit(struct kp_spi_controller_state *cs, int idx, unsigned long bit) +{ + unsigned long timeout; + timeout = jiffies + msecs_to_jiffies(1000); + while (!(kp_spi_read_reg(cs, idx) & bit)) { + if (time_after(jiffies, timeout)) { + if (!(kp_spi_read_reg(cs, idx) & bit)) { + return -ETIMEDOUT; + } else { + return 0; + } + } + cpu_relax(); + } + return 0; +} + +static unsigned +kp_spi_txrx_pio(struct spi_device *spidev, struct spi_transfer *transfer) +{ + struct kp_spi_controller_state *cs = spidev->controller_state; + unsigned int count = transfer->len; + unsigned int c = count; + + int i; + u8 *rx = transfer->rx_buf; + const u8 *tx = transfer->tx_buf; + int processed = 0; + + if (tx) { + for (i = 0 ; i < c ; i++) { + char val = *tx++; + + if (kp_spi_wait_for_reg_bit(cs, KP_SPI_REG_STATUS, KP_SPI_REG_STATUS_TXS) < 0) { + goto out; + } + + kp_spi_write_reg(cs, KP_SPI_REG_TXDATA, val); + processed++; + } + } + else if(rx) { + for (i = 0 ; i < c ; i++) { + char test=0; + + kp_spi_write_reg(cs, KP_SPI_REG_TXDATA, 0x00); + + if (kp_spi_wait_for_reg_bit(cs, KP_SPI_REG_STATUS, KP_SPI_REG_STATUS_RXS) < 0) { + goto out; + } + + test = kp_spi_read_reg(cs, KP_SPI_REG_RXDATA); + *rx++ = test; + processed++; + } + } + + if (kp_spi_wait_for_reg_bit(cs, KP_SPI_REG_STATUS, KP_SPI_REG_STATUS_EOT) < 0) { + //TODO: Figure out how to abort transaction?? This has never happened in practice though... + } + + out: + return processed; +} + +/***************** + * SPI Functions * + *****************/ +static int +kp_spi_setup(struct spi_device *spidev) +{ + union kp_spi_config sc; + struct kp_spi *kpspi = spi_master_get_devdata(spidev->master); + struct kp_spi_controller_state *cs; + + /* setup controller state */ + cs = spidev->controller_state; + if (!cs) { + cs = kzalloc(sizeof(*cs), GFP_KERNEL); + if(!cs) { + return -ENOMEM; + } + cs->base = kpspi->base; + cs->phys = kpspi->phys; + cs->chip_select = spidev->chip_select; + cs->word_len = spidev->bits_per_word; + cs->conf_cache = -1; + spidev->controller_state = cs; + } + + /* set config register */ + sc.bitfield.wl = spidev->bits_per_word - 1; + sc.bitfield.cs = spidev->chip_select; + sc.bitfield.spi_en = 0; + sc.bitfield.trm = 0; + sc.bitfield.ffen = 0; + kp_spi_write_reg(spidev->controller_state, KP_SPI_REG_CONFIG, sc.reg); + return 0; +} + +static int +kp_spi_transfer_one_message(struct spi_master *master, struct spi_message *m) +{ + struct kp_spi_controller_state *cs; + struct spi_device *spidev; + struct kp_spi *kpspi; + struct spi_transfer *transfer; + union kp_spi_config sc; + int status = 0; + + spidev = m->spi; + kpspi = spi_master_get_devdata(master); + m->actual_length = 0; + m->status = 0; + + cs = spidev->controller_state; + + /* reject invalid messages and transfers */ + if (list_empty(&m->transfers)) { + return -EINVAL; + } + + /* validate input */ + list_for_each_entry(transfer, &m->transfers, transfer_list) { + const void *tx_buf = transfer->tx_buf; + void *rx_buf = transfer->rx_buf; + unsigned len = transfer->len; + + if (transfer->speed_hz > KP_SPI_CLK || (len && !(rx_buf || tx_buf))) { + dev_dbg(kpspi->dev, " transfer: %d Hz, %d %s%s, %d bpw\n", + transfer->speed_hz, + len, + tx_buf ? "tx" : "", + rx_buf ? "rx" : "", + transfer->bits_per_word); + dev_dbg(kpspi->dev, " transfer -EINVAL\n"); + return -EINVAL; + } + if (transfer->speed_hz && (transfer->speed_hz < (KP_SPI_CLK >> 15))) { + dev_dbg(kpspi->dev, "speed_hz %d below minimum %d Hz\n", + transfer->speed_hz, + KP_SPI_CLK >> 15); + dev_dbg(kpspi->dev, " speed_hz -EINVAL\n"); + return -EINVAL; + } + } + + /* assert chip select to start the sequence*/ + sc.reg = kp_spi_read_reg(cs, KP_SPI_REG_CONFIG); + sc.bitfield.spi_en = 1; + kp_spi_write_reg(cs, KP_SPI_REG_CONFIG, sc.reg); + + /* work */ + if (kp_spi_wait_for_reg_bit(cs, KP_SPI_REG_STATUS, KP_SPI_REG_STATUS_EOT) < 0) { + dev_info(kpspi->dev, "EOT timed out\n"); + goto out; + } + + /* do the transfers for this message */ + list_for_each_entry(transfer, &m->transfers, transfer_list) { + if (transfer->tx_buf == NULL && transfer->rx_buf == NULL && transfer->len) { + status = -EINVAL; + break; + } + + /* transfer */ + if (transfer->len) { + unsigned int word_len = spidev->bits_per_word; + unsigned count; + + /* set up the transfer... */ + sc.reg = kp_spi_read_reg(cs, KP_SPI_REG_CONFIG); + + /* ...direction */ + if (transfer->tx_buf) { + sc.bitfield.trm = KP_SPI_REG_CONFIG_TRM_TX; + } + else if (transfer->rx_buf) { + sc.bitfield.trm = KP_SPI_REG_CONFIG_TRM_RX; + } + + /* ...word length */ + if (transfer->bits_per_word) { + word_len = transfer->bits_per_word; + } + cs->word_len = word_len; + sc.bitfield.wl = word_len-1; + + /* ...chip select */ + sc.bitfield.cs = spidev->chip_select; + + /* ...and write the new settings */ + kp_spi_write_reg(cs, KP_SPI_REG_CONFIG, sc.reg); + + /* do the transfer */ + count = kp_spi_txrx_pio(spidev, transfer); + m->actual_length += count; + + if (count != transfer->len) { + status = -EIO; + break; + } + } + + if (transfer->delay_usecs) { + udelay(transfer->delay_usecs); + } + } + + /* de-assert chip select to end the sequence */ + sc.reg = kp_spi_read_reg(cs, KP_SPI_REG_CONFIG); + sc.bitfield.spi_en = 0; + kp_spi_write_reg(cs, KP_SPI_REG_CONFIG, sc.reg); + + out: + /* done work */ + spi_finalize_current_message(master); + return 0; +} + +static void +kp_spi_cleanup(struct spi_device *spidev) +{ + struct kp_spi_controller_state *cs = spidev->controller_state; + if (cs) { + kfree(cs); + } +} + + + +/****************** + * Probe / Remove * + ******************/ +static int +kp_spi_probe(struct platform_device *pldev) +{ + struct kpc_core_device_platdata *drvdata; + struct spi_master *master; + struct kp_spi *kpspi; + struct resource *r; + int status = 0; + int i; + + drvdata = pldev->dev.platform_data; + if (!drvdata){ + dev_err(&pldev->dev, "kp_spi_probe: platform_data is NULL!\n"); + return -ENODEV; + } + + master = spi_alloc_master(&pldev->dev, sizeof(struct kp_spi)); + if (master == NULL) { + dev_err(&pldev->dev, "kp_spi_probe: master allocation failed\n"); + return -ENOMEM; + } + + /* set up the spi functions */ + master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; + master->bits_per_word_mask = (unsigned int)SPI_BPW_RANGE_MASK(4, 32); + master->setup = kp_spi_setup; + master->transfer_one_message = kp_spi_transfer_one_message; + master->cleanup = kp_spi_cleanup; + + platform_set_drvdata(pldev, master); + + kpspi = spi_master_get_devdata(master); + kpspi->master = master; + kpspi->dev = &pldev->dev; + + master->num_chipselect = 4; + if (pldev->id != -1) { + master->bus_num = pldev->id; + } + kpspi->pin_dir = 0; + + r = platform_get_resource(pldev, IORESOURCE_MEM, 0); + if (r == NULL) { + dev_err(&pldev->dev, "kp_spi_probe: Unable to get platform resources\n"); + status = -ENODEV; + goto free_master; + } + + kpspi->phys = (unsigned long)ioremap_nocache(r->start, resource_size(r)); + kpspi->base = (u64 __iomem *)kpspi->phys; + + status = spi_register_master(master); + if (status < 0) { + dev_err(&pldev->dev, "Unable to register SPI device\n"); + goto free_master; + } + + /* register the slave boards */ + #define NEW_SPI_DEVICE_FROM_BOARD_INFO_TABLE(table) \ + for (i = 0 ; i < ARRAY_SIZE(table) ; i++) { \ + spi_new_device(master, &(table[i])); \ + } + + switch ((drvdata->card_id & 0xFFFF0000) >> 16){ + case PCI_DEVICE_ID_DAKTRONICS_KADOKA_P2KR0: + NEW_SPI_DEVICE_FROM_BOARD_INFO_TABLE(p2kr0_board_info); + break; + default: + dev_err(&pldev->dev, "Unknown hardware, cant know what partition table to use!\n"); + goto free_master; + break; + } + + return status; + + free_master: + spi_master_put(master); + return status; +} + +static int +kp_spi_remove(struct platform_device *pldev) +{ + struct spi_master * master = platform_get_drvdata(pldev); + spi_unregister_master(master); + return 0; +} + + +static struct platform_driver kp_spi_driver = { + .driver = { + .name = KP_DRIVER_NAME_SPI, + }, + .probe = kp_spi_probe, + .remove = kp_spi_remove, +}; + +module_platform_driver(kp_spi_driver); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:kp_spi"); diff --git a/drivers/staging/kpc2000/kpc_spi/Makefile b/drivers/staging/kpc2000/kpc_spi/Makefile deleted file mode 100644 index 3018d200484f3..0000000000000 --- a/drivers/staging/kpc2000/kpc_spi/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0 - -obj-m += kpc2000_spi.o -kpc2000_spi-objs := spi_driver.o diff --git a/drivers/staging/kpc2000/kpc_spi/spi_driver.c b/drivers/staging/kpc2000/kpc_spi/spi_driver.c deleted file mode 100644 index e84bd7015ecf2..0000000000000 --- a/drivers/staging/kpc2000/kpc_spi/spi_driver.c +++ /dev/null @@ -1,548 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * KP2000 SPI controller driver - * - * Copyright (C) 2014-2018 Daktronics - * Author: Matt Sickler - * Very loosely based on spi-omap2-mcspi.c - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "../kpc.h" - -static struct mtd_partition p2kr0_spi0_parts[] = { - { .name = "SLOT_0", .size = 7798784, .offset = 0, }, - { .name = "SLOT_1", .size = 7798784, .offset = MTDPART_OFS_NXTBLK}, - { .name = "SLOT_2", .size = 7798784, .offset = MTDPART_OFS_NXTBLK}, - { .name = "SLOT_3", .size = 7798784, .offset = MTDPART_OFS_NXTBLK}, - { .name = "CS0_EXTRA", .size = MTDPART_SIZ_FULL, .offset = MTDPART_OFS_NXTBLK}, -}; -static struct mtd_partition p2kr0_spi1_parts[] = { - { .name = "SLOT_4", .size = 7798784, .offset = 0, }, - { .name = "SLOT_5", .size = 7798784, .offset = MTDPART_OFS_NXTBLK}, - { .name = "SLOT_6", .size = 7798784, .offset = MTDPART_OFS_NXTBLK}, - { .name = "SLOT_7", .size = 7798784, .offset = MTDPART_OFS_NXTBLK}, - { .name = "CS1_EXTRA", .size = MTDPART_SIZ_FULL, .offset = MTDPART_OFS_NXTBLK}, -}; - -static struct flash_platform_data p2kr0_spi0_pdata = { - .name = "SPI0", - .nr_parts = ARRAY_SIZE(p2kr0_spi0_parts), - .parts = p2kr0_spi0_parts, -}; -static struct flash_platform_data p2kr0_spi1_pdata = { - .name = "SPI1", - .nr_parts = ARRAY_SIZE(p2kr0_spi1_parts), - .parts = p2kr0_spi1_parts, -}; - -static struct spi_board_info p2kr0_board_info[] = { - { - .modalias = "n25q256a11", - .bus_num = 1, - .chip_select = 0, - .mode = SPI_MODE_0, - .platform_data = &p2kr0_spi0_pdata - }, - { - .modalias = "n25q256a11", - .bus_num = 1, - .chip_select = 1, - .mode = SPI_MODE_0, - .platform_data = &p2kr0_spi1_pdata - }, -}; - -/*************** - * SPI Defines * - ***************/ -#define KP_SPI_REG_CONFIG 0x0 /* 0x00 */ -#define KP_SPI_REG_STATUS 0x1 /* 0x08 */ -#define KP_SPI_REG_FFCTRL 0x2 /* 0x10 */ -#define KP_SPI_REG_TXDATA 0x3 /* 0x18 */ -#define KP_SPI_REG_RXDATA 0x4 /* 0x20 */ - -#define KP_SPI_CLK 48000000 -#define KP_SPI_MAX_FIFODEPTH 64 -#define KP_SPI_MAX_FIFOWCNT 0xFFFF - -#define KP_SPI_REG_CONFIG_TRM_TXRX 0 -#define KP_SPI_REG_CONFIG_TRM_RX 1 -#define KP_SPI_REG_CONFIG_TRM_TX 2 - -#define KP_SPI_REG_STATUS_RXS 0x01 -#define KP_SPI_REG_STATUS_TXS 0x02 -#define KP_SPI_REG_STATUS_EOT 0x04 -#define KP_SPI_REG_STATUS_TXFFE 0x10 -#define KP_SPI_REG_STATUS_TXFFF 0x20 -#define KP_SPI_REG_STATUS_RXFFE 0x40 -#define KP_SPI_REG_STATUS_RXFFF 0x80 - - - -/****************** - * SPI Structures * - ******************/ -struct kp_spi { - struct spi_master *master; - u64 __iomem *base; - unsigned long phys; - struct device *dev; - int fifo_depth; - unsigned int pin_dir:1; -}; - - -struct kp_spi_controller_state { - void __iomem *base; - unsigned long phys; - unsigned char chip_select; - int word_len; - s64 conf_cache; -}; - - -union kp_spi_config { - /* use this to access individual elements */ - struct __attribute__((packed)) spi_config_bitfield { - unsigned int pha : 1; /* spim_clk Phase */ - unsigned int pol : 1; /* spim_clk Polarity */ - unsigned int epol : 1; /* spim_csx Polarity */ - unsigned int dpe : 1; /* Transmission Enable */ - unsigned int wl : 5; /* Word Length */ - unsigned int : 3; - unsigned int trm : 2; /* TxRx Mode */ - unsigned int cs : 4; /* Chip Select */ - unsigned int wcnt : 7; /* Word Count */ - unsigned int ffen : 1; /* FIFO Enable */ - unsigned int spi_en : 1; /* SPI Enable */ - unsigned int : 5; - } bitfield; - /* use this to grab the whole register */ - u32 reg; -}; - - - -union kp_spi_status { - struct __attribute__((packed)) spi_status_bitfield { - unsigned int rx : 1; /* Rx Status */ - unsigned int tx : 1; /* Tx Status */ - unsigned int eo : 1; /* End of Transfer */ - unsigned int : 1; - unsigned int txffe : 1; /* Tx FIFO Empty */ - unsigned int txfff : 1; /* Tx FIFO Full */ - unsigned int rxffe : 1; /* Rx FIFO Empty */ - unsigned int rxfff : 1; /* Rx FIFO Full */ - unsigned int : 24; - } bitfield; - u32 reg; -}; - - - -union kp_spi_ffctrl { - struct __attribute__((packed)) spi_ffctrl_bitfield { - unsigned int ffstart : 1; /* FIFO Start */ - unsigned int : 31; - } bitfield; - u32 reg; -}; - - - -/*************** - * SPI Helpers * - ***************/ -static inline int -kp_spi_bytes_per_word(int word_len) -{ - if (word_len <= 8){ - return 1; - } - else if (word_len <= 16) { - return 2; - } - else { /* word_len <= 32 */ - return 4; - } -} - -static inline u64 -kp_spi_read_reg(struct kp_spi_controller_state *cs, int idx) -{ - u64 __iomem *addr = cs->base; - u64 val; - - addr += idx; - if ((idx == KP_SPI_REG_CONFIG) && (cs->conf_cache >= 0)){ - return cs->conf_cache; - } - val = readq((void*)addr); - return val; -} - -static inline void -kp_spi_write_reg(struct kp_spi_controller_state *cs, int idx, u64 val) -{ - u64 __iomem *addr = cs->base; - addr += idx; - writeq(val, (void*)addr); - if (idx == KP_SPI_REG_CONFIG) - cs->conf_cache = val; -} - -static int -kp_spi_wait_for_reg_bit(struct kp_spi_controller_state *cs, int idx, unsigned long bit) -{ - unsigned long timeout; - timeout = jiffies + msecs_to_jiffies(1000); - while (!(kp_spi_read_reg(cs, idx) & bit)) { - if (time_after(jiffies, timeout)) { - if (!(kp_spi_read_reg(cs, idx) & bit)) { - return -ETIMEDOUT; - } else { - return 0; - } - } - cpu_relax(); - } - return 0; -} - -static unsigned -kp_spi_txrx_pio(struct spi_device *spidev, struct spi_transfer *transfer) -{ - struct kp_spi_controller_state *cs = spidev->controller_state; - unsigned int count = transfer->len; - unsigned int c = count; - - int i; - u8 *rx = transfer->rx_buf; - const u8 *tx = transfer->tx_buf; - int processed = 0; - - if (tx) { - for (i = 0 ; i < c ; i++) { - char val = *tx++; - - if (kp_spi_wait_for_reg_bit(cs, KP_SPI_REG_STATUS, KP_SPI_REG_STATUS_TXS) < 0) { - goto out; - } - - kp_spi_write_reg(cs, KP_SPI_REG_TXDATA, val); - processed++; - } - } - else if(rx) { - for (i = 0 ; i < c ; i++) { - char test=0; - - kp_spi_write_reg(cs, KP_SPI_REG_TXDATA, 0x00); - - if (kp_spi_wait_for_reg_bit(cs, KP_SPI_REG_STATUS, KP_SPI_REG_STATUS_RXS) < 0) { - goto out; - } - - test = kp_spi_read_reg(cs, KP_SPI_REG_RXDATA); - *rx++ = test; - processed++; - } - } - - if (kp_spi_wait_for_reg_bit(cs, KP_SPI_REG_STATUS, KP_SPI_REG_STATUS_EOT) < 0) { - //TODO: Figure out how to abort transaction?? This has never happened in practice though... - } - - out: - return processed; -} - -/***************** - * SPI Functions * - *****************/ -static int -kp_spi_setup(struct spi_device *spidev) -{ - union kp_spi_config sc; - struct kp_spi *kpspi = spi_master_get_devdata(spidev->master); - struct kp_spi_controller_state *cs; - - /* setup controller state */ - cs = spidev->controller_state; - if (!cs) { - cs = kzalloc(sizeof(*cs), GFP_KERNEL); - if(!cs) { - return -ENOMEM; - } - cs->base = kpspi->base; - cs->phys = kpspi->phys; - cs->chip_select = spidev->chip_select; - cs->word_len = spidev->bits_per_word; - cs->conf_cache = -1; - spidev->controller_state = cs; - } - - /* set config register */ - sc.bitfield.wl = spidev->bits_per_word - 1; - sc.bitfield.cs = spidev->chip_select; - sc.bitfield.spi_en = 0; - sc.bitfield.trm = 0; - sc.bitfield.ffen = 0; - kp_spi_write_reg(spidev->controller_state, KP_SPI_REG_CONFIG, sc.reg); - return 0; -} - -static int -kp_spi_transfer_one_message(struct spi_master *master, struct spi_message *m) -{ - struct kp_spi_controller_state *cs; - struct spi_device *spidev; - struct kp_spi *kpspi; - struct spi_transfer *transfer; - union kp_spi_config sc; - int status = 0; - - spidev = m->spi; - kpspi = spi_master_get_devdata(master); - m->actual_length = 0; - m->status = 0; - - cs = spidev->controller_state; - - /* reject invalid messages and transfers */ - if (list_empty(&m->transfers)) { - return -EINVAL; - } - - /* validate input */ - list_for_each_entry(transfer, &m->transfers, transfer_list) { - const void *tx_buf = transfer->tx_buf; - void *rx_buf = transfer->rx_buf; - unsigned len = transfer->len; - - if (transfer->speed_hz > KP_SPI_CLK || (len && !(rx_buf || tx_buf))) { - dev_dbg(kpspi->dev, " transfer: %d Hz, %d %s%s, %d bpw\n", - transfer->speed_hz, - len, - tx_buf ? "tx" : "", - rx_buf ? "rx" : "", - transfer->bits_per_word); - dev_dbg(kpspi->dev, " transfer -EINVAL\n"); - return -EINVAL; - } - if (transfer->speed_hz && (transfer->speed_hz < (KP_SPI_CLK >> 15))) { - dev_dbg(kpspi->dev, "speed_hz %d below minimum %d Hz\n", - transfer->speed_hz, - KP_SPI_CLK >> 15); - dev_dbg(kpspi->dev, " speed_hz -EINVAL\n"); - return -EINVAL; - } - } - - /* assert chip select to start the sequence*/ - sc.reg = kp_spi_read_reg(cs, KP_SPI_REG_CONFIG); - sc.bitfield.spi_en = 1; - kp_spi_write_reg(cs, KP_SPI_REG_CONFIG, sc.reg); - - /* work */ - if (kp_spi_wait_for_reg_bit(cs, KP_SPI_REG_STATUS, KP_SPI_REG_STATUS_EOT) < 0) { - dev_info(kpspi->dev, "EOT timed out\n"); - goto out; - } - - /* do the transfers for this message */ - list_for_each_entry(transfer, &m->transfers, transfer_list) { - if (transfer->tx_buf == NULL && transfer->rx_buf == NULL && transfer->len) { - status = -EINVAL; - break; - } - - /* transfer */ - if (transfer->len) { - unsigned int word_len = spidev->bits_per_word; - unsigned count; - - /* set up the transfer... */ - sc.reg = kp_spi_read_reg(cs, KP_SPI_REG_CONFIG); - - /* ...direction */ - if (transfer->tx_buf) { - sc.bitfield.trm = KP_SPI_REG_CONFIG_TRM_TX; - } - else if (transfer->rx_buf) { - sc.bitfield.trm = KP_SPI_REG_CONFIG_TRM_RX; - } - - /* ...word length */ - if (transfer->bits_per_word) { - word_len = transfer->bits_per_word; - } - cs->word_len = word_len; - sc.bitfield.wl = word_len-1; - - /* ...chip select */ - sc.bitfield.cs = spidev->chip_select; - - /* ...and write the new settings */ - kp_spi_write_reg(cs, KP_SPI_REG_CONFIG, sc.reg); - - /* do the transfer */ - count = kp_spi_txrx_pio(spidev, transfer); - m->actual_length += count; - - if (count != transfer->len) { - status = -EIO; - break; - } - } - - if (transfer->delay_usecs) { - udelay(transfer->delay_usecs); - } - } - - /* de-assert chip select to end the sequence */ - sc.reg = kp_spi_read_reg(cs, KP_SPI_REG_CONFIG); - sc.bitfield.spi_en = 0; - kp_spi_write_reg(cs, KP_SPI_REG_CONFIG, sc.reg); - - out: - /* done work */ - spi_finalize_current_message(master); - return 0; -} - -static void -kp_spi_cleanup(struct spi_device *spidev) -{ - struct kp_spi_controller_state *cs = spidev->controller_state; - if (cs) { - kfree(cs); - } -} - - - -/****************** - * Probe / Remove * - ******************/ -static int -kp_spi_probe(struct platform_device *pldev) -{ - struct kpc_core_device_platdata *drvdata; - struct spi_master *master; - struct kp_spi *kpspi; - struct resource *r; - int status = 0; - int i; - - drvdata = pldev->dev.platform_data; - if (!drvdata){ - dev_err(&pldev->dev, "kp_spi_probe: platform_data is NULL!\n"); - return -ENODEV; - } - - master = spi_alloc_master(&pldev->dev, sizeof(struct kp_spi)); - if (master == NULL) { - dev_err(&pldev->dev, "kp_spi_probe: master allocation failed\n"); - return -ENOMEM; - } - - /* set up the spi functions */ - master->mode_bits = SPI_CPOL | SPI_CPHA | SPI_CS_HIGH; - master->bits_per_word_mask = (unsigned int)SPI_BPW_RANGE_MASK(4, 32); - master->setup = kp_spi_setup; - master->transfer_one_message = kp_spi_transfer_one_message; - master->cleanup = kp_spi_cleanup; - - platform_set_drvdata(pldev, master); - - kpspi = spi_master_get_devdata(master); - kpspi->master = master; - kpspi->dev = &pldev->dev; - - master->num_chipselect = 4; - if (pldev->id != -1) { - master->bus_num = pldev->id; - } - kpspi->pin_dir = 0; - - r = platform_get_resource(pldev, IORESOURCE_MEM, 0); - if (r == NULL) { - dev_err(&pldev->dev, "kp_spi_probe: Unable to get platform resources\n"); - status = -ENODEV; - goto free_master; - } - - kpspi->phys = (unsigned long)ioremap_nocache(r->start, resource_size(r)); - kpspi->base = (u64 __iomem *)kpspi->phys; - - status = spi_register_master(master); - if (status < 0) { - dev_err(&pldev->dev, "Unable to register SPI device\n"); - goto free_master; - } - - /* register the slave boards */ - #define NEW_SPI_DEVICE_FROM_BOARD_INFO_TABLE(table) \ - for (i = 0 ; i < ARRAY_SIZE(table) ; i++) { \ - spi_new_device(master, &(table[i])); \ - } - - switch ((drvdata->card_id & 0xFFFF0000) >> 16){ - case PCI_DEVICE_ID_DAKTRONICS_KADOKA_P2KR0: - NEW_SPI_DEVICE_FROM_BOARD_INFO_TABLE(p2kr0_board_info); - break; - default: - dev_err(&pldev->dev, "Unknown hardware, cant know what partition table to use!\n"); - goto free_master; - break; - } - - return status; - - free_master: - spi_master_put(master); - return status; -} - -static int -kp_spi_remove(struct platform_device *pldev) -{ - struct spi_master * master = platform_get_drvdata(pldev); - spi_unregister_master(master); - return 0; -} - - -static struct platform_driver kp_spi_driver = { - .driver = { - .name = KP_DRIVER_NAME_SPI, - }, - .probe = kp_spi_probe, - .remove = kp_spi_remove, -}; - -module_platform_driver(kp_spi_driver); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:kp_spi");