aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/spi
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2012-10-05 09:56:28 -0400
committerRalf Baechle <ralf@linux-mips.org>2012-10-05 09:56:28 -0400
commit382fc33b4a04e2dde89b4c69a6880e0c7d9761e2 (patch)
tree9d13ef333c4e841756c45bf29600ceac28e3f85e /drivers/spi
parent97541ccfb9db2bb9cd1dde6344d5834438d14bda (diff)
parent986936d7c2f83427bb3bf1e629eba4373438e151 (diff)
Merge branch 'master' of git://dev.phrozen.org/mips-next into mips-for-linux-next
Diffstat (limited to 'drivers/spi')
-rw-r--r--drivers/spi/Kconfig7
-rw-r--r--drivers/spi/Makefile1
-rw-r--r--drivers/spi/spi-octeon.c362
3 files changed, 370 insertions, 0 deletions
diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig
index 5f84b5563c2d..d654f88f4cfb 100644
--- a/drivers/spi/Kconfig
+++ b/drivers/spi/Kconfig
@@ -237,6 +237,13 @@ config SPI_OC_TINY
237 help 237 help
238 This is the driver for OpenCores tiny SPI master controller. 238 This is the driver for OpenCores tiny SPI master controller.
239 239
240config SPI_OCTEON
241 tristate "Cavium OCTEON SPI controller"
242 depends on CPU_CAVIUM_OCTEON
243 help
244 SPI host driver for the hardware found on some Cavium OCTEON
245 SOCs.
246
240config SPI_OMAP_UWIRE 247config SPI_OMAP_UWIRE
241 tristate "OMAP1 MicroWire" 248 tristate "OMAP1 MicroWire"
242 depends on ARCH_OMAP1 249 depends on ARCH_OMAP1
diff --git a/drivers/spi/Makefile b/drivers/spi/Makefile
index 3920dcf4c740..93d87bcdf3f5 100644
--- a/drivers/spi/Makefile
+++ b/drivers/spi/Makefile
@@ -38,6 +38,7 @@ obj-$(CONFIG_SPI_MPC52xx_PSC) += spi-mpc52xx-psc.o
38obj-$(CONFIG_SPI_MPC52xx) += spi-mpc52xx.o 38obj-$(CONFIG_SPI_MPC52xx) += spi-mpc52xx.o
39obj-$(CONFIG_SPI_NUC900) += spi-nuc900.o 39obj-$(CONFIG_SPI_NUC900) += spi-nuc900.o
40obj-$(CONFIG_SPI_OC_TINY) += spi-oc-tiny.o 40obj-$(CONFIG_SPI_OC_TINY) += spi-oc-tiny.o
41obj-$(CONFIG_SPI_OCTEON) += spi-octeon.o
41obj-$(CONFIG_SPI_OMAP_UWIRE) += spi-omap-uwire.o 42obj-$(CONFIG_SPI_OMAP_UWIRE) += spi-omap-uwire.o
42obj-$(CONFIG_SPI_OMAP_100K) += spi-omap-100k.o 43obj-$(CONFIG_SPI_OMAP_100K) += spi-omap-100k.o
43obj-$(CONFIG_SPI_OMAP24XX) += spi-omap2-mcspi.o 44obj-$(CONFIG_SPI_OMAP24XX) += spi-omap2-mcspi.o
diff --git a/drivers/spi/spi-octeon.c b/drivers/spi/spi-octeon.c
new file mode 100644
index 000000000000..ea8fb2efb0f8
--- /dev/null
+++ b/drivers/spi/spi-octeon.c
@@ -0,0 +1,362 @@
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/platform_device.h>
10#include <linux/interrupt.h>
11#include <linux/spi/spi.h>
12#include <linux/module.h>
13#include <linux/delay.h>
14#include <linux/init.h>
15#include <linux/io.h>
16#include <linux/of.h>
17
18#include <asm/octeon/octeon.h>
19#include <asm/octeon/cvmx-mpi-defs.h>
20
21#define OCTEON_SPI_CFG 0
22#define OCTEON_SPI_STS 0x08
23#define OCTEON_SPI_TX 0x10
24#define OCTEON_SPI_DAT0 0x80
25
26#define OCTEON_SPI_MAX_BYTES 9
27
28#define OCTEON_SPI_MAX_CLOCK_HZ 16000000
29
30struct octeon_spi {
31 struct spi_master *my_master;
32 u64 register_base;
33 u64 last_cfg;
34 u64 cs_enax;
35};
36
37struct octeon_spi_setup {
38 u32 max_speed_hz;
39 u8 chip_select;
40 u8 mode;
41 u8 bits_per_word;
42};
43
44static void octeon_spi_wait_ready(struct octeon_spi *p)
45{
46 union cvmx_mpi_sts mpi_sts;
47 unsigned int loops = 0;
48
49 do {
50 if (loops++)
51 __delay(500);
52 mpi_sts.u64 = cvmx_read_csr(p->register_base + OCTEON_SPI_STS);
53 } while (mpi_sts.s.busy);
54}
55
56static int octeon_spi_do_transfer(struct octeon_spi *p,
57 struct spi_message *msg,
58 struct spi_transfer *xfer,
59 bool last_xfer)
60{
61 union cvmx_mpi_cfg mpi_cfg;
62 union cvmx_mpi_tx mpi_tx;
63 unsigned int clkdiv;
64 unsigned int speed_hz;
65 int mode;
66 bool cpha, cpol;
67 int bits_per_word;
68 const u8 *tx_buf;
69 u8 *rx_buf;
70 int len;
71 int i;
72
73 struct octeon_spi_setup *msg_setup = spi_get_ctldata(msg->spi);
74
75 speed_hz = msg_setup->max_speed_hz;
76 mode = msg_setup->mode;
77 cpha = mode & SPI_CPHA;
78 cpol = mode & SPI_CPOL;
79 bits_per_word = msg_setup->bits_per_word;
80
81 if (xfer->speed_hz)
82 speed_hz = xfer->speed_hz;
83 if (xfer->bits_per_word)
84 bits_per_word = xfer->bits_per_word;
85
86 if (speed_hz > OCTEON_SPI_MAX_CLOCK_HZ)
87 speed_hz = OCTEON_SPI_MAX_CLOCK_HZ;
88
89 clkdiv = octeon_get_io_clock_rate() / (2 * speed_hz);
90
91 mpi_cfg.u64 = 0;
92
93 mpi_cfg.s.clkdiv = clkdiv;
94 mpi_cfg.s.cshi = (mode & SPI_CS_HIGH) ? 1 : 0;
95 mpi_cfg.s.lsbfirst = (mode & SPI_LSB_FIRST) ? 1 : 0;
96 mpi_cfg.s.wireor = (mode & SPI_3WIRE) ? 1 : 0;
97 mpi_cfg.s.idlelo = cpha != cpol;
98 mpi_cfg.s.cslate = cpha ? 1 : 0;
99 mpi_cfg.s.enable = 1;
100
101 if (msg_setup->chip_select < 4)
102 p->cs_enax |= 1ull << (12 + msg_setup->chip_select);
103 mpi_cfg.u64 |= p->cs_enax;
104
105 if (mpi_cfg.u64 != p->last_cfg) {
106 p->last_cfg = mpi_cfg.u64;
107 cvmx_write_csr(p->register_base + OCTEON_SPI_CFG, mpi_cfg.u64);
108 }
109 tx_buf = xfer->tx_buf;
110 rx_buf = xfer->rx_buf;
111 len = xfer->len;
112 while (len > OCTEON_SPI_MAX_BYTES) {
113 for (i = 0; i < OCTEON_SPI_MAX_BYTES; i++) {
114 u8 d;
115 if (tx_buf)
116 d = *tx_buf++;
117 else
118 d = 0;
119 cvmx_write_csr(p->register_base + OCTEON_SPI_DAT0 + (8 * i), d);
120 }
121 mpi_tx.u64 = 0;
122 mpi_tx.s.csid = msg_setup->chip_select;
123 mpi_tx.s.leavecs = 1;
124 mpi_tx.s.txnum = tx_buf ? OCTEON_SPI_MAX_BYTES : 0;
125 mpi_tx.s.totnum = OCTEON_SPI_MAX_BYTES;
126 cvmx_write_csr(p->register_base + OCTEON_SPI_TX, mpi_tx.u64);
127
128 octeon_spi_wait_ready(p);
129 if (rx_buf)
130 for (i = 0; i < OCTEON_SPI_MAX_BYTES; i++) {
131 u64 v = cvmx_read_csr(p->register_base + OCTEON_SPI_DAT0 + (8 * i));
132 *rx_buf++ = (u8)v;
133 }
134 len -= OCTEON_SPI_MAX_BYTES;
135 }
136
137 for (i = 0; i < len; i++) {
138 u8 d;
139 if (tx_buf)
140 d = *tx_buf++;
141 else
142 d = 0;
143 cvmx_write_csr(p->register_base + OCTEON_SPI_DAT0 + (8 * i), d);
144 }
145
146 mpi_tx.u64 = 0;
147 mpi_tx.s.csid = msg_setup->chip_select;
148 if (last_xfer)
149 mpi_tx.s.leavecs = xfer->cs_change;
150 else
151 mpi_tx.s.leavecs = !xfer->cs_change;
152 mpi_tx.s.txnum = tx_buf ? len : 0;
153 mpi_tx.s.totnum = len;
154 cvmx_write_csr(p->register_base + OCTEON_SPI_TX, mpi_tx.u64);
155
156 octeon_spi_wait_ready(p);
157 if (rx_buf)
158 for (i = 0; i < len; i++) {
159 u64 v = cvmx_read_csr(p->register_base + OCTEON_SPI_DAT0 + (8 * i));
160 *rx_buf++ = (u8)v;
161 }
162
163 if (xfer->delay_usecs)
164 udelay(xfer->delay_usecs);
165
166 return xfer->len;
167}
168
169static int octeon_spi_validate_bpw(struct spi_device *spi, u32 speed)
170{
171 switch (speed) {
172 case 8:
173 break;
174 default:
175 dev_err(&spi->dev, "Error: %d bits per word not supported\n",
176 speed);
177 return -EINVAL;
178 }
179 return 0;
180}
181
182static int octeon_spi_transfer_one_message(struct spi_master *master,
183 struct spi_message *msg)
184{
185 struct octeon_spi *p = spi_master_get_devdata(master);
186 unsigned int total_len = 0;
187 int status = 0;
188 struct spi_transfer *xfer;
189
190 /*
191 * We better have set the configuration via a call to .setup
192 * before we get here.
193 */
194 if (spi_get_ctldata(msg->spi) == NULL) {
195 status = -EINVAL;
196 goto err;
197 }
198
199 list_for_each_entry(xfer, &msg->transfers, transfer_list) {
200 if (xfer->bits_per_word) {
201 status = octeon_spi_validate_bpw(msg->spi,
202 xfer->bits_per_word);
203 if (status)
204 goto err;
205 }
206 }
207
208 list_for_each_entry(xfer, &msg->transfers, transfer_list) {
209 bool last_xfer = &xfer->transfer_list == msg->transfers.prev;
210 int r = octeon_spi_do_transfer(p, msg, xfer, last_xfer);
211 if (r < 0) {
212 status = r;
213 goto err;
214 }
215 total_len += r;
216 }
217err:
218 msg->status = status;
219 msg->actual_length = total_len;
220 spi_finalize_current_message(master);
221 return status;
222}
223
224static struct octeon_spi_setup *octeon_spi_new_setup(struct spi_device *spi)
225{
226 struct octeon_spi_setup *setup = kzalloc(sizeof(*setup), GFP_KERNEL);
227 if (!setup)
228 return NULL;
229
230 setup->max_speed_hz = spi->max_speed_hz;
231 setup->chip_select = spi->chip_select;
232 setup->mode = spi->mode;
233 setup->bits_per_word = spi->bits_per_word;
234 return setup;
235}
236
237static int octeon_spi_setup(struct spi_device *spi)
238{
239 int r;
240 struct octeon_spi_setup *new_setup;
241 struct octeon_spi_setup *old_setup = spi_get_ctldata(spi);
242
243 r = octeon_spi_validate_bpw(spi, spi->bits_per_word);
244 if (r)
245 return r;
246
247 new_setup = octeon_spi_new_setup(spi);
248 if (!new_setup)
249 return -ENOMEM;
250
251 spi_set_ctldata(spi, new_setup);
252 kfree(old_setup);
253
254 return 0;
255}
256
257static void octeon_spi_cleanup(struct spi_device *spi)
258{
259 struct octeon_spi_setup *old_setup = spi_get_ctldata(spi);
260 spi_set_ctldata(spi, NULL);
261 kfree(old_setup);
262}
263
264static int octeon_spi_nop_transfer_hardware(struct spi_master *master)
265{
266 return 0;
267}
268
269static int __devinit octeon_spi_probe(struct platform_device *pdev)
270{
271
272 struct resource *res_mem;
273 struct spi_master *master;
274 struct octeon_spi *p;
275 int err = -ENOENT;
276
277 master = spi_alloc_master(&pdev->dev, sizeof(struct octeon_spi));
278 if (!master)
279 return -ENOMEM;
280 p = spi_master_get_devdata(master);
281 platform_set_drvdata(pdev, p);
282 p->my_master = master;
283
284 res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
285
286 if (res_mem == NULL) {
287 dev_err(&pdev->dev, "found no memory resource\n");
288 err = -ENXIO;
289 goto fail;
290 }
291 if (!devm_request_mem_region(&pdev->dev, res_mem->start,
292 resource_size(res_mem), res_mem->name)) {
293 dev_err(&pdev->dev, "request_mem_region failed\n");
294 goto fail;
295 }
296 p->register_base = (u64)devm_ioremap(&pdev->dev, res_mem->start,
297 resource_size(res_mem));
298
299 /* Dynamic bus numbering */
300 master->bus_num = -1;
301 master->num_chipselect = 4;
302 master->mode_bits = SPI_CPHA |
303 SPI_CPOL |
304 SPI_CS_HIGH |
305 SPI_LSB_FIRST |
306 SPI_3WIRE;
307
308 master->setup = octeon_spi_setup;
309 master->cleanup = octeon_spi_cleanup;
310 master->prepare_transfer_hardware = octeon_spi_nop_transfer_hardware;
311 master->transfer_one_message = octeon_spi_transfer_one_message;
312 master->unprepare_transfer_hardware = octeon_spi_nop_transfer_hardware;
313
314 master->dev.of_node = pdev->dev.of_node;
315 err = spi_register_master(master);
316 if (err) {
317 dev_err(&pdev->dev, "register master failed: %d\n", err);
318 goto fail;
319 }
320
321 dev_info(&pdev->dev, "OCTEON SPI bus driver\n");
322
323 return 0;
324fail:
325 spi_master_put(master);
326 return err;
327}
328
329static int __devexit octeon_spi_remove(struct platform_device *pdev)
330{
331 struct octeon_spi *p = platform_get_drvdata(pdev);
332 u64 register_base = p->register_base;
333
334 spi_unregister_master(p->my_master);
335
336 /* Clear the CSENA* and put everything in a known state. */
337 cvmx_write_csr(register_base + OCTEON_SPI_CFG, 0);
338
339 return 0;
340}
341
342static struct of_device_id octeon_spi_match[] = {
343 { .compatible = "cavium,octeon-3010-spi", },
344 {},
345};
346MODULE_DEVICE_TABLE(of, octeon_spi_match);
347
348static struct platform_driver octeon_spi_driver = {
349 .driver = {
350 .name = "spi-octeon",
351 .owner = THIS_MODULE,
352 .of_match_table = octeon_spi_match,
353 },
354 .probe = octeon_spi_probe,
355 .remove = __devexit_p(octeon_spi_remove),
356};
357
358module_platform_driver(octeon_spi_driver);
359
360MODULE_DESCRIPTION("Cavium, Inc. OCTEON SPI bus driver");
361MODULE_AUTHOR("David Daney");
362MODULE_LICENSE("GPL");