1*ddc4c969Skettenis /* $OpenBSD: qciic_fdt.c,v 1.2 2024/10/17 17:58:58 kettenis Exp $ */ 204270302Spatrick /* 304270302Spatrick * Copyright (c) 2022 Mark Kettenis <kettenis@openbsd.org> 404270302Spatrick * 504270302Spatrick * Permission to use, copy, modify, and distribute this software for any 604270302Spatrick * purpose with or without fee is hereby granted, provided that the above 704270302Spatrick * copyright notice and this permission notice appear in all copies. 804270302Spatrick * 904270302Spatrick * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1004270302Spatrick * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1104270302Spatrick * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1204270302Spatrick * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1304270302Spatrick * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1404270302Spatrick * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1504270302Spatrick * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1604270302Spatrick */ 1704270302Spatrick 1804270302Spatrick #include <sys/param.h> 1904270302Spatrick #include <sys/systm.h> 2004270302Spatrick #include <sys/device.h> 2104270302Spatrick 2204270302Spatrick #include <machine/bus.h> 2304270302Spatrick #include <machine/intr.h> 2404270302Spatrick #include <machine/fdt.h> 2504270302Spatrick 2604270302Spatrick #include <dev/ofw/openfirm.h> 2704270302Spatrick #include <dev/ofw/ofw_gpio.h> 28*ddc4c969Skettenis #include <dev/ofw/ofw_pinctrl.h> 2904270302Spatrick #include <dev/ofw/fdt.h> 3004270302Spatrick 3104270302Spatrick #define _I2C_PRIVATE 3204270302Spatrick #include <dev/i2c/i2cvar.h> 3304270302Spatrick 3404270302Spatrick /* Registers */ 3504270302Spatrick #define GENI_I2C_TX_TRANS_LEN 0x26c 3604270302Spatrick #define GENI_I2C_RX_TRANS_LEN 0x270 3704270302Spatrick #define GENI_M_CMD0 0x600 3804270302Spatrick #define GENI_M_CMD0_OPCODE_I2C_WRITE (0x1 << 27) 3904270302Spatrick #define GENI_M_CMD0_OPCODE_I2C_READ (0x2 << 27) 4004270302Spatrick #define GENI_M_CMD0_SLV_ADDR_SHIFT 9 4104270302Spatrick #define GENI_M_CMD0_STOP_STRETCH (1 << 2) 4204270302Spatrick #define GENI_M_IRQ_STATUS 0x610 4304270302Spatrick #define GENI_M_IRQ_CLEAR 0x618 4404270302Spatrick #define GENI_M_IRQ_CMD_DONE (1 << 0) 4504270302Spatrick #define GENI_TX_FIFO 0x700 4604270302Spatrick #define GENI_RX_FIFO 0x780 4704270302Spatrick #define GENI_TX_FIFO_STATUS 0x800 4804270302Spatrick #define GENI_RX_FIFO_STATUS 0x804 4904270302Spatrick #define GENI_RX_FIFO_STATUS_WC(val) ((val) & 0xffffff) 5004270302Spatrick 5104270302Spatrick #define HREAD4(sc, reg) \ 5204270302Spatrick (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) 5304270302Spatrick #define HWRITE4(sc, reg, val) \ 5404270302Spatrick bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 5504270302Spatrick 5604270302Spatrick struct qciic_fdt_softc { 5704270302Spatrick struct device sc_dev; 5804270302Spatrick bus_space_tag_t sc_iot; 5904270302Spatrick bus_space_handle_t sc_ioh; 6004270302Spatrick 6104270302Spatrick int sc_node; 6204270302Spatrick struct device *sc_iic; 6304270302Spatrick 6404270302Spatrick struct i2c_controller sc_ic; 6504270302Spatrick }; 6604270302Spatrick 6704270302Spatrick int qciic_fdt_match(struct device *, void *, void *); 6804270302Spatrick void qciic_fdt_attach(struct device *, struct device *, void *); 6904270302Spatrick 7004270302Spatrick const struct cfattach qciic_fdt_ca = { 7104270302Spatrick sizeof (struct qciic_fdt_softc), qciic_fdt_match, qciic_fdt_attach 7204270302Spatrick }; 7304270302Spatrick 7404270302Spatrick int qciic_fdt_acquire_bus(void *, int); 7504270302Spatrick void qciic_fdt_release_bus(void *, int); 7604270302Spatrick int qciic_fdt_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t, 7704270302Spatrick void *, size_t, int); 7804270302Spatrick 7904270302Spatrick void *qciic_fdt_i2c_intr_establish(void *, void *, int, int (*)(void *), 8004270302Spatrick void *, const char *); 8104270302Spatrick void qciic_fdt_i2c_intr_disestablish(void *, void *); 8204270302Spatrick const char *qciic_fdt_i2c_intr_string(void *, void *); 8304270302Spatrick 8404270302Spatrick void qciic_fdt_bus_scan(struct device *, struct i2cbus_attach_args *, 8504270302Spatrick void *); 8604270302Spatrick 8704270302Spatrick int 8804270302Spatrick qciic_fdt_match(struct device *parent, void *match, void *aux) 8904270302Spatrick { 9004270302Spatrick struct fdt_attach_args *faa = aux; 9104270302Spatrick 9204270302Spatrick return OF_is_compatible(faa->fa_node, "qcom,geni-i2c"); 9304270302Spatrick } 9404270302Spatrick 9504270302Spatrick void 9604270302Spatrick qciic_fdt_attach(struct device *parent, struct device *self, void *aux) 9704270302Spatrick { 9804270302Spatrick struct qciic_fdt_softc *sc = (struct qciic_fdt_softc *)self; 9904270302Spatrick struct fdt_attach_args *faa = aux; 10004270302Spatrick struct i2cbus_attach_args iba; 10104270302Spatrick 10204270302Spatrick sc->sc_node = faa->fa_node; 10304270302Spatrick 10404270302Spatrick sc->sc_iot = faa->fa_iot; 10504270302Spatrick if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, faa->fa_reg[0].size, 10604270302Spatrick 0, &sc->sc_ioh)) { 10704270302Spatrick printf(": can't map registers\n"); 10804270302Spatrick return; 10904270302Spatrick } 11004270302Spatrick 11104270302Spatrick printf("\n"); 11204270302Spatrick 113*ddc4c969Skettenis pinctrl_byname(sc->sc_node, "default"); 114*ddc4c969Skettenis 11504270302Spatrick sc->sc_ic.ic_cookie = sc; 11604270302Spatrick sc->sc_ic.ic_acquire_bus = qciic_fdt_acquire_bus; 11704270302Spatrick sc->sc_ic.ic_release_bus = qciic_fdt_release_bus; 11804270302Spatrick sc->sc_ic.ic_exec = qciic_fdt_exec; 11904270302Spatrick sc->sc_ic.ic_intr_establish = qciic_fdt_i2c_intr_establish; 12004270302Spatrick sc->sc_ic.ic_intr_disestablish = qciic_fdt_i2c_intr_disestablish; 12104270302Spatrick sc->sc_ic.ic_intr_string = qciic_fdt_i2c_intr_string; 12204270302Spatrick 12304270302Spatrick memset(&iba, 0, sizeof(iba)); 12404270302Spatrick iba.iba_name = "iic"; 12504270302Spatrick iba.iba_tag = &sc->sc_ic; 12604270302Spatrick iba.iba_bus_scan = qciic_fdt_bus_scan; 12704270302Spatrick iba.iba_bus_scan_arg = &sc->sc_node; 12804270302Spatrick config_found(&sc->sc_dev, &iba, iicbus_print); 12904270302Spatrick } 13004270302Spatrick 13104270302Spatrick int 13204270302Spatrick qciic_fdt_acquire_bus(void *cookie, int flags) 13304270302Spatrick { 13404270302Spatrick return 0; 13504270302Spatrick } 13604270302Spatrick 13704270302Spatrick void 13804270302Spatrick qciic_fdt_release_bus(void *cookie, int flags) 13904270302Spatrick { 14004270302Spatrick } 14104270302Spatrick 14204270302Spatrick int 14304270302Spatrick qciic_fdt_wait(struct qciic_fdt_softc *sc, uint32_t bits) 14404270302Spatrick { 14504270302Spatrick uint32_t stat; 14604270302Spatrick int timo; 14704270302Spatrick 14804270302Spatrick for (timo = 50000; timo > 0; timo--) { 14904270302Spatrick stat = HREAD4(sc, GENI_M_IRQ_STATUS); 15004270302Spatrick if (stat & bits) 15104270302Spatrick break; 15204270302Spatrick delay(10); 15304270302Spatrick } 15404270302Spatrick if (timo == 0) 15504270302Spatrick return ETIMEDOUT; 15604270302Spatrick 15704270302Spatrick return 0; 15804270302Spatrick } 15904270302Spatrick 16004270302Spatrick int 16104270302Spatrick qciic_fdt_read(struct qciic_fdt_softc *sc, uint8_t *buf, size_t len) 16204270302Spatrick { 16304270302Spatrick uint32_t stat, word; 16404270302Spatrick int timo, i; 16504270302Spatrick 16604270302Spatrick word = 0; 16704270302Spatrick for (i = 0; i < len; i++) { 16804270302Spatrick if ((i % 4) == 0) { 16904270302Spatrick for (timo = 50000; timo > 0; timo--) { 17004270302Spatrick stat = HREAD4(sc, GENI_RX_FIFO_STATUS); 17104270302Spatrick if (GENI_RX_FIFO_STATUS_WC(stat) > 0) 17204270302Spatrick break; 17304270302Spatrick delay(10); 17404270302Spatrick } 17504270302Spatrick if (timo == 0) 17604270302Spatrick return ETIMEDOUT; 17704270302Spatrick word = HREAD4(sc, GENI_RX_FIFO); 17804270302Spatrick } 17904270302Spatrick buf[i] = word >> ((i % 4) * 8); 18004270302Spatrick } 18104270302Spatrick 18204270302Spatrick return 0; 18304270302Spatrick } 18404270302Spatrick 18504270302Spatrick int 18604270302Spatrick qciic_fdt_write(struct qciic_fdt_softc *sc, const uint8_t *buf, size_t len) 18704270302Spatrick { 18804270302Spatrick uint32_t stat, word; 18904270302Spatrick int timo, i; 19004270302Spatrick 19104270302Spatrick word = 0; 19204270302Spatrick for (i = 0; i < len; i++) { 19304270302Spatrick word |= buf[i] << ((i % 4) * 8); 19404270302Spatrick if ((i % 4) == 3 || i == (len - 1)) { 19504270302Spatrick for (timo = 50000; timo > 0; timo--) { 19604270302Spatrick stat = HREAD4(sc, GENI_TX_FIFO_STATUS); 19704270302Spatrick if (stat < 16) 19804270302Spatrick break; 19904270302Spatrick delay(10); 20004270302Spatrick } 20104270302Spatrick if (timo == 0) 20204270302Spatrick return ETIMEDOUT; 20304270302Spatrick HWRITE4(sc, GENI_TX_FIFO, word); 20404270302Spatrick word = 0; 20504270302Spatrick } 20604270302Spatrick } 20704270302Spatrick 20804270302Spatrick return 0; 20904270302Spatrick } 21004270302Spatrick 21104270302Spatrick int 21204270302Spatrick qciic_fdt_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmd, 21304270302Spatrick size_t cmdlen, void *buf, size_t buflen, int flags) 21404270302Spatrick { 21504270302Spatrick struct qciic_fdt_softc *sc = cookie; 21604270302Spatrick uint32_t m_cmd, m_param, stat; 21704270302Spatrick int error; 21804270302Spatrick 21904270302Spatrick m_param = addr << GENI_M_CMD0_SLV_ADDR_SHIFT; 22004270302Spatrick m_param |= GENI_M_CMD0_STOP_STRETCH; 22104270302Spatrick 22204270302Spatrick if (buflen == 0 && I2C_OP_STOP_P(op)) 22304270302Spatrick m_param &= ~GENI_M_CMD0_STOP_STRETCH; 22404270302Spatrick 22504270302Spatrick if (cmdlen > 0) { 22604270302Spatrick stat = HREAD4(sc, GENI_M_IRQ_STATUS); 22704270302Spatrick HWRITE4(sc, GENI_M_IRQ_CLEAR, stat); 22804270302Spatrick HWRITE4(sc, GENI_I2C_TX_TRANS_LEN, cmdlen); 22904270302Spatrick m_cmd = GENI_M_CMD0_OPCODE_I2C_WRITE | m_param; 23004270302Spatrick HWRITE4(sc, GENI_M_CMD0, m_cmd); 23104270302Spatrick 23204270302Spatrick error = qciic_fdt_write(sc, cmd, cmdlen); 23304270302Spatrick if (error) 23404270302Spatrick return error; 23504270302Spatrick 23604270302Spatrick error = qciic_fdt_wait(sc, GENI_M_IRQ_CMD_DONE); 23704270302Spatrick if (error) 23804270302Spatrick return error; 23904270302Spatrick } 24004270302Spatrick 24104270302Spatrick if (buflen == 0) 24204270302Spatrick return 0; 24304270302Spatrick 24404270302Spatrick if (I2C_OP_STOP_P(op)) 24504270302Spatrick m_param &= ~GENI_M_CMD0_STOP_STRETCH; 24604270302Spatrick 24704270302Spatrick if (I2C_OP_READ_P(op)) { 24804270302Spatrick stat = HREAD4(sc, GENI_M_IRQ_STATUS); 24904270302Spatrick HWRITE4(sc, GENI_M_IRQ_CLEAR, stat); 25004270302Spatrick HWRITE4(sc, GENI_I2C_RX_TRANS_LEN, buflen); 25104270302Spatrick m_cmd = GENI_M_CMD0_OPCODE_I2C_READ | m_param; 25204270302Spatrick HWRITE4(sc, GENI_M_CMD0, m_cmd); 25304270302Spatrick 25404270302Spatrick error = qciic_fdt_read(sc, buf, buflen); 25504270302Spatrick if (error) 25604270302Spatrick return error; 25704270302Spatrick 25804270302Spatrick error = qciic_fdt_wait(sc, GENI_M_IRQ_CMD_DONE); 25904270302Spatrick if (error) 26004270302Spatrick return error; 26104270302Spatrick } else { 26204270302Spatrick stat = HREAD4(sc, GENI_M_IRQ_STATUS); 26304270302Spatrick HWRITE4(sc, GENI_M_IRQ_CLEAR, stat); 26404270302Spatrick HWRITE4(sc, GENI_I2C_TX_TRANS_LEN, buflen); 26504270302Spatrick m_cmd = GENI_M_CMD0_OPCODE_I2C_WRITE | m_param; 26604270302Spatrick HWRITE4(sc, GENI_M_CMD0, m_cmd); 26704270302Spatrick 26804270302Spatrick error = qciic_fdt_write(sc, buf, buflen); 26904270302Spatrick if (error) 27004270302Spatrick return error; 27104270302Spatrick 27204270302Spatrick error = qciic_fdt_wait(sc, GENI_M_IRQ_CMD_DONE); 27304270302Spatrick if (error) 27404270302Spatrick return error; 27504270302Spatrick } 27604270302Spatrick 27704270302Spatrick return 0; 27804270302Spatrick } 27904270302Spatrick 28004270302Spatrick void * 28104270302Spatrick qciic_fdt_i2c_intr_establish(void *cookie, void *ih, int level, 28204270302Spatrick int (*func)(void *), void *arg, const char *name) 28304270302Spatrick { 28404270302Spatrick int node = *(int *)ih; 28504270302Spatrick 28604270302Spatrick return fdt_intr_establish(node, level, func, arg, (char *)name); 28704270302Spatrick } 28804270302Spatrick 28904270302Spatrick void 29004270302Spatrick qciic_fdt_i2c_intr_disestablish(void *cookie, void *ih) 29104270302Spatrick { 29204270302Spatrick fdt_intr_disestablish(ih); 29304270302Spatrick } 29404270302Spatrick 29504270302Spatrick const char * 29604270302Spatrick qciic_fdt_i2c_intr_string(void *cookie, void *ih) 29704270302Spatrick { 29804270302Spatrick static char irqstr[64]; 29904270302Spatrick 30004270302Spatrick snprintf(irqstr, sizeof(irqstr), "irq"); 30104270302Spatrick 30204270302Spatrick return irqstr; 30304270302Spatrick } 30404270302Spatrick 30504270302Spatrick void 30604270302Spatrick qciic_fdt_bus_scan(struct device *self, struct i2cbus_attach_args *iba, void *aux) 30704270302Spatrick { 30804270302Spatrick int iba_node = *(int *)aux; 30904270302Spatrick extern int iic_print(void *, const char *); 31004270302Spatrick struct i2c_attach_args ia; 31104270302Spatrick char name[32], status[32]; 31204270302Spatrick uint32_t reg[1]; 31304270302Spatrick int node; 31404270302Spatrick 31504270302Spatrick for (node = OF_child(iba_node); node; node = OF_peer(node)) { 31604270302Spatrick memset(name, 0, sizeof(name)); 31704270302Spatrick memset(status, 0, sizeof(status)); 31804270302Spatrick memset(reg, 0, sizeof(reg)); 31904270302Spatrick 32004270302Spatrick if (OF_getprop(node, "compatible", name, sizeof(name)) == -1) 32104270302Spatrick continue; 32204270302Spatrick if (name[0] == '\0') 32304270302Spatrick continue; 32404270302Spatrick 32504270302Spatrick if (OF_getprop(node, "status", status, sizeof(status)) > 0 && 32604270302Spatrick strcmp(status, "disabled") == 0) 32704270302Spatrick continue; 32804270302Spatrick 32904270302Spatrick if (OF_getprop(node, "reg", ®, sizeof(reg)) != sizeof(reg)) 33004270302Spatrick continue; 33104270302Spatrick 33204270302Spatrick memset(&ia, 0, sizeof(ia)); 33304270302Spatrick ia.ia_tag = iba->iba_tag; 33404270302Spatrick ia.ia_addr = bemtoh32(®[0]); 33504270302Spatrick ia.ia_name = name; 33604270302Spatrick ia.ia_cookie = &node; 33704270302Spatrick ia.ia_intr = &node; 33804270302Spatrick 33904270302Spatrick /* Quirk for ihidev(4) */ 34004270302Spatrick if (strcmp(name, "hid-over-i2c") == 0) { 34104270302Spatrick ia.ia_name = "ihidev"; 34204270302Spatrick ia.ia_size = OF_getpropint(node, "hid-descr-addr", 0); 34304270302Spatrick ia.ia_cookie = name; 34404270302Spatrick } 34504270302Spatrick 34604270302Spatrick config_found(self, &ia, iic_print); 34704270302Spatrick } 34804270302Spatrick } 349