11ed09d9cSrkujawa /*******************************************************************************
21ed09d9cSrkujawa Copyright (C) Marvell International Ltd. and its affiliates
31ed09d9cSrkujawa
41ed09d9cSrkujawa Developed by Semihalf
51ed09d9cSrkujawa
61ed09d9cSrkujawa ********************************************************************************
71ed09d9cSrkujawa Marvell BSD License
81ed09d9cSrkujawa
91ed09d9cSrkujawa If you received this File from Marvell, you may opt to use, redistribute and/or
101ed09d9cSrkujawa modify this File under the following licensing terms.
111ed09d9cSrkujawa Redistribution and use in source and binary forms, with or without modification,
121ed09d9cSrkujawa are permitted provided that the following conditions are met:
131ed09d9cSrkujawa
141ed09d9cSrkujawa * Redistributions of source code must retain the above copyright notice,
151ed09d9cSrkujawa this list of conditions and the following disclaimer.
161ed09d9cSrkujawa
171ed09d9cSrkujawa * Redistributions in binary form must reproduce the above copyright
181ed09d9cSrkujawa notice, this list of conditions and the following disclaimer in the
191ed09d9cSrkujawa documentation and/or other materials provided with the distribution.
201ed09d9cSrkujawa
211ed09d9cSrkujawa * Neither the name of Marvell nor the names of its contributors may be
221ed09d9cSrkujawa used to endorse or promote products derived from this software without
231ed09d9cSrkujawa specific prior written permission.
241ed09d9cSrkujawa
251ed09d9cSrkujawa THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
261ed09d9cSrkujawa ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
271ed09d9cSrkujawa WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
281ed09d9cSrkujawa DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
291ed09d9cSrkujawa ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
301ed09d9cSrkujawa (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
311ed09d9cSrkujawa LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
321ed09d9cSrkujawa ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
331ed09d9cSrkujawa (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
341ed09d9cSrkujawa SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
351ed09d9cSrkujawa
361ed09d9cSrkujawa *******************************************************************************/
371ed09d9cSrkujawa
381ed09d9cSrkujawa /*
391ed09d9cSrkujawa * Transfer mechanism extracted from arspi.c corresponding with the lines
401ed09d9cSrkujawa * 254-262 in this file.
411ed09d9cSrkujawa */
421ed09d9cSrkujawa
431ed09d9cSrkujawa #include <sys/param.h>
441ed09d9cSrkujawa #include <sys/device.h>
451ed09d9cSrkujawa
461ed09d9cSrkujawa #include <dev/spi/spivar.h>
471ed09d9cSrkujawa
481ed09d9cSrkujawa #include <dev/marvell/mvspireg.h>
491ed09d9cSrkujawa #include <dev/marvell/marvellvar.h>
501ed09d9cSrkujawa
511ed09d9cSrkujawa #include "locators.h"
521ed09d9cSrkujawa
531ed09d9cSrkujawa extern uint32_t mvTclk;
541ed09d9cSrkujawa
551ed09d9cSrkujawa struct mvspi_softc {
561ed09d9cSrkujawa struct spi_controller sc_spi;
571ed09d9cSrkujawa void *sc_ih;
581ed09d9cSrkujawa bool sc_interrupts;
591ed09d9cSrkujawa
601ed09d9cSrkujawa struct spi_transfer *sc_transfer;
611ed09d9cSrkujawa struct spi_chunk *sc_wchunk; /* For partial writes */
621ed09d9cSrkujawa struct spi_transq sc_transq;
631ed09d9cSrkujawa bus_space_tag_t sc_st;
641ed09d9cSrkujawa bus_space_handle_t sc_sh;
651ed09d9cSrkujawa bus_size_t sc_size;
661ed09d9cSrkujawa };
671ed09d9cSrkujawa
681ed09d9cSrkujawa int mvspi_match(struct device *, struct cfdata *, void *);
691ed09d9cSrkujawa void mvspi_attach(struct device *, struct device *, void *);
701ed09d9cSrkujawa /* SPI service routines */
711ed09d9cSrkujawa int mvspi_configure(void *, int, int, int);
721ed09d9cSrkujawa int mvspi_transfer(void *, struct spi_transfer *);
731ed09d9cSrkujawa /* Internal support */
741ed09d9cSrkujawa void mvspi_sched(struct mvspi_softc *);
751ed09d9cSrkujawa void mvspi_assert(struct mvspi_softc *sc);
761ed09d9cSrkujawa void mvspi_deassert(struct mvspi_softc *sc);
771ed09d9cSrkujawa
781ed09d9cSrkujawa #define GETREG(sc, x) \
791ed09d9cSrkujawa bus_space_read_4(sc->sc_st, sc->sc_sh, x)
801ed09d9cSrkujawa #define PUTREG(sc, x, v) \
811ed09d9cSrkujawa bus_space_write_4(sc->sc_st, sc->sc_sh, x, v)
821ed09d9cSrkujawa
831ed09d9cSrkujawa /* Attach structure */
841ed09d9cSrkujawa CFATTACH_DECL_NEW(mvspi_mbus, sizeof(struct mvspi_softc),
851ed09d9cSrkujawa mvspi_match, mvspi_attach, NULL, NULL);
861ed09d9cSrkujawa
871ed09d9cSrkujawa int
mvspi_match(struct device * parent,struct cfdata * cf,void * aux)881ed09d9cSrkujawa mvspi_match(struct device *parent, struct cfdata *cf, void *aux)
891ed09d9cSrkujawa {
901ed09d9cSrkujawa struct marvell_attach_args *mva = aux;
911ed09d9cSrkujawa
921ed09d9cSrkujawa if (strcmp(mva->mva_name, cf->cf_name) != 0)
931ed09d9cSrkujawa return 0;
941ed09d9cSrkujawa if (mva->mva_offset == MVA_OFFSET_DEFAULT ||
951ed09d9cSrkujawa mva->mva_irq == MVA_IRQ_DEFAULT)
961ed09d9cSrkujawa return 0;
971ed09d9cSrkujawa
981ed09d9cSrkujawa mva->mva_size = MVSPI_SIZE;
991ed09d9cSrkujawa return 1;
1001ed09d9cSrkujawa }
1011ed09d9cSrkujawa
1021ed09d9cSrkujawa void
mvspi_attach(struct device * parent,struct device * self,void * aux)1031ed09d9cSrkujawa mvspi_attach(struct device *parent, struct device *self, void *aux)
1041ed09d9cSrkujawa {
1051ed09d9cSrkujawa struct mvspi_softc *sc = device_private(self);
1061ed09d9cSrkujawa struct marvell_attach_args *mva = aux;
1071ed09d9cSrkujawa struct spibus_attach_args sba;
1081ed09d9cSrkujawa int ctl;
1091ed09d9cSrkujawa
1101ed09d9cSrkujawa aprint_normal(": Marvell SPI controller\n");
1111ed09d9cSrkujawa
1121ed09d9cSrkujawa /*
1131ed09d9cSrkujawa * Map registers.
1141ed09d9cSrkujawa */
1151ed09d9cSrkujawa sc->sc_st = mva->mva_iot;
1161ed09d9cSrkujawa sc->sc_size = mva->mva_size;
1171ed09d9cSrkujawa
1181ed09d9cSrkujawa if (bus_space_subregion(sc->sc_st, mva->mva_ioh, mva->mva_offset,
1191ed09d9cSrkujawa mva->mva_size, &sc->sc_sh)) {
1201ed09d9cSrkujawa aprint_error_dev(self, "Cannot map registers\n");
1211ed09d9cSrkujawa return;
1221ed09d9cSrkujawa }
1231ed09d9cSrkujawa
1241ed09d9cSrkujawa /*
1251ed09d9cSrkujawa * Initialize hardware.
1261ed09d9cSrkujawa */
1271ed09d9cSrkujawa ctl = GETREG(sc, MVSPI_INTCONF_REG);
1281ed09d9cSrkujawa
1291ed09d9cSrkujawa ctl &= MVSPI_DIRHS_MASK;
1301ed09d9cSrkujawa ctl &= MVSPI_1BYTE_MASK;
1311ed09d9cSrkujawa
1329015c01fSchristos PUTREG(sc, MVSPI_INTCONF_REG, ctl);
1331ed09d9cSrkujawa
1341ed09d9cSrkujawa /*
1351ed09d9cSrkujawa * Initialize SPI controller.
1361ed09d9cSrkujawa */
1371ed09d9cSrkujawa sc->sc_spi.sct_cookie = sc;
1381ed09d9cSrkujawa sc->sc_spi.sct_configure = mvspi_configure;
1391ed09d9cSrkujawa sc->sc_spi.sct_transfer = mvspi_transfer;
1401ed09d9cSrkujawa sc->sc_spi.sct_nslaves = 1;
1411ed09d9cSrkujawa
1421ed09d9cSrkujawa /*
1431ed09d9cSrkujawa * Initialize the queue.
1441ed09d9cSrkujawa */
1451ed09d9cSrkujawa spi_transq_init(&sc->sc_transq);
1461ed09d9cSrkujawa
1471ed09d9cSrkujawa /*
1481ed09d9cSrkujawa * Initialize and attach bus attach.
1491ed09d9cSrkujawa */
1509555f417Stnn memset(&sba, 0, sizeof(sba));
1511ed09d9cSrkujawa sba.sba_controller = &sc->sc_spi;
152c7fb772bSthorpej config_found(self, &sba, spibus_print, CFARGS_NONE);
1531ed09d9cSrkujawa }
1541ed09d9cSrkujawa
1551ed09d9cSrkujawa int
mvspi_configure(void * cookie,int slave,int mode,int speed)1561ed09d9cSrkujawa mvspi_configure(void *cookie, int slave, int mode, int speed)
1571ed09d9cSrkujawa {
1581ed09d9cSrkujawa struct mvspi_softc *sc = cookie;
1591ed09d9cSrkujawa uint32_t ctl = 0, spr, sppr;
1601ed09d9cSrkujawa uint32_t divider;
1611ed09d9cSrkujawa uint32_t best_spr = 0, best_sppr = 0;
1621ed09d9cSrkujawa uint32_t best_sppr0, best_spprhi;
1631ed09d9cSrkujawa uint8_t exact_match = 0;
1641ed09d9cSrkujawa uint32_t min_baud_offset = 0xFFFFFFFF;
1651ed09d9cSrkujawa
1661ed09d9cSrkujawa if (slave < 0 || slave > 7)
1671ed09d9cSrkujawa return EINVAL;
1681ed09d9cSrkujawa
1691ed09d9cSrkujawa switch(mode) {
1701ed09d9cSrkujawa case SPI_MODE_0:
1711ed09d9cSrkujawa ctl &= ~(MVSPI_CPOL_MASK);
1721ed09d9cSrkujawa /* In boards documentation, CPHA is inverted */
1731ed09d9cSrkujawa ctl &= MVSPI_CPHA_MASK;
1741ed09d9cSrkujawa break;
1751ed09d9cSrkujawa case SPI_MODE_1:
1761ed09d9cSrkujawa ctl |= MVSPI_CPOL_MASK;
1771ed09d9cSrkujawa ctl &= MVSPI_CPHA_MASK;
1781ed09d9cSrkujawa break;
1791ed09d9cSrkujawa case SPI_MODE_2:
1801ed09d9cSrkujawa ctl &= ~(MVSPI_CPOL_MASK);
1811ed09d9cSrkujawa ctl |= ~(MVSPI_CPHA_MASK);
1821ed09d9cSrkujawa break;
1831ed09d9cSrkujawa case SPI_MODE_3:
1841ed09d9cSrkujawa ctl |= MVSPI_CPOL_MASK;
1851ed09d9cSrkujawa ctl |= ~(MVSPI_CPHA_MASK);
1861ed09d9cSrkujawa break;
1871ed09d9cSrkujawa default:
1881ed09d9cSrkujawa return EINVAL;
1891ed09d9cSrkujawa }
1901ed09d9cSrkujawa
1911ed09d9cSrkujawa /* Find the best prescale configuration - less or equal:
192*100a3398Sandvar * SPI actual frequency = core_clk / (SPR * (2 ^ SPPR))
1931ed09d9cSrkujawa * Try to find the minimal SPR and SPPR values that offer
1941ed09d9cSrkujawa * the best prescale config.
1951ed09d9cSrkujawa *
1961ed09d9cSrkujawa */
1971ed09d9cSrkujawa for (spr = 1; spr <= MVSPI_SPR_MAXVALUE; spr++) {
1981ed09d9cSrkujawa for (sppr = 0; sppr <= MVSPI_SPPR_MAXVALUE; sppr++) {
1991ed09d9cSrkujawa divider = spr * (1 << sppr);
2001ed09d9cSrkujawa /* Check for higher - irrelevant */
2011ed09d9cSrkujawa if ((mvTclk / divider) > speed)
2021ed09d9cSrkujawa continue;
2031ed09d9cSrkujawa
2041ed09d9cSrkujawa /* Check for exact fit */
2051ed09d9cSrkujawa if ((mvTclk / divider) == speed) {
2061ed09d9cSrkujawa best_spr = spr;
2071ed09d9cSrkujawa best_sppr = sppr;
2081ed09d9cSrkujawa exact_match = 1;
2091ed09d9cSrkujawa break;
2101ed09d9cSrkujawa }
2111ed09d9cSrkujawa
2121ed09d9cSrkujawa /* Check if this is better than the previous one */
2131ed09d9cSrkujawa if ((speed - (mvTclk / divider)) < min_baud_offset) {
2141ed09d9cSrkujawa min_baud_offset = (speed - (mvTclk / divider));
2151ed09d9cSrkujawa best_spr = spr;
2161ed09d9cSrkujawa best_sppr = sppr;
2171ed09d9cSrkujawa }
2181ed09d9cSrkujawa }
2191ed09d9cSrkujawa
2201ed09d9cSrkujawa if (exact_match == 1)
2211ed09d9cSrkujawa break;
2221ed09d9cSrkujawa }
2231ed09d9cSrkujawa
2241ed09d9cSrkujawa if (best_spr == 0) {
2251ed09d9cSrkujawa printf("%s ERROR: SPI baud rate prescale error!\n", __func__);
2261ed09d9cSrkujawa return -1;
2271ed09d9cSrkujawa }
2281ed09d9cSrkujawa
2291ed09d9cSrkujawa ctl &= ~(MVSPI_SPR_MASK);
2301ed09d9cSrkujawa ctl &= ~(MVSPI_SPPR_MASK);
2311ed09d9cSrkujawa ctl |= best_spr;
2321ed09d9cSrkujawa
2331ed09d9cSrkujawa best_spprhi = best_sppr & MVSPI_SPPRHI_MASK;
2341ed09d9cSrkujawa best_spprhi = best_spprhi << 5;
2351ed09d9cSrkujawa
2361ed09d9cSrkujawa ctl |= best_spprhi;
2371ed09d9cSrkujawa
2381ed09d9cSrkujawa best_sppr0 = best_sppr & MVSPI_SPPR0_MASK;
2391ed09d9cSrkujawa best_sppr0 = best_sppr0 << 4;
2401ed09d9cSrkujawa
2411ed09d9cSrkujawa ctl |= best_sppr0;
2421ed09d9cSrkujawa
2431ed09d9cSrkujawa PUTREG(sc, MVSPI_INTCONF_REG, ctl);
2441ed09d9cSrkujawa
2451ed09d9cSrkujawa return 0;
2461ed09d9cSrkujawa }
2471ed09d9cSrkujawa
2481ed09d9cSrkujawa int
mvspi_transfer(void * cookie,struct spi_transfer * st)2491ed09d9cSrkujawa mvspi_transfer(void *cookie, struct spi_transfer *st)
2501ed09d9cSrkujawa {
2511ed09d9cSrkujawa struct mvspi_softc *sc = cookie;
252706bb852Skhorben int s;
2531ed09d9cSrkujawa
2541ed09d9cSrkujawa s = splbio();
2551ed09d9cSrkujawa spi_transq_enqueue(&sc->sc_transq, st);
2561ed09d9cSrkujawa if (sc->sc_transfer == NULL) {
2571ed09d9cSrkujawa mvspi_sched(sc);
2581ed09d9cSrkujawa }
2591ed09d9cSrkujawa splx(s);
2601ed09d9cSrkujawa return 0;
2611ed09d9cSrkujawa }
2621ed09d9cSrkujawa
2631ed09d9cSrkujawa void
mvspi_assert(struct mvspi_softc * sc)2641ed09d9cSrkujawa mvspi_assert(struct mvspi_softc *sc)
2651ed09d9cSrkujawa {
2661ed09d9cSrkujawa int ctl;
2671ed09d9cSrkujawa
2685d545ea0Schristos if (sc->sc_transfer->st_slave < 0 || sc->sc_transfer->st_slave > 7) {
2691ed09d9cSrkujawa printf("%s ERROR: Slave number %d not valid!\n", __func__, sc->sc_transfer->st_slave);
2701ed09d9cSrkujawa return;
2711ed09d9cSrkujawa } else
2721ed09d9cSrkujawa /* Enable appropriate CSn according to its slave number */
2731ed09d9cSrkujawa PUTREG(sc, MVSPI_CTRL_REG, (sc->sc_transfer->st_slave << 2));
2741ed09d9cSrkujawa
2751ed09d9cSrkujawa /* Enable CSnAct */
2761ed09d9cSrkujawa ctl = GETREG(sc, MVSPI_CTRL_REG);
2771ed09d9cSrkujawa ctl |= MVSPI_CSNACT_MASK;
2781ed09d9cSrkujawa PUTREG(sc, MVSPI_CTRL_REG, ctl);
2791ed09d9cSrkujawa }
2801ed09d9cSrkujawa
2811ed09d9cSrkujawa void
mvspi_deassert(struct mvspi_softc * sc)2821ed09d9cSrkujawa mvspi_deassert(struct mvspi_softc *sc)
2831ed09d9cSrkujawa {
2841ed09d9cSrkujawa int ctl = GETREG(sc, MVSPI_CTRL_REG);
2851ed09d9cSrkujawa ctl &= ~(MVSPI_CSNACT_MASK);
2861ed09d9cSrkujawa PUTREG(sc, MVSPI_CTRL_REG, ctl);
2871ed09d9cSrkujawa }
2881ed09d9cSrkujawa
2891ed09d9cSrkujawa void
mvspi_sched(struct mvspi_softc * sc)2901ed09d9cSrkujawa mvspi_sched(struct mvspi_softc *sc)
2911ed09d9cSrkujawa {
2921ed09d9cSrkujawa struct spi_transfer *st;
2931ed09d9cSrkujawa struct spi_chunk *chunk;
2941ed09d9cSrkujawa int i, j, ctl;
2951ed09d9cSrkujawa uint8_t byte;
2961ed09d9cSrkujawa int ready = FALSE;
2971ed09d9cSrkujawa
2981ed09d9cSrkujawa for (;;) {
2991ed09d9cSrkujawa if ((st = sc->sc_transfer) == NULL) {
3001ed09d9cSrkujawa if ((st = spi_transq_first(&sc->sc_transq)) == NULL) {
3011ed09d9cSrkujawa /* No work left to do */
3021ed09d9cSrkujawa break;
3031ed09d9cSrkujawa }
3041ed09d9cSrkujawa spi_transq_dequeue(&sc->sc_transq);
3051ed09d9cSrkujawa sc->sc_transfer = st;
3061ed09d9cSrkujawa }
3071ed09d9cSrkujawa
3081ed09d9cSrkujawa chunk = st->st_chunks;
3091ed09d9cSrkujawa
3101ed09d9cSrkujawa mvspi_assert(sc);
3111ed09d9cSrkujawa
3121ed09d9cSrkujawa do {
3131ed09d9cSrkujawa for (i = chunk->chunk_wresid; i > 0; i--) {
3141ed09d9cSrkujawa /* First clear the ready bit */
3151ed09d9cSrkujawa ctl = GETREG(sc, MVSPI_CTRL_REG);
3161ed09d9cSrkujawa ctl &= ~(MVSPI_CR_SMEMRDY);
3171ed09d9cSrkujawa PUTREG(sc, MVSPI_CTRL_REG, ctl);
3181ed09d9cSrkujawa
3191ed09d9cSrkujawa if (chunk->chunk_wptr){
3201ed09d9cSrkujawa byte = *chunk->chunk_wptr;
3211ed09d9cSrkujawa chunk->chunk_wptr++;
3221ed09d9cSrkujawa } else
3231ed09d9cSrkujawa byte = MVSPI_DUMMY_BYTE;
3241ed09d9cSrkujawa
3251ed09d9cSrkujawa /* Transmit data */
3261ed09d9cSrkujawa PUTREG(sc, MVSPI_DATAOUT_REG, byte);
3271ed09d9cSrkujawa
3281ed09d9cSrkujawa /* Wait with timeout for memory ready */
3291ed09d9cSrkujawa for (j = 0; j < MVSPI_WAIT_RDY_MAX_LOOP; j++) {
3301ed09d9cSrkujawa if (GETREG(sc, MVSPI_CTRL_REG) &
3311ed09d9cSrkujawa MVSPI_CR_SMEMRDY) {
3321ed09d9cSrkujawa ready = TRUE;
3331ed09d9cSrkujawa break;
3341ed09d9cSrkujawa }
3351ed09d9cSrkujawa
3361ed09d9cSrkujawa }
3371ed09d9cSrkujawa
3381ed09d9cSrkujawa if (!ready) {
3391ed09d9cSrkujawa mvspi_deassert(sc);
3401ed09d9cSrkujawa spi_done(st, EBUSY);
3411ed09d9cSrkujawa return;
3421ed09d9cSrkujawa }
3431ed09d9cSrkujawa
3441ed09d9cSrkujawa /* Check that the RX data is needed */
3451ed09d9cSrkujawa if (chunk->chunk_rptr) {
3461ed09d9cSrkujawa *chunk->chunk_rptr =
3471ed09d9cSrkujawa GETREG(sc, MVSPI_DATAIN_REG);
3481ed09d9cSrkujawa chunk->chunk_rptr++;
3491ed09d9cSrkujawa
3501ed09d9cSrkujawa }
3511ed09d9cSrkujawa
3521ed09d9cSrkujawa }
3531ed09d9cSrkujawa
3541ed09d9cSrkujawa chunk = chunk->chunk_next;
3551ed09d9cSrkujawa
3561ed09d9cSrkujawa } while (chunk != NULL);
3571ed09d9cSrkujawa
3581ed09d9cSrkujawa mvspi_deassert(sc);
3591ed09d9cSrkujawa
3601ed09d9cSrkujawa spi_done(st, 0);
3611ed09d9cSrkujawa sc->sc_transfer = NULL;
3621ed09d9cSrkujawa
3631ed09d9cSrkujawa
3641ed09d9cSrkujawa break;
3651ed09d9cSrkujawa }
3661ed09d9cSrkujawa }
367