1/*
2 * This file is subject to the terms and conditions of the GNU General Public
3 * License.  See the file "COPYING" in the main directory of this archive
4 * for more details.
5 *
6 * Copyright (C) 2011, 2012 Cavium, Inc.
7 */
8
9#include <linux/spi/spi.h>
10#include <linux/module.h>
11#include <linux/delay.h>
12#include <linux/io.h>
13
14#include "spi-cavium.h"
15
16static void octeon_spi_wait_ready(struct octeon_spi *p)
17{
18	union cvmx_mpi_sts mpi_sts;
19	unsigned int loops = 0;
20
21	do {
22		if (loops++)
23			__delay(500);
24		mpi_sts.u64 = readq(p->register_base + OCTEON_SPI_STS(p));
25	} while (mpi_sts.s.busy);
26}
27
28static int octeon_spi_do_transfer(struct octeon_spi *p,
29				  struct spi_message *msg,
30				  struct spi_transfer *xfer,
31				  bool last_xfer)
32{
33	struct spi_device *spi = msg->spi;
34	union cvmx_mpi_cfg mpi_cfg;
35	union cvmx_mpi_tx mpi_tx;
36	unsigned int clkdiv;
37	int mode;
38	bool cpha, cpol;
39	const u8 *tx_buf;
40	u8 *rx_buf;
41	int len;
42	int i;
43
44	mode = spi->mode;
45	cpha = mode & SPI_CPHA;
46	cpol = mode & SPI_CPOL;
47
48	clkdiv = p->sys_freq / (2 * xfer->speed_hz);
49
50	mpi_cfg.u64 = 0;
51
52	mpi_cfg.s.clkdiv = clkdiv;
53	mpi_cfg.s.cshi = (mode & SPI_CS_HIGH) ? 1 : 0;
54	mpi_cfg.s.lsbfirst = (mode & SPI_LSB_FIRST) ? 1 : 0;
55	mpi_cfg.s.wireor = (mode & SPI_3WIRE) ? 1 : 0;
56	mpi_cfg.s.idlelo = cpha != cpol;
57	mpi_cfg.s.cslate = cpha ? 1 : 0;
58	mpi_cfg.s.enable = 1;
59
60	if (spi_get_chipselect(spi, 0) < 4)
61		p->cs_enax |= 1ull << (12 + spi_get_chipselect(spi, 0));
62	mpi_cfg.u64 |= p->cs_enax;
63
64	if (mpi_cfg.u64 != p->last_cfg) {
65		p->last_cfg = mpi_cfg.u64;
66		writeq(mpi_cfg.u64, p->register_base + OCTEON_SPI_CFG(p));
67	}
68	tx_buf = xfer->tx_buf;
69	rx_buf = xfer->rx_buf;
70	len = xfer->len;
71	while (len > OCTEON_SPI_MAX_BYTES) {
72		for (i = 0; i < OCTEON_SPI_MAX_BYTES; i++) {
73			u8 d;
74			if (tx_buf)
75				d = *tx_buf++;
76			else
77				d = 0;
78			writeq(d, p->register_base + OCTEON_SPI_DAT0(p) + (8 * i));
79		}
80		mpi_tx.u64 = 0;
81		mpi_tx.s.csid = spi_get_chipselect(spi, 0);
82		mpi_tx.s.leavecs = 1;
83		mpi_tx.s.txnum = tx_buf ? OCTEON_SPI_MAX_BYTES : 0;
84		mpi_tx.s.totnum = OCTEON_SPI_MAX_BYTES;
85		writeq(mpi_tx.u64, p->register_base + OCTEON_SPI_TX(p));
86
87		octeon_spi_wait_ready(p);
88		if (rx_buf)
89			for (i = 0; i < OCTEON_SPI_MAX_BYTES; i++) {
90				u64 v = readq(p->register_base + OCTEON_SPI_DAT0(p) + (8 * i));
91				*rx_buf++ = (u8)v;
92			}
93		len -= OCTEON_SPI_MAX_BYTES;
94	}
95
96	for (i = 0; i < len; i++) {
97		u8 d;
98		if (tx_buf)
99			d = *tx_buf++;
100		else
101			d = 0;
102		writeq(d, p->register_base + OCTEON_SPI_DAT0(p) + (8 * i));
103	}
104
105	mpi_tx.u64 = 0;
106	mpi_tx.s.csid = spi_get_chipselect(spi, 0);
107	if (last_xfer)
108		mpi_tx.s.leavecs = xfer->cs_change;
109	else
110		mpi_tx.s.leavecs = !xfer->cs_change;
111	mpi_tx.s.txnum = tx_buf ? len : 0;
112	mpi_tx.s.totnum = len;
113	writeq(mpi_tx.u64, p->register_base + OCTEON_SPI_TX(p));
114
115	octeon_spi_wait_ready(p);
116	if (rx_buf)
117		for (i = 0; i < len; i++) {
118			u64 v = readq(p->register_base + OCTEON_SPI_DAT0(p) + (8 * i));
119			*rx_buf++ = (u8)v;
120		}
121
122	spi_transfer_delay_exec(xfer);
123
124	return xfer->len;
125}
126
127int octeon_spi_transfer_one_message(struct spi_controller *ctlr,
128				    struct spi_message *msg)
129{
130	struct octeon_spi *p = spi_controller_get_devdata(ctlr);
131	unsigned int total_len = 0;
132	int status = 0;
133	struct spi_transfer *xfer;
134
135	list_for_each_entry(xfer, &msg->transfers, transfer_list) {
136		bool last_xfer = list_is_last(&xfer->transfer_list,
137					      &msg->transfers);
138		int r = octeon_spi_do_transfer(p, msg, xfer, last_xfer);
139		if (r < 0) {
140			status = r;
141			goto err;
142		}
143		total_len += r;
144	}
145err:
146	msg->status = status;
147	msg->actual_length = total_len;
148	spi_finalize_current_message(ctlr);
149	return status;
150}
151