1 /* $OpenBSD: mvspi.c,v 1.3 2021/10/31 15:12:00 kettenis Exp $ */ 2 /* 3 * Copyright (c) 2019 Patrick Wildt <patrick@blueri.se> 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/stdint.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_pinctrl.h> 32 #include <dev/ofw/fdt.h> 33 34 /* registers */ 35 #define SPI_CTRL 0x00 36 #define SPI_CTRL_XFER_READY (1 << 1) 37 #define SPI_CTRL_CS(x) (1 << (16 + (x))) 38 #define SPI_CFG 0x04 39 #define SPI_CFG_BYTE_LEN (1 << 5) 40 #define SPI_CFG_CPHA (1 << 6) 41 #define SPI_CFG_CPOL (1 << 7) 42 #define SPI_CFG_FIFO_FLUSH (1 << 9) 43 #define SPI_CFG_FIFO_ENABLE (1 << 17) 44 #define SPI_CFG_PRESCALE_MASK 0x1f 45 #define SPI_DOUT 0x08 46 #define SPI_DIN 0x0c 47 48 #define DEVNAME(sc) ((sc)->sc_dev.dv_xname) 49 50 struct mvspi_softc { 51 struct device sc_dev; 52 bus_space_tag_t sc_iot; 53 bus_space_handle_t sc_ioh; 54 bus_size_t sc_ios; 55 int sc_node; 56 57 uint32_t sc_pfreq; 58 59 struct rwlock sc_buslock; 60 struct spi_controller sc_tag; 61 62 int sc_cs; 63 u_int sc_cs_delay; 64 }; 65 66 int mvspi_match(struct device *, void *, void *); 67 void mvspi_attach(struct device *, struct device *, void *); 68 int mvspi_detach(struct device *, int); 69 70 void mvspi_config(void *, struct spi_config *); 71 uint32_t mvspi_clkdiv(struct mvspi_softc *, uint32_t); 72 int mvspi_transfer(void *, char *, char *, int, int); 73 int mvspi_acquire_bus(void *, int); 74 void mvspi_release_bus(void *, int); 75 76 void mvspi_set_cs(struct mvspi_softc *, int, int); 77 int mvspi_wait_state(struct mvspi_softc *, uint32_t, uint32_t); 78 79 void mvspi_scan(struct mvspi_softc *); 80 81 #define HREAD4(sc, reg) \ 82 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) 83 #define HWRITE4(sc, reg, val) \ 84 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 85 #define HSET4(sc, reg, bits) \ 86 HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) 87 #define HCLR4(sc, reg, bits) \ 88 HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) 89 90 const struct cfattach mvspi_ca = { 91 sizeof(struct mvspi_softc), mvspi_match, mvspi_attach, mvspi_detach 92 }; 93 94 struct cfdriver mvspi_cd = { 95 NULL, "mvspi", DV_DULL 96 }; 97 98 int 99 mvspi_match(struct device *parent, void *match, void *aux) 100 { 101 struct fdt_attach_args *faa = aux; 102 103 return OF_is_compatible(faa->fa_node, "marvell,armada-3700-spi"); 104 } 105 106 void 107 mvspi_attach(struct device *parent, struct device *self, void *aux) 108 { 109 struct mvspi_softc *sc = (struct mvspi_softc *)self; 110 struct fdt_attach_args *faa = aux; 111 int timeout; 112 113 if (faa->fa_nreg < 1) 114 return; 115 116 sc->sc_iot = faa->fa_iot; 117 sc->sc_ios = faa->fa_reg[0].size; 118 sc->sc_node = faa->fa_node; 119 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 120 faa->fa_reg[0].size, 0, &sc->sc_ioh)) { 121 printf(": can't map registers\n"); 122 return; 123 } 124 125 printf("\n"); 126 127 pinctrl_byname(sc->sc_node, "default"); 128 clock_enable(sc->sc_node, NULL); 129 clock_set_assigned(sc->sc_node); 130 131 sc->sc_pfreq = clock_get_frequency(sc->sc_node, NULL); 132 133 /* drain input buffer */ 134 HSET4(sc, SPI_CFG, SPI_CFG_FIFO_FLUSH); 135 for (timeout = 1000; timeout > 0; timeout--) { 136 if ((HREAD4(sc, SPI_CFG) & SPI_CFG_FIFO_FLUSH) == 0) 137 break; 138 delay(10); 139 } 140 if (timeout == 0) { 141 printf("%s: timeout", sc->sc_dev.dv_xname); 142 return; 143 } 144 145 /* disable FIFO */ 146 HCLR4(sc, SPI_CFG, SPI_CFG_FIFO_ENABLE); 147 HCLR4(sc, SPI_CFG, SPI_CFG_BYTE_LEN); 148 149 rw_init(&sc->sc_buslock, sc->sc_dev.dv_xname); 150 151 sc->sc_tag.sc_cookie = sc; 152 sc->sc_tag.sc_config = mvspi_config; 153 sc->sc_tag.sc_transfer = mvspi_transfer; 154 sc->sc_tag.sc_acquire_bus = mvspi_acquire_bus; 155 sc->sc_tag.sc_release_bus = mvspi_release_bus; 156 157 mvspi_scan(sc); 158 } 159 160 int 161 mvspi_detach(struct device *self, int flags) 162 { 163 struct mvspi_softc *sc = (struct mvspi_softc *)self; 164 165 bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios); 166 return 0; 167 } 168 169 void 170 mvspi_config(void *cookie, struct spi_config *conf) 171 { 172 struct mvspi_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 HCLR4(sc, SPI_CFG, SPI_CFG_PRESCALE_MASK); 184 HSET4(sc, SPI_CFG, mvspi_clkdiv(sc, conf->sc_freq)); 185 186 if (conf->sc_flags & SPI_CONFIG_CPHA) 187 HSET4(sc, SPI_CFG, SPI_CFG_CPHA); 188 if (conf->sc_flags & SPI_CONFIG_CPOL) 189 HSET4(sc, SPI_CFG, SPI_CFG_CPOL); 190 } 191 192 uint32_t 193 mvspi_clkdiv(struct mvspi_softc *sc, uint32_t freq) 194 { 195 uint32_t pre; 196 197 pre = 0; 198 while ((freq * pre) < sc->sc_pfreq) 199 pre++; 200 if (pre > 0x1f) 201 pre = 0x1f; 202 else if (pre > 0xf) 203 pre = 0x10 + (pre + 1) / 2; 204 205 return pre; 206 } 207 208 int 209 mvspi_wait_state(struct mvspi_softc *sc, uint32_t mask, uint32_t value) 210 { 211 uint32_t state; 212 int timeout; 213 214 state = HREAD4(sc, SPI_CTRL); 215 for (timeout = 1000; timeout > 0; timeout--) { 216 if (((state = HREAD4(sc, SPI_CTRL)) & mask) == value) 217 return 0; 218 delay(10); 219 } 220 printf("%s: timeout mask %x value %x\n", __func__, mask, value); 221 return ETIMEDOUT; 222 } 223 224 void 225 mvspi_set_cs(struct mvspi_softc *sc, int cs, int on) 226 { 227 if (on) 228 HSET4(sc, SPI_CTRL, SPI_CTRL_CS(cs)); 229 else 230 HCLR4(sc, SPI_CTRL, SPI_CTRL_CS(cs)); 231 } 232 233 int 234 mvspi_transfer(void *cookie, char *out, char *in, int len, int flags) 235 { 236 struct mvspi_softc *sc = cookie; 237 int i = 0; 238 239 mvspi_set_cs(sc, sc->sc_cs, 1); 240 delay(sc->sc_cs_delay); 241 242 while (i < len) { 243 if (mvspi_wait_state(sc, SPI_CTRL_XFER_READY, 244 SPI_CTRL_XFER_READY)) 245 goto err; 246 if (out) 247 HWRITE4(sc, SPI_DOUT, out[i]); 248 else 249 HWRITE4(sc, SPI_DOUT, 0x0); 250 251 if (in) { 252 if (mvspi_wait_state(sc, SPI_CTRL_XFER_READY, 253 SPI_CTRL_XFER_READY)) 254 goto err; 255 in[i] = HREAD4(sc, SPI_DIN); 256 } 257 258 i++; 259 } 260 261 if (!ISSET(flags, SPI_KEEP_CS)) 262 mvspi_set_cs(sc, sc->sc_cs, 0); 263 return 0; 264 265 err: 266 mvspi_set_cs(sc, sc->sc_cs, 0); 267 return ETIMEDOUT; 268 } 269 270 int 271 mvspi_acquire_bus(void *cookie, int flags) 272 { 273 struct mvspi_softc *sc = cookie; 274 275 rw_enter(&sc->sc_buslock, RW_WRITE); 276 return 0; 277 } 278 279 void 280 mvspi_release_bus(void *cookie, int flags) 281 { 282 struct mvspi_softc *sc = cookie; 283 284 rw_exit(&sc->sc_buslock); 285 } 286 287 void 288 mvspi_scan(struct mvspi_softc *sc) 289 { 290 struct spi_attach_args sa; 291 uint32_t reg[1]; 292 char name[32]; 293 int node; 294 295 for (node = OF_child(sc->sc_node); node; node = OF_peer(node)) { 296 memset(name, 0, sizeof(name)); 297 memset(reg, 0, sizeof(reg)); 298 299 if (OF_getprop(node, "compatible", name, sizeof(name)) == -1) 300 continue; 301 if (name[0] == '\0') 302 continue; 303 304 if (OF_getprop(node, "reg", ®, sizeof(reg)) != sizeof(reg)) 305 continue; 306 307 memset(&sa, 0, sizeof(sa)); 308 sa.sa_tag = &sc->sc_tag; 309 sa.sa_name = name; 310 sa.sa_cookie = &node; 311 312 config_found(&sc->sc_dev, &sa, NULL); 313 } 314 } 315