ar71xx_spi.c revision 331722
1/*- 2 * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice unmodified, this list of conditions, and the following 10 * disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 */ 27 28#include <sys/cdefs.h> 29__FBSDID("$FreeBSD: stable/11/sys/mips/atheros/ar71xx_spi.c 331722 2018-03-29 02:50:57Z eadler $"); 30 31#include <sys/param.h> 32#include <sys/systm.h> 33 34#include <sys/bus.h> 35#include <sys/interrupt.h> 36#include <sys/malloc.h> 37#include <sys/kernel.h> 38#include <sys/module.h> 39#include <sys/rman.h> 40 41#include <vm/vm.h> 42#include <vm/pmap.h> 43#include <vm/vm_extern.h> 44 45#include <machine/bus.h> 46#include <machine/cpu.h> 47 48#include <dev/spibus/spi.h> 49#include <dev/spibus/spibusvar.h> 50#include "spibus_if.h" 51 52#include <mips/atheros/ar71xxreg.h> 53 54#undef AR71XX_SPI_DEBUG 55#ifdef AR71XX_SPI_DEBUG 56#define dprintf printf 57#else 58#define dprintf(x, arg...) 59#endif 60 61/* 62 * register space access macros 63 */ 64 65#define SPI_BARRIER_WRITE(sc) bus_barrier((sc)->sc_mem_res, 0, 0, \ 66 BUS_SPACE_BARRIER_WRITE) 67#define SPI_BARRIER_READ(sc) bus_barrier((sc)->sc_mem_res, 0, 0, \ 68 BUS_SPACE_BARRIER_READ) 69#define SPI_BARRIER_RW(sc) bus_barrier((sc)->sc_mem_res, 0, 0, \ 70 BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE) 71 72#define SPI_WRITE(sc, reg, val) do { \ 73 bus_write_4(sc->sc_mem_res, (reg), (val)); \ 74 } while (0) 75 76#define SPI_READ(sc, reg) bus_read_4(sc->sc_mem_res, (reg)) 77 78#define SPI_SET_BITS(sc, reg, bits) \ 79 SPI_WRITE(sc, reg, SPI_READ(sc, (reg)) | (bits)) 80 81#define SPI_CLEAR_BITS(sc, reg, bits) \ 82 SPI_WRITE(sc, reg, SPI_READ(sc, (reg)) & ~(bits)) 83 84struct ar71xx_spi_softc { 85 device_t sc_dev; 86 struct resource *sc_mem_res; 87 uint32_t sc_reg_ctrl; 88}; 89 90static int 91ar71xx_spi_probe(device_t dev) 92{ 93 device_set_desc(dev, "AR71XX SPI"); 94 return (BUS_PROBE_NOWILDCARD); 95} 96 97static int 98ar71xx_spi_attach(device_t dev) 99{ 100 struct ar71xx_spi_softc *sc = device_get_softc(dev); 101 int rid; 102 103 sc->sc_dev = dev; 104 rid = 0; 105 sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 106 RF_ACTIVE); 107 if (!sc->sc_mem_res) { 108 device_printf(dev, "Could not map memory\n"); 109 return (ENXIO); 110 } 111 112 SPI_WRITE(sc, AR71XX_SPI_FS, 1); 113 114 /* Flush out read before reading the control register */ 115 SPI_BARRIER_WRITE(sc); 116 117 sc->sc_reg_ctrl = SPI_READ(sc, AR71XX_SPI_CTRL); 118 119 /* 120 * XXX TODO: document what the SPI control register does. 121 */ 122 SPI_WRITE(sc, AR71XX_SPI_CTRL, 0x43); 123 124 /* 125 * Ensure the config register write has gone out before configuring 126 * the chip select mask. 127 */ 128 SPI_BARRIER_WRITE(sc); 129 SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, SPI_IO_CTRL_CSMASK); 130 131 /* 132 * .. and ensure the write has gone out before continuing. 133 */ 134 SPI_BARRIER_WRITE(sc); 135 136 device_add_child(dev, "spibus", -1); 137 return (bus_generic_attach(dev)); 138} 139 140static void 141ar71xx_spi_chip_activate(struct ar71xx_spi_softc *sc, int cs) 142{ 143 uint32_t ioctrl = SPI_IO_CTRL_CSMASK; 144 /* 145 * Put respective CSx to low 146 */ 147 ioctrl &= ~(SPI_IO_CTRL_CS0 << cs); 148 149 /* 150 * Make sure any other writes have gone out to the 151 * device before changing the chip select line; 152 * then ensure that it has made it out to the device 153 * before continuing. 154 */ 155 SPI_BARRIER_WRITE(sc); 156 SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, ioctrl); 157 SPI_BARRIER_WRITE(sc); 158} 159 160static void 161ar71xx_spi_chip_deactivate(struct ar71xx_spi_softc *sc, int cs) 162{ 163 /* 164 * Put all CSx to high 165 */ 166 SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, SPI_IO_CTRL_CSMASK); 167} 168 169static uint8_t 170ar71xx_spi_txrx(struct ar71xx_spi_softc *sc, int cs, uint8_t data) 171{ 172 int bit; 173 /* CS0 */ 174 uint32_t ioctrl = SPI_IO_CTRL_CSMASK; 175 /* 176 * low-level for selected CS 177 */ 178 ioctrl &= ~(SPI_IO_CTRL_CS0 << cs); 179 180 uint32_t iod, rds; 181 for (bit = 7; bit >=0; bit--) { 182 if (data & (1 << bit)) 183 iod = ioctrl | SPI_IO_CTRL_DO; 184 else 185 iod = ioctrl & ~SPI_IO_CTRL_DO; 186 SPI_BARRIER_WRITE(sc); 187 SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, iod); 188 SPI_BARRIER_WRITE(sc); 189 SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, iod | SPI_IO_CTRL_CLK); 190 } 191 192 /* 193 * Provide falling edge for connected device by clear clock bit. 194 */ 195 SPI_BARRIER_WRITE(sc); 196 SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, iod); 197 SPI_BARRIER_WRITE(sc); 198 rds = SPI_READ(sc, AR71XX_SPI_RDS); 199 200 return (rds & 0xff); 201} 202 203static int 204ar71xx_spi_transfer(device_t dev, device_t child, struct spi_command *cmd) 205{ 206 struct ar71xx_spi_softc *sc; 207 uint32_t cs; 208 uint8_t *buf_in, *buf_out; 209 int i; 210 211 sc = device_get_softc(dev); 212 213 spibus_get_cs(child, &cs); 214 215 cs &= ~SPIBUS_CS_HIGH; 216 217 ar71xx_spi_chip_activate(sc, cs); 218 219 KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz, 220 ("TX/RX command sizes should be equal")); 221 KASSERT(cmd->tx_data_sz == cmd->rx_data_sz, 222 ("TX/RX data sizes should be equal")); 223 224 /* 225 * Transfer command 226 */ 227 buf_out = (uint8_t *)cmd->tx_cmd; 228 buf_in = (uint8_t *)cmd->rx_cmd; 229 for (i = 0; i < cmd->tx_cmd_sz; i++) 230 buf_in[i] = ar71xx_spi_txrx(sc, cs, buf_out[i]); 231 232 /* 233 * Receive/transmit data (depends on command) 234 */ 235 buf_out = (uint8_t *)cmd->tx_data; 236 buf_in = (uint8_t *)cmd->rx_data; 237 for (i = 0; i < cmd->tx_data_sz; i++) 238 buf_in[i] = ar71xx_spi_txrx(sc, cs, buf_out[i]); 239 240 ar71xx_spi_chip_deactivate(sc, cs); 241 242 return (0); 243} 244 245static int 246ar71xx_spi_detach(device_t dev) 247{ 248 struct ar71xx_spi_softc *sc = device_get_softc(dev); 249 250 /* 251 * Ensure any other writes to the device are finished 252 * before we tear down the SPI device. 253 */ 254 SPI_BARRIER_WRITE(sc); 255 256 /* 257 * Restore the control register; ensure it has hit the 258 * hardware before continuing. 259 */ 260 SPI_WRITE(sc, AR71XX_SPI_CTRL, sc->sc_reg_ctrl); 261 SPI_BARRIER_WRITE(sc); 262 263 /* 264 * And now, put the flash back into mapped IO mode and 265 * ensure _that_ has completed before we finish up. 266 */ 267 SPI_WRITE(sc, AR71XX_SPI_FS, 0); 268 SPI_BARRIER_WRITE(sc); 269 270 if (sc->sc_mem_res) 271 bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); 272 273 return (0); 274} 275 276static device_method_t ar71xx_spi_methods[] = { 277 /* Device interface */ 278 DEVMETHOD(device_probe, ar71xx_spi_probe), 279 DEVMETHOD(device_attach, ar71xx_spi_attach), 280 DEVMETHOD(device_detach, ar71xx_spi_detach), 281 282 DEVMETHOD(spibus_transfer, ar71xx_spi_transfer), 283 284 {0, 0} 285}; 286 287static driver_t ar71xx_spi_driver = { 288 "spi", 289 ar71xx_spi_methods, 290 sizeof(struct ar71xx_spi_softc), 291}; 292 293static devclass_t ar71xx_spi_devclass; 294 295DRIVER_MODULE(ar71xx_spi, nexus, ar71xx_spi_driver, ar71xx_spi_devclass, 0, 0); 296