xref: /netbsd-src/sys/dev/marvell/mvspi.c (revision 100a3398b8d3c64e571cff36b46c23431b410e09)
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 spi_controller	sc_spi;
57 	void			*sc_ih;
58 	bool			sc_interrupts;
59 
60 	struct spi_transfer	*sc_transfer;
61 	struct spi_chunk	*sc_wchunk;	/* For partial writes */
62 	struct spi_transq	sc_transq;
63 	bus_space_tag_t		sc_st;
64 	bus_space_handle_t	sc_sh;
65 	bus_size_t		sc_size;
66 };
67 
68 int mvspi_match(struct device *, struct cfdata *, void *);
69 void mvspi_attach(struct device *, struct device *, void *);
70 /* SPI service routines */
71 int mvspi_configure(void *, int, int, int);
72 int mvspi_transfer(void *, struct spi_transfer *);
73 /* Internal support */
74 void mvspi_sched(struct mvspi_softc *);
75 void mvspi_assert(struct mvspi_softc *sc);
76 void mvspi_deassert(struct mvspi_softc *sc);
77 
78 #define	GETREG(sc, x)					\
79 	bus_space_read_4(sc->sc_st, sc->sc_sh, x)
80 #define	PUTREG(sc, x, v)				\
81 	bus_space_write_4(sc->sc_st, sc->sc_sh, x, v)
82 
83 /* Attach structure */
84 CFATTACH_DECL_NEW(mvspi_mbus, sizeof(struct mvspi_softc),
85     mvspi_match, mvspi_attach, NULL, NULL);
86 
87 int
mvspi_match(struct device * parent,struct cfdata * cf,void * aux)88 mvspi_match(struct device *parent, struct cfdata *cf, void *aux)
89 {
90 	struct marvell_attach_args *mva = aux;
91 
92 	if (strcmp(mva->mva_name, cf->cf_name) != 0)
93 		return 0;
94 	if (mva->mva_offset == MVA_OFFSET_DEFAULT ||
95 	    mva->mva_irq == MVA_IRQ_DEFAULT)
96 		return 0;
97 
98 	mva->mva_size = MVSPI_SIZE;
99 	return 1;
100 }
101 
102 void
mvspi_attach(struct device * parent,struct device * self,void * aux)103 mvspi_attach(struct device *parent, struct device *self, void *aux)
104 {
105 	struct mvspi_softc *sc =  device_private(self);
106   	struct marvell_attach_args *mva = aux;
107 	struct spibus_attach_args sba;
108 	int ctl;
109 
110 	aprint_normal(": Marvell SPI controller\n");
111 
112 	/*
113 	 * Map registers.
114 	 */
115 	sc->sc_st = mva->mva_iot;
116 	sc->sc_size = mva->mva_size;
117 
118 	if (bus_space_subregion(sc->sc_st, mva->mva_ioh, mva->mva_offset,
119 	    mva->mva_size, &sc->sc_sh)) {
120 		aprint_error_dev(self, "Cannot map registers\n");
121 		return;
122 	}
123 
124 	/*
125 	 * Initialize hardware.
126 	 */
127 	ctl = GETREG(sc, MVSPI_INTCONF_REG);
128 
129 	ctl &= MVSPI_DIRHS_MASK;
130 	ctl &= MVSPI_1BYTE_MASK;
131 
132 	PUTREG(sc, MVSPI_INTCONF_REG, ctl);
133 
134 	/*
135 	 * Initialize SPI controller.
136 	 */
137 	sc->sc_spi.sct_cookie = sc;
138 	sc->sc_spi.sct_configure = mvspi_configure;
139 	sc->sc_spi.sct_transfer = mvspi_transfer;
140 	sc->sc_spi.sct_nslaves = 1;
141 
142 	/*
143 	 * Initialize the queue.
144 	 */
145 	spi_transq_init(&sc->sc_transq);
146 
147 	/*
148 	 * Initialize and attach bus attach.
149 	 */
150 	memset(&sba, 0, sizeof(sba));
151 	sba.sba_controller = &sc->sc_spi;
152 	config_found(self, &sba, spibus_print, CFARGS_NONE);
153 }
154 
155 int
mvspi_configure(void * cookie,int slave,int mode,int speed)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 frequency = 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
mvspi_transfer(void * cookie,struct spi_transfer * st)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
mvspi_assert(struct mvspi_softc * sc)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
mvspi_deassert(struct mvspi_softc * sc)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
mvspi_sched(struct mvspi_softc * sc)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