Home | History | Annotate | Line # | Download | only in marvell
mvspi.c revision 1.2
      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