1 1.6 thorpej /* $NetBSD: qcomiic.c,v 1.6 2025/09/15 15:31:45 thorpej Exp $ */ 2 1.1 jmcneill 3 1.1 jmcneill /* $OpenBSD: qciic.c,v 1.7 2024/10/02 21:21:32 kettenis Exp $ */ 4 1.1 jmcneill /* 5 1.1 jmcneill * Copyright (c) 2022 Mark Kettenis <kettenis (at) openbsd.org> 6 1.1 jmcneill * 7 1.1 jmcneill * Permission to use, copy, modify, and distribute this software for any 8 1.1 jmcneill * purpose with or without fee is hereby granted, provided that the above 9 1.1 jmcneill * copyright notice and this permission notice appear in all copies. 10 1.1 jmcneill * 11 1.1 jmcneill * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 1.1 jmcneill * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 1.1 jmcneill * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 1.1 jmcneill * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 1.1 jmcneill * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 1.1 jmcneill * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 1.1 jmcneill * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 1.1 jmcneill */ 19 1.1 jmcneill 20 1.1 jmcneill #include <sys/param.h> 21 1.1 jmcneill #include <sys/bus.h> 22 1.1 jmcneill #include <sys/cpu.h> 23 1.1 jmcneill #include <sys/device.h> 24 1.1 jmcneill 25 1.1 jmcneill #include <dev/acpi/acpireg.h> 26 1.1 jmcneill #include <dev/acpi/acpivar.h> 27 1.1 jmcneill #include <dev/acpi/acpi_intr.h> 28 1.1 jmcneill 29 1.1 jmcneill #include <dev/i2c/i2cvar.h> 30 1.1 jmcneill 31 1.1 jmcneill /* Registers */ 32 1.1 jmcneill #define GENI_I2C_TX_TRANS_LEN 0x26c 33 1.1 jmcneill #define GENI_I2C_RX_TRANS_LEN 0x270 34 1.1 jmcneill #define GENI_M_CMD0 0x600 35 1.1 jmcneill #define GENI_M_CMD0_OPCODE_I2C_WRITE (0x1 << 27) 36 1.1 jmcneill #define GENI_M_CMD0_OPCODE_I2C_READ (0x2 << 27) 37 1.1 jmcneill #define GENI_M_CMD0_SLV_ADDR_SHIFT 9 38 1.1 jmcneill #define GENI_M_CMD0_STOP_STRETCH (1 << 2) 39 1.1 jmcneill #define GENI_M_IRQ_STATUS 0x610 40 1.1 jmcneill #define GENI_M_IRQ_CLEAR 0x618 41 1.1 jmcneill #define GENI_M_IRQ_CMD_DONE (1 << 0) 42 1.1 jmcneill #define GENI_TX_FIFO 0x700 43 1.1 jmcneill #define GENI_RX_FIFO 0x780 44 1.1 jmcneill #define GENI_TX_FIFO_STATUS 0x800 45 1.1 jmcneill #define GENI_RX_FIFO_STATUS 0x804 46 1.1 jmcneill #define GENI_RX_FIFO_STATUS_WC(val) ((val) & 0xffffff) 47 1.1 jmcneill 48 1.1 jmcneill #define HREAD4(sc, reg) \ 49 1.1 jmcneill bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)) 50 1.1 jmcneill #define HWRITE4(sc, reg, val) \ 51 1.1 jmcneill bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 52 1.1 jmcneill 53 1.1 jmcneill struct qciic_softc { 54 1.1 jmcneill device_t sc_dev; 55 1.1 jmcneill struct acpi_devnode *sc_acpi; 56 1.1 jmcneill bus_space_tag_t sc_iot; 57 1.1 jmcneill bus_space_handle_t sc_ioh; 58 1.1 jmcneill 59 1.1 jmcneill device_t sc_iic; 60 1.1 jmcneill 61 1.1 jmcneill struct i2c_controller sc_ic; 62 1.1 jmcneill }; 63 1.1 jmcneill 64 1.1 jmcneill static int qciic_acpi_match(device_t, cfdata_t, void *); 65 1.1 jmcneill static void qciic_acpi_attach(device_t, device_t, void *); 66 1.1 jmcneill static int qciic_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t, 67 1.1 jmcneill void *, size_t, int); 68 1.1 jmcneill 69 1.1 jmcneill CFATTACH_DECL_NEW(qcomiic, sizeof(struct qciic_softc), 70 1.1 jmcneill qciic_acpi_match, qciic_acpi_attach, NULL, NULL); 71 1.1 jmcneill 72 1.1 jmcneill static const struct device_compatible_entry compat_data[] = { 73 1.1 jmcneill { .compat = "QCOM0610" }, 74 1.1 jmcneill { .compat = "QCOM0811" }, 75 1.1 jmcneill { .compat = "QCOM0C10" }, 76 1.1 jmcneill DEVICE_COMPAT_EOL 77 1.1 jmcneill }; 78 1.1 jmcneill 79 1.1 jmcneill static int 80 1.1 jmcneill qciic_acpi_match(device_t parent, cfdata_t cf, void *aux) 81 1.1 jmcneill { 82 1.1 jmcneill struct acpi_attach_args *aa = aux; 83 1.1 jmcneill 84 1.1 jmcneill return acpi_compatible_match(aa, compat_data); 85 1.1 jmcneill } 86 1.1 jmcneill 87 1.1 jmcneill static void 88 1.1 jmcneill qciic_acpi_attach(device_t parent, device_t self, void *aux) 89 1.1 jmcneill { 90 1.1 jmcneill struct qciic_softc * const sc = device_private(self); 91 1.1 jmcneill struct acpi_attach_args *aa = aux; 92 1.1 jmcneill struct acpi_resources res; 93 1.1 jmcneill struct acpi_mem *mem; 94 1.1 jmcneill struct acpi_irq *irq; 95 1.1 jmcneill ACPI_STATUS rv; 96 1.1 jmcneill int error; 97 1.1 jmcneill 98 1.1 jmcneill sc->sc_dev = self; 99 1.1 jmcneill sc->sc_acpi = aa->aa_node; 100 1.1 jmcneill sc->sc_iot = aa->aa_memt; 101 1.1 jmcneill 102 1.1 jmcneill rv = acpi_resource_parse(sc->sc_dev, aa->aa_node->ad_handle, "_CRS", 103 1.1 jmcneill &res, &acpi_resource_parse_ops_default); 104 1.1 jmcneill if (ACPI_FAILURE(rv)) { 105 1.1 jmcneill return; 106 1.1 jmcneill } 107 1.1 jmcneill 108 1.1 jmcneill mem = acpi_res_mem(&res, 0); 109 1.1 jmcneill if (mem == NULL) { 110 1.1 jmcneill aprint_error_dev(self, "couldn't find mem resource\n"); 111 1.1 jmcneill goto done; 112 1.1 jmcneill } 113 1.1 jmcneill 114 1.1 jmcneill irq = acpi_res_irq(&res, 0); 115 1.1 jmcneill if (irq == NULL) { 116 1.1 jmcneill aprint_error_dev(self, "couldn't find irq resource\n"); 117 1.1 jmcneill goto done; 118 1.1 jmcneill } 119 1.1 jmcneill 120 1.1 jmcneill error = bus_space_map(sc->sc_iot, mem->ar_base, mem->ar_length, 0, 121 1.1 jmcneill &sc->sc_ioh); 122 1.1 jmcneill if (error != 0) { 123 1.1 jmcneill aprint_error_dev(self, "couldn't map registers\n"); 124 1.1 jmcneill return; 125 1.1 jmcneill } 126 1.1 jmcneill 127 1.1 jmcneill iic_tag_init(&sc->sc_ic); 128 1.1 jmcneill sc->sc_ic.ic_cookie = sc; 129 1.1 jmcneill sc->sc_ic.ic_exec = qciic_exec; 130 1.1 jmcneill 131 1.4 thorpej iicbus_attach(self, &sc->sc_ic); 132 1.3 jmcneill 133 1.3 jmcneill done: 134 1.3 jmcneill acpi_resource_cleanup(&res); 135 1.1 jmcneill } 136 1.1 jmcneill 137 1.1 jmcneill static int 138 1.1 jmcneill qciic_wait(struct qciic_softc *sc, uint32_t bits) 139 1.1 jmcneill { 140 1.1 jmcneill uint32_t stat; 141 1.1 jmcneill int timo; 142 1.1 jmcneill 143 1.1 jmcneill for (timo = 50000; timo > 0; timo--) { 144 1.1 jmcneill stat = HREAD4(sc, GENI_M_IRQ_STATUS); 145 1.1 jmcneill if (stat & bits) 146 1.1 jmcneill break; 147 1.1 jmcneill delay(10); 148 1.1 jmcneill } 149 1.1 jmcneill if (timo == 0) 150 1.1 jmcneill return ETIMEDOUT; 151 1.1 jmcneill 152 1.1 jmcneill return 0; 153 1.1 jmcneill } 154 1.1 jmcneill 155 1.1 jmcneill static int 156 1.1 jmcneill qciic_read(struct qciic_softc *sc, uint8_t *buf, size_t len) 157 1.1 jmcneill { 158 1.1 jmcneill uint32_t stat, word; 159 1.1 jmcneill int timo, i; 160 1.1 jmcneill 161 1.1 jmcneill word = 0; 162 1.1 jmcneill for (i = 0; i < len; i++) { 163 1.1 jmcneill if ((i % 4) == 0) { 164 1.1 jmcneill for (timo = 50000; timo > 0; timo--) { 165 1.1 jmcneill stat = HREAD4(sc, GENI_RX_FIFO_STATUS); 166 1.1 jmcneill if (GENI_RX_FIFO_STATUS_WC(stat) > 0) 167 1.1 jmcneill break; 168 1.1 jmcneill delay(10); 169 1.1 jmcneill } 170 1.1 jmcneill if (timo == 0) 171 1.1 jmcneill return ETIMEDOUT; 172 1.1 jmcneill word = HREAD4(sc, GENI_RX_FIFO); 173 1.1 jmcneill } 174 1.1 jmcneill buf[i] = word >> ((i % 4) * 8); 175 1.1 jmcneill } 176 1.1 jmcneill 177 1.1 jmcneill return 0; 178 1.1 jmcneill } 179 1.1 jmcneill 180 1.1 jmcneill static int 181 1.1 jmcneill qciic_write(struct qciic_softc *sc, const uint8_t *buf, size_t len) 182 1.1 jmcneill { 183 1.1 jmcneill uint32_t stat, word; 184 1.1 jmcneill int timo, i; 185 1.1 jmcneill 186 1.1 jmcneill word = 0; 187 1.1 jmcneill for (i = 0; i < len; i++) { 188 1.1 jmcneill word |= buf[i] << ((i % 4) * 8); 189 1.1 jmcneill if ((i % 4) == 3 || i == (len - 1)) { 190 1.1 jmcneill for (timo = 50000; timo > 0; timo--) { 191 1.1 jmcneill stat = HREAD4(sc, GENI_TX_FIFO_STATUS); 192 1.1 jmcneill if (stat < 16) 193 1.1 jmcneill break; 194 1.1 jmcneill delay(10); 195 1.1 jmcneill } 196 1.1 jmcneill if (timo == 0) 197 1.1 jmcneill return ETIMEDOUT; 198 1.1 jmcneill HWRITE4(sc, GENI_TX_FIFO, word); 199 1.1 jmcneill word = 0; 200 1.1 jmcneill } 201 1.1 jmcneill } 202 1.1 jmcneill 203 1.1 jmcneill return 0; 204 1.1 jmcneill } 205 1.1 jmcneill 206 1.1 jmcneill static int 207 1.1 jmcneill qciic_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmd, 208 1.1 jmcneill size_t cmdlen, void *buf, size_t buflen, int flags) 209 1.1 jmcneill { 210 1.1 jmcneill struct qciic_softc *sc = cookie; 211 1.1 jmcneill uint32_t m_cmd, m_param, stat; 212 1.1 jmcneill int error; 213 1.1 jmcneill 214 1.1 jmcneill m_param = addr << GENI_M_CMD0_SLV_ADDR_SHIFT; 215 1.1 jmcneill m_param |= GENI_M_CMD0_STOP_STRETCH; 216 1.1 jmcneill 217 1.1 jmcneill if (buflen == 0 && I2C_OP_STOP_P(op)) 218 1.1 jmcneill m_param &= ~GENI_M_CMD0_STOP_STRETCH; 219 1.1 jmcneill 220 1.1 jmcneill if (cmdlen > 0) { 221 1.1 jmcneill stat = HREAD4(sc, GENI_M_IRQ_STATUS); 222 1.1 jmcneill HWRITE4(sc, GENI_M_IRQ_CLEAR, stat); 223 1.1 jmcneill HWRITE4(sc, GENI_I2C_TX_TRANS_LEN, cmdlen); 224 1.1 jmcneill m_cmd = GENI_M_CMD0_OPCODE_I2C_WRITE | m_param; 225 1.1 jmcneill HWRITE4(sc, GENI_M_CMD0, m_cmd); 226 1.1 jmcneill 227 1.1 jmcneill error = qciic_write(sc, cmd, cmdlen); 228 1.1 jmcneill if (error) 229 1.1 jmcneill return error; 230 1.1 jmcneill 231 1.1 jmcneill error = qciic_wait(sc, GENI_M_IRQ_CMD_DONE); 232 1.1 jmcneill if (error) 233 1.1 jmcneill return error; 234 1.1 jmcneill } 235 1.1 jmcneill 236 1.1 jmcneill if (buflen == 0) 237 1.1 jmcneill return 0; 238 1.1 jmcneill 239 1.1 jmcneill if (I2C_OP_STOP_P(op)) 240 1.1 jmcneill m_param &= ~GENI_M_CMD0_STOP_STRETCH; 241 1.1 jmcneill 242 1.1 jmcneill if (I2C_OP_READ_P(op)) { 243 1.1 jmcneill stat = HREAD4(sc, GENI_M_IRQ_STATUS); 244 1.1 jmcneill HWRITE4(sc, GENI_M_IRQ_CLEAR, stat); 245 1.1 jmcneill HWRITE4(sc, GENI_I2C_RX_TRANS_LEN, buflen); 246 1.1 jmcneill m_cmd = GENI_M_CMD0_OPCODE_I2C_READ | m_param; 247 1.1 jmcneill HWRITE4(sc, GENI_M_CMD0, m_cmd); 248 1.1 jmcneill 249 1.1 jmcneill error = qciic_read(sc, buf, buflen); 250 1.1 jmcneill if (error) 251 1.1 jmcneill return error; 252 1.1 jmcneill 253 1.1 jmcneill error = qciic_wait(sc, GENI_M_IRQ_CMD_DONE); 254 1.1 jmcneill if (error) 255 1.1 jmcneill return error; 256 1.1 jmcneill } else { 257 1.1 jmcneill stat = HREAD4(sc, GENI_M_IRQ_STATUS); 258 1.1 jmcneill HWRITE4(sc, GENI_M_IRQ_CLEAR, stat); 259 1.1 jmcneill HWRITE4(sc, GENI_I2C_TX_TRANS_LEN, buflen); 260 1.1 jmcneill m_cmd = GENI_M_CMD0_OPCODE_I2C_WRITE | m_param; 261 1.1 jmcneill HWRITE4(sc, GENI_M_CMD0, m_cmd); 262 1.1 jmcneill 263 1.1 jmcneill error = qciic_write(sc, buf, buflen); 264 1.1 jmcneill if (error) 265 1.1 jmcneill return error; 266 1.1 jmcneill 267 1.1 jmcneill error = qciic_wait(sc, GENI_M_IRQ_CMD_DONE); 268 1.1 jmcneill if (error) 269 1.1 jmcneill return error; 270 1.1 jmcneill } 271 1.1 jmcneill 272 1.1 jmcneill return 0; 273 1.1 jmcneill } 274