]> git.kernelconcepts.de Git - karo-tx-uboot.git/blob - drivers/spi/oc_tiny_spi.c
Merge branch 'master' of git://git.denx.de/u-boot-mpc83xx
[karo-tx-uboot.git] / drivers / spi / oc_tiny_spi.c
1 /*
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
22 struct 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
30 struct tiny_spi_host {
31         uint base;
32         uint freq;
33         uint baudwidth;
34 };
35 static const struct tiny_spi_host tiny_spi_host_list[] =
36         CONFIG_SYS_TINY_SPI_LIST;
37
38 struct 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
47 int 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
52 void 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
61 void 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
70 void 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
81 void spi_init(void)
82 {
83 }
84
85 struct 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
110 void 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
118 int 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
130 void 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
139 int 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 }