1 /* $OpenBSD: aplspi.c,v 1.5 2023/07/23 11:17:50 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/ofw_power.h> 34 #include <dev/ofw/fdt.h> 35 36 #define SPI_CLKCFG 0x00 37 #define SPI_CLKCFG_EN 0xd 38 #define SPI_CONFIG 0x04 39 #define SPI_CONFIG_EN (1 << 18) 40 #define SPI_CONFIG_PIOEN (1 << 5) 41 #define SPI_STATUS 0x08 42 #define SPI_PIN 0x0c 43 #define SPI_PIN_CS (1 << 1) 44 #define SPI_TXDATA 0x10 45 #define SPI_RXDATA 0x20 46 #define SPI_CLKDIV 0x30 47 #define SPI_CLKDIV_MIN 2 48 #define SPI_CLKDIV_MAX 2047 49 #define SPI_RXCNT 0x34 50 #define SPI_CLKIDLE 0x38 51 #define SPI_TXCNT 0x4c 52 #define SPI_AVAIL 0x10c 53 #define SPI_AVAIL_TX(avail) ((avail >> 8) & 0xff) 54 #define SPI_AVAIL_RX(avail) ((avail >> 24) & 0xff) 55 #define SPI_SHIFTCFG 0x150 56 #define SPI_SHIFTCFG_OVERRIDE_CS (1 << 24) 57 #define SPI_PINCFG 0x154 58 #define SPI_PINCFG_KEEP_CS (1 << 1) 59 #define SPI_PINCFG_CS_IDLE_VAL (1 << 9) 60 61 #define SPI_FIFO_SIZE 16 62 63 #define DEVNAME(sc) ((sc)->sc_dev.dv_xname) 64 65 struct aplspi_softc { 66 struct device sc_dev; 67 bus_space_tag_t sc_iot; 68 bus_space_handle_t sc_ioh; 69 int sc_node; 70 71 uint32_t sc_pfreq; 72 73 struct spi_controller sc_tag; 74 struct mutex sc_mtx; 75 76 int sc_cs; 77 uint32_t *sc_csgpio; 78 int sc_csgpiolen; 79 u_int sc_cs_delay; 80 }; 81 82 int aplspi_match(struct device *, void *, void *); 83 void aplspi_attach(struct device *, struct device *, void *); 84 85 void aplspi_config(void *, struct spi_config *); 86 uint32_t aplspi_clkdiv(struct aplspi_softc *, uint32_t); 87 int aplspi_transfer(void *, char *, char *, int, int); 88 int aplspi_acquire_bus(void *, int); 89 void aplspi_release_bus(void *, int); 90 91 void aplspi_set_cs(struct aplspi_softc *, int, int); 92 int aplspi_wait_state(struct aplspi_softc *, uint32_t, uint32_t); 93 94 void aplspi_scan(struct aplspi_softc *); 95 96 #define HREAD4(sc, reg) \ 97 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) 98 #define HWRITE4(sc, reg, val) \ 99 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 100 #define HSET4(sc, reg, bits) \ 101 HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) 102 #define HCLR4(sc, reg, bits) \ 103 HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) 104 105 const struct cfattach aplspi_ca = { 106 sizeof(struct aplspi_softc), aplspi_match, aplspi_attach 107 }; 108 109 struct cfdriver aplspi_cd = { 110 NULL, "aplspi", DV_DULL 111 }; 112 113 int 114 aplspi_match(struct device *parent, void *match, void *aux) 115 { 116 struct fdt_attach_args *faa = aux; 117 118 return OF_is_compatible(faa->fa_node, "apple,spi"); 119 } 120 121 void 122 aplspi_attach(struct device *parent, struct device *self, void *aux) 123 { 124 struct aplspi_softc *sc = (struct aplspi_softc *)self; 125 struct fdt_attach_args *faa = aux; 126 127 if (faa->fa_nreg < 1) 128 return; 129 130 sc->sc_iot = faa->fa_iot; 131 sc->sc_node = faa->fa_node; 132 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 133 faa->fa_reg[0].size, 0, &sc->sc_ioh)) { 134 printf(": can't map registers\n"); 135 return; 136 } 137 138 sc->sc_csgpiolen = OF_getproplen(faa->fa_node, "cs-gpios"); 139 if (sc->sc_csgpiolen > 0) { 140 sc->sc_csgpio = malloc(sc->sc_csgpiolen, M_DEVBUF, M_WAITOK); 141 OF_getpropintarray(faa->fa_node, "cs-gpios", 142 sc->sc_csgpio, sc->sc_csgpiolen); 143 gpio_controller_config_pin(sc->sc_csgpio, GPIO_CONFIG_OUTPUT); 144 gpio_controller_set_pin(sc->sc_csgpio, 0); 145 } 146 147 printf("\n"); 148 149 sc->sc_pfreq = clock_get_frequency(sc->sc_node, NULL); 150 151 power_domain_enable(sc->sc_node); 152 pinctrl_byname(sc->sc_node, "default"); 153 154 /* Configure CS# pin for manual control. */ 155 HWRITE4(sc, SPI_PIN, SPI_PIN_CS); 156 HCLR4(sc, SPI_SHIFTCFG, SPI_SHIFTCFG_OVERRIDE_CS); 157 HCLR4(sc, SPI_PINCFG, SPI_PINCFG_CS_IDLE_VAL); 158 HSET4(sc, SPI_PINCFG, SPI_PINCFG_KEEP_CS); 159 160 sc->sc_tag.sc_cookie = sc; 161 sc->sc_tag.sc_config = aplspi_config; 162 sc->sc_tag.sc_transfer = aplspi_transfer; 163 sc->sc_tag.sc_acquire_bus = aplspi_acquire_bus; 164 sc->sc_tag.sc_release_bus = aplspi_release_bus; 165 166 mtx_init(&sc->sc_mtx, IPL_TTY); 167 168 aplspi_scan(sc); 169 } 170 171 void 172 aplspi_config(void *cookie, struct spi_config *conf) 173 { 174 struct aplspi_softc *sc = cookie; 175 int cs; 176 177 cs = conf->sc_cs; 178 if (cs > 4) { 179 printf("%s: invalid chip-select (%d)\n", DEVNAME(sc), cs); 180 return; 181 } 182 sc->sc_cs = cs; 183 sc->sc_cs_delay = conf->sc_cs_delay; 184 185 HWRITE4(sc, SPI_CLKCFG, 0); 186 187 HWRITE4(sc, SPI_CLKDIV, aplspi_clkdiv(sc, conf->sc_freq)); 188 HWRITE4(sc, SPI_CLKIDLE, 0); 189 190 HWRITE4(sc, SPI_CONFIG, SPI_CONFIG_EN); 191 HWRITE4(sc, SPI_CLKCFG, SPI_CLKCFG_EN); 192 HREAD4(sc, SPI_CONFIG); 193 } 194 195 uint32_t 196 aplspi_clkdiv(struct aplspi_softc *sc, uint32_t freq) 197 { 198 uint32_t div = 0; 199 200 while ((freq * div) < sc->sc_pfreq) 201 div++; 202 if (div < SPI_CLKDIV_MIN) 203 div = SPI_CLKDIV_MIN; 204 if (div > SPI_CLKDIV_MAX) 205 div = SPI_CLKDIV_MAX; 206 207 return div << 1; 208 } 209 210 void 211 aplspi_set_cs(struct aplspi_softc *sc, int cs, int on) 212 { 213 if (cs == 0) { 214 if (sc->sc_csgpio) 215 gpio_controller_set_pin(sc->sc_csgpio, on); 216 else 217 HWRITE4(sc, SPI_PIN, on ? 0 : SPI_PIN_CS); 218 } 219 } 220 221 int 222 aplspi_transfer(void *cookie, char *out, char *in, int len, int flags) 223 { 224 struct aplspi_softc *sc = cookie; 225 uint32_t avail, data, status; 226 int rsplen; 227 int count; 228 229 aplspi_set_cs(sc, sc->sc_cs, 1); 230 delay(sc->sc_cs_delay); 231 232 HWRITE4(sc, SPI_TXCNT, len); 233 HWRITE4(sc, SPI_RXCNT, len); 234 HWRITE4(sc, SPI_CONFIG, SPI_CONFIG_EN | SPI_CONFIG_PIOEN); 235 236 rsplen = len; 237 while (len > 0 || rsplen > 0) { 238 avail = HREAD4(sc, SPI_AVAIL); 239 count = SPI_AVAIL_RX(avail); 240 while (rsplen > 0 && count > 0) { 241 data = HREAD4(sc, SPI_RXDATA); 242 if (in) 243 *in++ = data; 244 rsplen--; 245 246 avail = HREAD4(sc, SPI_AVAIL); 247 count = SPI_AVAIL_RX(avail); 248 } 249 250 count = SPI_FIFO_SIZE - SPI_AVAIL_TX(avail); 251 while (len > 0 && count > 0) { 252 data = out ? *out++ : 0; 253 HWRITE4(sc, SPI_TXDATA, data); 254 len--; 255 count--; 256 } 257 } 258 259 HWRITE4(sc, SPI_CONFIG, SPI_CONFIG_EN); 260 status = HREAD4(sc, SPI_STATUS); 261 HWRITE4(sc, SPI_STATUS, status); 262 263 if (!ISSET(flags, SPI_KEEP_CS)) 264 aplspi_set_cs(sc, sc->sc_cs, 0); 265 266 return 0; 267 } 268 269 int 270 aplspi_acquire_bus(void *cookie, int flags) 271 { 272 struct aplspi_softc *sc = cookie; 273 274 mtx_enter(&sc->sc_mtx); 275 return 0; 276 } 277 278 void 279 aplspi_release_bus(void *cookie, int flags) 280 { 281 struct aplspi_softc *sc = cookie; 282 283 mtx_leave(&sc->sc_mtx); 284 } 285 286 void 287 aplspi_scan(struct aplspi_softc *sc) 288 { 289 struct spi_attach_args sa; 290 uint32_t reg[1]; 291 char name[32]; 292 int node; 293 294 for (node = OF_child(sc->sc_node); node; node = OF_peer(node)) { 295 memset(name, 0, sizeof(name)); 296 memset(reg, 0, sizeof(reg)); 297 298 if (OF_getprop(node, "compatible", name, sizeof(name)) == -1) 299 continue; 300 if (name[0] == '\0') 301 continue; 302 303 if (OF_getprop(node, "reg", ®, sizeof(reg)) != sizeof(reg)) 304 continue; 305 306 memset(&sa, 0, sizeof(sa)); 307 sa.sa_tag = &sc->sc_tag; 308 sa.sa_name = name; 309 sa.sa_cookie = &node; 310 311 config_found(&sc->sc_dev, &sa, NULL); 312 } 313 } 314