163d49afeSJan Glauber /*
263d49afeSJan Glauber * This file is subject to the terms and conditions of the GNU General Public
363d49afeSJan Glauber * License. See the file "COPYING" in the main directory of this archive
463d49afeSJan Glauber * for more details.
563d49afeSJan Glauber *
663d49afeSJan Glauber * Copyright (C) 2011, 2012 Cavium, Inc.
763d49afeSJan Glauber */
863d49afeSJan Glauber
963d49afeSJan Glauber #include <linux/spi/spi.h>
1063d49afeSJan Glauber #include <linux/module.h>
1163d49afeSJan Glauber #include <linux/delay.h>
1263d49afeSJan Glauber #include <linux/io.h>
1363d49afeSJan Glauber
1463d49afeSJan Glauber #include "spi-cavium.h"
1563d49afeSJan Glauber
octeon_spi_wait_ready(struct octeon_spi * p)1663d49afeSJan Glauber static void octeon_spi_wait_ready(struct octeon_spi *p)
1763d49afeSJan Glauber {
1863d49afeSJan Glauber union cvmx_mpi_sts mpi_sts;
1963d49afeSJan Glauber unsigned int loops = 0;
2063d49afeSJan Glauber
2163d49afeSJan Glauber do {
2263d49afeSJan Glauber if (loops++)
2363d49afeSJan Glauber __delay(500);
2463d49afeSJan Glauber mpi_sts.u64 = readq(p->register_base + OCTEON_SPI_STS(p));
2563d49afeSJan Glauber } while (mpi_sts.s.busy);
2663d49afeSJan Glauber }
2763d49afeSJan Glauber
octeon_spi_do_transfer(struct octeon_spi * p,struct spi_message * msg,struct spi_transfer * xfer,bool last_xfer)2863d49afeSJan Glauber static int octeon_spi_do_transfer(struct octeon_spi *p,
2963d49afeSJan Glauber struct spi_message *msg,
3063d49afeSJan Glauber struct spi_transfer *xfer,
3163d49afeSJan Glauber bool last_xfer)
3263d49afeSJan Glauber {
3363d49afeSJan Glauber struct spi_device *spi = msg->spi;
3463d49afeSJan Glauber union cvmx_mpi_cfg mpi_cfg;
3563d49afeSJan Glauber union cvmx_mpi_tx mpi_tx;
3663d49afeSJan Glauber unsigned int clkdiv;
3763d49afeSJan Glauber int mode;
3863d49afeSJan Glauber bool cpha, cpol;
3963d49afeSJan Glauber const u8 *tx_buf;
4063d49afeSJan Glauber u8 *rx_buf;
4163d49afeSJan Glauber int len;
4263d49afeSJan Glauber int i;
4363d49afeSJan Glauber
4463d49afeSJan Glauber mode = spi->mode;
4563d49afeSJan Glauber cpha = mode & SPI_CPHA;
4663d49afeSJan Glauber cpol = mode & SPI_CPOL;
4763d49afeSJan Glauber
4863d49afeSJan Glauber clkdiv = p->sys_freq / (2 * xfer->speed_hz);
4963d49afeSJan Glauber
5063d49afeSJan Glauber mpi_cfg.u64 = 0;
5163d49afeSJan Glauber
5263d49afeSJan Glauber mpi_cfg.s.clkdiv = clkdiv;
5363d49afeSJan Glauber mpi_cfg.s.cshi = (mode & SPI_CS_HIGH) ? 1 : 0;
5463d49afeSJan Glauber mpi_cfg.s.lsbfirst = (mode & SPI_LSB_FIRST) ? 1 : 0;
5563d49afeSJan Glauber mpi_cfg.s.wireor = (mode & SPI_3WIRE) ? 1 : 0;
5663d49afeSJan Glauber mpi_cfg.s.idlelo = cpha != cpol;
5763d49afeSJan Glauber mpi_cfg.s.cslate = cpha ? 1 : 0;
5863d49afeSJan Glauber mpi_cfg.s.enable = 1;
5963d49afeSJan Glauber
60*9e264f3fSAmit Kumar Mahapatra via Alsa-devel if (spi_get_chipselect(spi, 0) < 4)
61*9e264f3fSAmit Kumar Mahapatra via Alsa-devel p->cs_enax |= 1ull << (12 + spi_get_chipselect(spi, 0));
6263d49afeSJan Glauber mpi_cfg.u64 |= p->cs_enax;
6363d49afeSJan Glauber
6463d49afeSJan Glauber if (mpi_cfg.u64 != p->last_cfg) {
6563d49afeSJan Glauber p->last_cfg = mpi_cfg.u64;
6663d49afeSJan Glauber writeq(mpi_cfg.u64, p->register_base + OCTEON_SPI_CFG(p));
6763d49afeSJan Glauber }
6863d49afeSJan Glauber tx_buf = xfer->tx_buf;
6963d49afeSJan Glauber rx_buf = xfer->rx_buf;
7063d49afeSJan Glauber len = xfer->len;
7163d49afeSJan Glauber while (len > OCTEON_SPI_MAX_BYTES) {
7263d49afeSJan Glauber for (i = 0; i < OCTEON_SPI_MAX_BYTES; i++) {
7363d49afeSJan Glauber u8 d;
7463d49afeSJan Glauber if (tx_buf)
7563d49afeSJan Glauber d = *tx_buf++;
7663d49afeSJan Glauber else
7763d49afeSJan Glauber d = 0;
7863d49afeSJan Glauber writeq(d, p->register_base + OCTEON_SPI_DAT0(p) + (8 * i));
7963d49afeSJan Glauber }
8063d49afeSJan Glauber mpi_tx.u64 = 0;
81*9e264f3fSAmit Kumar Mahapatra via Alsa-devel mpi_tx.s.csid = spi_get_chipselect(spi, 0);
8263d49afeSJan Glauber mpi_tx.s.leavecs = 1;
8363d49afeSJan Glauber mpi_tx.s.txnum = tx_buf ? OCTEON_SPI_MAX_BYTES : 0;
8463d49afeSJan Glauber mpi_tx.s.totnum = OCTEON_SPI_MAX_BYTES;
8563d49afeSJan Glauber writeq(mpi_tx.u64, p->register_base + OCTEON_SPI_TX(p));
8663d49afeSJan Glauber
8763d49afeSJan Glauber octeon_spi_wait_ready(p);
8863d49afeSJan Glauber if (rx_buf)
8963d49afeSJan Glauber for (i = 0; i < OCTEON_SPI_MAX_BYTES; i++) {
9063d49afeSJan Glauber u64 v = readq(p->register_base + OCTEON_SPI_DAT0(p) + (8 * i));
9163d49afeSJan Glauber *rx_buf++ = (u8)v;
9263d49afeSJan Glauber }
9363d49afeSJan Glauber len -= OCTEON_SPI_MAX_BYTES;
9463d49afeSJan Glauber }
9563d49afeSJan Glauber
9663d49afeSJan Glauber for (i = 0; i < len; i++) {
9763d49afeSJan Glauber u8 d;
9863d49afeSJan Glauber if (tx_buf)
9963d49afeSJan Glauber d = *tx_buf++;
10063d49afeSJan Glauber else
10163d49afeSJan Glauber d = 0;
10263d49afeSJan Glauber writeq(d, p->register_base + OCTEON_SPI_DAT0(p) + (8 * i));
10363d49afeSJan Glauber }
10463d49afeSJan Glauber
10563d49afeSJan Glauber mpi_tx.u64 = 0;
106*9e264f3fSAmit Kumar Mahapatra via Alsa-devel mpi_tx.s.csid = spi_get_chipselect(spi, 0);
10763d49afeSJan Glauber if (last_xfer)
10863d49afeSJan Glauber mpi_tx.s.leavecs = xfer->cs_change;
10963d49afeSJan Glauber else
11063d49afeSJan Glauber mpi_tx.s.leavecs = !xfer->cs_change;
11163d49afeSJan Glauber mpi_tx.s.txnum = tx_buf ? len : 0;
11263d49afeSJan Glauber mpi_tx.s.totnum = len;
11363d49afeSJan Glauber writeq(mpi_tx.u64, p->register_base + OCTEON_SPI_TX(p));
11463d49afeSJan Glauber
11563d49afeSJan Glauber octeon_spi_wait_ready(p);
11663d49afeSJan Glauber if (rx_buf)
11763d49afeSJan Glauber for (i = 0; i < len; i++) {
11863d49afeSJan Glauber u64 v = readq(p->register_base + OCTEON_SPI_DAT0(p) + (8 * i));
11963d49afeSJan Glauber *rx_buf++ = (u8)v;
12063d49afeSJan Glauber }
12163d49afeSJan Glauber
122e74dc5c7SAlexandru Ardelean spi_transfer_delay_exec(xfer);
12363d49afeSJan Glauber
12463d49afeSJan Glauber return xfer->len;
12563d49afeSJan Glauber }
12663d49afeSJan Glauber
octeon_spi_transfer_one_message(struct spi_master * master,struct spi_message * msg)12763d49afeSJan Glauber int octeon_spi_transfer_one_message(struct spi_master *master,
12863d49afeSJan Glauber struct spi_message *msg)
12963d49afeSJan Glauber {
13063d49afeSJan Glauber struct octeon_spi *p = spi_master_get_devdata(master);
13163d49afeSJan Glauber unsigned int total_len = 0;
13263d49afeSJan Glauber int status = 0;
13363d49afeSJan Glauber struct spi_transfer *xfer;
13463d49afeSJan Glauber
13563d49afeSJan Glauber list_for_each_entry(xfer, &msg->transfers, transfer_list) {
13663d49afeSJan Glauber bool last_xfer = list_is_last(&xfer->transfer_list,
13763d49afeSJan Glauber &msg->transfers);
13863d49afeSJan Glauber int r = octeon_spi_do_transfer(p, msg, xfer, last_xfer);
13963d49afeSJan Glauber if (r < 0) {
14063d49afeSJan Glauber status = r;
14163d49afeSJan Glauber goto err;
14263d49afeSJan Glauber }
14363d49afeSJan Glauber total_len += r;
14463d49afeSJan Glauber }
14563d49afeSJan Glauber err:
14663d49afeSJan Glauber msg->status = status;
14763d49afeSJan Glauber msg->actual_length = total_len;
14863d49afeSJan Glauber spi_finalize_current_message(master);
14963d49afeSJan Glauber return status;
15063d49afeSJan Glauber }
151