ramips: add speed and mode settings to spi-mt7621

Signed-off-by: John Crispin <blogic@openwrt.org>

SVN-Revision: 46956
This commit is contained in:
John Crispin 2015-09-16 08:30:43 +00:00
parent 38c28154bd
commit 85d5b9984d

View file

@ -25,7 +25,7 @@
obj-$(CONFIG_SPI_OC_TINY) += spi-oc-tiny.o obj-$(CONFIG_SPI_OC_TINY) += spi-oc-tiny.o
--- /dev/null --- /dev/null
+++ b/drivers/spi/spi-mt7621.c +++ b/drivers/spi/spi-mt7621.c
@@ -0,0 +1,314 @@ @@ -0,0 +1,479 @@
+/* +/*
+ * spi-mt7621.c -- MediaTek MT7621 SPI controller driver + * spi-mt7621.c -- MediaTek MT7621 SPI controller driver
+ * + *
@ -70,6 +70,7 @@
+ +
+#define MT7621_SPI_OPCODE 0x04 +#define MT7621_SPI_OPCODE 0x04
+#define MT7621_SPI_DATA0 0x08 +#define MT7621_SPI_DATA0 0x08
+#define MT7621_SPI_DATA4 0x18
+#define SPI_CTL_TX_RX_CNT_MASK 0xff +#define SPI_CTL_TX_RX_CNT_MASK 0xff
+#define SPI_CTL_START BIT(8) +#define SPI_CTL_START BIT(8)
+ +
@ -78,6 +79,10 @@
+#define MT7621_SPI_MOREBUF 0x2c +#define MT7621_SPI_MOREBUF 0x2c
+#define MT7621_SPI_SPACE 0x3c +#define MT7621_SPI_SPACE 0x3c
+ +
+#define MT7621_CPHA BIT(5)
+#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; +struct mt7621_spi;
@ -108,17 +113,77 @@
+ iowrite32(val, rs->base + reg); + iowrite32(val, rs->base + reg);
+} +}
+ +
+static void mt7621_spi_reset(struct mt7621_spi *rs, int duplex)
+{
+ u32 master = mt7621_spi_read(rs, MT7621_SPI_MASTER);
+
+ master &= ~(0xfff << 16);
+ master |= 1 << 16;
+ master |= 7 << 29;
+ master |= 1 << 2;
+ if (duplex)
+ master |= 1 << 10;
+
+ mt7621_spi_write(rs, MT7621_SPI_MASTER, master);
+}
+
+static void mt7621_spi_set_cs(struct spi_device *spi, int enable) +static void mt7621_spi_set_cs(struct spi_device *spi, int enable)
+{ +{
+ struct mt7621_spi *rs = spidev_to_mt7621_spi(spi); + struct mt7621_spi *rs = spidev_to_mt7621_spi(spi);
+ int cs = spi->chip_select; + int cs = spi->chip_select;
+ u32 polar = 0; + u32 polar = 0;
+ +
+ mt7621_spi_reset(rs, cs);
+ if (enable) + if (enable)
+ polar = BIT(cs); + polar = BIT(cs);
+ mt7621_spi_write(rs, MT7621_SPI_POLAR, polar); + 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 &= ~(0xfff << 16);
+ reg |= (rate - 2) << 16;
+ rs->speed = speed;
+
+ reg &= ~MT7621_LSB_FIRST;
+ if (spi->mode & SPI_LSB_FIRST)
+ 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;
+ }
+ mt7621_spi_write(rs, MT7621_SPI_MASTER, reg);
+
+ return 0;
+}
+
+static inline int mt7621_spi_wait_till_ready(struct spi_device *spi) +static inline int mt7621_spi_wait_till_ready(struct spi_device *spi)
+{ +{
+ struct mt7621_spi *rs = spidev_to_mt7621_spi(spi); + struct mt7621_spi *rs = spidev_to_mt7621_spi(spi);
@ -138,11 +203,12 @@
+ return -ETIMEDOUT; + return -ETIMEDOUT;
+} +}
+ +
+static int mt7621_spi_transfer_one_message(struct spi_master *master, +static int mt7621_spi_transfer_half_duplex(struct spi_master *master,
+ struct spi_message *m) + struct spi_message *m)
+{ +{
+ struct mt7621_spi *rs = spi_master_get_devdata(master); + struct mt7621_spi *rs = spi_master_get_devdata(master);
+ struct spi_device *spi = m->spi; + struct spi_device *spi = m->spi;
+ unsigned int speed = spi->max_speed_hz;
+ struct spi_transfer *t = NULL; + struct spi_transfer *t = NULL;
+ int status = 0; + int status = 0;
+ int i, len = 0; + int i, len = 0;
@ -175,6 +241,10 @@
+ goto msg_done; + goto msg_done;
+ } + }
+ +
+ if (mt7621_spi_prepare(spi, speed)) {
+ status = -EIO;
+ goto msg_done;
+ }
+ data[0] = swab32(data[0]); + data[0] = swab32(data[0]);
+ if (len < 4) + if (len < 4)
+ data[0] >>= (4 - len) * 8; + data[0] >>= (4 - len) * 8;
@ -221,21 +291,116 @@
+ return 0; + return 0;
+} +}
+ +
+static int mt7621_spi_setup(struct spi_device *spi) +static int mt7621_spi_transfer_full_duplex(struct spi_master *master,
+ struct spi_message *m)
+{ +{
+ struct mt7621_spi *rs = spi_master_get_devdata(master);
+ struct spi_device *spi = m->spi;
+ 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 = 0;
+
+ 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;
+
+ if (!buf)
+ continue;
+
+ if (WARN_ON(len + t->len > 16)) {
+ status = -EIO;
+ goto msg_done;
+ }
+
+ for (i = 0; i < t->len; i++, len++)
+ data[len / 4] |= buf[i] << (8 * (len & 3));
+ }
+
+ if (WARN_ON(rx_len > 16)) {
+ status = -EIO;
+ goto msg_done;
+ }
+
+ if (mt7621_spi_prepare(spi, speed)) {
+ status = -EIO;
+ goto msg_done;
+ }
+
+ for (i = 0; i < len; i += 4)
+ mt7621_spi_write(rs, MT7621_SPI_DATA0 + i, data[i / 4]);
+
+ val |= len * 8;
+ val |= (rx_len * 8) << 12;
+ mt7621_spi_write(rs, MT7621_SPI_MOREBUF, val);
+
+ mt7621_spi_set_cs(spi, 1);
+
+ 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_DATA4 + i);
+
+ //m->actual_length = len + rx_len;
+ m->actual_length = rx_len;
+
+ len = 0;
+ list_for_each_entry(t, &m->transfers, transfer_list) {
+ u8 *buf = t->rx_buf;
+
+ if (!buf)
+ continue;
+
+ for (i = 0; i < t->len; i++, len++)
+ buf[i] = data[len / 4] >> (8 * (len & 3));
+ }
+
+msg_done:
+ m->status = status;
+ spi_finalize_current_message(master);
+
+ return 0; + return 0;
+} +}
+ +
+static void mt7621_spi_reset(struct mt7621_spi *rs) +static int mt7621_spi_transfer_one_message(struct spi_master *master,
+ struct spi_message *m)
+{ +{
+ u32 master = mt7621_spi_read(rs, MT7621_SPI_MASTER); + struct spi_device *spi = m->spi;
+ int cs = spi->chip_select;
+ +
+ master &= ~(0xfff << 16); + if (cs)
+ master |= 13 << 16; + return mt7621_spi_transfer_full_duplex(master, m);
+ master |= 7 << 29; + return mt7621_spi_transfer_half_duplex(master, m);
+ master |= 1 << 2; +}
+ +
+ mt7621_spi_write(rs, MT7621_SPI_MASTER, master); +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)))
+ 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[] = { +static const struct of_device_id mt7621_spi_match[] = {
@ -279,7 +444,7 @@
+ +
+ master = spi_alloc_master(&pdev->dev, sizeof(*rs)); + master = spi_alloc_master(&pdev->dev, sizeof(*rs));
+ if (master == NULL) { + if (master == NULL) {
+ dev_dbg(&pdev->dev, "master allocation failed\n"); + dev_info(&pdev->dev, "master allocation failed\n");
+ return -ENOMEM; + return -ENOMEM;
+ } + }
+ +
@ -299,12 +464,12 @@
+ rs->master = master; + rs->master = master;
+ rs->sys_freq = clk_get_rate(rs->clk); + rs->sys_freq = clk_get_rate(rs->clk);
+ rs->ops = ops; + rs->ops = ops;
+ dev_dbg(&pdev->dev, "sys_freq: %u\n", rs->sys_freq); + dev_info(&pdev->dev, "sys_freq: %u\n", rs->sys_freq);
+ spin_lock_irqsave(&rs->lock, flags); + spin_lock_irqsave(&rs->lock, flags);
+ +
+ device_reset(&pdev->dev); + device_reset(&pdev->dev);
+ +
+ mt7621_spi_reset(rs); + mt7621_spi_reset(rs, 0);
+ +
+ return spi_register_master(master); + return spi_register_master(master);
+} +}