1 /* $OpenBSD: aplspi.c,v 1.3 2021/12/11 20:04:37 kettenis Exp $ */ 2 /* 3 * Copyright (c) 2021 Mark Kettenis <kettenis@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 #include <sys/systm.h> 20 #include <sys/kernel.h> 21 #include <sys/device.h> 22 #include <sys/malloc.h> 23 #include <sys/mutex.h> 24 25 #include <machine/bus.h> 26 #include <machine/fdt.h> 27 28 #include <dev/spi/spivar.h> 29 #include <dev/ofw/openfirm.h> 30 #include <dev/ofw/ofw_clock.h> 31 #include <dev/ofw/ofw_gpio.h> 32 #include <dev/ofw/ofw_pinctrl.h> 33 #include <dev/ofw/fdt.h> 34 35 #define SPI_CLKCFG 0x00 36 #define SPI_CLKCFG_EN 0xd 37 #define SPI_CONFIG 0x04 38 #define SPI_CONFIG_EN (1 << 18) 39 #define SPI_CONFIG_PIOEN (1 << 5) 40 #define SPI_STATUS 0x08 41 #define SPI_PIN 0x0c 42 #define SPI_PIN_CS (1 << 1) 43 #define SPI_TXDATA 0x10 44 #define SPI_RXDATA 0x20 45 #define SPI_CLKDIV 0x30 46 #define SPI_CLKDIV_MIN 2 47 #define SPI_CLKDIV_MAX 2047 48 #define SPI_RXCNT 0x34 49 #define SPI_CLKIDLE 0x38 50 #define SPI_TXCNT 0x4c 51 #define SPI_AVAIL 0x10c 52 #define SPI_AVAIL_TX(avail) ((avail >> 8) & 0xff) 53 #define SPI_AVAIL_RX(avail) ((avail >> 24) & 0xff) 54 #define SPI_SHIFTCFG 0x150 55 #define SPI_SHIFTCFG_OVERRIDE_CS (1 << 24) 56 #define SPI_PINCFG 0x154 57 #define SPI_PINCFG_KEEP_CS (1 << 1) 58 #define SPI_PINCFG_CS_IDLE_VAL (1 << 9) 59 60 #define SPI_FIFO_SIZE 16 61 62 #define DEVNAME(sc) ((sc)->sc_dev.dv_xname) 63 64 struct aplspi_softc { 65 struct device sc_dev; 66 bus_space_tag_t sc_iot; 67 bus_space_handle_t sc_ioh; 68 int sc_node; 69 70 uint32_t sc_pfreq; 71 72 struct spi_controller sc_tag; 73 struct mutex sc_mtx; 74 75 int sc_cs; 76 uint32_t *sc_csgpio; 77 int sc_csgpiolen; 78 u_int sc_cs_delay; 79 }; 80 81 int aplspi_match(struct device *, void *, void *); 82 void aplspi_attach(struct device *, struct device *, void *); 83 84 void aplspi_config(void *, struct spi_config *); 85 uint32_t aplspi_clkdiv(struct aplspi_softc *, uint32_t); 86 int aplspi_transfer(void *, char *, char *, int, int); 87 int aplspi_acquire_bus(void *, int); 88 void aplspi_release_bus(void *, int); 89 90 void aplspi_set_cs(struct aplspi_softc *, int, int); 91 int aplspi_wait_state(struct aplspi_softc *, uint32_t, uint32_t); 92 93 void aplspi_scan(struct aplspi_softc *); 94 95 #define HREAD4(sc, reg) \ 96 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) 97 #define HWRITE4(sc, reg, val) \ 98 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 99 #define HSET4(sc, reg, bits) \ 100 HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) 101 #define HCLR4(sc, reg, bits) \ 102 HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) 103 104 struct cfattach aplspi_ca = { 105 sizeof(struct aplspi_softc), aplspi_match, aplspi_attach 106 }; 107 108 struct cfdriver aplspi_cd = { 109 NULL, "aplspi", DV_DULL 110 }; 111 112 int 113 aplspi_match(struct device *parent, void *match, void *aux) 114 { 115 struct fdt_attach_args *faa = aux; 116 117 return OF_is_compatible(faa->fa_node, "apple,spi"); 118 } 119 120 void 121 aplspi_attach(struct device *parent, struct device *self, void *aux) 122 { 123 struct aplspi_softc *sc = (struct aplspi_softc *)self; 124 struct fdt_attach_args *faa = aux; 125 126 if (faa->fa_nreg < 1) 127 return; 128 129 sc->sc_iot = faa->fa_iot; 130 sc->sc_node = faa->fa_node; 131 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 132 faa->fa_reg[0].size, 0, &sc->sc_ioh)) { 133 printf(": can't map registers\n"); 134 return; 135 } 136 137 sc->sc_csgpiolen = OF_getproplen(faa->fa_node, "cs-gpios"); 138 if (sc->sc_csgpiolen > 0) { 139 sc->sc_csgpio = malloc(sc->sc_csgpiolen, M_DEVBUF, M_WAITOK); 140 OF_getpropintarray(faa->fa_node, "cs-gpios", 141 sc->sc_csgpio, sc->sc_csgpiolen); 142 gpio_controller_config_pin(sc->sc_csgpio, GPIO_CONFIG_OUTPUT); 143 gpio_controller_set_pin(sc->sc_csgpio, 0); 144 } 145 146 printf("\n"); 147 148 sc->sc_pfreq = clock_get_frequency(sc->sc_node, NULL); 149 150 pinctrl_byname(sc->sc_node, "default"); 151 152 /* Configure CS# pin for manual control. */ 153 HWRITE4(sc, SPI_PIN, SPI_PIN_CS); 154 HCLR4(sc, SPI_SHIFTCFG, SPI_SHIFTCFG_OVERRIDE_CS); 155 HCLR4(sc, SPI_PINCFG, SPI_PINCFG_CS_IDLE_VAL); 156 HSET4(sc, SPI_PINCFG, SPI_PINCFG_KEEP_CS); 157 158 sc->sc_tag.sc_cookie = sc; 159 sc->sc_tag.sc_config = aplspi_config; 160 sc->sc_tag.sc_transfer = aplspi_transfer; 161 sc->sc_tag.sc_acquire_bus = aplspi_acquire_bus; 162 sc->sc_tag.sc_release_bus = aplspi_release_bus; 163 164 mtx_init(&sc->sc_mtx, IPL_TTY); 165 166 aplspi_scan(sc); 167 } 168 169 void 170 aplspi_config(void *cookie, struct spi_config *conf) 171 { 172 struct aplspi_softc *sc = cookie; 173 int cs; 174 175 cs = conf->sc_cs; 176 if (cs > 4) { 177 printf("%s: invalid chip-select (%d)\n", DEVNAME(sc), cs); 178 return; 179 } 180 sc->sc_cs = cs; 181 sc->sc_cs_delay = conf->sc_cs_delay; 182 183 HWRITE4(sc, SPI_CLKCFG, 0); 184 185 HWRITE4(sc, SPI_CLKDIV, aplspi_clkdiv(sc, conf->sc_freq)); 186 HWRITE4(sc, SPI_CLKIDLE, 0); 187 188 HWRITE4(sc, SPI_CONFIG, SPI_CONFIG_EN); 189 HWRITE4(sc, SPI_CLKCFG, SPI_CLKCFG_EN); 190 HREAD4(sc, SPI_CONFIG); 191 } 192 193 uint32_t 194 aplspi_clkdiv(struct aplspi_softc *sc, uint32_t freq) 195 { 196 uint32_t div = 0; 197 198 while ((freq * div) < sc->sc_pfreq) 199 div++; 200 if (div < SPI_CLKDIV_MIN) 201 div = SPI_CLKDIV_MIN; 202 if (div > SPI_CLKDIV_MAX) 203 div = SPI_CLKDIV_MAX; 204 205 return div << 1; 206 } 207 208 void 209 aplspi_set_cs(struct aplspi_softc *sc, int cs, int on) 210 { 211 if (cs == 0) { 212 if (sc->sc_csgpio) 213 gpio_controller_set_pin(sc->sc_csgpio, on); 214 else 215 HWRITE4(sc, SPI_PIN, on ? 0 : SPI_PIN_CS); 216 } 217 } 218 219 int 220 aplspi_transfer(void *cookie, char *out, char *in, int len, int flags) 221 { 222 struct aplspi_softc *sc = cookie; 223 uint32_t avail, data, status; 224 int rsplen; 225 int count; 226 227 aplspi_set_cs(sc, sc->sc_cs, 1); 228 delay(sc->sc_cs_delay); 229 230 HWRITE4(sc, SPI_TXCNT, len); 231 HWRITE4(sc, SPI_RXCNT, len); 232 HWRITE4(sc, SPI_CONFIG, SPI_CONFIG_EN | SPI_CONFIG_PIOEN); 233 234 rsplen = len; 235 while (len > 0 || rsplen > 0) { 236 avail = HREAD4(sc, SPI_AVAIL); 237 count = SPI_AVAIL_RX(avail); 238 while (rsplen > 0 && count > 0) { 239 data = HREAD4(sc, SPI_RXDATA); 240 if (in) 241 *in++ = data; 242 rsplen--; 243 244 avail = HREAD4(sc, SPI_AVAIL); 245 count = SPI_AVAIL_RX(avail); 246 } 247 248 count = SPI_FIFO_SIZE - SPI_AVAIL_TX(avail); 249 while (len > 0 && count > 0) { 250 data = out ? *out++ : 0; 251 HWRITE4(sc, SPI_TXDATA, data); 252 len--; 253 count--; 254 } 255 } 256 257 HWRITE4(sc, SPI_CONFIG, SPI_CONFIG_EN); 258 status = HREAD4(sc, SPI_STATUS); 259 HWRITE4(sc, SPI_STATUS, status); 260 261 if (!ISSET(flags, SPI_KEEP_CS)) 262 aplspi_set_cs(sc, sc->sc_cs, 0); 263 264 return 0; 265 } 266 267 int 268 aplspi_acquire_bus(void *cookie, int flags) 269 { 270 struct aplspi_softc *sc = cookie; 271 272 mtx_enter(&sc->sc_mtx); 273 return 0; 274 } 275 276 void 277 aplspi_release_bus(void *cookie, int flags) 278 { 279 struct aplspi_softc *sc = cookie; 280 281 mtx_leave(&sc->sc_mtx); 282 } 283 284 void 285 aplspi_scan(struct aplspi_softc *sc) 286 { 287 struct spi_attach_args sa; 288 uint32_t reg[1]; 289 char name[32]; 290 int node; 291 292 for (node = OF_child(sc->sc_node); node; node = OF_peer(node)) { 293 memset(name, 0, sizeof(name)); 294 memset(reg, 0, sizeof(reg)); 295 296 if (OF_getprop(node, "compatible", name, sizeof(name)) == -1) 297 continue; 298 if (name[0] == '\0') 299 continue; 300 301 if (OF_getprop(node, "reg", ®, sizeof(reg)) != sizeof(reg)) 302 continue; 303 304 memset(&sa, 0, sizeof(sa)); 305 sa.sa_tag = &sc->sc_tag; 306 sa.sa_name = name; 307 sa.sa_cookie = &node; 308 309 config_found(&sc->sc_dev, &sa, NULL); 310 } 311 } 312