Module Name: src Committed By: rkujawa Date: Wed May 1 12:17:17 UTC 2013
Added Files: src/sys/dev/marvell: mvspi.c mvspireg.h Log Message: Add support for Marvell's SPI controller as found on Armada XP. Obtained from Semihalf. To generate a diff of this commit: cvs rdiff -u -r0 -r1.1 src/sys/dev/marvell/mvspi.c \ src/sys/dev/marvell/mvspireg.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Added files: Index: src/sys/dev/marvell/mvspi.c diff -u /dev/null src/sys/dev/marvell/mvspi.c:1.1 --- /dev/null Wed May 1 12:17:17 2013 +++ src/sys/dev/marvell/mvspi.c Wed May 1 12:17:17 2013 @@ -0,0 +1,368 @@ +/******************************************************************************* +Copyright (C) Marvell International Ltd. and its affiliates + +Developed by Semihalf + +******************************************************************************** +Marvell BSD License + +If you received this File from Marvell, you may opt to use, redistribute and/or +modify this File under the following licensing terms. +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of Marvell nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*******************************************************************************/ + +/* + * Transfer mechanism extracted from arspi.c corresponding with the lines + * 254-262 in this file. + */ + +#include <sys/param.h> +#include <sys/device.h> + +#include <dev/spi/spivar.h> + +#include <dev/marvell/mvspireg.h> +#include <dev/marvell/marvellvar.h> + +#include "locators.h" + +extern uint32_t mvTclk; + +struct mvspi_softc { + struct device sc_dev; + struct spi_controller sc_spi; + void *sc_ih; + bool sc_interrupts; + + struct spi_transfer *sc_transfer; + struct spi_chunk *sc_wchunk; /* For partial writes */ + struct spi_transq sc_transq; + bus_space_tag_t sc_st; + bus_space_handle_t sc_sh; + bus_size_t sc_size; +}; + +int mvspi_match(struct device *, struct cfdata *, void *); +void mvspi_attach(struct device *, struct device *, void *); +/* SPI service routines */ +int mvspi_configure(void *, int, int, int); +int mvspi_transfer(void *, struct spi_transfer *); +/* Internal support */ +void mvspi_sched(struct mvspi_softc *); +void mvspi_assert(struct mvspi_softc *sc); +void mvspi_deassert(struct mvspi_softc *sc); + +#define GETREG(sc, x) \ + bus_space_read_4(sc->sc_st, sc->sc_sh, x) +#define PUTREG(sc, x, v) \ + bus_space_write_4(sc->sc_st, sc->sc_sh, x, v) + +/* Attach structure */ +CFATTACH_DECL_NEW(mvspi_mbus, sizeof(struct mvspi_softc), + mvspi_match, mvspi_attach, NULL, NULL); + +int +mvspi_match(struct device *parent, struct cfdata *cf, void *aux) +{ + struct marvell_attach_args *mva = aux; + + if (strcmp(mva->mva_name, cf->cf_name) != 0) + return 0; + if (mva->mva_offset == MVA_OFFSET_DEFAULT || + mva->mva_irq == MVA_IRQ_DEFAULT) + return 0; + + mva->mva_size = MVSPI_SIZE; + return 1; +} + +void +mvspi_attach(struct device *parent, struct device *self, void *aux) +{ + struct mvspi_softc *sc = device_private(self); + struct marvell_attach_args *mva = aux; + struct spibus_attach_args sba; + int ctl; + + aprint_normal(": Marvell SPI controller\n"); + + /* + * Map registers. + */ + sc->sc_st = mva->mva_iot; + sc->sc_size = mva->mva_size; + + if (bus_space_subregion(sc->sc_st, mva->mva_ioh, mva->mva_offset, + mva->mva_size, &sc->sc_sh)) { + aprint_error_dev(self, "Cannot map registers\n"); + return; + } + + /* + * Initialize hardware. + */ + ctl = GETREG(sc, MVSPI_INTCONF_REG); + + ctl &= MVSPI_DIRHS_MASK; + ctl &= MVSPI_1BYTE_MASK; + + PUTREG(sc, MVSPI_INTCONF_REG, ctl), + + /* + * Initialize SPI controller. + */ + sc->sc_spi.sct_cookie = sc; + sc->sc_spi.sct_configure = mvspi_configure; + sc->sc_spi.sct_transfer = mvspi_transfer; + sc->sc_spi.sct_nslaves = 1; + + /* + * Initialize the queue. + */ + spi_transq_init(&sc->sc_transq); + + /* + * Initialize and attach bus attach. + */ + sba.sba_controller = &sc->sc_spi; + (void) config_found_ia(self, "spibus", &sba, spibus_print); +} + +int +mvspi_configure(void *cookie, int slave, int mode, int speed) +{ + struct mvspi_softc *sc = cookie; + uint32_t ctl = 0, spr, sppr; + uint32_t divider; + uint32_t best_spr = 0, best_sppr = 0; + uint32_t best_sppr0, best_spprhi; + uint8_t exact_match = 0; + uint32_t min_baud_offset = 0xFFFFFFFF; + + if (slave < 0 || slave > 7) + return EINVAL; + + switch(mode) { + case SPI_MODE_0: + ctl &= ~(MVSPI_CPOL_MASK); + /* In boards documentation, CPHA is inverted */ + ctl &= MVSPI_CPHA_MASK; + break; + case SPI_MODE_1: + ctl |= MVSPI_CPOL_MASK; + ctl &= MVSPI_CPHA_MASK; + break; + case SPI_MODE_2: + ctl &= ~(MVSPI_CPOL_MASK); + ctl |= ~(MVSPI_CPHA_MASK); + break; + case SPI_MODE_3: + ctl |= MVSPI_CPOL_MASK; + ctl |= ~(MVSPI_CPHA_MASK); + break; + default: + return EINVAL; + } + + /* Find the best prescale configuration - less or equal: + * SPI actual frecuency = core_clk / (SPR * (2 ^ SPPR)) + * Try to find the minimal SPR and SPPR values that offer + * the best prescale config. + * + */ + for (spr = 1; spr <= MVSPI_SPR_MAXVALUE; spr++) { + for (sppr = 0; sppr <= MVSPI_SPPR_MAXVALUE; sppr++) { + divider = spr * (1 << sppr); + /* Check for higher - irrelevant */ + if ((mvTclk / divider) > speed) + continue; + + /* Check for exact fit */ + if ((mvTclk / divider) == speed) { + best_spr = spr; + best_sppr = sppr; + exact_match = 1; + break; + } + + /* Check if this is better than the previous one */ + if ((speed - (mvTclk / divider)) < min_baud_offset) { + min_baud_offset = (speed - (mvTclk / divider)); + best_spr = spr; + best_sppr = sppr; + } + } + + if (exact_match == 1) + break; + } + + if (best_spr == 0) { + printf("%s ERROR: SPI baud rate prescale error!\n", __func__); + return -1; + } + + ctl &= ~(MVSPI_SPR_MASK); + ctl &= ~(MVSPI_SPPR_MASK); + ctl |= best_spr; + + best_spprhi = best_sppr & MVSPI_SPPRHI_MASK; + best_spprhi = best_spprhi << 5; + + ctl |= best_spprhi; + + best_sppr0 = best_sppr & MVSPI_SPPR0_MASK; + best_sppr0 = best_sppr0 << 4; + + ctl |= best_sppr0; + + PUTREG(sc, MVSPI_INTCONF_REG, ctl); + + return 0; +} + +int +mvspi_transfer(void *cookie, struct spi_transfer *st) +{ + struct mvspi_softc *sc = cookie; + int s, er; + + er = 0; + + s = splbio(); + spi_transq_enqueue(&sc->sc_transq, st); + if (sc->sc_transfer == NULL) { + mvspi_sched(sc); + } + splx(s); + return 0; +} + +void +mvspi_assert(struct mvspi_softc *sc) +{ + int ctl; + + if (sc->sc_transfer->st_slave < 0 && sc->sc_transfer->st_slave > 7) { + printf("%s ERROR: Slave number %d not valid!\n", __func__, sc->sc_transfer->st_slave); + return; + } else + /* Enable appropriate CSn according to its slave number */ + PUTREG(sc, MVSPI_CTRL_REG, (sc->sc_transfer->st_slave << 2)); + + /* Enable CSnAct */ + ctl = GETREG(sc, MVSPI_CTRL_REG); + ctl |= MVSPI_CSNACT_MASK; + PUTREG(sc, MVSPI_CTRL_REG, ctl); +} + +void +mvspi_deassert(struct mvspi_softc *sc) +{ + int ctl = GETREG(sc, MVSPI_CTRL_REG); + ctl &= ~(MVSPI_CSNACT_MASK); + PUTREG(sc, MVSPI_CTRL_REG, ctl); +} + +void +mvspi_sched(struct mvspi_softc *sc) +{ + struct spi_transfer *st; + struct spi_chunk *chunk; + int i, j, ctl; + uint8_t byte; + int ready = FALSE; + + for (;;) { + if ((st = sc->sc_transfer) == NULL) { + if ((st = spi_transq_first(&sc->sc_transq)) == NULL) { + /* No work left to do */ + break; + } + spi_transq_dequeue(&sc->sc_transq); + sc->sc_transfer = st; + } + + chunk = st->st_chunks; + + mvspi_assert(sc); + + do { + for (i = chunk->chunk_wresid; i > 0; i--) { + /* First clear the ready bit */ + ctl = GETREG(sc, MVSPI_CTRL_REG); + ctl &= ~(MVSPI_CR_SMEMRDY); + PUTREG(sc, MVSPI_CTRL_REG, ctl); + + if (chunk->chunk_wptr){ + byte = *chunk->chunk_wptr; + chunk->chunk_wptr++; + } else + byte = MVSPI_DUMMY_BYTE; + + /* Transmit data */ + PUTREG(sc, MVSPI_DATAOUT_REG, byte); + + /* Wait with timeout for memory ready */ + for (j = 0; j < MVSPI_WAIT_RDY_MAX_LOOP; j++) { + if (GETREG(sc, MVSPI_CTRL_REG) & + MVSPI_CR_SMEMRDY) { + ready = TRUE; + break; + } + + } + + if (!ready) { + mvspi_deassert(sc); + spi_done(st, EBUSY); + return; + } + + /* Check that the RX data is needed */ + if (chunk->chunk_rptr) { + *chunk->chunk_rptr = + GETREG(sc, MVSPI_DATAIN_REG); + chunk->chunk_rptr++; + + } + + } + + chunk = chunk->chunk_next; + + } while (chunk != NULL); + + mvspi_deassert(sc); + + spi_done(st, 0); + sc->sc_transfer = NULL; + + + break; + } +} Index: src/sys/dev/marvell/mvspireg.h diff -u /dev/null src/sys/dev/marvell/mvspireg.h:1.1 --- /dev/null Wed May 1 12:17:17 2013 +++ src/sys/dev/marvell/mvspireg.h Wed May 1 12:17:17 2013 @@ -0,0 +1,80 @@ +/******************************************************************************* +Copyright (C) Marvell International Ltd. and its affiliates + +Developed by Semihalf + +******************************************************************************** +Marvell BSD License + +If you received this File from Marvell, you may opt to use, redistribute and/or +modify this File under the following licensing terms. +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of Marvell nor the names of its contributors may be + used to endorse or promote products derived from this software without + specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +*******************************************************************************/ + +#include "opt_mvsoc.h" + +#ifndef _MVSPIREG_H_ +#define _MVSPIREG_H_ + +#define MVSPI_SIZE 0x50 /* Size of MVSPI */ + +/* Definition of registers */ +#define MVSPI_CTRL_REG 0x00 /* MVSPI Control Register */ +#define MVSPI_INTCONF_REG 0x04 /* MVSPI Interface Configuration Register */ +#define MVSPI_DATAOUT_REG 0x08 /* MVSPI Data Out Register */ +#define MVSPI_DATAIN_REG 0x0C /* MVSPI Data In Register */ +#define MVSPI_IRQCAUSE_REG 0x10 /* MVSPI Interrupt Cause Register */ +#define MVSPI_IRQMASK_REG 0x14 /* MVSPI Interrupt Mask Register */ +#define MVSPI_TIMEPAR1_REG 0x18 /* MVSPI Timing Parameters 1 Register*/ +#define MVSPI_TIMEPAR2_REG 0x1C /* MVSPI Timing Parameters 2 Register */ +#define MVSPI_DIRWRITE_REG 0x20 /* MVSPI Direct Write Configuration Register*/ +#define MVSPI_DIRWRITEHD_REG 0x24 /* MVSPI Direct Write Header Register */ +#define MVSPI_DIRREADHD_REG 0x28 /* MVSPI Direct Read Header Register */ +#define MVSPI_CSADRDEC_REG 0x2C /* MVSPI CS Address Decode Register */ +#define MVSPI_CSnTIMPAR_REG 0x30 /* MVSPI CSn Timing Parameters Register */ +#define MVSPI_CNTVER_REG 0x50 /* MVSPI Controller Version Register */ + +/* Masks */ +#define MVSPI_CPOL_MASK 0x0800 /* CPOL bit = 1 */ +#define MVSPI_CPHA_MASK 0x1000 /* CPHA bit = 1 */ +#define MVSPI_DIRHS_MASK 0xFBFF /* SPI Direct Read High Speed Transaction Mask */ +#define MVSPI_1BYTE_MASK 0xFFDF /* Number of bits in each I/O transfer Mask */ +#define MVSPI_SPR_MASK 0x0007 /* SPR field mask */ +#define MVSPI_SPPR_MASK 0x00D0 /* SPPR field mask */ +#define MVSPI_SPPRHI_MASK 0x00C0 /* SPPR_HI field mask */ +#define MVSPI_SPPR0_MASK 0x0010 /* SPPR0 field mask */ +#define MVSPI_CSNACT_MASK 0x0001 /* CSn transfer acknowledge bit */ + +#define MVSPI_CR_SMEMRDY 0x0002 /* MVSPI Control Register Serial Memory Data Transfer Ready */ + +#define MVSPI_DUMMY_BYTE 0xFF /* Dummy byte */ + +#define MVSPI_WAIT_RDY_MAX_LOOP 100000 /* Transfer timeout threshold */ +#define MVSPI_SPR_MAXVALUE 15 /* Maximum value for SPR coeficient */ +#define MVSPI_SPPR_MAXVALUE 7 /* Maximum value for SPPR coeficient */ + +#endif /* _MVSPIREG_H_ */