Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/sys/dev/marvell Add support for Marvell's SPI controller as ...



details:   https://anonhg.NetBSD.org/src/rev/47506622e82c
branches:  trunk
changeset: 786546:47506622e82c
user:      rkujawa <rkujawa%NetBSD.org@localhost>
date:      Wed May 01 12:17:17 2013 +0000

description:
Add support for Marvell's SPI controller as found on Armada XP.

Obtained from Semihalf.

diffstat:

 sys/dev/marvell/mvspi.c    |  368 +++++++++++++++++++++++++++++++++++++++++++++
 sys/dev/marvell/mvspireg.h |   80 +++++++++
 2 files changed, 448 insertions(+), 0 deletions(-)

diffs (truncated from 456 to 300 lines):

diff -r cf8af19192b7 -r 47506622e82c sys/dev/marvell/mvspi.c
--- /dev/null   Thu Jan 01 00:00:00 1970 +0000
+++ b/sys/dev/marvell/mvspi.c   Wed May 01 12:17:17 2013 +0000
@@ -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;



Home | Main Index | Thread Index | Old Index