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(loops: 500);
24 mpi_sts.u64 = readq(addr: 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, idx: 0) < 4)
61 p->cs_enax |= 1ull << (12 + spi_get_chipselect(spi, idx: 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(val: mpi_cfg.u64, addr: 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(val: d, addr: p->register_base + OCTEON_SPI_DAT0(p) + (8 * i));
79 }
80 mpi_tx.u64 = 0;
81 mpi_tx.s.csid = spi_get_chipselect(spi, idx: 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(val: mpi_tx.u64, addr: 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(addr: 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(val: d, addr: 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, idx: 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(val: mpi_tx.u64, addr: 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(addr: p->register_base + OCTEON_SPI_DAT0(p) + (8 * i));
119 *rx_buf++ = (u8)v;
120 }
121
122 spi_transfer_delay_exec(t: 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(list: &xfer->transfer_list,
137 head: &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

source code of linux/drivers/spi/spi-cavium.c