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