--- /dev/null
-
- struct mt7621_spi_ops *ops;
+// SPDX-License-Identifier: GPL-2.0
+//
+// spi-mt7621.c -- MediaTek MT7621 SPI controller driver
+//
+// Copyright (C) 2011 Sergiy <piratfm@gmail.com>
+// Copyright (C) 2011-2013 Gabor Juhos <juhosg@openwrt.org>
+// Copyright (C) 2014-2015 Felix Fietkau <nbd@nbd.name>
+//
+// Some parts are based on spi-orion.c:
+// Author: Shadi Ammouri <shadi@marvell.com>
+// Copyright (C) 2007-2008 Marvell Ltd.
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/reset.h>
+#include <linux/spi/spi.h>
+
+#define DRIVER_NAME "spi-mt7621"
+
+/* in usec */
+#define RALINK_SPI_WAIT_MAX_LOOP 2000
+
+/* SPISTAT register bit field */
+#define SPISTAT_BUSY BIT(0)
+
+#define MT7621_SPI_TRANS 0x00
+#define SPITRANS_BUSY BIT(16)
+
+#define MT7621_SPI_OPCODE 0x04
+#define MT7621_SPI_DATA0 0x08
+#define MT7621_SPI_DATA4 0x18
+#define SPI_CTL_TX_RX_CNT_MASK 0xff
+#define SPI_CTL_START BIT(8)
+
+#define MT7621_SPI_MASTER 0x28
+#define MASTER_MORE_BUFMODE BIT(2)
+#define MASTER_FULL_DUPLEX BIT(10)
+#define MASTER_RS_CLK_SEL GENMASK(27, 16)
+#define MASTER_RS_CLK_SEL_SHIFT 16
+#define MASTER_RS_SLAVE_SEL GENMASK(31, 29)
+
+#define MT7621_SPI_MOREBUF 0x2c
+#define MT7621_SPI_POLAR 0x38
+#define MT7621_SPI_SPACE 0x3c
+
+#define MT7621_CPHA BIT(5)
+#define MT7621_CPOL BIT(4)
+#define MT7621_LSB_FIRST BIT(3)
+
+struct mt7621_spi {
+ struct spi_controller *master;
+ void __iomem *base;
+ unsigned int sys_freq;
+ unsigned int speed;
+ struct clk *clk;
+ int pending_write;
- spi->max_speed_hz = (rs->sys_freq / 2);
+};
+
+static inline struct mt7621_spi *spidev_to_mt7621_spi(struct spi_device *spi)
+{
+ return spi_controller_get_devdata(spi->master);
+}
+
+static inline u32 mt7621_spi_read(struct mt7621_spi *rs, u32 reg)
+{
+ return ioread32(rs->base + reg);
+}
+
+static inline void mt7621_spi_write(struct mt7621_spi *rs, u32 reg, u32 val)
+{
+ iowrite32(val, rs->base + reg);
+}
+
+static void mt7621_spi_set_cs(struct spi_device *spi, int enable)
+{
+ struct mt7621_spi *rs = spidev_to_mt7621_spi(spi);
+ int cs = spi->chip_select;
+ u32 polar = 0;
+ u32 master;
+
+ /*
+ * Select SPI device 7, enable "more buffer mode" and disable
+ * full-duplex (only half-duplex really works on this chip
+ * reliably)
+ */
+ master = mt7621_spi_read(rs, MT7621_SPI_MASTER);
+ master |= MASTER_RS_SLAVE_SEL | MASTER_MORE_BUFMODE;
+ master &= ~MASTER_FULL_DUPLEX;
+ mt7621_spi_write(rs, MT7621_SPI_MASTER, master);
+
+ rs->pending_write = 0;
+
+ if (enable)
+ polar = BIT(cs);
+ mt7621_spi_write(rs, MT7621_SPI_POLAR, polar);
+}
+
+static int mt7621_spi_prepare(struct spi_device *spi, unsigned int speed)
+{
+ struct mt7621_spi *rs = spidev_to_mt7621_spi(spi);
+ u32 rate;
+ u32 reg;
+
+ dev_dbg(&spi->dev, "speed:%u\n", speed);
+
+ rate = DIV_ROUND_UP(rs->sys_freq, speed);
+ dev_dbg(&spi->dev, "rate-1:%u\n", rate);
+
+ if (rate > 4097)
+ return -EINVAL;
+
+ if (rate < 2)
+ rate = 2;
+
+ reg = mt7621_spi_read(rs, MT7621_SPI_MASTER);
+ reg &= ~MASTER_RS_CLK_SEL;
+ reg |= (rate - 2) << MASTER_RS_CLK_SEL_SHIFT;
+ rs->speed = speed;
+
+ reg &= ~MT7621_LSB_FIRST;
+ if (spi->mode & SPI_LSB_FIRST)
+ reg |= MT7621_LSB_FIRST;
+
+ /*
+ * This SPI controller seems to be tested on SPI flash only and some
+ * bits are swizzled under other SPI modes probably due to incorrect
+ * wiring inside the silicon. Only mode 0 works correctly.
+ */
+ reg &= ~(MT7621_CPHA | MT7621_CPOL);
+
+ mt7621_spi_write(rs, MT7621_SPI_MASTER, reg);
+
+ return 0;
+}
+
+static inline int mt7621_spi_wait_till_ready(struct mt7621_spi *rs)
+{
+ int i;
+
+ for (i = 0; i < RALINK_SPI_WAIT_MAX_LOOP; i++) {
+ u32 status;
+
+ status = mt7621_spi_read(rs, MT7621_SPI_TRANS);
+ if ((status & SPITRANS_BUSY) == 0)
+ return 0;
+ cpu_relax();
+ udelay(1);
+ }
+
+ return -ETIMEDOUT;
+}
+
+static void mt7621_spi_read_half_duplex(struct mt7621_spi *rs,
+ int rx_len, u8 *buf)
+{
+ int tx_len;
+
+ /*
+ * Combine with any pending write, and perform one or more half-duplex
+ * transactions reading 'len' bytes. Data to be written is already in
+ * MT7621_SPI_DATA.
+ */
+ tx_len = rs->pending_write;
+ rs->pending_write = 0;
+
+ while (rx_len || tx_len) {
+ int i;
+ u32 val = (min(tx_len, 4) * 8) << 24;
+ int rx = min(rx_len, 32);
+
+ if (tx_len > 4)
+ val |= (tx_len - 4) * 8;
+ val |= (rx * 8) << 12;
+ mt7621_spi_write(rs, MT7621_SPI_MOREBUF, val);
+
+ tx_len = 0;
+
+ val = mt7621_spi_read(rs, MT7621_SPI_TRANS);
+ val |= SPI_CTL_START;
+ mt7621_spi_write(rs, MT7621_SPI_TRANS, val);
+
+ mt7621_spi_wait_till_ready(rs);
+
+ for (i = 0; i < rx; i++) {
+ if ((i % 4) == 0)
+ val = mt7621_spi_read(rs, MT7621_SPI_DATA0 + i);
+ *buf++ = val & 0xff;
+ val >>= 8;
+ }
+
+ rx_len -= i;
+ }
+}
+
+static inline void mt7621_spi_flush(struct mt7621_spi *rs)
+{
+ mt7621_spi_read_half_duplex(rs, 0, NULL);
+}
+
+static void mt7621_spi_write_half_duplex(struct mt7621_spi *rs,
+ int tx_len, const u8 *buf)
+{
+ int len = rs->pending_write;
+ int val = 0;
+
+ if (len & 3) {
+ val = mt7621_spi_read(rs, MT7621_SPI_OPCODE + (len & ~3));
+ if (len < 4) {
+ val <<= (4 - len) * 8;
+ val = swab32(val);
+ }
+ }
+
+ while (tx_len > 0) {
+ if (len >= 36) {
+ rs->pending_write = len;
+ mt7621_spi_flush(rs);
+ len = 0;
+ }
+
+ val |= *buf++ << (8 * (len & 3));
+ len++;
+ if ((len & 3) == 0) {
+ if (len == 4)
+ /* The byte-order of the opcode is weird! */
+ val = swab32(val);
+ mt7621_spi_write(rs, MT7621_SPI_OPCODE + len - 4, val);
+ val = 0;
+ }
+ tx_len -= 1;
+ }
+
+ if (len & 3) {
+ if (len < 4) {
+ val = swab32(val);
+ val >>= (4 - len) * 8;
+ }
+ mt7621_spi_write(rs, MT7621_SPI_OPCODE + (len & ~3), val);
+ }
+
+ rs->pending_write = len;
+}
+
+static int mt7621_spi_transfer_one_message(struct spi_controller *master,
+ struct spi_message *m)
+{
+ struct mt7621_spi *rs = spi_controller_get_devdata(master);
+ struct spi_device *spi = m->spi;
+ unsigned int speed = spi->max_speed_hz;
+ struct spi_transfer *t = NULL;
+ int status = 0;
+
+ mt7621_spi_wait_till_ready(rs);
+
+ list_for_each_entry(t, &m->transfers, transfer_list)
+ if (t->speed_hz < speed)
+ speed = t->speed_hz;
+
+ if (mt7621_spi_prepare(spi, speed)) {
+ status = -EIO;
+ goto msg_done;
+ }
+
+ /* Assert CS */
+ mt7621_spi_set_cs(spi, 1);
+
+ m->actual_length = 0;
+ list_for_each_entry(t, &m->transfers, transfer_list) {
+ if ((t->rx_buf) && (t->tx_buf)) {
+ /*
+ * This controller will shift some extra data out
+ * of spi_opcode if (mosi_bit_cnt > 0) &&
+ * (cmd_bit_cnt == 0). So the claimed full-duplex
+ * support is broken since we have no way to read
+ * the MISO value during that bit.
+ */
+ status = -EIO;
+ goto msg_done;
+ } else if (t->rx_buf) {
+ mt7621_spi_read_half_duplex(rs, t->len, t->rx_buf);
+ } else if (t->tx_buf) {
+ mt7621_spi_write_half_duplex(rs, t->len, t->tx_buf);
+ }
+ m->actual_length += t->len;
+ }
+
+ /* Flush data and deassert CS */
+ mt7621_spi_flush(rs);
+ mt7621_spi_set_cs(spi, 0);
+
+msg_done:
+ m->status = status;
+ spi_finalize_current_message(master);
+
+ return 0;
+}
+
+static int mt7621_spi_setup(struct spi_device *spi)
+{
+ struct mt7621_spi *rs = spidev_to_mt7621_spi(spi);
+
+ if ((spi->max_speed_hz == 0) ||
+ (spi->max_speed_hz > (rs->sys_freq / 2)))
- struct mt7621_spi_ops *ops;
++ spi->max_speed_hz = rs->sys_freq / 2;
+
+ if (spi->max_speed_hz < (rs->sys_freq / 4097)) {
+ dev_err(&spi->dev, "setup: requested speed is too low %d Hz\n",
+ spi->max_speed_hz);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct of_device_id mt7621_spi_match[] = {
+ { .compatible = "ralink,mt7621-spi" },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mt7621_spi_match);
+
+static int mt7621_spi_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ struct spi_controller *master;
+ struct mt7621_spi *rs;
+ void __iomem *base;
+ struct resource *r;
+ int status = 0;
+ struct clk *clk;
- ops = (struct mt7621_spi_ops *)match->data;
+ int ret;
+
+ match = of_match_device(mt7621_spi_match, &pdev->dev);
+ if (!match)
+ return -EINVAL;
- rs->ops = ops;
+
+ r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, r);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "unable to get SYS clock, err=%d\n",
+ status);
+ return PTR_ERR(clk);
+ }
+
+ status = clk_prepare_enable(clk);
+ if (status)
+ return status;
+
+ master = spi_alloc_master(&pdev->dev, sizeof(*rs));
+ if (!master) {
+ dev_info(&pdev->dev, "master allocation failed\n");
+ return -ENOMEM;
+ }
+
+ master->mode_bits = SPI_LSB_FIRST;
+ master->flags = SPI_CONTROLLER_HALF_DUPLEX;
+ master->setup = mt7621_spi_setup;
+ master->transfer_one_message = mt7621_spi_transfer_one_message;
+ master->bits_per_word_mask = SPI_BPW_MASK(8);
+ master->dev.of_node = pdev->dev.of_node;
+ master->num_chipselect = 2;
+
+ dev_set_drvdata(&pdev->dev, master);
+
+ rs = spi_controller_get_devdata(master);
+ rs->base = base;
+ rs->clk = clk;
+ rs->master = master;
+ rs->sys_freq = clk_get_rate(rs->clk);
+ rs->pending_write = 0;
+ dev_info(&pdev->dev, "sys_freq: %u\n", rs->sys_freq);
+
+ ret = device_reset(&pdev->dev);
+ if (ret) {
+ dev_err(&pdev->dev, "SPI reset failed!\n");
+ return ret;
+ }
+
+ return devm_spi_register_controller(&pdev->dev, master);
+}
+
+static int mt7621_spi_remove(struct platform_device *pdev)
+{
+ struct spi_controller *master;
+ struct mt7621_spi *rs;
+
+ master = dev_get_drvdata(&pdev->dev);
+ rs = spi_controller_get_devdata(master);
+
+ clk_disable_unprepare(rs->clk);
+
+ return 0;
+}
+
+MODULE_ALIAS("platform:" DRIVER_NAME);
+
+static struct platform_driver mt7621_spi_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .of_match_table = mt7621_spi_match,
+ },
+ .probe = mt7621_spi_probe,
+ .remove = mt7621_spi_remove,
+};
+
+module_platform_driver(mt7621_spi_driver);
+
+MODULE_DESCRIPTION("MT7621 SPI driver");
+MODULE_AUTHOR("Felix Fietkau <nbd@nbd.name>");
+MODULE_LICENSE("GPL");
--- /dev/null
- unsigned char pha : 1; /* spim_clk Phase */
- unsigned char pol : 1; /* spim_clk Polarity */
- unsigned char epol : 1; /* spim_csx Polarity */
- unsigned char dpe : 1; /* Transmission Enable */
- unsigned char wl : 5; /* Word Length */
- unsigned char : 3;
- unsigned char trm : 2; /* TxRx Mode */
- unsigned char cs : 4; /* Chip Select */
- unsigned char wcnt : 7; /* Word Count */
- unsigned char ffen : 1; /* FIFO Enable */
- unsigned char spi_en : 1; /* SPI Enable */
- unsigned char : 5;
+ // SPDX-License-Identifier: GPL-2.0+
+ /*
+ * KP2000 SPI controller driver
+ *
+ * Copyright (C) 2014-2018 Daktronics
+ * Author: Matt Sickler <matt.sickler@daktronics.com>
+ * Very loosely based on spi-omap2-mcspi.c
+ */
+
+ #include <linux/kernel.h>
+ #include <linux/init.h>
+ #include <linux/interrupt.h>
+ #include <linux/io-64-nonatomic-lo-hi.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/slab.h>
+ #include <linux/pm_runtime.h>
+ #include <linux/of.h>
+ #include <linux/of_device.h>
+ #include <linux/gcd.h>
+ #include <linux/spi/spi.h>
+ #include <linux/spi/flash.h>
+ #include <linux/mtd/partitions.h>
+
+ #include "../kpc.h"
+ #include "spi_parts.h"
+
+
+ /***************
+ * 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 char rx : 1; /* Rx Status */
- unsigned char tx : 1; /* Tx Status */
- unsigned char eo : 1; /* End of Transfer */
- unsigned char : 1;
- unsigned char txffe : 1; /* Tx FIFO Empty */
- unsigned char txfff : 1; /* Tx FIFO Full */
- unsigned char rxffe : 1; /* Rx FIFO Empty */
- unsigned char rxfff : 1; /* Rx FIFO Full */
- unsigned int : 24;
++ 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 char ffstart : 1; /* FIFO Start */
- unsigned int : 31;
++ 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");