blob: fc01fb83a21b64b9e06d2d77d5954e97414590a2 [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
93 tiny_spi = malloc(sizeof(*tiny_spi));
94 if (!tiny_spi)
95 return NULL;
96 memset(tiny_spi, 0, sizeof(*tiny_spi));
97
98 tiny_spi->slave.bus = bus;
99 tiny_spi->slave.cs = cs;
100 tiny_spi->host = &tiny_spi_host_list[bus];
101 tiny_spi->mode = mode & (SPI_CPOL | SPI_CPHA);
102 tiny_spi->flg = mode & SPI_CS_HIGH ? 1 : 0;
103 spi_set_speed(&tiny_spi->slave, hz);
104
105 debug("%s: bus:%i cs:%i base:%lx\n", __func__,
106 bus, cs, tiny_spi->host->base);
107 return &tiny_spi->slave;
108}
109
110void spi_free_slave(struct spi_slave *slave)
111{
112 struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave);
113
114 gpio_free(slave->cs);
115 free(tiny_spi);
116}
117
118int spi_claim_bus(struct spi_slave *slave)
119{
120 struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave);
121 struct tiny_spi_regs *regs = (void *)tiny_spi->host->base;
122
123 debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs);
124 gpio_direction_output(slave->cs, !tiny_spi->flg);
125 writel(tiny_spi->mode, &regs->control);
126 writel(tiny_spi->baud, &regs->baud);
127 return 0;
128}
129
130void spi_release_bus(struct spi_slave *slave)
131{
132 debug("%s: bus:%i cs:%i\n", __func__, slave->bus, slave->cs);
133}
134
135#ifndef CONFIG_TINY_SPI_IDLE_VAL
136# define CONFIG_TINY_SPI_IDLE_VAL 0xff
137#endif
138
139int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
140 void *din, unsigned long flags)
141{
142 struct tiny_spi_slave *tiny_spi = to_tiny_spi_slave(slave);
143 struct tiny_spi_regs *regs = (void *)tiny_spi->host->base;
144 const u8 *txp = dout;
145 u8 *rxp = din;
146 uint bytes = bitlen / 8;
147 uint i;
148
149 debug("%s: bus:%i cs:%i bitlen:%i bytes:%i flags:%lx\n", __func__,
150 slave->bus, slave->cs, bitlen, bytes, flags);
151 if (bitlen == 0)
152 goto done;
153
154 /* assume to do 8 bits transfers */
155 if (bitlen % 8) {
156 flags |= SPI_XFER_END;
157 goto done;
158 }
159
160 if (flags & SPI_XFER_BEGIN)
161 spi_cs_activate(slave);
162
163 /* we need to tighten the transfer loop */
164 if (txp && rxp) {
165 writeb(*txp++, &regs->txdata);
166 if (bytes > 1) {
167 writeb(*txp++, &regs->txdata);
168 for (i = 2; i < bytes; i++) {
169 u8 rx, tx = *txp++;
170 while (!(readb(&regs->status) &
171 TINY_SPI_STATUS_TXR))
172 ;
173 rx = readb(&regs->txdata);
174 writeb(tx, &regs->txdata);
175 *rxp++ = rx;
176 }
177 while (!(readb(&regs->status) &
178 TINY_SPI_STATUS_TXR))
179 ;
180 *rxp++ = readb(&regs->txdata);
181 }
182 while (!(readb(&regs->status) &
183 TINY_SPI_STATUS_TXE))
184 ;
185 *rxp++ = readb(&regs->rxdata);
186 } else if (rxp) {
187 writeb(CONFIG_TINY_SPI_IDLE_VAL, &regs->txdata);
188 if (bytes > 1) {
189 writeb(CONFIG_TINY_SPI_IDLE_VAL,
190 &regs->txdata);
191 for (i = 2; i < bytes; i++) {
192 u8 rx;
193 while (!(readb(&regs->status) &
194 TINY_SPI_STATUS_TXR))
195 ;
196 rx = readb(&regs->txdata);
197 writeb(CONFIG_TINY_SPI_IDLE_VAL,
198 &regs->txdata);
199 *rxp++ = rx;
200 }
201 while (!(readb(&regs->status) &
202 TINY_SPI_STATUS_TXR))
203 ;
204 *rxp++ = readb(&regs->txdata);
205 }
206 while (!(readb(&regs->status) &
207 TINY_SPI_STATUS_TXE))
208 ;
209 *rxp++ = readb(&regs->rxdata);
210 } else if (txp) {
211 writeb(*txp++, &regs->txdata);
212 if (bytes > 1) {
213 writeb(*txp++, &regs->txdata);
214 for (i = 2; i < bytes; i++) {
215 u8 tx = *txp++;
216 while (!(readb(&regs->status) &
217 TINY_SPI_STATUS_TXR))
218 ;
219 writeb(tx, &regs->txdata);
220 }
221 }
222 while (!(readb(&regs->status) &
223 TINY_SPI_STATUS_TXE))
224 ;
225 } else {
226 writeb(CONFIG_TINY_SPI_IDLE_VAL, &regs->txdata);
227 if (bytes > 1) {
228 writeb(CONFIG_TINY_SPI_IDLE_VAL,
229 &regs->txdata);
230 for (i = 2; i < bytes; i++) {
231 while (!(readb(&regs->status) &
232 TINY_SPI_STATUS_TXR))
233 ;
234 writeb(CONFIG_TINY_SPI_IDLE_VAL,
235 &regs->txdata);
236 }
237 }
238 while (!(readb(&regs->status) &
239 TINY_SPI_STATUS_TXE))
240 ;
241 }
242
243 done:
244 if (flags & SPI_XFER_END)
245 spi_cs_deactivate(slave);
246
247 return 0;
248}