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$"); 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#include <machine/pmap.h> 48 49#include <dev/spibus/spi.h> 50#include <dev/spibus/spibusvar.h> 51#include "spibus_if.h" 52 53#include <mips/atheros/ar71xxreg.h> 54 55#undef AR71XX_SPI_DEBUG 56#ifdef AR71XX_SPI_DEBUG 57#define dprintf printf 58#else 59#define dprintf(x, arg...) 60#endif 61 62/* 63 * register space access macros 64 */ 65#define SPI_WRITE(sc, reg, val) do { \ 66 bus_write_4(sc->sc_mem_res, (reg), (val)); \ 67 } while (0) 68 69#define SPI_READ(sc, reg) bus_read_4(sc->sc_mem_res, (reg)) 70 71#define SPI_SET_BITS(sc, reg, bits) \ 72 SPI_WRITE(sc, reg, SPI_READ(sc, (reg)) | (bits)) 73 74#define SPI_CLEAR_BITS(sc, reg, bits) \ 75 SPI_WRITE(sc, reg, SPI_READ(sc, (reg)) & ~(bits)) 76 77struct ar71xx_spi_softc { 78 device_t sc_dev; 79 struct resource *sc_mem_res; 80 uint32_t sc_reg_ctrl; 81}; 82 83static int 84ar71xx_spi_probe(device_t dev) 85{ 86 device_set_desc(dev, "AR71XX SPI"); 87 return (0); 88} 89 90static int 91ar71xx_spi_attach(device_t dev) 92{ 93 struct ar71xx_spi_softc *sc = device_get_softc(dev); 94 int rid; 95 96 sc->sc_dev = dev; 97 rid = 0; 98 sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 99 RF_ACTIVE); 100 if (!sc->sc_mem_res) { 101 device_printf(dev, "Could not map memory\n"); 102 return (ENXIO); 103 } 104 105 106 SPI_WRITE(sc, AR71XX_SPI_FS, 1); 107 sc->sc_reg_ctrl = SPI_READ(sc, AR71XX_SPI_CTRL); 108 SPI_WRITE(sc, AR71XX_SPI_CTRL, 0x43); 109 SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, SPI_IO_CTRL_CSMASK); 110 111 device_add_child(dev, "spibus", 0); 112 return (bus_generic_attach(dev)); 113} 114 115static void 116ar71xx_spi_chip_activate(struct ar71xx_spi_softc *sc, int cs) 117{ 118 uint32_t ioctrl = SPI_IO_CTRL_CSMASK; 119 /* 120 * Put respective CSx to low 121 */ 122 ioctrl &= ~(SPI_IO_CTRL_CS0 << cs); 123 124 SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, ioctrl); 125} 126 127static void 128ar71xx_spi_chip_deactivate(struct ar71xx_spi_softc *sc, int cs) 129{ 130 /* 131 * Put all CSx to high 132 */ 133 SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, SPI_IO_CTRL_CSMASK); 134} 135 136static uint8_t 137ar71xx_spi_txrx(struct ar71xx_spi_softc *sc, int cs, uint8_t data) 138{ 139 int bit; 140 /* CS0 */ 141 uint32_t ioctrl = SPI_IO_CTRL_CSMASK; 142 /* 143 * low-level for selected CS 144 */ 145 ioctrl &= ~(SPI_IO_CTRL_CS0 << cs); 146 147 uint32_t iod, rds; 148 for (bit = 7; bit >=0; bit--) { 149 if (data & (1 << bit)) 150 iod = ioctrl | SPI_IO_CTRL_DO; 151 else 152 iod = ioctrl & ~SPI_IO_CTRL_DO; 153 SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, iod); 154 SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, iod | SPI_IO_CTRL_CLK); 155 } 156 157 /* 158 * Provide falling edge for connected device by clear clock bit. 159 */ 160 SPI_WRITE(sc, AR71XX_SPI_IO_CTRL, iod); 161 rds = SPI_READ(sc, AR71XX_SPI_RDS); 162 163 return (rds & 0xff); 164} 165 166static int 167ar71xx_spi_transfer(device_t dev, device_t child, struct spi_command *cmd) 168{ 169 struct ar71xx_spi_softc *sc; 170 uint8_t *buf_in, *buf_out; 171 struct spibus_ivar *devi = SPIBUS_IVAR(child); 172 int i; 173 174 sc = device_get_softc(dev); 175 176 ar71xx_spi_chip_activate(sc, devi->cs); 177 178 KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz, 179 ("TX/RX command sizes should be equal")); 180 KASSERT(cmd->tx_data_sz == cmd->rx_data_sz, 181 ("TX/RX data sizes should be equal")); 182 183 /* 184 * Transfer command 185 */ 186 buf_out = (uint8_t *)cmd->tx_cmd; 187 buf_in = (uint8_t *)cmd->rx_cmd; 188 for (i = 0; i < cmd->tx_cmd_sz; i++) 189 buf_in[i] = ar71xx_spi_txrx(sc, devi->cs, buf_out[i]); 190 191 /* 192 * Receive/transmit data (depends on command) 193 */ 194 buf_out = (uint8_t *)cmd->tx_data; 195 buf_in = (uint8_t *)cmd->rx_data; 196 for (i = 0; i < cmd->tx_data_sz; i++) 197 buf_in[i] = ar71xx_spi_txrx(sc, devi->cs, buf_out[i]); 198 199 ar71xx_spi_chip_deactivate(sc, devi->cs); 200 201 return (0); 202} 203 204static int 205ar71xx_spi_detach(device_t dev) 206{ 207 struct ar71xx_spi_softc *sc = device_get_softc(dev); 208 209 SPI_WRITE(sc, AR71XX_SPI_CTRL, sc->sc_reg_ctrl); 210 SPI_WRITE(sc, AR71XX_SPI_FS, 0); 211 212 if (sc->sc_mem_res) 213 bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); 214 215 return (0); 216} 217 218static device_method_t ar71xx_spi_methods[] = { 219 /* Device interface */ 220 DEVMETHOD(device_probe, ar71xx_spi_probe), 221 DEVMETHOD(device_attach, ar71xx_spi_attach), 222 DEVMETHOD(device_detach, ar71xx_spi_detach), 223 224 DEVMETHOD(spibus_transfer, ar71xx_spi_transfer), 225 226 {0, 0} 227}; 228 229static driver_t ar71xx_spi_driver = { 230 "spi", 231 ar71xx_spi_methods, 232 sizeof(struct ar71xx_spi_softc), 233}; 234 235static devclass_t ar71xx_spi_devclass; 236 237DRIVER_MODULE(ar71xx_spi, nexus, ar71xx_spi_driver, ar71xx_spi_devclass, 0, 0); 238