1 /* $OpenBSD: amliic.c,v 1.5 2021/10/24 17:52:26 mpi Exp $ */ 2 /* 3 * Copyright (c) 2019 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/intr.h> 23 #include <machine/bus.h> 24 #include <machine/fdt.h> 25 26 #define _I2C_PRIVATE 27 #include <dev/i2c/i2cvar.h> 28 29 #include <dev/ofw/openfirm.h> 30 #include <dev/ofw/ofw_clock.h> 31 #include <dev/ofw/ofw_pinctrl.h> 32 #include <dev/ofw/fdt.h> 33 34 /* Registers. */ 35 #define I2C_M_CONTROL 0x00 36 #define I2C_M_CONTROL_QTR_CLK_DLY_SHIFT 12 37 #define I2C_M_CONTROL_QTR_CLK_EXT_SHIFT 28 38 #define I2C_M_CONTROL_ERROR (1 << 3) 39 #define I2C_M_CONTROL_STATUS (1 << 2) 40 #define I2C_M_CONTROL_START (1 << 0) 41 #define I2C_M_SLAVE_ADDRESS 0x04 42 #define I2C_M_TOKEN_LIST0 0x08 43 #define I2C_M_TOKEN_LIST1 0x0c 44 #define I2C_M_TOKEN_WDATA0 0x10 45 #define I2C_M_TOKEN_WDATA1 0x14 46 #define I2C_M_TOKEN_RDATA0 0x18 47 #define I2C_M_TOKEN_RDATA1 0x1c 48 49 /* Token definitions. */ 50 #define END 0x0 51 #define START 0x1 52 #define SLAVE_ADDR_WRITE 0x2 53 #define SLAVE_ADDR_READ 0x3 54 #define DATA 0x4 55 #define DATA_LAST 0x5 56 #define STOP 0x6 57 58 #define HREAD4(sc, reg) \ 59 (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) 60 #define HWRITE4(sc, reg, val) \ 61 bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) 62 #define HSET4(sc, reg, bits) \ 63 HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) 64 #define HCLR4(sc, reg, bits) \ 65 HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) 66 67 struct amliic_softc { 68 struct device sc_dev; 69 bus_space_tag_t sc_iot; 70 bus_space_handle_t sc_ioh; 71 72 int sc_node; 73 struct i2c_controller sc_ic; 74 }; 75 76 int amliic_match(struct device *, void *, void *); 77 void amliic_attach(struct device *, struct device *, void *); 78 79 const struct cfattach amliic_ca = { 80 sizeof (struct amliic_softc), amliic_match, amliic_attach 81 }; 82 83 struct cfdriver amliic_cd = { 84 NULL, "amliic", DV_DULL 85 }; 86 87 int amliic_acquire_bus(void *, int); 88 void amliic_release_bus(void *, int); 89 int amliic_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t, 90 void *, size_t, int); 91 92 void amliic_bus_scan(struct device *, struct i2cbus_attach_args *, void *); 93 94 int 95 amliic_match(struct device *parent, void *match, void *aux) 96 { 97 struct fdt_attach_args *faa = aux; 98 99 return OF_is_compatible(faa->fa_node, "amlogic,meson-axg-i2c"); 100 } 101 102 void 103 amliic_attach(struct device *parent, struct device *self, void *aux) 104 { 105 struct amliic_softc *sc = (struct amliic_softc *)self; 106 struct fdt_attach_args *faa = aux; 107 struct i2cbus_attach_args iba; 108 uint32_t clock_speed, bus_speed; 109 uint32_t div, divl, divh; 110 111 if (faa->fa_nreg < 1) { 112 printf(": no registers\n"); 113 return; 114 } 115 116 sc->sc_iot = faa->fa_iot; 117 sc->sc_node = faa->fa_node; 118 119 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 120 faa->fa_reg[0].size, 0, &sc->sc_ioh)) { 121 printf(": can't map registers\n"); 122 return; 123 } 124 125 printf("\n"); 126 127 pinctrl_byname(sc->sc_node, "default"); 128 129 clock_enable_all(sc->sc_node); 130 131 clock_speed = clock_get_frequency(sc->sc_node, NULL); 132 bus_speed = OF_getpropint(sc->sc_node, "clock-frequency", 100000); 133 134 div = clock_speed / bus_speed / 4; 135 divl = div & 0x3ff; 136 divh = div >> 10; 137 HWRITE4(sc, I2C_M_CONTROL, divh << I2C_M_CONTROL_QTR_CLK_EXT_SHIFT | 138 divl << I2C_M_CONTROL_QTR_CLK_DLY_SHIFT); 139 140 sc->sc_ic.ic_cookie = sc; 141 sc->sc_ic.ic_acquire_bus = amliic_acquire_bus; 142 sc->sc_ic.ic_release_bus = amliic_release_bus; 143 sc->sc_ic.ic_exec = amliic_exec; 144 145 /* Configure its children */ 146 memset(&iba, 0, sizeof(iba)); 147 iba.iba_name = "iic"; 148 iba.iba_tag = &sc->sc_ic; 149 iba.iba_bus_scan = amliic_bus_scan; 150 iba.iba_bus_scan_arg = &sc->sc_node; 151 152 config_found(&sc->sc_dev, &iba, iicbus_print); 153 } 154 155 int 156 amliic_acquire_bus(void *cookie, int flags) 157 { 158 return 0; 159 } 160 161 void 162 amliic_release_bus(void *cookie, int flags) 163 { 164 } 165 166 int 167 amliic_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmd, 168 size_t cmdlen, void *buf, size_t buflen, int flags) 169 { 170 struct amliic_softc *sc = cookie; 171 uint64_t tokens = 0; 172 uint64_t data = 0; 173 uint32_t rdata; 174 uint32_t ctrl; 175 size_t pos, len; 176 int i = 0, j = 0; 177 int timo, k; 178 179 #define SET_TOKEN(i, t) \ 180 tokens |= (((uint64_t)(t)) << ((i) * 4)) 181 #define SET_DATA(i, d) \ 182 data |= (((uint64_t)(d)) << ((i) * 8)) 183 184 if (cmdlen > 8) 185 return EINVAL; 186 187 if (cmdlen > 0) { 188 SET_TOKEN(i++, START); 189 SET_TOKEN(i++, SLAVE_ADDR_WRITE); 190 for (k = 0; k < cmdlen; k++) { 191 SET_TOKEN(i++, DATA); 192 SET_DATA(j++, ((uint8_t *)cmd)[k]); 193 } 194 } 195 196 if (I2C_OP_READ_P(op)) { 197 SET_TOKEN(i++, START); 198 SET_TOKEN(i++, SLAVE_ADDR_READ); 199 } else if (cmdlen == 0) { 200 SET_TOKEN(i++, START); 201 SET_TOKEN(i++, SLAVE_ADDR_WRITE); 202 } 203 204 HWRITE4(sc, I2C_M_SLAVE_ADDRESS, addr << 1); 205 206 pos = 0; 207 while (pos < buflen) { 208 len = MIN(buflen - pos, 8 - j); 209 210 if (I2C_OP_READ_P(op)) { 211 for (k = 0; k < len; k++) 212 SET_TOKEN(i++, (pos == (buflen - 1)) ? 213 DATA_LAST : DATA); 214 } else { 215 for (k = 0; k < len; k++) { 216 SET_TOKEN(i++, DATA); 217 SET_DATA(j++, ((uint8_t *)buf)[pos++]); 218 } 219 } 220 221 if (pos == buflen && I2C_OP_STOP_P(op)) 222 SET_TOKEN(i++, STOP); 223 224 SET_TOKEN(i++, END); 225 226 /* Write slave address, tokens and data to hardware. */ 227 HWRITE4(sc, I2C_M_TOKEN_LIST0, tokens); 228 HWRITE4(sc, I2C_M_TOKEN_LIST1, tokens >> 32); 229 HWRITE4(sc, I2C_M_TOKEN_WDATA0, data); 230 HWRITE4(sc, I2C_M_TOKEN_WDATA1, data >> 32); 231 232 /* Start token list processing. */ 233 HSET4(sc, I2C_M_CONTROL, I2C_M_CONTROL_START); 234 for (timo = 50000; timo > 0; timo--) { 235 ctrl = HREAD4(sc, I2C_M_CONTROL); 236 if ((ctrl & I2C_M_CONTROL_STATUS) == 0) 237 break; 238 delay(10); 239 } 240 HCLR4(sc, I2C_M_CONTROL, I2C_M_CONTROL_START); 241 if (ctrl & I2C_M_CONTROL_ERROR) 242 return EIO; 243 if (timo == 0) 244 return ETIMEDOUT; 245 246 if (I2C_OP_READ_P(op)) { 247 rdata = HREAD4(sc, I2C_M_TOKEN_RDATA0); 248 for (i = 0; i < len; i++) { 249 if (i == 4) 250 rdata = HREAD4(sc, I2C_M_TOKEN_RDATA1); 251 ((uint8_t *)buf)[pos++] = rdata; 252 rdata >>= 8; 253 } 254 } 255 256 /* Reset tokens. */ 257 tokens = 0; 258 data = 0; 259 i = j = 0; 260 } 261 262 return 0; 263 } 264 265 void 266 amliic_bus_scan(struct device *self, struct i2cbus_attach_args *iba, void *arg) 267 { 268 int iba_node = *(int *)arg; 269 struct i2c_attach_args ia; 270 char name[32], status[32]; 271 uint32_t reg[1]; 272 int node; 273 274 for (node = OF_child(iba_node); node; node = OF_peer(node)) { 275 memset(name, 0, sizeof(name)); 276 memset(status, 0, sizeof(status)); 277 memset(reg, 0, sizeof(reg)); 278 279 if (OF_getprop(node, "compatible", name, sizeof(name)) == -1) 280 continue; 281 if (name[0] == '\0') 282 continue; 283 284 if (OF_getprop(node, "status", status, sizeof(status)) > 0 && 285 strcmp(status, "disabled") == 0) 286 continue; 287 288 if (OF_getprop(node, "reg", ®, sizeof(reg)) != sizeof(reg)) 289 continue; 290 291 memset(&ia, 0, sizeof(ia)); 292 ia.ia_tag = iba->iba_tag; 293 ia.ia_addr = bemtoh32(®[0]); 294 ia.ia_name = name; 295 ia.ia_cookie = &node; 296 config_found(self, &ia, iic_print); 297 } 298 } 299