/* $OpenBSD: sxirsb.c,v 1.6 2022/07/09 20:52:46 kettenis Exp $ */ /* * Copyright (c) 2017 Mark Kettenis * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include #include #include #include #include #include #include #include #include #include #define RSB_CTRL 0x0000 #define RSB_CTRL_START_TRANS (1 << 7) #define RSB_CTRL_ABORT_TRANS (1 << 6) #define RSB_CTRL_GLOBAL_INT_ENB (1 << 1) #define RSB_CTRL_SOFT_RESET (1 << 0) #define RSB_CCR 0x0004 #define RSB_CCR_CD_ODLY_SHIFT 8 #define RSB_CCR_CD_ODLY_MAX 0x7 #define RSB_CCR_CK_DIV_SHIFT 0 #define RSB_CCR_CK_DIV_MAX 0xff #define RSB_STAT 0x000c #define RSB_STAT_TRANS_OVER (1 << 0) #define RSB_AR 0x0010 #define RSB_DATA 0x001c #define RSB_DMCR 0x0028 #define RSB_DMCR_DEVICE_MODE_START (1U << 31) #define RSB_DMCR_DEVICE_MODE_DATA 0x7c3e00 #define RSB_CMD 0x002c #define RSB_DAR 0x0030 #define SRTA 0xe8 #define RD8 0x8b #define RD16 0x9c #define RD32 0xa6 #define WR8 0x4e #define WR16 0x59 #define WR32 0x63 #define HREAD4(sc, reg) \ (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) #define HWRITE4(sc, reg, val) \ bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) #define HSET4(sc, reg, bits) \ HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) #define HCLR4(sc, reg, bits) \ HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) struct sxirsb_softc { struct device sc_dev; bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh; int sc_node; int sc_addr; }; int sxirsb_match(struct device *, void *, void *); void sxirsb_attach(struct device *, struct device *, void *); int sxirsb_activate(struct device *, int); const struct cfattach sxirsb_ca = { sizeof(struct sxirsb_softc), sxirsb_match, sxirsb_attach, NULL, sxirsb_activate }; struct cfdriver sxirsb_cd = { NULL, "sxirsb", DV_DULL }; int sxirsb_init(struct sxirsb_softc *); uint8_t sxirsb_rta(uint16_t); int sxirsb_match(struct device *parent, void *match, void *aux) { struct fdt_attach_args *faa = aux; return OF_is_compatible(faa->fa_node, "allwinner,sun8i-a23-rsb"); } void sxirsb_attach(struct device *parent, struct device *self, void *aux) { struct sxirsb_softc *sc = (struct sxirsb_softc *)self; struct fdt_attach_args *faa = aux; struct rsb_attach_args ra; char name[32]; uint32_t reg; int node; if (faa->fa_nreg < 1) { printf(": no registers\n"); return; } sc->sc_node = faa->fa_node; sc->sc_iot = faa->fa_iot; if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, faa->fa_reg[0].size, 0, &sc->sc_ioh)) { printf(": can't map registers\n"); return; } printf("\n"); if (sxirsb_init(sc)) return; for (node = OF_child(faa->fa_node); node; node = OF_peer(node)) { reg = OF_getpropint(node, "reg", 0); if (reg == 0) continue; memset(name, 0, sizeof(name)); if (OF_getprop(node, "compatible", name, sizeof(name)) == -1) continue; if (name[0] == '\0') continue; memset(&ra, 0, sizeof(ra)); ra.ra_cookie = sc; ra.ra_da = reg; ra.ra_rta = sxirsb_rta(reg); ra.ra_name = name; ra.ra_node = node; config_found(self, &ra, rsb_print); } } int sxirsb_activate(struct device *self, int act) { struct sxirsb_softc *sc = (struct sxirsb_softc *)self; int error = 0; switch (act) { case DVACT_RESUME: error = sxirsb_init(sc); if (error) return error; error = config_activate_children(self, act); break; default: error = config_activate_children(self, act); break; } return error; } int sxirsb_init(struct sxirsb_softc *sc) { uint32_t freq, parent_freq, div, odly; uint32_t reg; uint8_t rta; int node; int timo; pinctrl_byname(sc->sc_node, "default"); clock_enable_all(sc->sc_node); reset_deassert_all(sc->sc_node); HWRITE4(sc, RSB_CTRL, RSB_CTRL_SOFT_RESET); for (timo = 1000; timo > 0; timo--) { if ((HREAD4(sc, RSB_CTRL) & RSB_CTRL_SOFT_RESET) == 0) break; delay(100); } if (timo == 0) { printf("%s: reset failed\n", sc->sc_dev.dv_xname); return EIO; } freq = OF_getpropint(sc->sc_node, "clock-frequency", 3000000); parent_freq = clock_get_frequency_idx(sc->sc_node, 0); div = parent_freq / freq / 2; if (div == 0) div = 1; if (div > (RSB_CCR_CK_DIV_MAX + 1)) div = (RSB_CCR_CK_DIV_MAX + 1); odly = div >> 1; if (odly == 0) odly = 1; if (odly > RSB_CCR_CD_ODLY_MAX) odly = RSB_CCR_CD_ODLY_MAX; HWRITE4(sc, RSB_CCR, (odly << RSB_CCR_CD_ODLY_SHIFT) | ((div - 1) << RSB_CCR_CK_DIV_SHIFT)); HWRITE4(sc, RSB_DMCR, RSB_DMCR_DEVICE_MODE_START | RSB_DMCR_DEVICE_MODE_DATA); for (timo = 1000; timo > 0; timo--) { if ((HREAD4(sc, RSB_DMCR) & RSB_DMCR_DEVICE_MODE_START) == 0) break; delay(100); } if (timo == 0) { printf("%s: mode switch failed\n", sc->sc_dev.dv_xname); return EIO; } for (node = OF_child(sc->sc_node); node; node = OF_peer(node)) { reg = OF_getpropint(node, "reg", 0); if (reg == 0) continue; rta = sxirsb_rta(reg); HWRITE4(sc, RSB_CMD, SRTA); HWRITE4(sc, RSB_DAR, (rta << 16 | reg)); HSET4(sc, RSB_CTRL, RSB_CTRL_START_TRANS); for (timo = 1000; timo > 0; timo--) { if ((HREAD4(sc, RSB_CTRL) & RSB_CTRL_START_TRANS) == 0) break; delay(10); } if (timo == 0) { printf("%s: SRTA failed for device 0x%03x\n", sc->sc_dev.dv_xname, reg); return EIO; } } return 0; } /* * Use a fixed device address to run-time address mapping. This keeps * things simple and matches what Linux does. */ struct rsb_addr_map { uint16_t da; uint8_t rta; }; struct rsb_addr_map rsb_addr_map[] = { { 0x3a3, 0x2d }, { 0x745, 0x3a }, { 0xe89, 0x4e } }; uint8_t sxirsb_rta(uint16_t da) { int i; for (i = 0; i < nitems(rsb_addr_map); i++) { if (rsb_addr_map[i].da == da) return rsb_addr_map[i].rta; } return 0; } int sxirsb_do_trans(struct sxirsb_softc *sc) { uint16_t stat; int timo; HSET4(sc, RSB_CTRL, RSB_CTRL_START_TRANS); for (timo = 1000; timo > 0; timo--) { if ((HREAD4(sc, RSB_CTRL) & RSB_CTRL_START_TRANS) == 0) break; delay(10); } stat = HREAD4(sc, RSB_STAT); HWRITE4(sc, RSB_STAT, stat); if (stat != RSB_STAT_TRANS_OVER) return EIO; if (timo == 0) return ETIMEDOUT; return 0; } uint8_t rsb_read_1(void *cookie, uint8_t rta, uint8_t addr) { struct sxirsb_softc *sc = cookie; HWRITE4(sc, RSB_CMD, RD8); HWRITE4(sc, RSB_DAR, rta << 16); HWRITE4(sc, RSB_AR, addr); if (sxirsb_do_trans(sc)) { printf("%s: RD8 failed for run-time address 0x%02x\n", sc->sc_dev.dv_xname, rta); return 0xff; } return HREAD4(sc, RSB_DATA); } uint16_t rsb_read_2(void *cookie, uint8_t rta, uint8_t addr) { struct sxirsb_softc *sc = cookie; HWRITE4(sc, RSB_CMD, RD16); HWRITE4(sc, RSB_DAR, rta << 16); HWRITE4(sc, RSB_AR, addr); if (sxirsb_do_trans(sc)) { printf("%s: RD16 failed for run-time address 0x%02x\n", sc->sc_dev.dv_xname, rta); return 0xff; } return HREAD4(sc, RSB_DATA); } void rsb_write_1(void *cookie, uint8_t rta, uint8_t addr, uint8_t data) { struct sxirsb_softc *sc = cookie; HWRITE4(sc, RSB_CMD, WR8); HWRITE4(sc, RSB_DAR, rta << 16); HWRITE4(sc, RSB_AR, addr); HWRITE4(sc, RSB_DATA, data); if (sxirsb_do_trans(sc)) { printf("%s: WR8 failed for run-time address 0x%02x\n", sc->sc_dev.dv_xname, rta); return; } } void rsb_write_2(void *cookie, uint8_t rta, uint8_t addr, uint16_t data) { struct sxirsb_softc *sc = cookie; HWRITE4(sc, RSB_CMD, WR16); HWRITE4(sc, RSB_DAR, rta << 16); HWRITE4(sc, RSB_AR, addr); HWRITE4(sc, RSB_DATA, data); if (sxirsb_do_trans(sc)) { printf("%s: WR16 failed for run-time address 0x%02x\n", sc->sc_dev.dv_xname, rta); return; } } int rsb_print(void *aux, const char *pnp) { struct rsb_attach_args *ra = aux; if (pnp != NULL) printf("\"%s\" at %s", ra->ra_name, pnp); printf(" addr 0x%x", ra->ra_da); return (UNCONF); }