#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 spi_controller sc_spi;
void *sc_ih;
bool sc_interrupts;
struct spi_transfer *sc_transfer;
struct spi_chunk *sc_wchunk;
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 *);
int mvspi_configure(void *, int, int, int);
int mvspi_transfer(void *, struct spi_transfer *);
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)
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;
int ctl;
aprint_normal(": Marvell SPI controller\n");
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;
}
ctl = GETREG(sc, MVSPI_INTCONF_REG);
ctl &= MVSPI_DIRHS_MASK;
ctl &= MVSPI_1BYTE_MASK;
PUTREG(sc, MVSPI_INTCONF_REG, ctl);
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;
spi_transq_init(&sc->sc_transq);
spibus_attach(self, &sc->sc_spi);
}
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);
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;
}
for (spr = 1; spr <= MVSPI_SPR_MAXVALUE; spr++) {
for (sppr = 0; sppr <= MVSPI_SPPR_MAXVALUE; sppr++) {
divider = spr * (1 << sppr);
if ((mvTclk / divider) > speed)
continue;
if ((mvTclk / divider) == speed) {
best_spr = spr;
best_sppr = sppr;
exact_match = 1;
break;
}
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;
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
PUTREG(sc, MVSPI_CTRL_REG, (sc->sc_transfer->st_slave << 2));
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) {
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--) {
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;
PUTREG(sc, MVSPI_DATAOUT_REG, byte);
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;
}
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;
}
}