blob: f4b1bad22e85f787212194ff265908139946254e [file] [log] [blame]
Hans-Christian Egtvedt60445cb2008-05-16 11:10:32 +02001/*
2 * Copyright (C) 2007 Atmel Corporation
3 *
4 * See file CREDITS for list of people who contributed to this
5 * project.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License as
9 * published by the Free Software Foundation; either version 2 of
10 * the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
20 * MA 02111-1307 USA
21 */
22#include <common.h>
23#include <spi.h>
24#include <malloc.h>
25
26#include <asm/io.h>
27
28#include <asm/arch/clk.h>
Reinhard Meyer329f0f52010-11-03 16:32:56 +010029#include <asm/arch/hardware.h>
Hans-Christian Egtvedt60445cb2008-05-16 11:10:32 +020030
31#include "atmel_spi.h"
32
33void spi_init()
34{
35
36}
37
38struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
39 unsigned int max_hz, unsigned int mode)
40{
41 struct atmel_spi_slave *as;
42 unsigned int scbr;
43 u32 csrx;
44 void *regs;
45
Reinhard Meyerf09d3b22010-08-09 13:37:59 +020046 if (!spi_cs_is_valid(bus, cs))
Hans-Christian Egtvedt60445cb2008-05-16 11:10:32 +020047 return NULL;
48
49 switch (bus) {
50 case 0:
Reinhard Meyer329f0f52010-11-03 16:32:56 +010051 regs = (void *)ATMEL_BASE_SPI0;
Hans-Christian Egtvedt60445cb2008-05-16 11:10:32 +020052 break;
Reinhard Meyer329f0f52010-11-03 16:32:56 +010053#ifdef ATMEL_BASE_SPI1
Hans-Christian Egtvedt60445cb2008-05-16 11:10:32 +020054 case 1:
Reinhard Meyer329f0f52010-11-03 16:32:56 +010055 regs = (void *)ATMEL_BASE_SPI1;
Hans-Christian Egtvedt60445cb2008-05-16 11:10:32 +020056 break;
57#endif
Reinhard Meyer329f0f52010-11-03 16:32:56 +010058#ifdef ATMEL_BASE_SPI2
Hans-Christian Egtvedt60445cb2008-05-16 11:10:32 +020059 case 2:
Reinhard Meyer329f0f52010-11-03 16:32:56 +010060 regs = (void *)ATMEL_BASE_SPI2;
Hans-Christian Egtvedt60445cb2008-05-16 11:10:32 +020061 break;
62#endif
Reinhard Meyer329f0f52010-11-03 16:32:56 +010063#ifdef ATMEL_BASE_SPI3
Hans-Christian Egtvedt60445cb2008-05-16 11:10:32 +020064 case 3:
Reinhard Meyer329f0f52010-11-03 16:32:56 +010065 regs = (void *)ATMEL_BASE_SPI3;
Hans-Christian Egtvedt60445cb2008-05-16 11:10:32 +020066 break;
67#endif
68 default:
69 return NULL;
70 }
71
72
73 scbr = (get_spi_clk_rate(bus) + max_hz - 1) / max_hz;
74 if (scbr > ATMEL_SPI_CSRx_SCBR_MAX)
75 /* Too low max SCK rate */
76 return NULL;
77 if (scbr < 1)
78 scbr = 1;
79
80 csrx = ATMEL_SPI_CSRx_SCBR(scbr);
81 csrx |= ATMEL_SPI_CSRx_BITS(ATMEL_SPI_BITS_8);
82 if (!(mode & SPI_CPHA))
83 csrx |= ATMEL_SPI_CSRx_NCPHA;
84 if (mode & SPI_CPOL)
85 csrx |= ATMEL_SPI_CSRx_CPOL;
86
Simon Glassd3504fe2013-03-18 19:23:40 +000087 as = spi_alloc_slave(struct atmel_spi_slave, bus, cs);
Hans-Christian Egtvedt60445cb2008-05-16 11:10:32 +020088 if (!as)
89 return NULL;
90
Hans-Christian Egtvedt60445cb2008-05-16 11:10:32 +020091 as->regs = regs;
92 as->mr = ATMEL_SPI_MR_MSTR | ATMEL_SPI_MR_MODFDIS
Bo Shenac81e1c2012-12-05 22:22:17 +000093#if defined(CONFIG_AT91SAM9X5) || defined(CONFIG_AT91SAM9M10G45)
Bo Shen65c57552012-08-19 20:32:22 +000094 | ATMEL_SPI_MR_WDRBT
95#endif
Hans-Christian Egtvedt60445cb2008-05-16 11:10:32 +020096 | ATMEL_SPI_MR_PCS(~(1 << cs) & 0xf);
97 spi_writel(as, CSR(cs), csrx);
98
99 return &as->slave;
100}
101
102void spi_free_slave(struct spi_slave *slave)
103{
104 struct atmel_spi_slave *as = to_atmel_spi(slave);
105
106 free(as);
107}
108
109int spi_claim_bus(struct spi_slave *slave)
110{
111 struct atmel_spi_slave *as = to_atmel_spi(slave);
112
113 /* Enable the SPI hardware */
114 spi_writel(as, CR, ATMEL_SPI_CR_SPIEN);
115
116 /*
117 * Select the slave. This should set SCK to the correct
118 * initial state, etc.
119 */
120 spi_writel(as, MR, as->mr);
121
122 return 0;
123}
124
125void spi_release_bus(struct spi_slave *slave)
126{
127 struct atmel_spi_slave *as = to_atmel_spi(slave);
128
129 /* Disable the SPI hardware */
130 spi_writel(as, CR, ATMEL_SPI_CR_SPIDIS);
131}
132
133int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
134 const void *dout, void *din, unsigned long flags)
135{
136 struct atmel_spi_slave *as = to_atmel_spi(slave);
137 unsigned int len_tx;
138 unsigned int len_rx;
139 unsigned int len;
Hans-Christian Egtvedt60445cb2008-05-16 11:10:32 +0200140 u32 status;
141 const u8 *txp = dout;
142 u8 *rxp = din;
143 u8 value;
144
Hans-Christian Egtvedt60445cb2008-05-16 11:10:32 +0200145 if (bitlen == 0)
146 /* Finish any previously submitted transfers */
147 goto out;
148
149 /*
150 * TODO: The controller can do non-multiple-of-8 bit
151 * transfers, but this driver currently doesn't support it.
152 *
153 * It's also not clear how such transfers are supposed to be
154 * represented as a stream of bytes...this is a limitation of
155 * the current SPI interface.
156 */
157 if (bitlen % 8) {
158 /* Errors always terminate an ongoing transfer */
159 flags |= SPI_XFER_END;
160 goto out;
161 }
162
163 len = bitlen / 8;
164
165 /*
166 * The controller can do automatic CS control, but it is
167 * somewhat quirky, and it doesn't really buy us much anyway
168 * in the context of U-Boot.
169 */
Reinhard Meyerf09d3b22010-08-09 13:37:59 +0200170 if (flags & SPI_XFER_BEGIN) {
Hans-Christian Egtvedt60445cb2008-05-16 11:10:32 +0200171 spi_cs_activate(slave);
Reinhard Meyerf09d3b22010-08-09 13:37:59 +0200172 /*
173 * sometimes the RDR is not empty when we get here,
174 * in theory that should not happen, but it DOES happen.
175 * Read it here to be on the safe side.
176 * That also clears the OVRES flag. Required if the
177 * following loop exits due to OVRES!
178 */
179 spi_readl(as, RDR);
180 }
Hans-Christian Egtvedt60445cb2008-05-16 11:10:32 +0200181
182 for (len_tx = 0, len_rx = 0; len_rx < len; ) {
183 status = spi_readl(as, SR);
184
185 if (status & ATMEL_SPI_SR_OVRES)
186 return -1;
187
188 if (len_tx < len && (status & ATMEL_SPI_SR_TDRE)) {
189 if (txp)
190 value = *txp++;
191 else
192 value = 0;
193 spi_writel(as, TDR, value);
194 len_tx++;
195 }
196 if (status & ATMEL_SPI_SR_RDRF) {
197 value = spi_readl(as, RDR);
198 if (rxp)
199 *rxp++ = value;
200 len_rx++;
201 }
202 }
203
204out:
205 if (flags & SPI_XFER_END) {
206 /*
207 * Wait until the transfer is completely done before
208 * we deactivate CS.
209 */
210 do {
211 status = spi_readl(as, SR);
212 } while (!(status & ATMEL_SPI_SR_TXEMPTY));
213
214 spi_cs_deactivate(slave);
215 }
216
217 return 0;
218}