xref: /openbmc/linux/drivers/spi/spi-cavium.c (revision 9e264f3f)
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