xref: /openbmc/u-boot/drivers/spi/mxs_spi.c (revision fe24d614)
1 /*
2  * Freescale i.MX28 SPI driver
3  *
4  * Copyright (C) 2011 Marek Vasut <marek.vasut@gmail.com>
5  * on behalf of DENX Software Engineering GmbH
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  * NOTE: This driver only supports the SPI-controller chipselects,
23  *       GPIO driven chipselects are not supported.
24  */
25 
26 #include <common.h>
27 #include <malloc.h>
28 #include <spi.h>
29 #include <asm/errno.h>
30 #include <asm/io.h>
31 #include <asm/arch/clock.h>
32 #include <asm/arch/imx-regs.h>
33 #include <asm/arch/sys_proto.h>
34 #include <asm/arch/dma.h>
35 
36 #define	MXS_SPI_MAX_TIMEOUT	1000000
37 #define	MXS_SPI_PORT_OFFSET	0x2000
38 #define MXS_SSP_CHIPSELECT_MASK		0x00300000
39 #define MXS_SSP_CHIPSELECT_SHIFT	20
40 
41 #define MXSSSP_SMALL_TRANSFER	512
42 
43 /*
44  * CONFIG_MXS_SPI_DMA_ENABLE: Experimental mixed PIO/DMA support for MXS SPI
45  *                            host. Use with utmost caution!
46  *
47  *                            Enabling this is not yet recommended since this
48  *                            still doesn't support transfers to/from unaligned
49  *                            addresses. Therefore this driver will not work
50  *                            for example with saving environment. This is
51  *                            caused by DMA alignment constraints on MXS.
52  */
53 
54 struct mxs_spi_slave {
55 	struct spi_slave	slave;
56 	uint32_t		max_khz;
57 	uint32_t		mode;
58 	struct mxs_ssp_regs	*regs;
59 	struct mxs_dma_desc	*desc;
60 };
61 
62 static inline struct mxs_spi_slave *to_mxs_slave(struct spi_slave *slave)
63 {
64 	return container_of(slave, struct mxs_spi_slave, slave);
65 }
66 
67 void spi_init(void)
68 {
69 }
70 
71 int spi_cs_is_valid(unsigned int bus, unsigned int cs)
72 {
73 	/* MXS SPI: 4 ports and 3 chip selects maximum */
74 	if (bus > 3 || cs > 2)
75 		return 0;
76 	else
77 		return 1;
78 }
79 
80 struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
81 				  unsigned int max_hz, unsigned int mode)
82 {
83 	struct mxs_spi_slave *mxs_slave;
84 	uint32_t addr;
85 	struct mxs_ssp_regs *ssp_regs;
86 	int reg;
87 	struct mxs_dma_desc *desc;
88 
89 	if (!spi_cs_is_valid(bus, cs)) {
90 		printf("mxs_spi: invalid bus %d / chip select %d\n", bus, cs);
91 		return NULL;
92 	}
93 
94 	mxs_slave = calloc(sizeof(struct mxs_spi_slave), 1);
95 	if (!mxs_slave)
96 		return NULL;
97 
98 	desc = mxs_dma_desc_alloc();
99 	if (!desc)
100 		goto err_desc;
101 
102 	if (mxs_dma_init_channel(bus))
103 		goto err_init;
104 
105 	addr = MXS_SSP0_BASE + (bus * MXS_SPI_PORT_OFFSET);
106 
107 	mxs_slave->slave.bus = bus;
108 	mxs_slave->slave.cs = cs;
109 	mxs_slave->max_khz = max_hz / 1000;
110 	mxs_slave->mode = mode;
111 	mxs_slave->regs = (struct mxs_ssp_regs *)addr;
112 	mxs_slave->desc = desc;
113 	ssp_regs = mxs_slave->regs;
114 
115 	reg = readl(&ssp_regs->hw_ssp_ctrl0);
116 	reg &= ~(MXS_SSP_CHIPSELECT_MASK);
117 	reg |= cs << MXS_SSP_CHIPSELECT_SHIFT;
118 
119 	writel(reg, &ssp_regs->hw_ssp_ctrl0);
120 	return &mxs_slave->slave;
121 
122 err_init:
123 	mxs_dma_desc_free(desc);
124 err_desc:
125 	free(mxs_slave);
126 	return NULL;
127 }
128 
129 void spi_free_slave(struct spi_slave *slave)
130 {
131 	struct mxs_spi_slave *mxs_slave = to_mxs_slave(slave);
132 	mxs_dma_desc_free(mxs_slave->desc);
133 	free(mxs_slave);
134 }
135 
136 int spi_claim_bus(struct spi_slave *slave)
137 {
138 	struct mxs_spi_slave *mxs_slave = to_mxs_slave(slave);
139 	struct mxs_ssp_regs *ssp_regs = mxs_slave->regs;
140 	uint32_t reg = 0;
141 
142 	mxs_reset_block(&ssp_regs->hw_ssp_ctrl0_reg);
143 
144 	writel(SSP_CTRL0_BUS_WIDTH_ONE_BIT, &ssp_regs->hw_ssp_ctrl0);
145 
146 	reg = SSP_CTRL1_SSP_MODE_SPI | SSP_CTRL1_WORD_LENGTH_EIGHT_BITS;
147 	reg |= (mxs_slave->mode & SPI_CPOL) ? SSP_CTRL1_POLARITY : 0;
148 	reg |= (mxs_slave->mode & SPI_CPHA) ? SSP_CTRL1_PHASE : 0;
149 	writel(reg, &ssp_regs->hw_ssp_ctrl1);
150 
151 	writel(0, &ssp_regs->hw_ssp_cmd0);
152 
153 	mx28_set_ssp_busclock(slave->bus, mxs_slave->max_khz);
154 
155 	return 0;
156 }
157 
158 void spi_release_bus(struct spi_slave *slave)
159 {
160 }
161 
162 static void mxs_spi_start_xfer(struct mxs_ssp_regs *ssp_regs)
163 {
164 	writel(SSP_CTRL0_LOCK_CS, &ssp_regs->hw_ssp_ctrl0_set);
165 	writel(SSP_CTRL0_IGNORE_CRC, &ssp_regs->hw_ssp_ctrl0_clr);
166 }
167 
168 static void mxs_spi_end_xfer(struct mxs_ssp_regs *ssp_regs)
169 {
170 	writel(SSP_CTRL0_LOCK_CS, &ssp_regs->hw_ssp_ctrl0_clr);
171 	writel(SSP_CTRL0_IGNORE_CRC, &ssp_regs->hw_ssp_ctrl0_set);
172 }
173 
174 static int mxs_spi_xfer_pio(struct mxs_spi_slave *slave,
175 			char *data, int length, int write, unsigned long flags)
176 {
177 	struct mxs_ssp_regs *ssp_regs = slave->regs;
178 
179 	if (flags & SPI_XFER_BEGIN)
180 		mxs_spi_start_xfer(ssp_regs);
181 
182 	while (length--) {
183 		/* We transfer 1 byte */
184 		writel(1, &ssp_regs->hw_ssp_xfer_size);
185 
186 		if ((flags & SPI_XFER_END) && !length)
187 			mxs_spi_end_xfer(ssp_regs);
188 
189 		if (write)
190 			writel(SSP_CTRL0_READ, &ssp_regs->hw_ssp_ctrl0_clr);
191 		else
192 			writel(SSP_CTRL0_READ, &ssp_regs->hw_ssp_ctrl0_set);
193 
194 		writel(SSP_CTRL0_RUN, &ssp_regs->hw_ssp_ctrl0_set);
195 
196 		if (mxs_wait_mask_set(&ssp_regs->hw_ssp_ctrl0_reg,
197 			SSP_CTRL0_RUN, MXS_SPI_MAX_TIMEOUT)) {
198 			printf("MXS SPI: Timeout waiting for start\n");
199 			return -ETIMEDOUT;
200 		}
201 
202 		if (write)
203 			writel(*data++, &ssp_regs->hw_ssp_data);
204 
205 		writel(SSP_CTRL0_DATA_XFER, &ssp_regs->hw_ssp_ctrl0_set);
206 
207 		if (!write) {
208 			if (mxs_wait_mask_clr(&ssp_regs->hw_ssp_status_reg,
209 				SSP_STATUS_FIFO_EMPTY, MXS_SPI_MAX_TIMEOUT)) {
210 				printf("MXS SPI: Timeout waiting for data\n");
211 				return -ETIMEDOUT;
212 			}
213 
214 			*data = readl(&ssp_regs->hw_ssp_data);
215 			data++;
216 		}
217 
218 		if (mxs_wait_mask_clr(&ssp_regs->hw_ssp_ctrl0_reg,
219 			SSP_CTRL0_RUN, MXS_SPI_MAX_TIMEOUT)) {
220 			printf("MXS SPI: Timeout waiting for finish\n");
221 			return -ETIMEDOUT;
222 		}
223 	}
224 
225 	return 0;
226 }
227 
228 static int mxs_spi_xfer_dma(struct mxs_spi_slave *slave,
229 			char *data, int length, int write, unsigned long flags)
230 {
231 	struct mxs_dma_desc *desc = slave->desc;
232 	struct mxs_ssp_regs *ssp_regs = slave->regs;
233 	uint32_t ctrl0 = SSP_CTRL0_DATA_XFER;
234 	uint32_t cache_data_count;
235 	int dmach;
236 
237 	memset(desc, 0, sizeof(struct mxs_dma_desc));
238 	desc->address = (dma_addr_t)desc;
239 
240 	if (flags & SPI_XFER_BEGIN)
241 		ctrl0 |= SSP_CTRL0_LOCK_CS;
242 	if (flags & SPI_XFER_END)
243 		ctrl0 |= SSP_CTRL0_IGNORE_CRC;
244 	if (!write)
245 		ctrl0 |= SSP_CTRL0_READ;
246 
247 	writel(length, &ssp_regs->hw_ssp_xfer_size);
248 
249 	if (length % ARCH_DMA_MINALIGN)
250 		cache_data_count = roundup(length, ARCH_DMA_MINALIGN);
251 	else
252 		cache_data_count = length;
253 
254 	if (!write) {
255 		slave->desc->cmd.data = MXS_DMA_DESC_COMMAND_DMA_WRITE;
256 		slave->desc->cmd.address = (dma_addr_t)data;
257 	} else {
258 		slave->desc->cmd.data = MXS_DMA_DESC_COMMAND_DMA_READ;
259 		slave->desc->cmd.address = (dma_addr_t)data;
260 
261 		/* Flush data to DRAM so DMA can pick them up */
262 		flush_dcache_range((uint32_t)data,
263 			(uint32_t)(data + cache_data_count));
264 	}
265 
266 	slave->desc->cmd.data |= MXS_DMA_DESC_IRQ | MXS_DMA_DESC_DEC_SEM |
267 				(length << MXS_DMA_DESC_BYTES_OFFSET) |
268 				(1 << MXS_DMA_DESC_PIO_WORDS_OFFSET) |
269 				MXS_DMA_DESC_WAIT4END;
270 
271 	slave->desc->cmd.pio_words[0] = ctrl0;
272 
273 	dmach = MXS_DMA_CHANNEL_AHB_APBH_SSP0 + slave->slave.bus;
274 	mxs_dma_desc_append(dmach, slave->desc);
275 	if (mxs_dma_go(dmach))
276 		return -EINVAL;
277 
278 	/* The data arrived into DRAM, invalidate cache over them */
279 	if (!write) {
280 		invalidate_dcache_range((uint32_t)data,
281 			(uint32_t)(data + cache_data_count));
282 	}
283 
284 	return 0;
285 }
286 
287 int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
288 		const void *dout, void *din, unsigned long flags)
289 {
290 	struct mxs_spi_slave *mxs_slave = to_mxs_slave(slave);
291 	struct mxs_ssp_regs *ssp_regs = mxs_slave->regs;
292 	int len = bitlen / 8;
293 	char dummy;
294 	int write = 0;
295 	char *data = NULL;
296 
297 #ifdef CONFIG_MXS_SPI_DMA_ENABLE
298 	int dma = 1;
299 #else
300 	int dma = 0;
301 #endif
302 
303 	if (bitlen == 0) {
304 		if (flags & SPI_XFER_END) {
305 			din = (void *)&dummy;
306 			len = 1;
307 		} else
308 			return 0;
309 	}
310 
311 	/* Half-duplex only */
312 	if (din && dout)
313 		return -EINVAL;
314 	/* No data */
315 	if (!din && !dout)
316 		return 0;
317 
318 	if (dout) {
319 		data = (char *)dout;
320 		write = 1;
321 	} else if (din) {
322 		data = (char *)din;
323 		write = 0;
324 	}
325 
326 	/*
327 	 * Check for alignment, if the buffer is aligned, do DMA transfer,
328 	 * PIO otherwise. This is a temporary workaround until proper bounce
329 	 * buffer is in place.
330 	 */
331 	if (dma) {
332 		if (((uint32_t)data) & (ARCH_DMA_MINALIGN - 1))
333 			dma = 0;
334 		if (((uint32_t)len) & (ARCH_DMA_MINALIGN - 1))
335 			dma = 0;
336 	}
337 
338 	if (!dma || (len < MXSSSP_SMALL_TRANSFER)) {
339 		writel(SSP_CTRL1_DMA_ENABLE, &ssp_regs->hw_ssp_ctrl1_clr);
340 		return mxs_spi_xfer_pio(mxs_slave, data, len, write, flags);
341 	} else {
342 		writel(SSP_CTRL1_DMA_ENABLE, &ssp_regs->hw_ssp_ctrl1_set);
343 		return mxs_spi_xfer_dma(mxs_slave, data, len, write, flags);
344 	}
345 }
346