Index: sys/mips/atheros/ar71xx_spi.c =================================================================== --- sys/mips/atheros/ar71xx_spi.c (revision 234999) +++ sys/mips/atheros/ar71xx_spi.c (working copy) @@ -35,7 +35,9 @@ #include #include #include +#include #include +#include #include #include @@ -51,6 +53,7 @@ #include "spibus_if.h" #include +#include #undef AR71XX_SPI_DEBUG #ifdef AR71XX_SPI_DEBUG @@ -59,6 +62,7 @@ #define dprintf(x, arg...) #endif + /* * register space access macros */ @@ -76,18 +80,125 @@ struct ar71xx_spi_softc { device_t sc_dev; + device_t sc_owner; + struct mtx sc_mtx; struct resource *sc_mem_res; uint32_t sc_reg_ctrl; + struct spi_config sc_config; + int sc_pre_delay; + int sc_post_delay; }; +#define AR71XX_SPI_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define AR71XX_SPI_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define AR71XX_SPI_LOCK_INIT(_sc) \ + mtx_init(&_sc->sc_mtx, device_get_nameunit(_sc->sc_dev), \ + "ar71xx_spi", MTX_DEF) +#define AR71XX_SPI_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); +#define AR71XX_SPI_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); +#define AR71XX_SPI_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); + static int ar71xx_spi_probe(device_t dev) { + device_set_desc(dev, "AR71XX SPI"); return (0); } + +/* + * Configure the hardware clock to achieve the requested frequency without + * going over. If the requested frequency is less than the minimum possible + * hardware frequency, software delays will be used to meet the target. + */ static int +ar71xx_spi_set_clock(struct ar71xx_spi_softc *sc, unsigned int hz) +{ + unsigned int div; + unsigned int actual_divisor; + unsigned int actual_hz; + unsigned int addl_period_us; + uint64_t temp64; + + if (0 == hz) { + /* maximum frequency possible */ + div = 0; + } else { + /* + * Figure the divider value that would achieve the closest + * frequency to that requested without going over. + */ + div = (ar71xx_ahb_freq() - 1) / (2 * hz); + } + + + /* Avoid chip bug at 0 == div */ + if (0 == div) { + div = 1; + } + + if (div > SPI_CTRL_CLOCK_DIVIDER_MAX) { + div = SPI_CTRL_CLOCK_DIVIDER_MAX; + } + + actual_divisor = 2 * (div + 1); + + /* What the hardware will generate, rounded up. */ + actual_hz = (ar71xx_ahb_freq() + actual_divisor - 1) / actual_divisor; + + if (0 == hz) { + hz = actual_hz; + } + + /* + * At requested clock rates below the hardware minimum, figure out + * what delays need to be inserted when feeding bits to the hardware + * in order to stay under the target. + */ + if (actual_hz > hz) { + + /* + * Compute the additional number of microseconds we need in + * the clock period. In the spirit of not exceeding the + * target frequency, round what we need up, round what the + * hardware gives us down. + */ + addl_period_us = (1000000 + hz - 1) / hz + 1000000 / actual_hz; + + sc->sc_pre_delay = addl_period_us / 2; + sc->sc_post_delay = addl_period_us - sc->sc_pre_delay; + + /* + * A high resolution, rounded up inversion of sum of + * hardware and software period components, so one can be + * confident a requested hardware maximum clock rate is not + * being exceeded. + */ + temp64 = 1000000ull * actual_divisor + + (uint64_t)addl_period_us * (uint64_t)ar71xx_ahb_freq(); + + sc->sc_config.clock_hz = + (1000000ull * (uint64_t)ar71xx_ahb_freq() + temp64 - 1) / + temp64; + } else { + sc->sc_pre_delay = 0; + sc->sc_post_delay = 0; + sc->sc_config.clock_hz = ar71xx_ahb_freq() / actual_divisor; + } + +#if 0 + device_printf(sc->sc_dev, "Requested hz=%u reg=0x%02x pre=%u post=%u\n", + hz, div, + sc->sc_pre_delay, sc->sc_post_delay); +#endif + + SPI_WRITE(sc, AR71XX_SPI_CTRL, SPI_CTRL_REMAP_DISABLE | div); + + return (0); +} + +static int ar71xx_spi_attach(device_t dev) { struct ar71xx_spi_softc *sc = device_get_softc(dev); @@ -102,12 +213,20 @@ return (ENXIO); } + AR71XX_SPI_LOCK_INIT(sc); SPI_WRITE(sc, AR71XX_SPI_FS, 1); - sc->sc_reg_ctrl = SPI_READ(sc, AR71XX_SPI_CTRL); - SPI_WRITE(sc, AR71XX_SPI_CTRL, 0x43); - SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, SPI_IO_CTRL_CSMASK); + sc->sc_reg_ctrl = SPI_READ(sc, AR71XX_SPI_CTRL); + + /* + * The following is equivalent to AR71XX_SPI_CTRL = 0x43, which has + * been the historical default. + */ + ar71xx_spi_set_clock(sc, (ar71xx_ahb_freq() - 1) / 6); + SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, SPI_IO_CTRL_CS(0)); + + device_printf(dev, "attaching at spibus0\n"); device_add_child(dev, "spibus", 0); return (bus_generic_attach(dev)); } @@ -115,49 +234,97 @@ static void ar71xx_spi_chip_activate(struct ar71xx_spi_softc *sc, int cs) { - uint32_t ioctrl = SPI_IO_CTRL_CSMASK; + /* - * Put respective CSx to low + * Activate the respective device (we have three CS pins allowing + * 7 muxed devices on bus). + * At any given moment only one device should be selected on bus. + * if cs is set to 0 it will be the same as SPI_IO_CTRL_CSMASK + * (0x7 - all pins high - no device selected). */ - ioctrl &= ~(SPI_IO_CTRL_CS0 << cs); - - SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, ioctrl); + SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, SPI_IO_CTRL_CS(cs)); } static void ar71xx_spi_chip_deactivate(struct ar71xx_spi_softc *sc, int cs) { + /* - * Put all CSx to high + * Deactivate all devices, put all CSx to high */ - SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, SPI_IO_CTRL_CSMASK); + SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, SPI_IO_CTRL_CS(0)); } +static void +ar71xx_spi_acquire_bus(device_t dev, device_t child) +{ + struct spibus_ivar *devi = SPIBUS_IVAR(child); + struct ar71xx_spi_softc *sc; + + sc = device_get_softc(dev); + AR71XX_SPI_ASSERT_UNLOCKED(sc); + + AR71XX_SPI_LOCK(sc); + if (sc->sc_owner) + panic("ar71xx_spi: cannot serialize the access to device."); + + sc->sc_owner = child; + ar71xx_spi_chip_activate(sc, devi->cs); +} + +static void +ar71xx_spi_release_bus(device_t dev, device_t child) +{ + struct spibus_ivar *devi = SPIBUS_IVAR(child); + struct ar71xx_spi_softc *sc; + + sc = device_get_softc(dev); + AR71XX_SPI_ASSERT_LOCKED(sc); + + if (!sc->sc_owner) + panic("ar71xx_spi: releasing unowned bus."); + if (sc->sc_owner != child) + panic("ar71xx_spi: you don't own the bus. game over."); + + ar71xx_spi_chip_deactivate(sc, devi->cs); + sc->sc_owner = NULL; + AR71XX_SPI_UNLOCK(sc); +} + static uint8_t ar71xx_spi_txrx(struct ar71xx_spi_softc *sc, int cs, uint8_t data) { int bit; - /* CS0 */ - uint32_t ioctrl = SPI_IO_CTRL_CSMASK; + uint32_t ioctrl; + + AR71XX_SPI_ASSERT_LOCKED(sc); + /* - * low-level for selected CS + * activate the selected CS */ - ioctrl &= ~(SPI_IO_CTRL_CS0 << cs); + ioctrl = SPI_IO_CTRL_CS(cs); - uint32_t iod, rds; + uint32_t iod = 0, rds; for (bit = 7; bit >=0; bit--) { if (data & (1 << bit)) iod = ioctrl | SPI_IO_CTRL_DO; else iod = ioctrl & ~SPI_IO_CTRL_DO; + SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, iod); + if (sc->sc_pre_delay) { + DELAY(sc->sc_pre_delay); + } SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, iod | SPI_IO_CTRL_CLK); + if (sc->sc_post_delay) { + DELAY(sc->sc_post_delay); + } } /* * Provide falling edge for connected device by clear clock bit. */ - SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, iod); + SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, iod); rds = SPI_READ(sc, AR71XX_SPI_RDS); return (rds & 0xff); @@ -169,12 +336,19 @@ struct ar71xx_spi_softc *sc; uint8_t *buf_in, *buf_out; struct spibus_ivar *devi = SPIBUS_IVAR(child); + int cs = devi->cs; int i; sc = device_get_softc(dev); - ar71xx_spi_chip_activate(sc, devi->cs); + if ((cmd->flags & SPI_SKIP_CHIP_SELECT) == 0) + ar71xx_spi_acquire_bus(dev, child); + if (cmd->flags & SPI_CHIP_SELECT_HIGH) { + ar71xx_spi_chip_deactivate(sc, devi->cs); + cs = 0; + } + KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz, ("TX/RX command sizes should be equal")); KASSERT(cmd->tx_data_sz == cmd->rx_data_sz, @@ -186,7 +360,7 @@ buf_out = (uint8_t *)cmd->tx_cmd; buf_in = (uint8_t *)cmd->rx_cmd; for (i = 0; i < cmd->tx_cmd_sz; i++) - buf_in[i] = ar71xx_spi_txrx(sc, devi->cs, buf_out[i]); + buf_in[i] = ar71xx_spi_txrx(sc, cs, buf_out[i]); /* * Receive/transmit data (depends on command) @@ -194,14 +368,53 @@ buf_out = (uint8_t *)cmd->tx_data; buf_in = (uint8_t *)cmd->rx_data; for (i = 0; i < cmd->tx_data_sz; i++) - buf_in[i] = ar71xx_spi_txrx(sc, devi->cs, buf_out[i]); + buf_in[i] = ar71xx_spi_txrx(sc, cs, buf_out[i]); - ar71xx_spi_chip_deactivate(sc, devi->cs); + if ((cmd->flags & SPI_SKIP_CHIP_SELECT) == 0) { + ar71xx_spi_release_bus(dev, child); + } else if (cmd->flags & SPI_CHIP_SELECT_HIGH) { + ar71xx_spi_chip_activate(sc, devi->cs); + } return (0); } static int +ar71xx_spi_write(device_t dev, uint32_t reg, uint32_t data) +{ + struct ar71xx_spi_softc *sc = device_get_softc(dev); + + AR71XX_SPI_ASSERT_LOCKED(sc); + SPI_WRITE(sc, reg, data); + return (0); +} + +static int +ar71xx_spi_set_config(device_t dev, struct spi_config *cfg) +{ + struct ar71xx_spi_softc *sc = device_get_softc(dev); + + AR71XX_SPI_ASSERT_LOCKED(sc); + if (ar71xx_spi_set_clock(sc, cfg->clock_hz)) { + return (ENXIO); + } + + return (0); +} + +static int +ar71xx_spi_get_config(device_t dev, struct spi_config *cfg) +{ + struct ar71xx_spi_softc *sc = device_get_softc(dev); + + AR71XX_SPI_ASSERT_LOCKED(sc); + + *cfg = sc->sc_config; + + return (0); +} + +static int ar71xx_spi_detach(device_t dev) { struct ar71xx_spi_softc *sc = device_get_softc(dev); @@ -209,6 +422,8 @@ SPI_WRITE(sc, AR71XX_SPI_CTRL, sc->sc_reg_ctrl); SPI_WRITE(sc, AR71XX_SPI_FS, 0); + AR71XX_SPI_LOCK_DESTROY(sc); + if (sc->sc_mem_res) bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); @@ -221,7 +436,12 @@ DEVMETHOD(device_attach, ar71xx_spi_attach), DEVMETHOD(device_detach, ar71xx_spi_detach), + DEVMETHOD(spibus_acquire_bus, ar71xx_spi_acquire_bus), + DEVMETHOD(spibus_release_bus, ar71xx_spi_release_bus), DEVMETHOD(spibus_transfer, ar71xx_spi_transfer), + DEVMETHOD(spibus_write, ar71xx_spi_write), + DEVMETHOD(spibus_get_config, ar71xx_spi_get_config), + DEVMETHOD(spibus_set_config, ar71xx_spi_set_config), {0, 0} }; Index: sys/mips/atheros/ar71xxreg.h =================================================================== --- sys/mips/atheros/ar71xxreg.h (revision 234999) +++ sys/mips/atheros/ar71xxreg.h (working copy) @@ -299,6 +299,13 @@ /* * GigE adapters region */ +#define AR71XX_MII0_CTRL 0x18070000 +#define AR71XX_MII1_CTRL 0x18070004 +#define MII_CTRL_SPEED_SHIFT 4 +#define MII_CTRL_SPEED_MASK 3 +#define MII_CTRL_SPEED_10 0 +#define MII_CTRL_SPEED_100 1 +#define MII_CTRL_SPEED_1000 2 #define AR71XX_MAC0_BASE 0x19000000 #define AR71XX_MAC1_BASE 0x1A000000 @@ -501,11 +508,12 @@ #define AR71XX_SPI_CTRL 0x04 #define SPI_CTRL_REMAP_DISABLE (1 << 6) #define SPI_CTRL_CLOCK_DIVIDER_MASK ((1 << 6) - 1) +#define SPI_CTRL_CLOCK_DIVIDER_MAX SPI_CTRL_CLOCK_DIVIDER_MASK #define AR71XX_SPI_IO_CTRL 0x08 #define SPI_IO_CTRL_CS2 (1 << 18) #define SPI_IO_CTRL_CS1 (1 << 17) #define SPI_IO_CTRL_CS0 (1 << 16) -#define SPI_IO_CTRL_CSMASK (7 << 16) +#define SPI_IO_CTRL_CS(cs) ((7 - cs) << 16) #define SPI_IO_CTRL_CLK (1 << 8) #define SPI_IO_CTRL_DO 1 #define AR71XX_SPI_RDS 0x0C