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