#include <sys/param.h>
#include <sys/systm.h>
#include <sys/device.h>
#include <machine/bus.h>
#include <machine/fdt.h>
#include <dev/fdt/rsbvar.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_clock.h>
#include <dev/ofw/ofw_pinctrl.h>
#include <dev/ofw/fdt.h>
#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;
}
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);
}