Signed-off-by: John Crispin <blogic@openwrt.org>
---
-Note: This patch contains upstream mt7621-spi at 9c562d8411a54f6731cdc587c29968d9e8610c85
-
drivers/spi/Kconfig | 6 +
drivers/spi/Makefile | 1 +
drivers/spi/spi-mt7621.c | 480 ++++++++++++++++++++++++++++++++++++++++++++++
obj-$(CONFIG_SPI_OC_TINY) += spi-oc-tiny.o
--- /dev/null
+++ b/drivers/spi/spi-mt7621.c
-@@ -0,0 +1,515 @@
+@@ -0,0 +1,494 @@
+/*
+ * spi-mt7621.c -- MediaTek MT7621 SPI controller driver
+ *
+#define MT7621_CPOL BIT(4)
+#define MT7621_LSB_FIRST BIT(3)
+
-+#define RT2880_SPI_MODE_BITS (SPI_CPOL | SPI_CPHA | \
-+ SPI_LSB_FIRST | SPI_CS_HIGH)
++#define RT2880_SPI_MODE_BITS (SPI_CPOL | SPI_CPHA | SPI_LSB_FIRST | SPI_CS_HIGH)
+
+struct mt7621_spi;
+
+ unsigned int sys_freq;
+ unsigned int speed;
+ struct clk *clk;
-+ int pending_write;
+
+ struct mt7621_spi_ops *ops;
+};
+
+ master |= 7 << 29;
+ master |= 1 << 2;
++#ifdef CONFIG_SOC_MT7620
+ if (duplex)
+ master |= 1 << 10;
+ else
++#endif
+ master &= ~(1 << 10);
+
+ mt7621_spi_write(rs, MT7621_SPI_MASTER, master);
-+ rs->pending_write = 0;
+}
+
+static void mt7621_spi_set_cs(struct spi_device *spi, int enable)
+ int cs = spi->chip_select;
+ u32 polar = 0;
+
-+ mt7621_spi_reset(rs, cs);
++ mt7621_spi_reset(rs, cs);
+ if (enable)
+ polar = BIT(cs);
+ mt7621_spi_write(rs, MT7621_SPI_POLAR, polar);
+ reg |= MT7621_LSB_FIRST;
+
+ reg &= ~(MT7621_CPHA | MT7621_CPOL);
-+ switch (spi->mode & (SPI_CPOL | SPI_CPHA)) {
-+ case SPI_MODE_0:
-+ break;
-+ case SPI_MODE_1:
-+ reg |= MT7621_CPHA;
-+ break;
-+ case SPI_MODE_2:
-+ reg |= MT7621_CPOL;
-+ break;
-+ case SPI_MODE_3:
-+ reg |= MT7621_CPOL | MT7621_CPHA;
-+ break;
++ switch(spi->mode & (SPI_CPOL | SPI_CPHA)) {
++ case SPI_MODE_0:
++ break;
++ case SPI_MODE_1:
++ reg |= MT7621_CPHA;
++ break;
++ case SPI_MODE_2:
++ reg |= MT7621_CPOL;
++ break;
++ case SPI_MODE_3:
++ reg |= MT7621_CPOL | MT7621_CPHA;
++ break;
+ }
+ mt7621_spi_write(rs, MT7621_SPI_MASTER, reg);
+
+ return 0;
+}
+
-+static inline int mt7621_spi_wait_till_ready(struct mt7621_spi *rs)
++static inline int mt7621_spi_wait_till_ready(struct spi_device *spi)
+{
++ struct mt7621_spi *rs = spidev_to_mt7621_spi(spi);
+ 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)
++ 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)
-+{
-+ /* 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*
-+ */
-+ int 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 val = 0;
-+ int len = rs->pending_write;
-+
-+ 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_half_duplex(struct spi_master *master,
+ struct spi_message *m)
+{
+ unsigned int speed = spi->max_speed_hz;
+ struct spi_transfer *t = NULL;
+ int status = 0;
++ int i, len = 0;
++ int rx_len = 0;
++ u32 data[9] = { 0 };
++ u32 val;
++
++ mt7621_spi_wait_till_ready(spi);
++
++ list_for_each_entry(t, &m->transfers, transfer_list) {
++ const u8 *buf = t->tx_buf;
++
++ if (t->rx_buf)
++ rx_len += t->len;
+
-+ mt7621_spi_wait_till_ready(rs);
++ if (!buf)
++ continue;
+
-+ list_for_each_entry(t, &m->transfers, transfer_list)
+ if (t->speed_hz < speed)
+ speed = t->speed_hz;
+
++ /*
++ * m25p80 might attempt to write more data than we can handle.
++ * truncate the message to what we can fit into the registers
++ */
++ if (len + t->len > 36)
++ t->len = 36 - len;
++
++ for (i = 0; i < t->len; i++, len++)
++ data[len / 4] |= buf[i] << (8 * (len & 3));
++ }
++
++ if (WARN_ON(rx_len > 32)) {
++ status = -EIO;
++ goto msg_done;
++ }
++
+ if (mt7621_spi_prepare(spi, speed)) {
+ status = -EIO;
+ goto msg_done;
+ }
++ data[0] = swab32(data[0]);
++ if (len < 4)
++ data[0] >>= (4 - len) * 8;
++
++ for (i = 0; i < len; i += 4)
++ mt7621_spi_write(rs, MT7621_SPI_OPCODE + i, data[i / 4]);
++
++ val = (min_t(int, len, 4) * 8) << 24;
++ if (len > 4)
++ val |= (len - 4) * 8;
++ val |= (rx_len * 8) << 12;
++ mt7621_spi_write(rs, MT7621_SPI_MOREBUF, val);
+
+ mt7621_spi_set_cs(spi, 1);
-+ m->actual_length = 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(spi);
++
++ mt7621_spi_set_cs(spi, 0);
++
++ for (i = 0; i < rx_len; i += 4)
++ data[i / 4] = mt7621_spi_read(rs, MT7621_SPI_DATA0 + i);
++
++ m->actual_length = len + rx_len;
++
++ len = 0;
+ list_for_each_entry(t, &m->transfers, transfer_list) {
-+ 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;
++ u8 *buf = t->rx_buf;
++
++ if (!buf)
++ continue;
++
++ for (i = 0; i < t->len; i++, len++)
++ buf[i] = data[len / 4] >> (8 * (len & 3));
+ }
-+ mt7621_spi_flush(rs);
+
-+ mt7621_spi_set_cs(spi, 0);
+msg_done:
+ m->status = status;
+ spi_finalize_current_message(master);
+ return 0;
+}
+
++#ifdef CONFIG_SOC_MT7620
+static int mt7621_spi_transfer_full_duplex(struct spi_master *master,
+ struct spi_message *m)
+{
+ u32 data[9] = { 0 };
+ u32 val = 0;
+
-+ mt7621_spi_wait_till_ready(rs);
++ mt7621_spi_wait_till_ready(spi);
+
+ list_for_each_entry(t, &m->transfers, transfer_list) {
+ const u8 *buf = t->tx_buf;
+ val |= SPI_CTL_START;
+ mt7621_spi_write(rs, MT7621_SPI_TRANS, val);
+
-+ mt7621_spi_wait_till_ready(rs);
++ mt7621_spi_wait_till_ready(spi);
+
+ mt7621_spi_set_cs(spi, 0);
+
+
+ return 0;
+}
++#endif
+
+static int mt7621_spi_transfer_one_message(struct spi_master *master,
+ struct spi_message *m)
+{
+ struct spi_device *spi = m->spi;
++#ifdef CONFIG_SOC_MT7620
+ int cs = spi->chip_select;
+
+ if (cs)
+ return mt7621_spi_transfer_full_duplex(master, m);
++#endif
+ return mt7621_spi_transfer_half_duplex(master, m);
+}
+
+};
+MODULE_DEVICE_TABLE(of, mt7621_spi_match);
+
++static size_t mt7621_max_transfer_size(struct spi_device *spi)
++{
++ return 32;
++}
++
+static int mt7621_spi_probe(struct platform_device *pdev)
+{
+ const struct of_device_id *match;
+ master->bits_per_word_mask = SPI_BPW_MASK(8);
+ master->dev.of_node = pdev->dev.of_node;
+ master->num_chipselect = 2;
++ master->max_transfer_size = mt7621_max_transfer_size;
+
+ dev_set_drvdata(&pdev->dev, master);
+
+ rs->master = master;
+ rs->sys_freq = clk_get_rate(rs->clk);
+ rs->ops = ops;
-+ rs->pending_write = 0;
+ dev_info(&pdev->dev, "sys_freq: %u\n", rs->sys_freq);
+
+ device_reset(&pdev->dev);
+static struct platform_driver mt7621_spi_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
++ .owner = THIS_MODULE,
+ .of_match_table = mt7621_spi_match,
+ },
+ .probe = mt7621_spi_probe,