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