#include <sys/cdefs.h>
__KERNEL_RCSID(0, "$NetBSD: ralink_i2c.c,v 1.10 2025/12/10 21:33:03 andvar Exp $");
#include <sys/param.h>
#include <sys/bus.h>
#include <sys/device.h>
#include <sys/errno.h>
#include <sys/kernel.h>
#include <sys/proc.h>
#include <sys/systm.h>
#include <dev/i2c/i2cvar.h>
#include <mips/ralink/ralink_var.h>
#include <mips/ralink/ralink_reg.h>
#if 0
#define CLKDIV_VALUE 533
#endif
#define CLKDIV_VALUE 4264
#define i2c_busy_loop (clkdiv*30)
#define max_ee_busy_loop (clkdiv*25)
typedef struct ra_i2c_softc {
device_t sc_dev;
struct i2c_controller sc_i2c;
bus_space_tag_t sc_memt;
bus_space_handle_t sc_i2c_memh;
bus_space_handle_t sc_sy_memh;
} ra_i2c_softc_t;
static int ra_i2c_match(device_t, cfdata_t, void *);
static void ra_i2c_attach(device_t, device_t, void *);
static int i2c_write(ra_i2c_softc_t *, u_long, const u_char *, u_long);
static int i2c_read(ra_i2c_softc_t *, u_long, u_char *, u_long);
#ifdef NOTYET
static void i2c_write_stop(ra_i2c_softc_t *, u_long, u_char *);
static void i2c_read_stop(ra_i2c_softc_t *, u_long, u_char *);
#endif
int ra_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t,
void *, size_t, int);
void ra_i2c_reset(ra_i2c_softc_t *);
CFATTACH_DECL_NEW(ri2c, sizeof(struct ra_i2c_softc),
ra_i2c_match, ra_i2c_attach, NULL, NULL);
unsigned int clkdiv = CLKDIV_VALUE;
static inline void
ra_i2c_busy_wait(ra_i2c_softc_t *sc)
{
for (int i=0; i < i2c_busy_loop; i++) {
uint32_t r;
r = bus_space_read_4(sc->sc_memt, sc->sc_i2c_memh,
RA_I2C_STATUS);
if ((r & I2C_STATUS_BUSY) == 0)
break;
}
}
int
ra_i2c_match(device_t parent, cfdata_t cf, void *aux)
{
return 1;
}
void
ra_i2c_attach(device_t parent, device_t self, void *aux)
{
ra_i2c_softc_t * const sc = device_private(self);
const struct mainbus_attach_args *ma = aux;
uint32_t r;
int error;
aprint_naive(": Ralink I2C controller\n");
aprint_normal(": Ralink I2C controller\n");
sc->sc_memt = ma->ma_memt;
if ((error = bus_space_map(ma->ma_memt, RA_SYSCTL_BASE, 0x10000,
0, &sc->sc_sy_memh)) != 0) {
aprint_error_dev(self, "unable to map Sysctl registers, "
"error=%d\n", error);
return;
}
if ((error = bus_space_map(sc->sc_memt, RA_I2C_BASE, 0x100,
0, &sc->sc_i2c_memh)) != 0) {
aprint_error_dev(self, "unable to map registers, "
"error=%d\n", error);
bus_space_unmap(ma->ma_memt, sc->sc_sy_memh, 0x10000);
return;
}
r = bus_space_read_4(sc->sc_memt, sc->sc_sy_memh,
RA_SYSCTL_GPIOMODE);
r &= ~GPIOMODE_I2C;
bus_space_write_4(sc->sc_memt, sc->sc_sy_memh,
RA_SYSCTL_GPIOMODE, r);
iic_tag_init(&sc->sc_i2c);
sc->sc_i2c.ic_cookie = sc;
sc->sc_i2c.ic_exec = ra_i2c_exec;
iicbus_attach(self, &sc->sc_i2c);
}
int
ra_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmdbuf,
size_t cmdlen, void *buf, size_t len, int flags)
{
ra_i2c_softc_t * const sc = cookie;
KASSERT(addr <= 127);
if (addr > 127)
return -1;
bus_space_write_4(sc->sc_memt, sc->sc_i2c_memh, RA_I2C_DEVADDR,
addr);
ra_i2c_reset(sc);
switch(op) {
case I2C_OP_WRITE:
return i2c_write(sc, addr, cmdbuf, cmdlen);
break;
case I2C_OP_READ:
return i2c_read(sc, addr, buf, len);
break;
#ifdef NOTYET
case I2C_OP_WRITE_WITH_STOP:
i2c_write_stop(sc, addr, buf);
break;
case I2C_OP_READ_WITH_STOP:
i2c_read_stop(sc, addr, buf);
break;
#endif
default:
return -1;
}
return 0;
}
static int
i2c_write(ra_i2c_softc_t *sc, u_long addr, const u_char *data,
u_long nbytes)
{
uint32_t r;
int i, j;
bus_space_write_4(sc->sc_memt, sc->sc_i2c_memh, RA_I2C_DEVADDR,
addr);
bus_space_write_4(sc->sc_memt, sc->sc_i2c_memh, RA_I2C_BYTECNT,
nbytes - 1);
bus_space_write_4(sc->sc_memt, sc->sc_i2c_memh, RA_I2C_STARTXFR,
I2C_OP_WRITE);
for (i=0; i < nbytes; i++) {
for (j=0; j < max_ee_busy_loop; j++) {
r = bus_space_read_4(sc->sc_memt, sc->sc_i2c_memh,
RA_I2C_STATUS);
if ((r & I2C_STATUS_SDOEMPTY) != 0) {
bus_space_write_4(sc->sc_memt, sc->sc_i2c_memh,
RA_I2C_DATAOUT, data[i]);
break;
}
}
#if 0
if ((r & I2C_STATUS_ACKERR) != 0) {
aprint_error_dev(sc->sc_dev, "ACK error in %s\n",
__func__);
return EAGAIN;
}
#endif
if (j == max_ee_busy_loop) {
aprint_error_dev(sc->sc_dev, "timeout error in %s\n",
__func__);
return EAGAIN;
}
}
ra_i2c_busy_wait(sc);
return 0;
}
static int
i2c_read(ra_i2c_softc_t *sc, u_long addr, u_char *data, u_long nbytes)
{
bus_space_write_4(sc->sc_memt, sc->sc_i2c_memh, RA_I2C_DEVADDR,
addr);
bus_space_write_4(sc->sc_memt, sc->sc_i2c_memh, RA_I2C_BYTECNT,
nbytes - 1);
bus_space_write_4(sc->sc_memt, sc->sc_i2c_memh, RA_I2C_STARTXFR,
I2C_OP_READ);
for (u_int i = 0; i < nbytes; i++) {
u_long j;
uint32_t r;
for (j=0; j < max_ee_busy_loop; j++) {
r = bus_space_read_4(sc->sc_memt, sc->sc_i2c_memh,
RA_I2C_STATUS);
if ((r & I2C_STATUS_DATARDY) != 0) {
data[i] = bus_space_read_4(
sc->sc_memt, sc->sc_i2c_memh,
RA_I2C_DATAIN);
break;
}
}
#if 0
if ((r & I2C_STATUS_ACKERR) != 0) {
aprint_error_dev(sc->sc_dev, "ACK error in %s\n",
__func__);
return EAGAIN;
}
#endif
if (j == max_ee_busy_loop) {
aprint_error_dev(sc->sc_dev, "timeout error in %s\n",
__func__);
return EAGAIN;
}
}
ra_i2c_busy_wait(sc);
return 0;
}
#ifdef NOTYET
static void
i2c_write_stop(ra_i2c_softc_t *sc, u_long address, u_char *data)
{
}
static void
i2c_read_stop(ra_i2c_softc_t *sc, u_long address, u_char *data)
{
}
#endif
void
ra_i2c_reset(ra_i2c_softc_t *sc)
{
uint32_t r;
r = bus_space_read_4(sc->sc_memt, sc->sc_sy_memh, RA_SYSCTL_RST);
bus_space_write_4(sc->sc_memt, sc->sc_sy_memh, RA_SYSCTL_RST,
r | RST_I2C);
bus_space_write_4(sc->sc_memt, sc->sc_sy_memh, RA_SYSCTL_RST, r);
r = I2C_CONFIG_ADDRLEN(I2C_CONFIG_ADDRLEN_8) |
I2C_CONFIG_DEVADLEN(I2C_CONFIG_DEVADLEN_7) |
I2C_CONFIG_ADDRDIS;
bus_space_write_4(sc->sc_memt, sc->sc_i2c_memh, RA_I2C_CONFIG, r);
bus_space_write_4(sc->sc_memt, sc->sc_i2c_memh, RA_I2C_CLKDIV,
clkdiv);
}