rt305x_spi.c revision 331506
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 331506 2018-03-24 23:23:31Z ian $"); 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 cs &= ~SPIBUS_CS_HIGH; 230 231 if (cs != 0) 232 /* Only 1 CS */ 233 return (ENXIO); 234 235 /* There is always a command to transfer. */ 236 tx_buf = (uint8_t *)(cmd->tx_cmd); 237 238 /* Perform some fixup because RT305X dont support duplex SPI */ 239 switch(tx_buf[0]) { 240 case CMD_READ_IDENT: 241 cmd->tx_cmd_sz = 1; 242 cmd->rx_cmd_sz = 3; 243 break; 244 case CMD_WRITE_ENABLE: 245 case CMD_WRITE_DISABLE: 246 cmd->tx_cmd_sz = 1; 247 cmd->rx_cmd_sz = 0; 248 break; 249 case CMD_READ_STATUS: 250 cmd->tx_cmd_sz = 1; 251 cmd->rx_cmd_sz = 1; 252 break; 253 case CMD_READ: 254 cmd->tx_cmd_sz = 4; 255 case CMD_FAST_READ: 256 cmd->tx_cmd_sz = 5; 257 cmd->rx_cmd_sz = cmd->tx_data_sz = 0; 258 break; 259 case CMD_SECTOR_ERASE: 260 cmd->tx_cmd_sz = 4; 261 cmd->rx_cmd_sz = cmd->tx_data_sz = 0; 262 break; 263 case CMD_PAGE_PROGRAM: 264 cmd->tx_cmd_sz = 4; 265 cmd->rx_cmd_sz = cmd->rx_data_sz = 0; 266 break; 267 } 268 269 rt305x_spi_chip_activate(sc); 270 271 if (cmd->tx_cmd_sz + cmd->rx_cmd_sz) { 272 buf = (uint8_t *)(cmd->rx_cmd); 273 tx_buf = (uint8_t *)(cmd->tx_cmd); 274 sz = cmd->tx_cmd_sz + cmd->rx_cmd_sz; 275 276 for (i = 0; i < sz; i++) { 277 if(i < cmd->tx_cmd_sz) { 278 byte = tx_buf[i]; 279 error = rt305x_spi_txrx(sc, &byte, 280 RT305X_SPI_WRITE); 281 if (error) 282 goto rt305x_spi_transfer_fail; 283 continue; 284 } 285 error = rt305x_spi_txrx(sc, &byte, 286 RT305X_SPI_READ); 287 if (error) 288 goto rt305x_spi_transfer_fail; 289 buf[i] = byte; 290 } 291 } 292 293 /* 294 * Transfer/Receive data 295 */ 296 297 if (cmd->tx_data_sz + cmd->rx_data_sz) { 298 write = (cmd->tx_data_sz > 0)?1:0; 299 buf = (uint8_t *)(write ? cmd->tx_data : cmd->rx_data); 300 sz = write ? cmd->tx_data_sz : cmd->rx_data_sz; 301 302 for (i = 0; i < sz; i++) { 303 byte = buf[i]; 304 error = rt305x_spi_txrx(sc, &byte, 305 write?RT305X_SPI_WRITE:RT305X_SPI_READ); 306 if (error) 307 goto rt305x_spi_transfer_fail; 308 buf[i] = byte; 309 } 310 } 311rt305x_spi_transfer_fail: 312 rt305x_spi_chip_deactivate(sc); 313 314 return (error); 315} 316 317#ifdef FDT 318static phandle_t 319rt305x_spi_get_node(device_t bus, device_t dev) 320{ 321 322 /* We only have one child, the SPI bus, which needs our own node. */ 323 return (ofw_bus_get_node(bus)); 324} 325#endif 326 327static device_method_t rt305x_spi_methods[] = { 328 /* Device interface */ 329 DEVMETHOD(device_probe, rt305x_spi_probe), 330 DEVMETHOD(device_attach, rt305x_spi_attach), 331 DEVMETHOD(device_detach, rt305x_spi_detach), 332 333 DEVMETHOD(spibus_transfer, rt305x_spi_transfer), 334 335#ifdef FDT 336 /* ofw_bus interface */ 337 DEVMETHOD(ofw_bus_get_node, rt305x_spi_get_node), 338#endif 339 340 DEVMETHOD_END 341}; 342 343static driver_t rt305x_spi_driver = { 344 .name = "spi", 345 .methods = rt305x_spi_methods, 346 .size = sizeof(struct rt305x_spi_softc), 347}; 348 349static devclass_t rt305x_spi_devclass; 350 351DRIVER_MODULE(rt305x_spi, obio, rt305x_spi_driver, rt305x_spi_devclass, 0, 0); 352#ifdef FDT 353DRIVER_MODULE(rt305x_spi, simplebus, rt305x_spi_driver, rt305x_spi_devclass, 0, 0); 354#endif 355