1 /* $NetBSD: qcomiic.c,v 1.3 2024/12/18 21:23:27 jmcneill 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@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 #include <dev/acpi/acpi_i2c.h> 29 30 #include <dev/i2c/i2cvar.h> 31 32 /* Registers */ 33 #define GENI_I2C_TX_TRANS_LEN 0x26c 34 #define GENI_I2C_RX_TRANS_LEN 0x270 35 #define GENI_M_CMD0 0x600 36 #define GENI_M_CMD0_OPCODE_I2C_WRITE (0x1 << 27) 37 #define GENI_M_CMD0_OPCODE_I2C_READ (0x2 << 27) 38 #define GENI_M_CMD0_SLV_ADDR_SHIFT 9 39 #define GENI_M_CMD0_STOP_STRETCH (1 << 2) 40 #define GENI_M_IRQ_STATUS 0x610 41 #define GENI_M_IRQ_CLEAR 0x618 42 #define GENI_M_IRQ_CMD_DONE (1 << 0) 43 #define GENI_TX_FIFO 0x700 44 #define GENI_RX_FIFO 0x780 45 #define GENI_TX_FIFO_STATUS 0x800 46 #define GENI_RX_FIFO_STATUS 0x804 47 #define GENI_RX_FIFO_STATUS_WC(val) ((val) & 0xffffff) 48 49 #define HREAD4(sc, reg) \ 50 bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)) 51 #define HWRITE4(sc, reg, val) \ 52 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 53 54 struct qciic_softc { 55 device_t sc_dev; 56 struct acpi_devnode *sc_acpi; 57 bus_space_tag_t sc_iot; 58 bus_space_handle_t sc_ioh; 59 60 device_t sc_iic; 61 62 struct i2c_controller sc_ic; 63 }; 64 65 static int qciic_acpi_match(device_t, cfdata_t, void *); 66 static void qciic_acpi_attach(device_t, device_t, void *); 67 static int qciic_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t, 68 void *, size_t, int); 69 70 CFATTACH_DECL_NEW(qcomiic, sizeof(struct qciic_softc), 71 qciic_acpi_match, qciic_acpi_attach, NULL, NULL); 72 73 static const struct device_compatible_entry compat_data[] = { 74 { .compat = "QCOM0610" }, 75 { .compat = "QCOM0811" }, 76 { .compat = "QCOM0C10" }, 77 DEVICE_COMPAT_EOL 78 }; 79 80 static int 81 qciic_acpi_match(device_t parent, cfdata_t cf, void *aux) 82 { 83 struct acpi_attach_args *aa = aux; 84 85 return acpi_compatible_match(aa, compat_data); 86 } 87 88 static void 89 qciic_acpi_attach(device_t parent, device_t self, void *aux) 90 { 91 struct qciic_softc * const sc = device_private(self); 92 struct acpi_attach_args *aa = aux; 93 struct i2cbus_attach_args iba; 94 struct acpi_resources res; 95 struct acpi_mem *mem; 96 struct acpi_irq *irq; 97 ACPI_STATUS rv; 98 int error; 99 100 sc->sc_dev = self; 101 sc->sc_acpi = aa->aa_node; 102 sc->sc_iot = aa->aa_memt; 103 104 rv = acpi_resource_parse(sc->sc_dev, aa->aa_node->ad_handle, "_CRS", 105 &res, &acpi_resource_parse_ops_default); 106 if (ACPI_FAILURE(rv)) { 107 return; 108 } 109 110 mem = acpi_res_mem(&res, 0); 111 if (mem == NULL) { 112 aprint_error_dev(self, "couldn't find mem resource\n"); 113 goto done; 114 } 115 116 irq = acpi_res_irq(&res, 0); 117 if (irq == NULL) { 118 aprint_error_dev(self, "couldn't find irq resource\n"); 119 goto done; 120 } 121 122 error = bus_space_map(sc->sc_iot, mem->ar_base, mem->ar_length, 0, 123 &sc->sc_ioh); 124 if (error != 0) { 125 aprint_error_dev(self, "couldn't map registers\n"); 126 return; 127 } 128 129 iic_tag_init(&sc->sc_ic); 130 sc->sc_ic.ic_cookie = sc; 131 sc->sc_ic.ic_exec = qciic_exec; 132 133 acpi_i2c_register(aa->aa_node, self, &sc->sc_ic); 134 135 memset(&iba, 0, sizeof(iba)); 136 iba.iba_tag = &sc->sc_ic; 137 iba.iba_child_devices = acpi_enter_i2c_devs(self, sc->sc_acpi); 138 139 config_found(self, &iba, iicbus_print, CFARGS_NONE); 140 141 done: 142 acpi_resource_cleanup(&res); 143 } 144 145 static int 146 qciic_wait(struct qciic_softc *sc, uint32_t bits) 147 { 148 uint32_t stat; 149 int timo; 150 151 for (timo = 50000; timo > 0; timo--) { 152 stat = HREAD4(sc, GENI_M_IRQ_STATUS); 153 if (stat & bits) 154 break; 155 delay(10); 156 } 157 if (timo == 0) 158 return ETIMEDOUT; 159 160 return 0; 161 } 162 163 static int 164 qciic_read(struct qciic_softc *sc, uint8_t *buf, size_t len) 165 { 166 uint32_t stat, word; 167 int timo, i; 168 169 word = 0; 170 for (i = 0; i < len; i++) { 171 if ((i % 4) == 0) { 172 for (timo = 50000; timo > 0; timo--) { 173 stat = HREAD4(sc, GENI_RX_FIFO_STATUS); 174 if (GENI_RX_FIFO_STATUS_WC(stat) > 0) 175 break; 176 delay(10); 177 } 178 if (timo == 0) 179 return ETIMEDOUT; 180 word = HREAD4(sc, GENI_RX_FIFO); 181 } 182 buf[i] = word >> ((i % 4) * 8); 183 } 184 185 return 0; 186 } 187 188 static int 189 qciic_write(struct qciic_softc *sc, const uint8_t *buf, size_t len) 190 { 191 uint32_t stat, word; 192 int timo, i; 193 194 word = 0; 195 for (i = 0; i < len; i++) { 196 word |= buf[i] << ((i % 4) * 8); 197 if ((i % 4) == 3 || i == (len - 1)) { 198 for (timo = 50000; timo > 0; timo--) { 199 stat = HREAD4(sc, GENI_TX_FIFO_STATUS); 200 if (stat < 16) 201 break; 202 delay(10); 203 } 204 if (timo == 0) 205 return ETIMEDOUT; 206 HWRITE4(sc, GENI_TX_FIFO, word); 207 word = 0; 208 } 209 } 210 211 return 0; 212 } 213 214 static int 215 qciic_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmd, 216 size_t cmdlen, void *buf, size_t buflen, int flags) 217 { 218 struct qciic_softc *sc = cookie; 219 uint32_t m_cmd, m_param, stat; 220 int error; 221 222 m_param = addr << GENI_M_CMD0_SLV_ADDR_SHIFT; 223 m_param |= GENI_M_CMD0_STOP_STRETCH; 224 225 if (buflen == 0 && I2C_OP_STOP_P(op)) 226 m_param &= ~GENI_M_CMD0_STOP_STRETCH; 227 228 if (cmdlen > 0) { 229 stat = HREAD4(sc, GENI_M_IRQ_STATUS); 230 HWRITE4(sc, GENI_M_IRQ_CLEAR, stat); 231 HWRITE4(sc, GENI_I2C_TX_TRANS_LEN, cmdlen); 232 m_cmd = GENI_M_CMD0_OPCODE_I2C_WRITE | m_param; 233 HWRITE4(sc, GENI_M_CMD0, m_cmd); 234 235 error = qciic_write(sc, cmd, cmdlen); 236 if (error) 237 return error; 238 239 error = qciic_wait(sc, GENI_M_IRQ_CMD_DONE); 240 if (error) 241 return error; 242 } 243 244 if (buflen == 0) 245 return 0; 246 247 if (I2C_OP_STOP_P(op)) 248 m_param &= ~GENI_M_CMD0_STOP_STRETCH; 249 250 if (I2C_OP_READ_P(op)) { 251 stat = HREAD4(sc, GENI_M_IRQ_STATUS); 252 HWRITE4(sc, GENI_M_IRQ_CLEAR, stat); 253 HWRITE4(sc, GENI_I2C_RX_TRANS_LEN, buflen); 254 m_cmd = GENI_M_CMD0_OPCODE_I2C_READ | m_param; 255 HWRITE4(sc, GENI_M_CMD0, m_cmd); 256 257 error = qciic_read(sc, buf, buflen); 258 if (error) 259 return error; 260 261 error = qciic_wait(sc, GENI_M_IRQ_CMD_DONE); 262 if (error) 263 return error; 264 } else { 265 stat = HREAD4(sc, GENI_M_IRQ_STATUS); 266 HWRITE4(sc, GENI_M_IRQ_CLEAR, stat); 267 HWRITE4(sc, GENI_I2C_TX_TRANS_LEN, buflen); 268 m_cmd = GENI_M_CMD0_OPCODE_I2C_WRITE | m_param; 269 HWRITE4(sc, GENI_M_CMD0, m_cmd); 270 271 error = qciic_write(sc, buf, buflen); 272 if (error) 273 return error; 274 275 error = qciic_wait(sc, GENI_M_IRQ_CMD_DONE); 276 if (error) 277 return error; 278 } 279 280 return 0; 281 } 282