1 /* $OpenBSD: amliic.c,v 1.3 2021/03/11 09:15:25 patrick 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 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_send_start(void *, int); 90 int amliic_send_stop(void *, int); 91 int amliic_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t, 92 void *, size_t, int); 93 94 void amliic_bus_scan(struct device *, struct i2cbus_attach_args *, void *); 95 96 int 97 amliic_match(struct device *parent, void *match, void *aux) 98 { 99 struct fdt_attach_args *faa = aux; 100 101 return OF_is_compatible(faa->fa_node, "amlogic,meson-axg-i2c"); 102 } 103 104 void 105 amliic_attach(struct device *parent, struct device *self, void *aux) 106 { 107 struct amliic_softc *sc = (struct amliic_softc *)self; 108 struct fdt_attach_args *faa = aux; 109 struct i2cbus_attach_args iba; 110 uint32_t clock_speed, bus_speed; 111 uint32_t div, divl, divh; 112 113 if (faa->fa_nreg < 1) { 114 printf(": no registers\n"); 115 return; 116 } 117 118 sc->sc_iot = faa->fa_iot; 119 sc->sc_node = faa->fa_node; 120 121 if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, 122 faa->fa_reg[0].size, 0, &sc->sc_ioh)) { 123 printf(": can't map registers\n"); 124 return; 125 } 126 127 printf("\n"); 128 129 pinctrl_byname(sc->sc_node, "default"); 130 131 clock_enable_all(sc->sc_node); 132 133 clock_speed = clock_get_frequency(sc->sc_node, NULL); 134 bus_speed = OF_getpropint(sc->sc_node, "clock-frequency", 100000); 135 136 div = clock_speed / bus_speed / 4; 137 divl = div & 0x3ff; 138 divh = div >> 10; 139 HWRITE4(sc, I2C_M_CONTROL, divh << I2C_M_CONTROL_QTR_CLK_EXT_SHIFT | 140 divl << I2C_M_CONTROL_QTR_CLK_DLY_SHIFT); 141 142 sc->sc_ic.ic_cookie = sc; 143 sc->sc_ic.ic_acquire_bus = amliic_acquire_bus; 144 sc->sc_ic.ic_release_bus = amliic_release_bus; 145 sc->sc_ic.ic_exec = amliic_exec; 146 147 /* Configure its children */ 148 memset(&iba, 0, sizeof(iba)); 149 iba.iba_name = "iic"; 150 iba.iba_tag = &sc->sc_ic; 151 iba.iba_bus_scan = amliic_bus_scan; 152 iba.iba_bus_scan_arg = &sc->sc_node; 153 154 config_found(&sc->sc_dev, &iba, iicbus_print); 155 } 156 157 int 158 amliic_acquire_bus(void *cookie, int flags) 159 { 160 return 0; 161 } 162 163 void 164 amliic_release_bus(void *cookie, int flags) 165 { 166 } 167 168 int 169 amliic_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmd, 170 size_t cmdlen, void *buf, size_t buflen, int flags) 171 { 172 struct amliic_softc *sc = cookie; 173 uint64_t tokens = 0; 174 uint64_t data = 0; 175 uint32_t rdata; 176 uint32_t ctrl; 177 size_t pos, len; 178 int i = 0, j = 0; 179 int timo, k; 180 181 #define SET_TOKEN(i, t) \ 182 tokens |= (((uint64_t)(t)) << ((i) * 4)) 183 #define SET_DATA(i, d) \ 184 data |= (((uint64_t)(d)) << ((i) * 8)) 185 186 if (cmdlen > 8) 187 return EINVAL; 188 189 if (cmdlen > 0) { 190 SET_TOKEN(i++, START); 191 SET_TOKEN(i++, SLAVE_ADDR_WRITE); 192 for (k = 0; k < cmdlen; k++) { 193 SET_TOKEN(i++, DATA); 194 SET_DATA(j++, ((uint8_t *)cmd)[k]); 195 } 196 } 197 198 if (I2C_OP_READ_P(op)) { 199 SET_TOKEN(i++, START); 200 SET_TOKEN(i++, SLAVE_ADDR_READ); 201 } else if (cmdlen == 0) { 202 SET_TOKEN(i++, START); 203 SET_TOKEN(i++, SLAVE_ADDR_WRITE); 204 } 205 206 HWRITE4(sc, I2C_M_SLAVE_ADDRESS, addr << 1); 207 208 pos = 0; 209 while (pos < buflen) { 210 len = MIN(buflen - pos, 8 - j); 211 212 if (I2C_OP_READ_P(op)) { 213 for (k = 0; k < len; k++) 214 SET_TOKEN(i++, (pos == (buflen - 1)) ? 215 DATA_LAST : DATA); 216 } else { 217 for (k = 0; k < len; k++) { 218 SET_TOKEN(i++, DATA); 219 SET_DATA(j++, ((uint8_t *)buf)[pos++]); 220 } 221 } 222 223 if (pos == buflen && I2C_OP_STOP_P(op)) 224 SET_TOKEN(i++, STOP); 225 226 SET_TOKEN(i++, END); 227 228 /* Write slave address, tokens and data to hardware. */ 229 HWRITE4(sc, I2C_M_TOKEN_LIST0, tokens); 230 HWRITE4(sc, I2C_M_TOKEN_LIST1, tokens >> 32); 231 HWRITE4(sc, I2C_M_TOKEN_WDATA0, data); 232 HWRITE4(sc, I2C_M_TOKEN_WDATA1, data >> 32); 233 234 /* Start token list processing. */ 235 HSET4(sc, I2C_M_CONTROL, I2C_M_CONTROL_START); 236 for (timo = 50000; timo > 0; timo--) { 237 ctrl = HREAD4(sc, I2C_M_CONTROL); 238 if ((ctrl & I2C_M_CONTROL_STATUS) == 0) 239 break; 240 delay(10); 241 } 242 HCLR4(sc, I2C_M_CONTROL, I2C_M_CONTROL_START); 243 if (ctrl & I2C_M_CONTROL_ERROR) 244 return EIO; 245 if (timo == 0) 246 return ETIMEDOUT; 247 248 if (I2C_OP_READ_P(op)) { 249 rdata = HREAD4(sc, I2C_M_TOKEN_RDATA0); 250 for (i = 0; i < len; i++) { 251 if (i == 4) 252 rdata = HREAD4(sc, I2C_M_TOKEN_RDATA1); 253 ((uint8_t *)buf)[pos++] = rdata; 254 rdata >>= 8; 255 } 256 } 257 258 /* Reset tokens. */ 259 tokens = 0; 260 data = 0; 261 i = j = 0; 262 } 263 264 return 0; 265 } 266 267 void 268 amliic_bus_scan(struct device *self, struct i2cbus_attach_args *iba, void *arg) 269 { 270 int iba_node = *(int *)arg; 271 struct i2c_attach_args ia; 272 char name[32], status[32]; 273 uint32_t reg[1]; 274 int node; 275 276 for (node = OF_child(iba_node); node; node = OF_peer(node)) { 277 memset(name, 0, sizeof(name)); 278 memset(status, 0, sizeof(status)); 279 memset(reg, 0, sizeof(reg)); 280 281 if (OF_getprop(node, "compatible", name, sizeof(name)) == -1) 282 continue; 283 if (name[0] == '\0') 284 continue; 285 286 if (OF_getprop(node, "status", status, sizeof(status)) > 0 && 287 strcmp(status, "disabled") == 0) 288 continue; 289 290 if (OF_getprop(node, "reg", ®, sizeof(reg)) != sizeof(reg)) 291 continue; 292 293 memset(&ia, 0, sizeof(ia)); 294 ia.ia_tag = iba->iba_tag; 295 ia.ia_addr = bemtoh32(®[0]); 296 ia.ia_name = name; 297 ia.ia_cookie = &node; 298 config_found(self, &ia, iic_print); 299 } 300 } 301