Home | History | Annotate | Line # | Download | only in marvell
      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
     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
    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 	int ctl;
    108 
    109 	aprint_normal(": Marvell SPI controller\n");
    110 
    111 	/*
    112 	 * Map registers.
    113 	 */
    114 	sc->sc_st = mva->mva_iot;
    115 	sc->sc_size = mva->mva_size;
    116 
    117 	if (bus_space_subregion(sc->sc_st, mva->mva_ioh, mva->mva_offset,
    118 	    mva->mva_size, &sc->sc_sh)) {
    119 		aprint_error_dev(self, "Cannot map registers\n");
    120 		return;
    121 	}
    122 
    123 	/*
    124 	 * Initialize hardware.
    125 	 */
    126 	ctl = GETREG(sc, MVSPI_INTCONF_REG);
    127 
    128 	ctl &= MVSPI_DIRHS_MASK;
    129 	ctl &= MVSPI_1BYTE_MASK;
    130 
    131 	PUTREG(sc, MVSPI_INTCONF_REG, ctl);
    132 
    133 	/*
    134 	 * Initialize SPI controller.
    135 	 */
    136 	sc->sc_spi.sct_cookie = sc;
    137 	sc->sc_spi.sct_configure = mvspi_configure;
    138 	sc->sc_spi.sct_transfer = mvspi_transfer;
    139 	sc->sc_spi.sct_nslaves = 1;
    140 
    141 	/*
    142 	 * Initialize the queue.
    143 	 */
    144 	spi_transq_init(&sc->sc_transq);
    145 
    146 	/*
    147 	 * Initialize and attach bus attach.
    148 	 */
    149 	spibus_attach(self, &sc->sc_spi);
    150 }
    151 
    152 int
    153 mvspi_configure(void *cookie, int slave, int mode, int speed)
    154 {
    155 	struct mvspi_softc *sc = cookie;
    156 	uint32_t ctl = 0, spr, sppr;
    157 	uint32_t divider;
    158 	uint32_t best_spr = 0, best_sppr = 0;
    159 	uint32_t best_sppr0, best_spprhi;
    160 	uint8_t exact_match = 0;
    161 	uint32_t min_baud_offset = 0xFFFFFFFF;
    162 
    163 	if (slave < 0 || slave > 7)
    164 		return EINVAL;
    165 
    166 	switch(mode) {
    167 		case SPI_MODE_0:
    168 			ctl &= ~(MVSPI_CPOL_MASK);
    169 			/* In boards documentation, CPHA is inverted */
    170 			ctl &= MVSPI_CPHA_MASK;
    171 			break;
    172 		case SPI_MODE_1:
    173 			ctl |= MVSPI_CPOL_MASK;
    174 			ctl &= MVSPI_CPHA_MASK;
    175 			break;
    176 		case SPI_MODE_2:
    177 			ctl &= ~(MVSPI_CPOL_MASK);
    178 			ctl |= ~(MVSPI_CPHA_MASK);
    179 			break;
    180 		case SPI_MODE_3:
    181 			ctl |= MVSPI_CPOL_MASK;
    182 			ctl |= ~(MVSPI_CPHA_MASK);
    183 			break;
    184 		default:
    185 			return EINVAL;
    186 	}
    187 
    188 	/* Find the best prescale configuration - less or equal:
    189 	 * SPI actual frequency = core_clk / (SPR * (2 ^ SPPR))
    190 	 * Try to find the minimal SPR and SPPR values that offer
    191 	 * the best prescale config.
    192 	 *
    193 	 */
    194 	for (spr = 1; spr <= MVSPI_SPR_MAXVALUE; spr++) {
    195 		for (sppr = 0; sppr <= MVSPI_SPPR_MAXVALUE; sppr++) {
    196 			divider = spr * (1 << sppr);
    197 			/* Check for higher - irrelevant */
    198 			if ((mvTclk / divider) > speed)
    199 				continue;
    200 
    201 			/* Check for exact fit */
    202 			if ((mvTclk / divider) == speed) {
    203 				best_spr = spr;
    204 				best_sppr = sppr;
    205 				exact_match = 1;
    206 				break;
    207 			}
    208 
    209 			/* Check if this is better than the previous one */
    210 			if ((speed - (mvTclk / divider)) < min_baud_offset) {
    211 				min_baud_offset = (speed - (mvTclk / divider));
    212 				best_spr = spr;
    213 				best_sppr = sppr;
    214 			}
    215 		}
    216 
    217 		if (exact_match == 1)
    218 			break;
    219 	}
    220 
    221 	if (best_spr == 0) {
    222 		printf("%s ERROR: SPI baud rate prescale error!\n", __func__);
    223 		return -1;
    224 	}
    225 
    226 	ctl &= ~(MVSPI_SPR_MASK);
    227 	ctl &= ~(MVSPI_SPPR_MASK);
    228 	ctl |= best_spr;
    229 
    230 	best_spprhi = best_sppr & MVSPI_SPPRHI_MASK;
    231 	best_spprhi = best_spprhi << 5;
    232 
    233 	ctl |= best_spprhi;
    234 
    235 	best_sppr0 = best_sppr & MVSPI_SPPR0_MASK;
    236 	best_sppr0 = best_sppr0 << 4;
    237 
    238 	ctl |= best_sppr0;
    239 
    240 	PUTREG(sc, MVSPI_INTCONF_REG, ctl);
    241 
    242 	return 0;
    243 }
    244 
    245 int
    246 mvspi_transfer(void *cookie, struct spi_transfer *st)
    247 {
    248 	struct mvspi_softc *sc = cookie;
    249 	int s;
    250 
    251 	s = splbio();
    252 	spi_transq_enqueue(&sc->sc_transq, st);
    253 	if (sc->sc_transfer == NULL) {
    254 		mvspi_sched(sc);
    255 	}
    256 	splx(s);
    257 	return 0;
    258 }
    259 
    260 void
    261 mvspi_assert(struct mvspi_softc *sc)
    262 {
    263 	int ctl;
    264 
    265 	if (sc->sc_transfer->st_slave < 0 || sc->sc_transfer->st_slave > 7) {
    266 		printf("%s ERROR: Slave number %d not valid!\n",  __func__, sc->sc_transfer->st_slave);
    267 		return;
    268 	} else
    269 		/* Enable appropriate CSn according to its slave number */
    270 		PUTREG(sc, MVSPI_CTRL_REG, (sc->sc_transfer->st_slave << 2));
    271 
    272 	/* Enable CSnAct */
    273 	ctl = GETREG(sc, MVSPI_CTRL_REG);
    274 	ctl |= MVSPI_CSNACT_MASK;
    275 	PUTREG(sc, MVSPI_CTRL_REG, ctl);
    276 }
    277 
    278 void
    279 mvspi_deassert(struct mvspi_softc *sc)
    280 {
    281 	int ctl = GETREG(sc, MVSPI_CTRL_REG);
    282 	ctl &= ~(MVSPI_CSNACT_MASK);
    283 	PUTREG(sc, MVSPI_CTRL_REG, ctl);
    284 }
    285 
    286 void
    287 mvspi_sched(struct mvspi_softc *sc)
    288 {
    289 	struct spi_transfer *st;
    290 	struct spi_chunk *chunk;
    291 	int i, j, ctl;
    292 	uint8_t byte;
    293 	int ready = FALSE;
    294 
    295 	for (;;) {
    296 		if ((st = sc->sc_transfer) == NULL) {
    297 			if ((st = spi_transq_first(&sc->sc_transq)) == NULL) {
    298 				/* No work left to do */
    299 				break;
    300 			}
    301 			spi_transq_dequeue(&sc->sc_transq);
    302 			sc->sc_transfer = st;
    303 		}
    304 
    305 		chunk = st->st_chunks;
    306 
    307 		mvspi_assert(sc);
    308 
    309 		do {
    310 			for (i = chunk->chunk_wresid; i > 0; i--) {
    311 				/* First clear the ready bit */
    312 				ctl = GETREG(sc, MVSPI_CTRL_REG);
    313 				ctl &= ~(MVSPI_CR_SMEMRDY);
    314 				PUTREG(sc, MVSPI_CTRL_REG, ctl);
    315 
    316 				if (chunk->chunk_wptr){
    317 					byte = *chunk->chunk_wptr;
    318 					chunk->chunk_wptr++;
    319 				} else
    320 					byte = MVSPI_DUMMY_BYTE;
    321 
    322 				/* Transmit data */
    323 				PUTREG(sc, MVSPI_DATAOUT_REG, byte);
    324 
    325 				/* Wait with timeout for memory ready */
    326 				for (j = 0; j < MVSPI_WAIT_RDY_MAX_LOOP; j++) {
    327 					if (GETREG(sc, MVSPI_CTRL_REG) &
    328 						MVSPI_CR_SMEMRDY) {
    329 						ready = TRUE;
    330 						break;
    331 					}
    332 
    333 				}
    334 
    335 				if (!ready) {
    336 					mvspi_deassert(sc);
    337 					spi_done(st, EBUSY);
    338 					return;
    339 				}
    340 
    341 				/* Check that the RX data is needed */
    342 				if (chunk->chunk_rptr) {
    343 					*chunk->chunk_rptr =
    344 						GETREG(sc, MVSPI_DATAIN_REG);
    345 					chunk->chunk_rptr++;
    346 
    347 				}
    348 
    349 			}
    350 
    351 			chunk = chunk->chunk_next;
    352 
    353 		} while (chunk != NULL);
    354 
    355 		mvspi_deassert(sc);
    356 
    357 		spi_done(st, 0);
    358 		sc->sc_transfer = NULL;
    359 
    360 
    361 		break;
    362 	}
    363 }
    364