rt305x_spi.c revision 310158
1/*- 2 * Copyright (c) 2009, Oleksandr Tymoshenko <gonzo@FreeBSD.org> 3 * Copyright (c) 2011, Aleksandr Rybalko <ray@FreeBSD.org> 4 * Copyright (c) 2013, Alexander A. Mityaev <sansan@adm.ua> 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/rt305x/rt305x_spi.c 310158 2016-12-16 15:45:09Z manu $"); 32 33#include <sys/param.h> 34#include <sys/systm.h> 35#include <sys/bus.h> 36 37#include <sys/kernel.h> 38#include <sys/module.h> 39#include <sys/rman.h> 40#include <sys/lock.h> 41#include <sys/mutex.h> 42 43#include <machine/bus.h> 44#include <machine/cpu.h> 45//#include <machine/pmap.h> 46 47#include <dev/spibus/spi.h> 48#include <dev/spibus/spibusvar.h> 49#include "spibus_if.h" 50 51#include "opt_platform.h" 52 53#ifdef FDT 54#include <dev/ofw/openfirm.h> 55#include <dev/ofw/ofw_bus.h> 56#include <dev/ofw/ofw_bus_subr.h> 57#endif 58 59#include <mips/rt305x/rt305xreg.h> 60#include <dev/flash/mx25lreg.h> 61 62#undef RT305X_SPI_DEBUG 63#ifdef RT305X_SPI_DEBUG 64#define dprintf printf 65#else 66#define dprintf(x, arg...) 67#endif 68 69/* 70 * register space access macros 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 rt305x_spi_softc { 85 device_t sc_dev; 86 struct resource *sc_mem_res; 87}; 88 89static int rt305x_spi_probe(device_t); 90static int rt305x_spi_attach(device_t); 91static int rt305x_spi_detach(device_t); 92static int rt305x_spi_wait(struct rt305x_spi_softc *); 93static void rt305x_spi_chip_activate(struct rt305x_spi_softc *); 94static void rt305x_spi_chip_deactivate(struct rt305x_spi_softc *); 95static uint8_t rt305x_spi_txrx(struct rt305x_spi_softc *, uint8_t *, int); 96static int rt305x_spi_transfer(device_t, device_t, struct spi_command *); 97#ifdef FDT 98static phandle_t rt305x_spi_get_node(device_t, device_t); 99#endif 100 101static int 102rt305x_spi_probe(device_t dev) 103{ 104#ifdef FDT 105 if (!ofw_bus_is_compatible(dev, "ralink,rt305x-spi")) 106 return(ENXIO); 107#endif 108 device_set_desc(dev, "RT305X SPI"); 109 return (0); 110} 111 112static int 113rt305x_spi_attach(device_t dev) 114{ 115 struct rt305x_spi_softc *sc = device_get_softc(dev); 116 int rid; 117 118 sc->sc_dev = dev; 119 rid = 0; 120 sc->sc_mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 121 RF_ACTIVE); 122 if (!sc->sc_mem_res) { 123 device_printf(dev, "Could not map memory\n"); 124 return (ENXIO); 125 } 126 127 if (rt305x_spi_wait(sc)) { 128 bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); 129 return (EBUSY); 130 } 131 132 SPI_WRITE(sc, RT305X_SPICFG, MSBFIRST | SPICLKPOL | TX_ON_CLK_FALL | 133 SPI_CLK_DIV8); /* XXX: make it configurable */ 134 /* 135 * W25Q64CV max 104MHz, bus 120-192 MHz, so divide by 2. 136 * Update: divide by 4, DEV2 to fast for flash. 137 */ 138 139 device_add_child(dev, "spibus", 0); 140 return (bus_generic_attach(dev)); 141} 142 143static int 144rt305x_spi_detach(device_t dev) 145{ 146 struct rt305x_spi_softc *sc = device_get_softc(dev); 147 148 SPI_SET_BITS(sc, RT305X_SPICTL, HIZSMOSI | CS_HIGH); 149 150 if (sc->sc_mem_res) 151 bus_release_resource(dev, SYS_RES_MEMORY, 0, sc->sc_mem_res); 152 153 return (0); 154} 155 156static void 157rt305x_spi_chip_activate(struct rt305x_spi_softc *sc) 158{ 159// printf("%s\n", __func__); 160 rt305x_spi_wait(sc); 161 /* 162 * Put all CSx to low 163 */ 164 SPI_CLEAR_BITS(sc, RT305X_SPICTL, CS_HIGH | HIZSMOSI); 165} 166 167static void 168rt305x_spi_chip_deactivate(struct rt305x_spi_softc *sc) 169{ 170// printf("%s\n", __func__); 171 rt305x_spi_wait(sc); 172 /* 173 * Put all CSx to high 174 */ 175 SPI_SET_BITS(sc, RT305X_SPICTL, CS_HIGH | HIZSMOSI); 176} 177 178static int 179rt305x_spi_wait(struct rt305x_spi_softc *sc) 180{ 181 int i = 1000; 182 183 while (i--) { 184 if (!SPI_READ(sc, RT305X_SPIBUSY)) 185 break; 186 } 187 if (i == 0) { 188 printf("busy\n"); 189 return (1); 190 } 191 192 return (0); 193} 194 195static uint8_t 196rt305x_spi_txrx(struct rt305x_spi_softc *sc, uint8_t *data, int write) 197{ 198 199 if (rt305x_spi_wait(sc)) 200 return (EBUSY); 201 202 if (write == RT305X_SPI_WRITE) { 203 SPI_WRITE(sc, RT305X_SPIDATA, *data); 204 SPI_SET_BITS(sc, RT305X_SPICTL, START_WRITE); 205 //printf("%s(W:%d)\n", __func__, *data); 206 } else {/* RT305X_SPI_READ */ 207 SPI_SET_BITS(sc, RT305X_SPICTL, START_READ); 208 if (rt305x_spi_wait(sc)) 209 return (EBUSY); 210 211 *data = SPI_READ(sc, RT305X_SPIDATA) & 0xff; 212 //printf("%s(R:%d)\n", __func__, *data); 213 } 214 return (0); 215} 216 217static int 218rt305x_spi_transfer(device_t dev, device_t child, struct spi_command *cmd) 219{ 220 struct rt305x_spi_softc *sc; 221 uint32_t cs; 222 uint8_t *buf, byte, *tx_buf; 223 int i, sz, error = 0, write = 0; 224 225 sc = device_get_softc(dev); 226 227 spibus_get_cs(child, &cs); 228 229 if (cs != 0) 230 /* Only 1 CS */ 231 return (ENXIO); 232 233 /* There is always a command to transfer. */ 234 tx_buf = (uint8_t *)(cmd->tx_cmd); 235 236 /* Perform some fixup because RT305X dont support duplex SPI */ 237 switch(tx_buf[0]) { 238 case CMD_READ_IDENT: 239 cmd->tx_cmd_sz = 1; 240 cmd->rx_cmd_sz = 3; 241 break; 242 case CMD_WRITE_ENABLE: 243 case CMD_WRITE_DISABLE: 244 cmd->tx_cmd_sz = 1; 245 cmd->rx_cmd_sz = 0; 246 break; 247 case CMD_READ_STATUS: 248 cmd->tx_cmd_sz = 1; 249 cmd->rx_cmd_sz = 1; 250 break; 251 case CMD_READ: 252 cmd->tx_cmd_sz = 4; 253 case CMD_FAST_READ: 254 cmd->tx_cmd_sz = 5; 255 cmd->rx_cmd_sz = cmd->tx_data_sz = 0; 256 break; 257 case CMD_SECTOR_ERASE: 258 cmd->tx_cmd_sz = 4; 259 cmd->rx_cmd_sz = cmd->tx_data_sz = 0; 260 break; 261 case CMD_PAGE_PROGRAM: 262 cmd->tx_cmd_sz = 4; 263 cmd->rx_cmd_sz = cmd->rx_data_sz = 0; 264 break; 265 } 266 267 rt305x_spi_chip_activate(sc); 268 269 if (cmd->tx_cmd_sz + cmd->rx_cmd_sz) { 270 buf = (uint8_t *)(cmd->rx_cmd); 271 tx_buf = (uint8_t *)(cmd->tx_cmd); 272 sz = cmd->tx_cmd_sz + cmd->rx_cmd_sz; 273 274 for (i = 0; i < sz; i++) { 275 if(i < cmd->tx_cmd_sz) { 276 byte = tx_buf[i]; 277 error = rt305x_spi_txrx(sc, &byte, 278 RT305X_SPI_WRITE); 279 if (error) 280 goto rt305x_spi_transfer_fail; 281 continue; 282 } 283 error = rt305x_spi_txrx(sc, &byte, 284 RT305X_SPI_READ); 285 if (error) 286 goto rt305x_spi_transfer_fail; 287 buf[i] = byte; 288 } 289 } 290 291 /* 292 * Transfer/Receive data 293 */ 294 295 if (cmd->tx_data_sz + cmd->rx_data_sz) { 296 write = (cmd->tx_data_sz > 0)?1:0; 297 buf = (uint8_t *)(write ? cmd->tx_data : cmd->rx_data); 298 sz = write ? cmd->tx_data_sz : cmd->rx_data_sz; 299 300 for (i = 0; i < sz; i++) { 301 byte = buf[i]; 302 error = rt305x_spi_txrx(sc, &byte, 303 write?RT305X_SPI_WRITE:RT305X_SPI_READ); 304 if (error) 305 goto rt305x_spi_transfer_fail; 306 buf[i] = byte; 307 } 308 } 309rt305x_spi_transfer_fail: 310 rt305x_spi_chip_deactivate(sc); 311 312 return (error); 313} 314 315#ifdef FDT 316static phandle_t 317rt305x_spi_get_node(device_t bus, device_t dev) 318{ 319 320 /* We only have one child, the SPI bus, which needs our own node. */ 321 return (ofw_bus_get_node(bus)); 322} 323#endif 324 325static device_method_t rt305x_spi_methods[] = { 326 /* Device interface */ 327 DEVMETHOD(device_probe, rt305x_spi_probe), 328 DEVMETHOD(device_attach, rt305x_spi_attach), 329 DEVMETHOD(device_detach, rt305x_spi_detach), 330 331 DEVMETHOD(spibus_transfer, rt305x_spi_transfer), 332 333#ifdef FDT 334 /* ofw_bus interface */ 335 DEVMETHOD(ofw_bus_get_node, rt305x_spi_get_node), 336#endif 337 338 DEVMETHOD_END 339}; 340 341static driver_t rt305x_spi_driver = { 342 .name = "spi", 343 .methods = rt305x_spi_methods, 344 .size = sizeof(struct rt305x_spi_softc), 345}; 346 347static devclass_t rt305x_spi_devclass; 348 349DRIVER_MODULE(rt305x_spi, obio, rt305x_spi_driver, rt305x_spi_devclass, 0, 0); 350#ifdef FDT 351DRIVER_MODULE(rt305x_spi, simplebus, rt305x_spi_driver, rt305x_spi_devclass, 0, 0); 352#endif 353