1301409Slandonf/*- 2301409Slandonf * Copyright (c) 2016 Michael Zhilin <mizhka@gmail.com> 3302189Slandonf * Copyright (c) 2016 Landon Fuller <landonf@FreeBSD.org> 4301409Slandonf * All rights reserved. 5301409Slandonf * 6301409Slandonf * Redistribution and use in source and binary forms, with or without 7301409Slandonf * modification, are permitted provided that the following conditions 8301409Slandonf * are met: 9301409Slandonf * 1. Redistributions of source code must retain the above copyright 10301409Slandonf * notice, this list of conditions and the following disclaimer, 11301409Slandonf * without modification. 12301409Slandonf * 2. Redistributions in binary form must reproduce at minimum a disclaimer 13301409Slandonf * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any 14301409Slandonf * redistribution must be conditioned upon including a substantially 15301409Slandonf * similar Disclaimer requirement for further binary redistribution. 16301409Slandonf * 17301409Slandonf * NO WARRANTY 18301409Slandonf * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 19301409Slandonf * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 20301409Slandonf * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY 21301409Slandonf * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 22301409Slandonf * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY, 23301409Slandonf * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24301409Slandonf * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25301409Slandonf * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER 26301409Slandonf * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27301409Slandonf * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 28301409Slandonf * THE POSSIBILITY OF SUCH DAMAGES. 29301409Slandonf */ 30301409Slandonf 31301409Slandonf#include <sys/cdefs.h> 32301409Slandonf__FBSDID("$FreeBSD$"); 33301409Slandonf 34301409Slandonf#include <sys/param.h> 35301409Slandonf#include <sys/systm.h> 36301409Slandonf#include <sys/kernel.h> 37301409Slandonf#include <sys/module.h> 38301409Slandonf#include <sys/errno.h> 39301409Slandonf#include <sys/rman.h> 40301409Slandonf#include <sys/bus.h> 41301409Slandonf 42301409Slandonf#include <machine/bus.h> 43301409Slandonf 44301409Slandonf#include <dev/bhnd/bhndvar.h> 45302189Slandonf 46301409Slandonf#include <dev/spibus/spi.h> 47301409Slandonf 48302189Slandonf#include "bhnd_chipc_if.h" 49302189Slandonf 50301409Slandonf#include "spibus_if.h" 51301409Slandonf 52301409Slandonf#include "chipcreg.h" 53301409Slandonf#include "chipcvar.h" 54301409Slandonf#include "chipc_slicer.h" 55301409Slandonf 56302189Slandonf#include "chipc_spi.h" 57301409Slandonf 58301409Slandonfstatic int chipc_spi_probe(device_t dev); 59301409Slandonfstatic int chipc_spi_attach(device_t dev); 60302189Slandonfstatic int chipc_spi_detach(device_t dev); 61301409Slandonfstatic int chipc_spi_transfer(device_t dev, device_t child, 62301409Slandonf struct spi_command *cmd); 63301409Slandonfstatic int chipc_spi_txrx(struct chipc_spi_softc *sc, uint8_t in, 64301409Slandonf uint8_t* out); 65301409Slandonfstatic int chipc_spi_wait(struct chipc_spi_softc *sc); 66301409Slandonf 67302189Slandonfstatic int 68302189Slandonfchipc_spi_probe(device_t dev) 69302189Slandonf{ 70302189Slandonf device_set_desc(dev, "Broadcom ChipCommon SPI"); 71302189Slandonf return (BUS_PROBE_NOWILDCARD); 72302189Slandonf} 73301409Slandonf 74302189Slandonfstatic int 75302189Slandonfchipc_spi_attach(device_t dev) 76301409Slandonf{ 77302189Slandonf struct chipc_spi_softc *sc; 78302189Slandonf struct chipc_caps *ccaps; 79302189Slandonf device_t flash_dev; 80302189Slandonf device_t spibus; 81302189Slandonf const char *flash_name; 82302189Slandonf int error; 83301409Slandonf 84302189Slandonf sc = device_get_softc(dev); 85301409Slandonf 86302189Slandonf /* Allocate SPI controller registers */ 87302189Slandonf sc->sc_rid = 1; 88302189Slandonf sc->sc_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &sc->sc_rid, 89302189Slandonf RF_ACTIVE); 90302189Slandonf if (sc->sc_res == NULL) { 91302189Slandonf device_printf(dev, "failed to allocate device registers\n"); 92302189Slandonf return (ENXIO); 93301409Slandonf } 94301409Slandonf 95302189Slandonf /* Allocate flash shadow region */ 96302189Slandonf sc->sc_flash_rid = 0; 97302189Slandonf sc->sc_flash_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, 98302189Slandonf &sc->sc_flash_rid, RF_ACTIVE); 99302189Slandonf if (sc->sc_flash_res == NULL) { 100302189Slandonf device_printf(dev, "failed to allocate flash region\n"); 101302189Slandonf error = ENXIO; 102302189Slandonf goto failed; 103301409Slandonf } 104301409Slandonf 105302189Slandonf /* 106302189Slandonf * Add flash device 107302189Slandonf * 108302189Slandonf * XXX: This should be replaced with a DEVICE_IDENTIFY implementation 109302189Slandonf * in chipc-specific subclasses of the mx25l and at45d drivers. 110302189Slandonf */ 111302189Slandonf if ((spibus = device_add_child(dev, "spibus", -1)) == NULL) { 112302189Slandonf device_printf(dev, "failed to add spibus\n"); 113302189Slandonf error = ENXIO; 114302189Slandonf goto failed; 115301409Slandonf } 116301409Slandonf 117302189Slandonf /* Let spibus perform full attach before we try to call 118302189Slandonf * BUS_ADD_CHILD() */ 119302189Slandonf if ((error = bus_generic_attach(dev))) 120302189Slandonf goto failed; 121301409Slandonf 122302189Slandonf /* Determine flash type and add the flash child */ 123302189Slandonf ccaps = BHND_CHIPC_GET_CAPS(device_get_parent(dev)); 124302189Slandonf flash_name = chipc_sflash_device_name(ccaps->flash_type); 125302189Slandonf if (flash_name != NULL) { 126302189Slandonf flash_dev = BUS_ADD_CHILD(spibus, 0, flash_name, -1); 127302189Slandonf if (flash_dev == NULL) { 128302189Slandonf device_printf(dev, "failed to add %s\n", flash_name); 129302189Slandonf error = ENXIO; 130302189Slandonf goto failed; 131302189Slandonf } 132301409Slandonf 133302189Slandonf chipc_register_slicer(ccaps->flash_type); 134302189Slandonf 135302189Slandonf if ((error = device_probe_and_attach(flash_dev))) { 136302189Slandonf device_printf(dev, "failed to attach %s: %d\n", 137302189Slandonf flash_name, error); 138302189Slandonf goto failed; 139302189Slandonf } 140301409Slandonf } 141301409Slandonf 142302189Slandonf return (0); 143301409Slandonf 144302189Slandonffailed: 145302189Slandonf device_delete_children(dev); 146301409Slandonf 147302189Slandonf if (sc->sc_res != NULL) 148302189Slandonf bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid, 149302189Slandonf sc->sc_res); 150302189Slandonf 151302189Slandonf if (sc->sc_flash_res != NULL) 152302189Slandonf bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_flash_rid, 153302189Slandonf sc->sc_flash_res); 154302189Slandonf 155302189Slandonf return (error); 156301409Slandonf} 157301409Slandonf 158301409Slandonfstatic int 159302189Slandonfchipc_spi_detach(device_t dev) 160301409Slandonf{ 161301409Slandonf struct chipc_spi_softc *sc; 162302189Slandonf int error; 163301409Slandonf 164301409Slandonf sc = device_get_softc(dev); 165301409Slandonf 166302189Slandonf if ((error = bus_generic_detach(dev))) 167302189Slandonf return (error); 168301409Slandonf 169302189Slandonf bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_rid, sc->sc_res); 170302189Slandonf bus_release_resource(dev, SYS_RES_MEMORY, sc->sc_flash_rid, 171302189Slandonf sc->sc_flash_res); 172302189Slandonf return (0); 173301409Slandonf} 174301409Slandonf 175301409Slandonfstatic int 176301409Slandonfchipc_spi_wait(struct chipc_spi_softc *sc) 177301409Slandonf{ 178301409Slandonf int i; 179301409Slandonf 180301409Slandonf for (i = CHIPC_SPI_MAXTRIES; i > 0; i--) 181301409Slandonf if (!(SPI_READ(sc, CHIPC_SPI_FLASHCTL) & CHIPC_SPI_FLASHCTL_START)) 182301409Slandonf break; 183301409Slandonf 184301409Slandonf if (i > 0) 185301409Slandonf return (0); 186301409Slandonf 187302189Slandonf BHND_DEBUG_DEV(sc->sc_dev, "busy"); 188301409Slandonf return (-1); 189301409Slandonf} 190301409Slandonf 191301409Slandonfstatic int 192301409Slandonfchipc_spi_txrx(struct chipc_spi_softc *sc, uint8_t out, uint8_t* in) 193301409Slandonf{ 194301409Slandonf uint32_t ctl; 195301409Slandonf 196301409Slandonf ctl = CHIPC_SPI_FLASHCTL_START | CHIPC_SPI_FLASHCTL_CSACTIVE | out; 197301409Slandonf SPI_BARRIER_WRITE(sc); 198301409Slandonf SPI_WRITE(sc, CHIPC_SPI_FLASHCTL, ctl); 199301409Slandonf SPI_BARRIER_WRITE(sc); 200301409Slandonf 201301409Slandonf if (chipc_spi_wait(sc)) 202301409Slandonf return (-1); 203301409Slandonf 204301409Slandonf *in = SPI_READ(sc, CHIPC_SPI_FLASHDATA) & 0xff; 205301409Slandonf return (0); 206301409Slandonf} 207301409Slandonf 208301409Slandonfstatic int 209301409Slandonfchipc_spi_transfer(device_t dev, device_t child, struct spi_command *cmd) 210301409Slandonf{ 211301409Slandonf struct chipc_spi_softc *sc; 212301409Slandonf uint8_t *buf_in; 213301409Slandonf uint8_t *buf_out; 214301409Slandonf int i; 215301409Slandonf 216301409Slandonf sc = device_get_softc(dev); 217301409Slandonf KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz, 218301409Slandonf ("TX/RX command sizes should be equal")); 219301409Slandonf KASSERT(cmd->tx_data_sz == cmd->rx_data_sz, 220301409Slandonf ("TX/RX data sizes should be equal")); 221301409Slandonf 222301409Slandonf if (cmd->tx_cmd_sz == 0) { 223301409Slandonf BHND_DEBUG_DEV(child, "size of command is ZERO"); 224301409Slandonf return (EIO); 225301409Slandonf } 226301409Slandonf 227301409Slandonf SPI_BARRIER_WRITE(sc); 228301409Slandonf SPI_WRITE(sc, CHIPC_SPI_FLASHADDR, 0); 229301409Slandonf SPI_BARRIER_WRITE(sc); 230301409Slandonf 231301409Slandonf /* 232301409Slandonf * Transfer command 233301409Slandonf */ 234301409Slandonf buf_out = (uint8_t *)cmd->tx_cmd; 235301409Slandonf buf_in = (uint8_t *)cmd->rx_cmd; 236301409Slandonf for (i = 0; i < cmd->tx_cmd_sz; i++) 237301409Slandonf if (chipc_spi_txrx(sc, buf_out[i], &(buf_in[i]))) 238301409Slandonf return (EIO); 239301409Slandonf 240301409Slandonf /* 241301409Slandonf * Receive/transmit data 242301409Slandonf */ 243301409Slandonf buf_out = (uint8_t *)cmd->tx_data; 244301409Slandonf buf_in = (uint8_t *)cmd->rx_data; 245301409Slandonf for (i = 0; i < cmd->tx_data_sz; i++) 246301409Slandonf if (chipc_spi_txrx(sc, buf_out[i], &(buf_in[i]))) 247301409Slandonf return (EIO); 248301409Slandonf 249301409Slandonf /* 250301409Slandonf * Clear CS bit and whole control register 251301409Slandonf */ 252301409Slandonf SPI_BARRIER_WRITE(sc); 253301409Slandonf SPI_WRITE(sc, CHIPC_SPI_FLASHCTL, 0); 254301409Slandonf SPI_BARRIER_WRITE(sc); 255301409Slandonf 256301409Slandonf return (0); 257301409Slandonf} 258301409Slandonf 259301409Slandonfstatic device_method_t chipc_spi_methods[] = { 260301409Slandonf DEVMETHOD(device_probe, chipc_spi_probe), 261301409Slandonf DEVMETHOD(device_attach, chipc_spi_attach), 262302189Slandonf DEVMETHOD(device_detach, chipc_spi_detach), 263302189Slandonf 264301409Slandonf /* SPI */ 265301409Slandonf DEVMETHOD(spibus_transfer, chipc_spi_transfer), 266301409Slandonf DEVMETHOD_END 267301409Slandonf}; 268301409Slandonf 269301409Slandonfstatic driver_t chipc_spi_driver = { 270301409Slandonf "spi", 271301409Slandonf chipc_spi_methods, 272301409Slandonf sizeof(struct chipc_spi_softc), 273301409Slandonf}; 274301409Slandonf 275301409Slandonfstatic devclass_t chipc_spi_devclass; 276301409Slandonf 277301409SlandonfDRIVER_MODULE(chipc_spi, bhnd_chipc, chipc_spi_driver, chipc_spi_devclass, 278302189Slandonf 0, 0); 279