exynos5_spi.c revision 331506
1/*- 2 * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com> 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, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27/* 28 * Exynos 5 Serial Peripheral Interface (SPI) 29 */ 30 31#include <sys/cdefs.h> 32__FBSDID("$FreeBSD: stable/11/sys/arm/samsung/exynos/exynos5_spi.c 331506 2018-03-24 23:23:31Z ian $"); 33 34#include <sys/param.h> 35#include <sys/systm.h> 36#include <sys/bus.h> 37#include <sys/kernel.h> 38#include <sys/module.h> 39#include <sys/malloc.h> 40#include <sys/rman.h> 41#include <sys/timeet.h> 42#include <sys/timetc.h> 43#include <sys/watchdog.h> 44 45#include <dev/spibus/spi.h> 46#include <dev/spibus/spibusvar.h> 47 48#include "spibus_if.h" 49 50#include <dev/ofw/openfirm.h> 51#include <dev/ofw/ofw_bus.h> 52#include <dev/ofw/ofw_bus_subr.h> 53 54#include <machine/bus.h> 55#include <machine/cpu.h> 56#include <machine/intr.h> 57 58#include <arm/samsung/exynos/exynos5_common.h> 59 60#define CH_CFG 0x00 /* SPI configuration */ 61#define SW_RST (1 << 5) /* Reset */ 62#define RX_CH_ON (1 << 1) /* SPI Rx Channel On */ 63#define TX_CH_ON (1 << 0) /* SPI Tx Channel On */ 64#define MODE_CFG 0x08 /* FIFO control */ 65#define CS_REG 0x0C /* slave selection control */ 66#define NSSOUT (1 << 0) 67#define SPI_INT_EN 0x10 /* interrupt enable */ 68#define SPI_STATUS 0x14 /* SPI status */ 69#define TX_FIFO_LVL_S 6 70#define TX_FIFO_LVL_M 0x1ff 71#define RX_FIFO_LVL_S 15 72#define RX_FIFO_LVL_M 0x1ff 73#define SPI_TX_DATA 0x18 /* Tx data */ 74#define SPI_RX_DATA 0x1C /* Rx data */ 75#define PACKET_CNT_REG 0x20 /* packet count */ 76#define PENDING_CLR_REG 0x24 /* interrupt pending clear */ 77#define SWAP_CFG 0x28 /* swap configuration */ 78#define FB_CLK_SEL 0x2C /* feedback clock selection */ 79#define FB_CLK_180 0x2 /* 180 degree phase lagging */ 80 81struct spi_softc { 82 struct resource *res[2]; 83 bus_space_tag_t bst; 84 bus_space_handle_t bsh; 85 device_t dev; 86}; 87 88struct spi_softc *spi_sc; 89 90static struct resource_spec spi_spec[] = { 91 { SYS_RES_MEMORY, 0, RF_ACTIVE }, 92 { SYS_RES_IRQ, 0, RF_ACTIVE }, 93 { -1, 0 } 94}; 95 96static int 97spi_probe(device_t dev) 98{ 99 100 if (!ofw_bus_status_okay(dev)) 101 return (ENXIO); 102 103 if (!ofw_bus_is_compatible(dev, "samsung,exynos5-spi")) 104 return (ENXIO); 105 106 device_set_desc(dev, "Exynos 5 Serial Peripheral Interface (SPI)"); 107 return (BUS_PROBE_DEFAULT); 108} 109 110static int 111spi_attach(device_t dev) 112{ 113 struct spi_softc *sc; 114 int reg; 115 116 sc = device_get_softc(dev); 117 sc->dev = dev; 118 119 if (bus_alloc_resources(dev, spi_spec, sc->res)) { 120 device_printf(dev, "could not allocate resources\n"); 121 return (ENXIO); 122 } 123 124 /* Memory interface */ 125 sc->bst = rman_get_bustag(sc->res[0]); 126 sc->bsh = rman_get_bushandle(sc->res[0]); 127 128 spi_sc = sc; 129 130 WRITE4(sc, FB_CLK_SEL, FB_CLK_180); 131 132 reg = READ4(sc, CH_CFG); 133 reg |= (RX_CH_ON | TX_CH_ON); 134 WRITE4(sc, CH_CFG, reg); 135 136 device_add_child(dev, "spibus", 0); 137 return (bus_generic_attach(dev)); 138} 139 140static int 141spi_txrx(struct spi_softc *sc, uint8_t *out_buf, 142 uint8_t *in_buf, int bufsz, int cs) 143{ 144 uint32_t reg; 145 uint32_t i; 146 147 if (bufsz == 0) { 148 /* Nothing to transfer */ 149 return (0); 150 } 151 152 /* Reset registers */ 153 reg = READ4(sc, CH_CFG); 154 reg |= SW_RST; 155 WRITE4(sc, CH_CFG, reg); 156 reg &= ~SW_RST; 157 WRITE4(sc, CH_CFG, reg); 158 159 /* Assert CS */ 160 reg = READ4(sc, CS_REG); 161 reg &= ~NSSOUT; 162 WRITE4(sc, CS_REG, reg); 163 164 for (i = 0; i < bufsz; i++) { 165 166 /* TODO: Implement FIFO operation */ 167 168 /* Wait all the data shifted out */ 169 while (READ4(sc, SPI_STATUS) & \ 170 (TX_FIFO_LVL_M << TX_FIFO_LVL_S)) 171 continue; 172 173 WRITE1(sc, SPI_TX_DATA, out_buf[i]); 174 175 /* Wait until no data available */ 176 while ((READ4(sc, SPI_STATUS) & \ 177 (RX_FIFO_LVL_M << RX_FIFO_LVL_S)) == 0) 178 continue; 179 180 in_buf[i] = READ1(sc, SPI_RX_DATA); 181 } 182 183 /* Deassert CS */ 184 reg = READ4(sc, CS_REG); 185 reg |= NSSOUT; 186 WRITE4(sc, CS_REG, reg); 187 188 return (0); 189} 190 191static int 192spi_transfer(device_t dev, device_t child, struct spi_command *cmd) 193{ 194 struct spi_softc *sc; 195 uint32_t cs; 196 197 sc = device_get_softc(dev); 198 199 KASSERT(cmd->tx_cmd_sz == cmd->rx_cmd_sz, 200 ("%s: TX/RX command sizes should be equal", __func__)); 201 KASSERT(cmd->tx_data_sz == cmd->rx_data_sz, 202 ("%s: TX/RX data sizes should be equal", __func__)); 203 204 /* get the proper chip select */ 205 spibus_get_cs(child, &cs); 206 207 cs &= ~SPIBUS_CS_HIGH; 208 209 /* Command */ 210 spi_txrx(sc, cmd->tx_cmd, cmd->rx_cmd, cmd->tx_cmd_sz, cs); 211 212 /* Data */ 213 spi_txrx(sc, cmd->tx_data, cmd->rx_data, cmd->tx_data_sz, cs); 214 215 return (0); 216} 217 218static device_method_t spi_methods[] = { 219 DEVMETHOD(device_probe, spi_probe), 220 DEVMETHOD(device_attach, spi_attach), 221 222 /* SPI interface */ 223 DEVMETHOD(spibus_transfer, spi_transfer), 224 225 { 0, 0 } 226}; 227 228static driver_t spi_driver = { 229 "spi", 230 spi_methods, 231 sizeof(struct spi_softc), 232}; 233 234static devclass_t spi_devclass; 235 236DRIVER_MODULE(spi, simplebus, spi_driver, spi_devclass, 0, 0); 237