aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/mt7621-spi/spi-mt7621.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/mt7621-spi/spi-mt7621.c')
-rw-r--r--drivers/staging/mt7621-spi/spi-mt7621.c206
1 files changed, 118 insertions, 88 deletions
diff --git a/drivers/staging/mt7621-spi/spi-mt7621.c b/drivers/staging/mt7621-spi/spi-mt7621.c
index 37f299080410..d045b5568e0f 100644
--- a/drivers/staging/mt7621-spi/spi-mt7621.c
+++ b/drivers/staging/mt7621-spi/spi-mt7621.c
@@ -55,7 +55,8 @@
#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;
@@ -65,6 +66,7 @@ struct mt7621_spi {
unsigned int sys_freq;
unsigned int speed;
struct clk *clk;
+ int pending_write;
struct mt7621_spi_ops *ops;
};
@@ -96,6 +98,7 @@ static void mt7621_spi_reset(struct mt7621_spi *rs, int duplex)
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)
@@ -104,7 +107,7 @@ 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);
@@ -137,36 +140,34 @@ static int mt7621_spi_prepare(struct spi_device *spi, unsigned int speed)
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 spi_device *spi)
+static inline int mt7621_spi_wait_till_ready(struct mt7621_spi *rs)
{
- 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);
}
@@ -174,90 +175,124 @@ static inline int mt7621_spi_wait_till_ready(struct spi_device *spi)
return -ETIMEDOUT;
}
-static int mt7621_spi_transfer_half_duplex(struct spi_master *master,
- struct spi_message *m)
+static void mt7621_spi_read_half_duplex(struct mt7621_spi *rs,
+ int rx_len, u8 *buf)
{
- 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;
+ /* 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;
- mt7621_spi_wait_till_ready(spi);
+ rs->pending_write = 0;
- list_for_each_entry(t, &m->transfers, transfer_list) {
- const u8 *buf = t->tx_buf;
+ while (rx_len || tx_len) {
+ int i;
+ u32 val = (min(tx_len, 4) * 8) << 24;
+ int rx = min(rx_len, 32);
- if (t->rx_buf)
- rx_len += t->len;
+ if (tx_len > 4)
+ val |= (tx_len - 4) * 8;
+ val |= (rx * 8) << 12;
+ mt7621_spi_write(rs, MT7621_SPI_MOREBUF, val);
- if (!buf)
- continue;
+ tx_len = 0;
- if (t->speed_hz < speed)
- speed = t->speed_hz;
+ val = mt7621_spi_read(rs, MT7621_SPI_TRANS);
+ val |= SPI_CTL_START;
+ mt7621_spi_write(rs, MT7621_SPI_TRANS, val);
- if (WARN_ON(len + t->len > 36)) {
- status = -EIO;
- goto msg_done;
- }
+ mt7621_spi_wait_till_ready(rs);
- for (i = 0; i < t->len; i++, len++)
- data[len / 4] |= buf[i] << (8 * (len & 3));
+ 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;
}
+}
- if (WARN_ON(rx_len > 32)) {
- status = -EIO;
- goto msg_done;
- }
+static inline void mt7621_spi_flush(struct mt7621_spi *rs)
+{
+ mt7621_spi_read_half_duplex(rs, 0, NULL);
+}
- if (mt7621_spi_prepare(spi, speed)) {
- status = -EIO;
- goto msg_done;
+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);
+ }
}
- 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);
+ while (tx_len > 0) {
+ if (len >= 36) {
+ rs->pending_write = len;
+ mt7621_spi_flush(rs);
+ len = 0;
+ }
- val = mt7621_spi_read(rs, MT7621_SPI_TRANS);
- val |= SPI_CTL_START;
- mt7621_spi_write(rs, MT7621_SPI_TRANS, val);
+ 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;
+}
- mt7621_spi_wait_till_ready(spi);
+static int mt7621_spi_transfer_half_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;
- mt7621_spi_set_cs(spi, 0);
+ mt7621_spi_wait_till_ready(rs);
- for (i = 0; i < rx_len; i += 4)
- data[i / 4] = mt7621_spi_read(rs, MT7621_SPI_DATA0 + i);
+ list_for_each_entry(t, &m->transfers, transfer_list)
+ if (t->speed_hz < speed)
+ speed = t->speed_hz;
- m->actual_length = len + rx_len;
+ if (mt7621_spi_prepare(spi, speed)) {
+ status = -EIO;
+ goto msg_done;
+ }
- len = 0;
+ mt7621_spi_set_cs(spi, 1);
+ m->actual_length = 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));
+ 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;
}
+ mt7621_spi_flush(rs);
+ mt7621_spi_set_cs(spi, 0);
msg_done:
m->status = status;
spi_finalize_current_message(master);
@@ -278,7 +313,7 @@ static int mt7621_spi_transfer_full_duplex(struct spi_master *master,
u32 data[9] = { 0 };
u32 val = 0;
- mt7621_spi_wait_till_ready(spi);
+ mt7621_spi_wait_till_ready(rs);
list_for_each_entry(t, &m->transfers, transfer_list) {
const u8 *buf = t->tx_buf;
@@ -323,7 +358,7 @@ static int mt7621_spi_transfer_full_duplex(struct spi_master *master,
val |= SPI_CTL_START;
mt7621_spi_write(rs, MT7621_SPI_TRANS, val);
- mt7621_spi_wait_till_ready(spi);
+ mt7621_spi_wait_till_ready(rs);
mt7621_spi_set_cs(spi, 0);
@@ -384,11 +419,6 @@ static const struct of_device_id mt7621_spi_match[] = {
};
MODULE_DEVICE_TABLE(of, mt7621_spi_match);
-static size_t max_transfer_size(struct spi_device *spi)
-{
- return 32;
-}
-
static int mt7621_spi_probe(struct platform_device *pdev)
{
const struct of_device_id *match;
@@ -434,7 +464,6 @@ static int mt7621_spi_probe(struct platform_device *pdev)
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 = max_transfer_size;
dev_set_drvdata(&pdev->dev, master);
@@ -444,6 +473,7 @@ static int mt7621_spi_probe(struct platform_device *pdev)
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);