blob: 6f7b1edd602c7a145375b32a42158b861b691628 [file] [log] [blame]
Thomas Choudea63862011-04-11 19:48:47 +00001/*
2 * Opencore tiny_spi driver
3 *
4 * http://opencores.org/project,tiny_spi
5 *
6 * based on bfin_spi.c
7 * Copyright (c) 2005-2008 Analog Devices Inc.
8 * Copyright (C) 2010 Thomas Chou <thomas@wytron.com.tw>
9 *
10 * Licensed under the GPL-2 or later.
11 */
12
13#include <common.h>
14#include <asm/io.h>
15#include <malloc.h>
16#include <spi.h>
17#include <asm/gpio.h>
18
19#define TINY_SPI_STATUS_TXE 0x1
20#define TINY_SPI_STATUS_TXR 0x2
21
22struct tiny_spi_regs {
23 unsigned rxdata; /* Rx data reg */
24 unsigned txdata; /* Tx data reg */
25 unsigned status; /* Status reg */
26 unsigned control; /* Control reg */
27 unsigned baud; /* Baud reg */
28};
29
30struct tiny_spi_host {
31 uint base;
32 uint freq;
33 uint baudwidth;
34};
35static const struct tiny_spi_host tiny_spi_host_list[] =
36 CONFIG_SYS_TINY_SPI_LIST;
37
38struct tiny_spi_slave {
39 struct spi_slave slave;
40 const struct tiny_spi_host *host;
41 uint mode;
42 uint baud;
43 uint flg;
44};
45#define to_tiny_spi_slave(s) container_of(s, struct tiny_spi_slave, slave)
46
47int spi_cs_is_valid(unsigned int bus, unsigned int cs)
48{
49 return bus < ARRAY_SIZE(tiny_spi_host_list) && gpio_is_valid(cs);
50}
51
52void spi_cs_activate(struct spi_slave *slave)
53{
54 struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave);
55 unsigned int cs = slave->cs;
56
57 gpio_set_value(cs, tiny_spi->flg);
58 debug("%s: SPI_CS_GPIO:%x\n", __func__, gpio_get_value(cs));
59}
60
61void spi_cs_deactivate(struct spi_slave *slave)
62{
63 struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave);
64 unsigned int cs = slave->cs;
65
66 gpio_set_value(cs, !tiny_spi->flg);
67 debug("%s: SPI_CS_GPIO:%x\n", __func__, gpio_get_value(cs));
68}
69
70void spi_set_speed(struct spi_slave *slave, uint hz)
71{
72 struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave);
73 const struct tiny_spi_host *host = tiny_spi->host;
74
75 tiny_spi->baud = min(DIV_ROUND_UP(host->freq, hz * 2),
76 (1 << host->baudwidth)) - 1;
77 debug("%s: speed %u actual %u\n", __func__, hz,
78 host->freq / ((tiny_spi->baud + 1) * 2));
79}
80
81void spi_init(void)
82{
83}
84
85struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
86 unsigned int hz, unsigned int mode)
87{
88 struct tiny_spi_slave *tiny_spi;
89
90 if (!spi_cs_is_valid(bus, cs) || gpio_request(cs, "tiny_spi"))
91 return NULL;
92
Simon Glassd3504fe2013-03-18 19:23:40 +000093 tiny_spi = spi_alloc_slave(struct tiny_spi_slave, bus, cs);
Thomas Choudea63862011-04-11 19:48:47 +000094 if (!tiny_spi)
95 return NULL;
Thomas Choudea63862011-04-11 19:48:47 +000096
Thomas Choudea63862011-04-11 19:48:47 +000097 tiny_spi->host = &tiny_spi_host_list[bus];
98 tiny_spi->mode = mode & (SPI_CPOL | SPI_CPHA);
99 tiny_spi->flg = mode & SPI_CS_HIGH ? 1 : 0;
100 spi_set_speed(&tiny_spi->slave, hz);
101
102 debug("%s: bus:%i cs:%i base:%lx\n", __func__,
103 bus, cs, tiny_spi->host->base);
104 return &tiny_spi->slave;
105}
106
107void spi_free_slave(struct spi_slave *slave)
108{
109 struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave);
110
111 gpio_free(slave->cs);
112 free(tiny_spi);
113}
114
115int spi_claim_bus(struct spi_slave *slave)
116{
117 struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave);
118 struct tiny_spi_regs *regs = (void *)tiny_spi->host->base;
119
120 debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs);
121 gpio_direction_output(slave->cs, !tiny_spi->flg);
122 writel(tiny_spi->mode, &regs->control);
123 writel(tiny_spi->baud, &regs->baud);
124 return 0;
125}
126
127void spi_release_bus(struct spi_slave *slave)
128{
129 debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs);
130}
131
132#ifndef CONFIG_TINY_SPI_IDLE_VAL
133# define CONFIG_TINY_SPI_IDLE_VAL 0xff
134#endif
135
136int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
137 void *din, unsigned long flags)
138{
139 struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave);
140 struct tiny_spi_regs *regs = (void *)tiny_spi->host->base;
141 const u8 *txp = dout;
142 u8 *rxp = din;
143 uint bytes = bitlen / 8;
144 uint i;
145
146 debug("%s: bus:%i cs:%i bitlen:%i bytes:%i flags:%lx\n", __func__,
147 slave->bus, slave->cs, bitlen, bytes, flags);
148 if (bitlen == 0)
149 goto done;
150
151 /* assume to do 8 bits transfers */
152 if (bitlen % 8) {
153 flags |= SPI_XFER_END;
154 goto done;
155 }
156
157 if (flags & SPI_XFER_BEGIN)
158 spi_cs_activate(slave);
159
160 /* we need to tighten the transfer loop */
161 if (txp && rxp) {
162 writeb(*txp++, &regs->txdata);
163 if (bytes > 1) {
164 writeb(*txp++, &regs->txdata);
165 for (i = 2; i < bytes; i++) {
166 u8 rx, tx = *txp++;
167 while (!(readb(&regs->status) &
168 TINY_SPI_STATUS_TXR))
169 ;
170 rx = readb(&regs->txdata);
171 writeb(tx, &regs->txdata);
172 *rxp++ = rx;
173 }
174 while (!(readb(&regs->status) &
175 TINY_SPI_STATUS_TXR))
176 ;
177 *rxp++ = readb(&regs->txdata);
178 }
179 while (!(readb(&regs->status) &
180 TINY_SPI_STATUS_TXE))
181 ;
182 *rxp++ = readb(&regs->rxdata);
183 } else if (rxp) {
184 writeb(CONFIG_TINY_SPI_IDLE_VAL, &regs->txdata);
185 if (bytes > 1) {
186 writeb(CONFIG_TINY_SPI_IDLE_VAL,
187 &regs->txdata);
188 for (i = 2; i < bytes; i++) {
189 u8 rx;
190 while (!(readb(&regs->status) &
191 TINY_SPI_STATUS_TXR))
192 ;
193 rx = readb(&regs->txdata);
194 writeb(CONFIG_TINY_SPI_IDLE_VAL,
195 &regs->txdata);
196 *rxp++ = rx;
197 }
198 while (!(readb(&regs->status) &
199 TINY_SPI_STATUS_TXR))
200 ;
201 *rxp++ = readb(&regs->txdata);
202 }
203 while (!(readb(&regs->status) &
204 TINY_SPI_STATUS_TXE))
205 ;
206 *rxp++ = readb(&regs->rxdata);
207 } else if (txp) {
208 writeb(*txp++, &regs->txdata);
209 if (bytes > 1) {
210 writeb(*txp++, &regs->txdata);
211 for (i = 2; i < bytes; i++) {
212 u8 tx = *txp++;
213 while (!(readb(&regs->status) &
214 TINY_SPI_STATUS_TXR))
215 ;
216 writeb(tx, &regs->txdata);
217 }
218 }
219 while (!(readb(&regs->status) &
220 TINY_SPI_STATUS_TXE))
221 ;
222 } else {
223 writeb(CONFIG_TINY_SPI_IDLE_VAL, &regs->txdata);
224 if (bytes > 1) {
225 writeb(CONFIG_TINY_SPI_IDLE_VAL,
226 &regs->txdata);
227 for (i = 2; i < bytes; i++) {
228 while (!(readb(&regs->status) &
229 TINY_SPI_STATUS_TXR))
230 ;
231 writeb(CONFIG_TINY_SPI_IDLE_VAL,
232 &regs->txdata);
233 }
234 }
235 while (!(readb(&regs->status) &
236 TINY_SPI_STATUS_TXE))
237 ;
238 }
239
240 done:
241 if (flags & SPI_XFER_END)
242 spi_cs_deactivate(slave);
243
244 return 0;
245}