xref: /netbsd-src/sys/dev/marvell/mvspi.c (revision 946379e7b37692fc43f68eb0d1c10daa0a7f3b6c)
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 	sba.sba_controller = &sc->sc_spi;
152 	(void) config_found_ia(self, "spibus", &sba, spibus_print);
153 }
154 
155 int
156 mvspi_configure(void *cookie, int slave, int mode, int speed)
157 {
158 	struct mvspi_softc *sc = cookie;
159 	uint32_t ctl = 0, spr, sppr;
160 	uint32_t divider;
161 	uint32_t best_spr = 0, best_sppr = 0;
162 	uint32_t best_sppr0, best_spprhi;
163 	uint8_t exact_match = 0;
164 	uint32_t min_baud_offset = 0xFFFFFFFF;
165 
166 	if (slave < 0 || slave > 7)
167 		return EINVAL;
168 
169 	switch(mode) {
170 		case SPI_MODE_0:
171 			ctl &= ~(MVSPI_CPOL_MASK);
172 			/* In boards documentation, CPHA is inverted */
173 			ctl &= MVSPI_CPHA_MASK;
174 			break;
175 		case SPI_MODE_1:
176 			ctl |= MVSPI_CPOL_MASK;
177 			ctl &= MVSPI_CPHA_MASK;
178 			break;
179 		case SPI_MODE_2:
180 			ctl &= ~(MVSPI_CPOL_MASK);
181 			ctl |= ~(MVSPI_CPHA_MASK);
182 			break;
183 		case SPI_MODE_3:
184 			ctl |= MVSPI_CPOL_MASK;
185 			ctl |= ~(MVSPI_CPHA_MASK);
186 			break;
187 		default:
188 			return EINVAL;
189 	}
190 
191 	/* Find the best prescale configuration - less or equal:
192 	 * SPI actual frecuency = core_clk / (SPR * (2 ^ SPPR))
193 	 * Try to find the minimal SPR and SPPR values that offer
194 	 * the best prescale config.
195 	 *
196 	 */
197 	for (spr = 1; spr <= MVSPI_SPR_MAXVALUE; spr++) {
198 		for (sppr = 0; sppr <= MVSPI_SPPR_MAXVALUE; sppr++) {
199 			divider = spr * (1 << sppr);
200 			/* Check for higher - irrelevant */
201 			if ((mvTclk / divider) > speed)
202 				continue;
203 
204 			/* Check for exact fit */
205 			if ((mvTclk / divider) == speed) {
206 				best_spr = spr;
207 				best_sppr = sppr;
208 				exact_match = 1;
209 				break;
210 			}
211 
212 			/* Check if this is better than the previous one */
213 			if ((speed - (mvTclk / divider)) < min_baud_offset) {
214 				min_baud_offset = (speed - (mvTclk / divider));
215 				best_spr = spr;
216 				best_sppr = sppr;
217 			}
218 		}
219 
220 		if (exact_match == 1)
221 			break;
222 	}
223 
224 	if (best_spr == 0) {
225 		printf("%s ERROR: SPI baud rate prescale error!\n", __func__);
226 		return -1;
227 	}
228 
229 	ctl &= ~(MVSPI_SPR_MASK);
230 	ctl &= ~(MVSPI_SPPR_MASK);
231 	ctl |= best_spr;
232 
233 	best_spprhi = best_sppr & MVSPI_SPPRHI_MASK;
234 	best_spprhi = best_spprhi << 5;
235 
236 	ctl |= best_spprhi;
237 
238 	best_sppr0 = best_sppr & MVSPI_SPPR0_MASK;
239 	best_sppr0 = best_sppr0 << 4;
240 
241 	ctl |= best_sppr0;
242 
243 	PUTREG(sc, MVSPI_INTCONF_REG, ctl);
244 
245 	return 0;
246 }
247 
248 int
249 mvspi_transfer(void *cookie, struct spi_transfer *st)
250 {
251 	struct mvspi_softc *sc = cookie;
252 	int s;
253 
254 	s = splbio();
255 	spi_transq_enqueue(&sc->sc_transq, st);
256 	if (sc->sc_transfer == NULL) {
257 		mvspi_sched(sc);
258 	}
259 	splx(s);
260 	return 0;
261 }
262 
263 void
264 mvspi_assert(struct mvspi_softc *sc)
265 {
266 	int ctl;
267 
268 	if (sc->sc_transfer->st_slave < 0 || sc->sc_transfer->st_slave > 7) {
269 		printf("%s ERROR: Slave number %d not valid!\n",  __func__, sc->sc_transfer->st_slave);
270 		return;
271 	} else
272 		/* Enable appropriate CSn according to its slave number */
273 		PUTREG(sc, MVSPI_CTRL_REG, (sc->sc_transfer->st_slave << 2));
274 
275 	/* Enable CSnAct */
276 	ctl = GETREG(sc, MVSPI_CTRL_REG);
277 	ctl |= MVSPI_CSNACT_MASK;
278 	PUTREG(sc, MVSPI_CTRL_REG, ctl);
279 }
280 
281 void
282 mvspi_deassert(struct mvspi_softc *sc)
283 {
284 	int ctl = GETREG(sc, MVSPI_CTRL_REG);
285 	ctl &= ~(MVSPI_CSNACT_MASK);
286 	PUTREG(sc, MVSPI_CTRL_REG, ctl);
287 }
288 
289 void
290 mvspi_sched(struct mvspi_softc *sc)
291 {
292 	struct spi_transfer *st;
293 	struct spi_chunk *chunk;
294 	int i, j, ctl;
295 	uint8_t byte;
296 	int ready = FALSE;
297 
298 	for (;;) {
299 		if ((st = sc->sc_transfer) == NULL) {
300 			if ((st = spi_transq_first(&sc->sc_transq)) == NULL) {
301 				/* No work left to do */
302 				break;
303 			}
304 			spi_transq_dequeue(&sc->sc_transq);
305 			sc->sc_transfer = st;
306 		}
307 
308 		chunk = st->st_chunks;
309 
310 		mvspi_assert(sc);
311 
312 		do {
313 			for (i = chunk->chunk_wresid; i > 0; i--) {
314 				/* First clear the ready bit */
315 				ctl = GETREG(sc, MVSPI_CTRL_REG);
316 				ctl &= ~(MVSPI_CR_SMEMRDY);
317 				PUTREG(sc, MVSPI_CTRL_REG, ctl);
318 
319 				if (chunk->chunk_wptr){
320 					byte = *chunk->chunk_wptr;
321 					chunk->chunk_wptr++;
322 				} else
323 					byte = MVSPI_DUMMY_BYTE;
324 
325 				/* Transmit data */
326 				PUTREG(sc, MVSPI_DATAOUT_REG, byte);
327 
328 				/* Wait with timeout for memory ready */
329 				for (j = 0; j < MVSPI_WAIT_RDY_MAX_LOOP; j++) {
330 					if (GETREG(sc, MVSPI_CTRL_REG) &
331 						MVSPI_CR_SMEMRDY) {
332 						ready = TRUE;
333 						break;
334 					}
335 
336 				}
337 
338 				if (!ready) {
339 					mvspi_deassert(sc);
340 					spi_done(st, EBUSY);
341 					return;
342 				}
343 
344 				/* Check that the RX data is needed */
345 				if (chunk->chunk_rptr) {
346 					*chunk->chunk_rptr =
347 						GETREG(sc, MVSPI_DATAIN_REG);
348 					chunk->chunk_rptr++;
349 
350 				}
351 
352 			}
353 
354 			chunk = chunk->chunk_next;
355 
356 		} while (chunk != NULL);
357 
358 		mvspi_deassert(sc);
359 
360 		spi_done(st, 0);
361 		sc->sc_transfer = NULL;
362 
363 
364 		break;
365 	}
366 }
367