xref: /netbsd-src/sys/dev/marvell/mvspi.c (revision 181254a7b1bdde6873432bffef2d2decc4b5c22f)
1 /*******************************************************************************
2 Copyright (C) Marvell International Ltd. and its affiliates
3 
4 Developed by Semihalf
5 
6 ********************************************************************************
7 Marvell BSD License
8 
9 If you received this File from Marvell, you may opt to use, redistribute and/or
10 modify this File under the following licensing terms.
11 Redistribution and use in source and binary forms, with or without modification,
12 are permitted provided that the following conditions are met:
13 
14     *   Redistributions of source code must retain the above copyright notice,
15             this list of conditions and the following disclaimer.
16 
17     *   Redistributions in binary form must reproduce the above copyright
18         notice, this list of conditions and the following disclaimer in the
19         documentation and/or other materials provided with the distribution.
20 
21     *   Neither the name of Marvell nor the names of its contributors may be
22         used to endorse or promote products derived from this software without
23         specific prior written permission.
24 
25 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
26 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
27 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
28 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
29 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
30 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
32 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
33 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
34 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
35 
36 *******************************************************************************/
37 
38 /*
39  * Transfer mechanism extracted from arspi.c corresponding with the lines
40  * 254-262 in this file.
41  */
42 
43 #include <sys/param.h>
44 #include <sys/device.h>
45 
46 #include <dev/spi/spivar.h>
47 
48 #include <dev/marvell/mvspireg.h>
49 #include <dev/marvell/marvellvar.h>
50 
51 #include "locators.h"
52 
53 extern uint32_t mvTclk;
54 
55 struct mvspi_softc {
56 	struct device		sc_dev;
57 	struct spi_controller	sc_spi;
58 	void			*sc_ih;
59 	bool			sc_interrupts;
60 
61 	struct spi_transfer	*sc_transfer;
62 	struct spi_chunk	*sc_wchunk;	/* For partial writes */
63 	struct spi_transq	sc_transq;
64 	bus_space_tag_t		sc_st;
65 	bus_space_handle_t	sc_sh;
66 	bus_size_t		sc_size;
67 };
68 
69 int mvspi_match(struct device *, struct cfdata *, void *);
70 void mvspi_attach(struct device *, struct device *, void *);
71 /* SPI service routines */
72 int mvspi_configure(void *, int, int, int);
73 int mvspi_transfer(void *, struct spi_transfer *);
74 /* Internal support */
75 void mvspi_sched(struct mvspi_softc *);
76 void mvspi_assert(struct mvspi_softc *sc);
77 void mvspi_deassert(struct mvspi_softc *sc);
78 
79 #define	GETREG(sc, x)					\
80 	bus_space_read_4(sc->sc_st, sc->sc_sh, x)
81 #define	PUTREG(sc, x, v)				\
82 	bus_space_write_4(sc->sc_st, sc->sc_sh, x, v)
83 
84 /* Attach structure */
85 CFATTACH_DECL_NEW(mvspi_mbus, sizeof(struct mvspi_softc),
86     mvspi_match, mvspi_attach, NULL, NULL);
87 
88 int
89 mvspi_match(struct device *parent, struct cfdata *cf, void *aux)
90 {
91 	struct marvell_attach_args *mva = aux;
92 
93 	if (strcmp(mva->mva_name, cf->cf_name) != 0)
94 		return 0;
95 	if (mva->mva_offset == MVA_OFFSET_DEFAULT ||
96 	    mva->mva_irq == MVA_IRQ_DEFAULT)
97 		return 0;
98 
99 	mva->mva_size = MVSPI_SIZE;
100 	return 1;
101 }
102 
103 void
104 mvspi_attach(struct device *parent, struct device *self, void *aux)
105 {
106 	struct mvspi_softc *sc =  device_private(self);
107   	struct marvell_attach_args *mva = aux;
108 	struct spibus_attach_args sba;
109 	int ctl;
110 
111 	aprint_normal(": Marvell SPI controller\n");
112 
113 	/*
114 	 * Map registers.
115 	 */
116 	sc->sc_st = mva->mva_iot;
117 	sc->sc_size = mva->mva_size;
118 
119 	if (bus_space_subregion(sc->sc_st, mva->mva_ioh, mva->mva_offset,
120 	    mva->mva_size, &sc->sc_sh)) {
121 		aprint_error_dev(self, "Cannot map registers\n");
122 		return;
123 	}
124 
125 	/*
126 	 * Initialize hardware.
127 	 */
128 	ctl = GETREG(sc, MVSPI_INTCONF_REG);
129 
130 	ctl &= MVSPI_DIRHS_MASK;
131 	ctl &= MVSPI_1BYTE_MASK;
132 
133 	PUTREG(sc, MVSPI_INTCONF_REG, ctl);
134 
135 	/*
136 	 * Initialize SPI controller.
137 	 */
138 	sc->sc_spi.sct_cookie = sc;
139 	sc->sc_spi.sct_configure = mvspi_configure;
140 	sc->sc_spi.sct_transfer = mvspi_transfer;
141 	sc->sc_spi.sct_nslaves = 1;
142 
143 	/*
144 	 * Initialize the queue.
145 	 */
146 	spi_transq_init(&sc->sc_transq);
147 
148 	/*
149 	 * Initialize and attach bus attach.
150 	 */
151 	memset(&sba, 0, sizeof(sba));
152 	sba.sba_controller = &sc->sc_spi;
153 	(void) config_found_ia(self, "spibus", &sba, spibus_print);
154 }
155 
156 int
157 mvspi_configure(void *cookie, int slave, int mode, int speed)
158 {
159 	struct mvspi_softc *sc = cookie;
160 	uint32_t ctl = 0, spr, sppr;
161 	uint32_t divider;
162 	uint32_t best_spr = 0, best_sppr = 0;
163 	uint32_t best_sppr0, best_spprhi;
164 	uint8_t exact_match = 0;
165 	uint32_t min_baud_offset = 0xFFFFFFFF;
166 
167 	if (slave < 0 || slave > 7)
168 		return EINVAL;
169 
170 	switch(mode) {
171 		case SPI_MODE_0:
172 			ctl &= ~(MVSPI_CPOL_MASK);
173 			/* In boards documentation, CPHA is inverted */
174 			ctl &= MVSPI_CPHA_MASK;
175 			break;
176 		case SPI_MODE_1:
177 			ctl |= MVSPI_CPOL_MASK;
178 			ctl &= MVSPI_CPHA_MASK;
179 			break;
180 		case SPI_MODE_2:
181 			ctl &= ~(MVSPI_CPOL_MASK);
182 			ctl |= ~(MVSPI_CPHA_MASK);
183 			break;
184 		case SPI_MODE_3:
185 			ctl |= MVSPI_CPOL_MASK;
186 			ctl |= ~(MVSPI_CPHA_MASK);
187 			break;
188 		default:
189 			return EINVAL;
190 	}
191 
192 	/* Find the best prescale configuration - less or equal:
193 	 * SPI actual frecuency = core_clk / (SPR * (2 ^ SPPR))
194 	 * Try to find the minimal SPR and SPPR values that offer
195 	 * the best prescale config.
196 	 *
197 	 */
198 	for (spr = 1; spr <= MVSPI_SPR_MAXVALUE; spr++) {
199 		for (sppr = 0; sppr <= MVSPI_SPPR_MAXVALUE; sppr++) {
200 			divider = spr * (1 << sppr);
201 			/* Check for higher - irrelevant */
202 			if ((mvTclk / divider) > speed)
203 				continue;
204 
205 			/* Check for exact fit */
206 			if ((mvTclk / divider) == speed) {
207 				best_spr = spr;
208 				best_sppr = sppr;
209 				exact_match = 1;
210 				break;
211 			}
212 
213 			/* Check if this is better than the previous one */
214 			if ((speed - (mvTclk / divider)) < min_baud_offset) {
215 				min_baud_offset = (speed - (mvTclk / divider));
216 				best_spr = spr;
217 				best_sppr = sppr;
218 			}
219 		}
220 
221 		if (exact_match == 1)
222 			break;
223 	}
224 
225 	if (best_spr == 0) {
226 		printf("%s ERROR: SPI baud rate prescale error!\n", __func__);
227 		return -1;
228 	}
229 
230 	ctl &= ~(MVSPI_SPR_MASK);
231 	ctl &= ~(MVSPI_SPPR_MASK);
232 	ctl |= best_spr;
233 
234 	best_spprhi = best_sppr & MVSPI_SPPRHI_MASK;
235 	best_spprhi = best_spprhi << 5;
236 
237 	ctl |= best_spprhi;
238 
239 	best_sppr0 = best_sppr & MVSPI_SPPR0_MASK;
240 	best_sppr0 = best_sppr0 << 4;
241 
242 	ctl |= best_sppr0;
243 
244 	PUTREG(sc, MVSPI_INTCONF_REG, ctl);
245 
246 	return 0;
247 }
248 
249 int
250 mvspi_transfer(void *cookie, struct spi_transfer *st)
251 {
252 	struct mvspi_softc *sc = cookie;
253 	int s;
254 
255 	s = splbio();
256 	spi_transq_enqueue(&sc->sc_transq, st);
257 	if (sc->sc_transfer == NULL) {
258 		mvspi_sched(sc);
259 	}
260 	splx(s);
261 	return 0;
262 }
263 
264 void
265 mvspi_assert(struct mvspi_softc *sc)
266 {
267 	int ctl;
268 
269 	if (sc->sc_transfer->st_slave < 0 || sc->sc_transfer->st_slave > 7) {
270 		printf("%s ERROR: Slave number %d not valid!\n",  __func__, sc->sc_transfer->st_slave);
271 		return;
272 	} else
273 		/* Enable appropriate CSn according to its slave number */
274 		PUTREG(sc, MVSPI_CTRL_REG, (sc->sc_transfer->st_slave << 2));
275 
276 	/* Enable CSnAct */
277 	ctl = GETREG(sc, MVSPI_CTRL_REG);
278 	ctl |= MVSPI_CSNACT_MASK;
279 	PUTREG(sc, MVSPI_CTRL_REG, ctl);
280 }
281 
282 void
283 mvspi_deassert(struct mvspi_softc *sc)
284 {
285 	int ctl = GETREG(sc, MVSPI_CTRL_REG);
286 	ctl &= ~(MVSPI_CSNACT_MASK);
287 	PUTREG(sc, MVSPI_CTRL_REG, ctl);
288 }
289 
290 void
291 mvspi_sched(struct mvspi_softc *sc)
292 {
293 	struct spi_transfer *st;
294 	struct spi_chunk *chunk;
295 	int i, j, ctl;
296 	uint8_t byte;
297 	int ready = FALSE;
298 
299 	for (;;) {
300 		if ((st = sc->sc_transfer) == NULL) {
301 			if ((st = spi_transq_first(&sc->sc_transq)) == NULL) {
302 				/* No work left to do */
303 				break;
304 			}
305 			spi_transq_dequeue(&sc->sc_transq);
306 			sc->sc_transfer = st;
307 		}
308 
309 		chunk = st->st_chunks;
310 
311 		mvspi_assert(sc);
312 
313 		do {
314 			for (i = chunk->chunk_wresid; i > 0; i--) {
315 				/* First clear the ready bit */
316 				ctl = GETREG(sc, MVSPI_CTRL_REG);
317 				ctl &= ~(MVSPI_CR_SMEMRDY);
318 				PUTREG(sc, MVSPI_CTRL_REG, ctl);
319 
320 				if (chunk->chunk_wptr){
321 					byte = *chunk->chunk_wptr;
322 					chunk->chunk_wptr++;
323 				} else
324 					byte = MVSPI_DUMMY_BYTE;
325 
326 				/* Transmit data */
327 				PUTREG(sc, MVSPI_DATAOUT_REG, byte);
328 
329 				/* Wait with timeout for memory ready */
330 				for (j = 0; j < MVSPI_WAIT_RDY_MAX_LOOP; j++) {
331 					if (GETREG(sc, MVSPI_CTRL_REG) &
332 						MVSPI_CR_SMEMRDY) {
333 						ready = TRUE;
334 						break;
335 					}
336 
337 				}
338 
339 				if (!ready) {
340 					mvspi_deassert(sc);
341 					spi_done(st, EBUSY);
342 					return;
343 				}
344 
345 				/* Check that the RX data is needed */
346 				if (chunk->chunk_rptr) {
347 					*chunk->chunk_rptr =
348 						GETREG(sc, MVSPI_DATAIN_REG);
349 					chunk->chunk_rptr++;
350 
351 				}
352 
353 			}
354 
355 			chunk = chunk->chunk_next;
356 
357 		} while (chunk != NULL);
358 
359 		mvspi_deassert(sc);
360 
361 		spi_done(st, 0);
362 		sc->sc_transfer = NULL;
363 
364 
365 		break;
366 	}
367 }
368