1 /* $OpenBSD: aplspi.c,v 1.6 2024/05/13 01:15:50 jsg 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 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 const 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 power_domain_enable(sc->sc_node); 151 pinctrl_byname(sc->sc_node, "default"); 152 153 /* Configure CS# pin for manual control. */ 154 HWRITE4(sc, SPI_PIN, SPI_PIN_CS); 155 HCLR4(sc, SPI_SHIFTCFG, SPI_SHIFTCFG_OVERRIDE_CS); 156 HCLR4(sc, SPI_PINCFG, SPI_PINCFG_CS_IDLE_VAL); 157 HSET4(sc, SPI_PINCFG, SPI_PINCFG_KEEP_CS); 158 159 sc->sc_tag.sc_cookie = sc; 160 sc->sc_tag.sc_config = aplspi_config; 161 sc->sc_tag.sc_transfer = aplspi_transfer; 162 sc->sc_tag.sc_acquire_bus = aplspi_acquire_bus; 163 sc->sc_tag.sc_release_bus = aplspi_release_bus; 164 165 mtx_init(&sc->sc_mtx, IPL_TTY); 166 167 aplspi_scan(sc); 168 } 169 170 void 171 aplspi_config(void *cookie, struct spi_config *conf) 172 { 173 struct aplspi_softc *sc = cookie; 174 int cs; 175 176 cs = conf->sc_cs; 177 if (cs > 4) { 178 printf("%s: invalid chip-select (%d)\n", DEVNAME(sc), cs); 179 return; 180 } 181 sc->sc_cs = cs; 182 sc->sc_cs_delay = conf->sc_cs_delay; 183 184 HWRITE4(sc, SPI_CLKCFG, 0); 185 186 HWRITE4(sc, SPI_CLKDIV, aplspi_clkdiv(sc, conf->sc_freq)); 187 HWRITE4(sc, SPI_CLKIDLE, 0); 188 189 HWRITE4(sc, SPI_CONFIG, SPI_CONFIG_EN); 190 HWRITE4(sc, SPI_CLKCFG, SPI_CLKCFG_EN); 191 HREAD4(sc, SPI_CONFIG); 192 } 193 194 uint32_t 195 aplspi_clkdiv(struct aplspi_softc *sc, uint32_t freq) 196 { 197 uint32_t div = 0; 198 199 while ((freq * div) < sc->sc_pfreq) 200 div++; 201 if (div < SPI_CLKDIV_MIN) 202 div = SPI_CLKDIV_MIN; 203 if (div > SPI_CLKDIV_MAX) 204 div = SPI_CLKDIV_MAX; 205 206 return div << 1; 207 } 208 209 void 210 aplspi_set_cs(struct aplspi_softc *sc, int cs, int on) 211 { 212 if (cs == 0) { 213 if (sc->sc_csgpio) 214 gpio_controller_set_pin(sc->sc_csgpio, on); 215 else 216 HWRITE4(sc, SPI_PIN, on ? 0 : SPI_PIN_CS); 217 } 218 } 219 220 int 221 aplspi_transfer(void *cookie, char *out, char *in, int len, int flags) 222 { 223 struct aplspi_softc *sc = cookie; 224 uint32_t avail, data, status; 225 int rsplen; 226 int count; 227 228 aplspi_set_cs(sc, sc->sc_cs, 1); 229 delay(sc->sc_cs_delay); 230 231 HWRITE4(sc, SPI_TXCNT, len); 232 HWRITE4(sc, SPI_RXCNT, len); 233 HWRITE4(sc, SPI_CONFIG, SPI_CONFIG_EN | SPI_CONFIG_PIOEN); 234 235 rsplen = len; 236 while (len > 0 || rsplen > 0) { 237 avail = HREAD4(sc, SPI_AVAIL); 238 count = SPI_AVAIL_RX(avail); 239 while (rsplen > 0 && count > 0) { 240 data = HREAD4(sc, SPI_RXDATA); 241 if (in) 242 *in++ = data; 243 rsplen--; 244 245 avail = HREAD4(sc, SPI_AVAIL); 246 count = SPI_AVAIL_RX(avail); 247 } 248 249 count = SPI_FIFO_SIZE - SPI_AVAIL_TX(avail); 250 while (len > 0 && count > 0) { 251 data = out ? *out++ : 0; 252 HWRITE4(sc, SPI_TXDATA, data); 253 len--; 254 count--; 255 } 256 } 257 258 HWRITE4(sc, SPI_CONFIG, SPI_CONFIG_EN); 259 status = HREAD4(sc, SPI_STATUS); 260 HWRITE4(sc, SPI_STATUS, status); 261 262 if (!ISSET(flags, SPI_KEEP_CS)) 263 aplspi_set_cs(sc, sc->sc_cs, 0); 264 265 return 0; 266 } 267 268 int 269 aplspi_acquire_bus(void *cookie, int flags) 270 { 271 struct aplspi_softc *sc = cookie; 272 273 mtx_enter(&sc->sc_mtx); 274 return 0; 275 } 276 277 void 278 aplspi_release_bus(void *cookie, int flags) 279 { 280 struct aplspi_softc *sc = cookie; 281 282 mtx_leave(&sc->sc_mtx); 283 } 284 285 void 286 aplspi_scan(struct aplspi_softc *sc) 287 { 288 struct spi_attach_args sa; 289 uint32_t reg[1]; 290 char name[32]; 291 int node; 292 293 for (node = OF_child(sc->sc_node); node; node = OF_peer(node)) { 294 memset(name, 0, sizeof(name)); 295 memset(reg, 0, sizeof(reg)); 296 297 if (OF_getprop(node, "compatible", name, sizeof(name)) == -1) 298 continue; 299 if (name[0] == '\0') 300 continue; 301 302 if (OF_getprop(node, "reg", ®, sizeof(reg)) != sizeof(reg)) 303 continue; 304 305 memset(&sa, 0, sizeof(sa)); 306 sa.sa_tag = &sc->sc_tag; 307 sa.sa_name = name; 308 sa.sa_cookie = &node; 309 310 config_found(&sc->sc_dev, &sa, NULL); 311 } 312 } 313