Merge tag 'rust-fixes-6.6' of https://github.com/Rust-for-Linux/linux
[platform/kernel/linux-rpi.git] / drivers / spi / spi-cavium.c
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
16 static 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
28 static 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
127 int octeon_spi_transfer_one_message(struct spi_master *master,
128                                     struct spi_message *msg)
129 {
130         struct octeon_spi *p = spi_master_get_devdata(master);
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         }
145 err:
146         msg->status = status;
147         msg->actual_length = total_len;
148         spi_finalize_current_message(master);
149         return status;
150 }