1 /* $OpenBSD: qciic_fdt.c,v 1.2 2024/10/17 17:58:58 kettenis Exp $ */ 2 /* 3 * Copyright (c) 2022 Mark Kettenis <kettenis@openbsd.org> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/param.h> 19 #include <sys/systm.h> 20 #include <sys/device.h> 21 22 #include <machine/bus.h> 23 #include <machine/intr.h> 24 #include <machine/fdt.h> 25 26 #include <dev/ofw/openfirm.h> 27 #include <dev/ofw/ofw_gpio.h> 28 #include <dev/ofw/ofw_pinctrl.h> 29 #include <dev/ofw/fdt.h> 30 31 #define _I2C_PRIVATE 32 #include <dev/i2c/i2cvar.h> 33 34 /* Registers */ 35 #define GENI_I2C_TX_TRANS_LEN 0x26c 36 #define GENI_I2C_RX_TRANS_LEN 0x270 37 #define GENI_M_CMD0 0x600 38 #define GENI_M_CMD0_OPCODE_I2C_WRITE (0x1 << 27) 39 #define GENI_M_CMD0_OPCODE_I2C_READ (0x2 << 27) 40 #define GENI_M_CMD0_SLV_ADDR_SHIFT 9 41 #define GENI_M_CMD0_STOP_STRETCH (1 << 2) 42 #define GENI_M_IRQ_STATUS 0x610 43 #define GENI_M_IRQ_CLEAR 0x618 44 #define GENI_M_IRQ_CMD_DONE (1 << 0) 45 #define GENI_TX_FIFO 0x700 46 #define GENI_RX_FIFO 0x780 47 #define GENI_TX_FIFO_STATUS 0x800 48 #define GENI_RX_FIFO_STATUS 0x804 49 #define GENI_RX_FIFO_STATUS_WC(val) ((val) & 0xffffff) 50 51 #define HREAD4(sc, reg) \ 52 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) 53 #define HWRITE4(sc, reg, val) \ 54 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 55 56 struct qciic_fdt_softc { 57 struct device sc_dev; 58 bus_space_tag_t sc_iot; 59 bus_space_handle_t sc_ioh; 60 61 int sc_node; 62 struct device *sc_iic; 63 64 struct i2c_controller sc_ic; 65 }; 66 67 int qciic_fdt_match(struct device *, void *, void *); 68 void qciic_fdt_attach(struct device *, struct device *, void *); 69 70 const struct cfattach qciic_fdt_ca = { 71 sizeof (struct qciic_fdt_softc), qciic_fdt_match, qciic_fdt_attach 72 }; 73 74 int qciic_fdt_acquire_bus(void *, int); 75 void qciic_fdt_release_bus(void *, int); 76 int qciic_fdt_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t, 77 void *, size_t, int); 78 79 void *qciic_fdt_i2c_intr_establish(void *, void *, int, int (*)(void *), 80 void *, const char *); 81 void qciic_fdt_i2c_intr_disestablish(void *, void *); 82 const char *qciic_fdt_i2c_intr_string(void *, void *); 83 84 void qciic_fdt_bus_scan(struct device *, struct i2cbus_attach_args *, 85 void *); 86 87 int 88 qciic_fdt_match(struct device *parent, void *match, void *aux) 89 { 90 struct fdt_attach_args *faa = aux; 91 92 return OF_is_compatible(faa->fa_node, "qcom,geni-i2c"); 93 } 94 95 void 96 qciic_fdt_attach(struct device *parent, struct device *self, void *aux) 97 { 98 struct qciic_fdt_softc *sc = (struct qciic_fdt_softc *)self; 99 struct fdt_attach_args *faa = aux; 100 struct i2cbus_attach_args iba; 101 102 sc->sc_node = faa->fa_node; 103 104 sc->sc_iot = faa->fa_iot; 105 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, faa->fa_reg[0].size, 106 0, &sc->sc_ioh)) { 107 printf(": can't map registers\n"); 108 return; 109 } 110 111 printf("\n"); 112 113 pinctrl_byname(sc->sc_node, "default"); 114 115 sc->sc_ic.ic_cookie = sc; 116 sc->sc_ic.ic_acquire_bus = qciic_fdt_acquire_bus; 117 sc->sc_ic.ic_release_bus = qciic_fdt_release_bus; 118 sc->sc_ic.ic_exec = qciic_fdt_exec; 119 sc->sc_ic.ic_intr_establish = qciic_fdt_i2c_intr_establish; 120 sc->sc_ic.ic_intr_disestablish = qciic_fdt_i2c_intr_disestablish; 121 sc->sc_ic.ic_intr_string = qciic_fdt_i2c_intr_string; 122 123 memset(&iba, 0, sizeof(iba)); 124 iba.iba_name = "iic"; 125 iba.iba_tag = &sc->sc_ic; 126 iba.iba_bus_scan = qciic_fdt_bus_scan; 127 iba.iba_bus_scan_arg = &sc->sc_node; 128 config_found(&sc->sc_dev, &iba, iicbus_print); 129 } 130 131 int 132 qciic_fdt_acquire_bus(void *cookie, int flags) 133 { 134 return 0; 135 } 136 137 void 138 qciic_fdt_release_bus(void *cookie, int flags) 139 { 140 } 141 142 int 143 qciic_fdt_wait(struct qciic_fdt_softc *sc, uint32_t bits) 144 { 145 uint32_t stat; 146 int timo; 147 148 for (timo = 50000; timo > 0; timo--) { 149 stat = HREAD4(sc, GENI_M_IRQ_STATUS); 150 if (stat & bits) 151 break; 152 delay(10); 153 } 154 if (timo == 0) 155 return ETIMEDOUT; 156 157 return 0; 158 } 159 160 int 161 qciic_fdt_read(struct qciic_fdt_softc *sc, uint8_t *buf, size_t len) 162 { 163 uint32_t stat, word; 164 int timo, i; 165 166 word = 0; 167 for (i = 0; i < len; i++) { 168 if ((i % 4) == 0) { 169 for (timo = 50000; timo > 0; timo--) { 170 stat = HREAD4(sc, GENI_RX_FIFO_STATUS); 171 if (GENI_RX_FIFO_STATUS_WC(stat) > 0) 172 break; 173 delay(10); 174 } 175 if (timo == 0) 176 return ETIMEDOUT; 177 word = HREAD4(sc, GENI_RX_FIFO); 178 } 179 buf[i] = word >> ((i % 4) * 8); 180 } 181 182 return 0; 183 } 184 185 int 186 qciic_fdt_write(struct qciic_fdt_softc *sc, const uint8_t *buf, size_t len) 187 { 188 uint32_t stat, word; 189 int timo, i; 190 191 word = 0; 192 for (i = 0; i < len; i++) { 193 word |= buf[i] << ((i % 4) * 8); 194 if ((i % 4) == 3 || i == (len - 1)) { 195 for (timo = 50000; timo > 0; timo--) { 196 stat = HREAD4(sc, GENI_TX_FIFO_STATUS); 197 if (stat < 16) 198 break; 199 delay(10); 200 } 201 if (timo == 0) 202 return ETIMEDOUT; 203 HWRITE4(sc, GENI_TX_FIFO, word); 204 word = 0; 205 } 206 } 207 208 return 0; 209 } 210 211 int 212 qciic_fdt_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmd, 213 size_t cmdlen, void *buf, size_t buflen, int flags) 214 { 215 struct qciic_fdt_softc *sc = cookie; 216 uint32_t m_cmd, m_param, stat; 217 int error; 218 219 m_param = addr << GENI_M_CMD0_SLV_ADDR_SHIFT; 220 m_param |= GENI_M_CMD0_STOP_STRETCH; 221 222 if (buflen == 0 && I2C_OP_STOP_P(op)) 223 m_param &= ~GENI_M_CMD0_STOP_STRETCH; 224 225 if (cmdlen > 0) { 226 stat = HREAD4(sc, GENI_M_IRQ_STATUS); 227 HWRITE4(sc, GENI_M_IRQ_CLEAR, stat); 228 HWRITE4(sc, GENI_I2C_TX_TRANS_LEN, cmdlen); 229 m_cmd = GENI_M_CMD0_OPCODE_I2C_WRITE | m_param; 230 HWRITE4(sc, GENI_M_CMD0, m_cmd); 231 232 error = qciic_fdt_write(sc, cmd, cmdlen); 233 if (error) 234 return error; 235 236 error = qciic_fdt_wait(sc, GENI_M_IRQ_CMD_DONE); 237 if (error) 238 return error; 239 } 240 241 if (buflen == 0) 242 return 0; 243 244 if (I2C_OP_STOP_P(op)) 245 m_param &= ~GENI_M_CMD0_STOP_STRETCH; 246 247 if (I2C_OP_READ_P(op)) { 248 stat = HREAD4(sc, GENI_M_IRQ_STATUS); 249 HWRITE4(sc, GENI_M_IRQ_CLEAR, stat); 250 HWRITE4(sc, GENI_I2C_RX_TRANS_LEN, buflen); 251 m_cmd = GENI_M_CMD0_OPCODE_I2C_READ | m_param; 252 HWRITE4(sc, GENI_M_CMD0, m_cmd); 253 254 error = qciic_fdt_read(sc, buf, buflen); 255 if (error) 256 return error; 257 258 error = qciic_fdt_wait(sc, GENI_M_IRQ_CMD_DONE); 259 if (error) 260 return error; 261 } else { 262 stat = HREAD4(sc, GENI_M_IRQ_STATUS); 263 HWRITE4(sc, GENI_M_IRQ_CLEAR, stat); 264 HWRITE4(sc, GENI_I2C_TX_TRANS_LEN, buflen); 265 m_cmd = GENI_M_CMD0_OPCODE_I2C_WRITE | m_param; 266 HWRITE4(sc, GENI_M_CMD0, m_cmd); 267 268 error = qciic_fdt_write(sc, buf, buflen); 269 if (error) 270 return error; 271 272 error = qciic_fdt_wait(sc, GENI_M_IRQ_CMD_DONE); 273 if (error) 274 return error; 275 } 276 277 return 0; 278 } 279 280 void * 281 qciic_fdt_i2c_intr_establish(void *cookie, void *ih, int level, 282 int (*func)(void *), void *arg, const char *name) 283 { 284 int node = *(int *)ih; 285 286 return fdt_intr_establish(node, level, func, arg, (char *)name); 287 } 288 289 void 290 qciic_fdt_i2c_intr_disestablish(void *cookie, void *ih) 291 { 292 fdt_intr_disestablish(ih); 293 } 294 295 const char * 296 qciic_fdt_i2c_intr_string(void *cookie, void *ih) 297 { 298 static char irqstr[64]; 299 300 snprintf(irqstr, sizeof(irqstr), "irq"); 301 302 return irqstr; 303 } 304 305 void 306 qciic_fdt_bus_scan(struct device *self, struct i2cbus_attach_args *iba, void *aux) 307 { 308 int iba_node = *(int *)aux; 309 extern int iic_print(void *, const char *); 310 struct i2c_attach_args ia; 311 char name[32], status[32]; 312 uint32_t reg[1]; 313 int node; 314 315 for (node = OF_child(iba_node); node; node = OF_peer(node)) { 316 memset(name, 0, sizeof(name)); 317 memset(status, 0, sizeof(status)); 318 memset(reg, 0, sizeof(reg)); 319 320 if (OF_getprop(node, "compatible", name, sizeof(name)) == -1) 321 continue; 322 if (name[0] == '\0') 323 continue; 324 325 if (OF_getprop(node, "status", status, sizeof(status)) > 0 && 326 strcmp(status, "disabled") == 0) 327 continue; 328 329 if (OF_getprop(node, "reg", ®, sizeof(reg)) != sizeof(reg)) 330 continue; 331 332 memset(&ia, 0, sizeof(ia)); 333 ia.ia_tag = iba->iba_tag; 334 ia.ia_addr = bemtoh32(®[0]); 335 ia.ia_name = name; 336 ia.ia_cookie = &node; 337 ia.ia_intr = &node; 338 339 /* Quirk for ihidev(4) */ 340 if (strcmp(name, "hid-over-i2c") == 0) { 341 ia.ia_name = "ihidev"; 342 ia.ia_size = OF_getpropint(node, "hid-descr-addr", 0); 343 ia.ia_cookie = name; 344 } 345 346 config_found(self, &ia, iic_print); 347 } 348 } 349