1 /* $OpenBSD: amliic.c,v 1.1 2019/10/07 18:54:03 kettenis 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 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 + buflen > 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 for (k = 0; k < buflen; k++) 200 SET_TOKEN(i++, (k == (buflen - 1)) ? DATA_LAST : DATA); 201 } else { 202 if (cmdlen == 0) { 203 SET_TOKEN(i++, START); 204 SET_TOKEN(i++, SLAVE_ADDR_WRITE); 205 } 206 for (k = 0; k < buflen; k++) { 207 SET_TOKEN(i++, DATA); 208 SET_DATA(j++, ((uint8_t *)buf)[k]); 209 } 210 } 211 212 if (I2C_OP_STOP_P(op)) 213 SET_TOKEN(i++, STOP); 214 215 SET_TOKEN(i++, END); 216 217 /* Write slave address, tokens and associated data to hardware. */ 218 HWRITE4(sc, I2C_M_SLAVE_ADDRESS, addr << 1); 219 HWRITE4(sc, I2C_M_TOKEN_LIST0, tokens); 220 HWRITE4(sc, I2C_M_TOKEN_LIST1, tokens >> 32); 221 HWRITE4(sc, I2C_M_TOKEN_WDATA0, data); 222 HWRITE4(sc, I2C_M_TOKEN_WDATA1, data >> 32); 223 224 /* Start token list processing. */ 225 HSET4(sc, I2C_M_CONTROL, I2C_M_CONTROL_START); 226 for (timo = 50000; timo > 0; timo--) { 227 if ((HREAD4(sc, I2C_M_CONTROL) & I2C_M_CONTROL_STATUS) == 0) 228 break; 229 delay(10); 230 } 231 HCLR4(sc, I2C_M_CONTROL, I2C_M_CONTROL_START); 232 if (timo == 0 || HREAD4(sc, I2C_M_CONTROL) & I2C_M_CONTROL_ERROR) 233 return EIO; 234 235 if (I2C_OP_READ_P(op)) { 236 for (i = 0; i < buflen; i++) { 237 if (i % 4 == 0) 238 rdata = HREAD4(sc, I2C_M_TOKEN_RDATA0 + i); 239 ((uint8_t *)buf)[i] = rdata; 240 rdata >>= 8; 241 } 242 } 243 244 return 0; 245 } 246 247 void 248 amliic_bus_scan(struct device *self, struct i2cbus_attach_args *iba, void *arg) 249 { 250 int iba_node = *(int *)arg; 251 struct i2c_attach_args ia; 252 char name[32]; 253 uint32_t reg[1]; 254 int node; 255 256 for (node = OF_child(iba_node); node; node = OF_peer(node)) { 257 memset(name, 0, sizeof(name)); 258 memset(reg, 0, sizeof(reg)); 259 260 if (OF_getprop(node, "compatible", name, sizeof(name)) == -1) 261 continue; 262 if (name[0] == '\0') 263 continue; 264 265 if (OF_getprop(node, "reg", ®, sizeof(reg)) != sizeof(reg)) 266 continue; 267 268 memset(&ia, 0, sizeof(ia)); 269 ia.ia_tag = iba->iba_tag; 270 ia.ia_addr = bemtoh32(®[0]); 271 ia.ia_name = name; 272 ia.ia_cookie = &node; 273 config_found(self, &ia, iic_print); 274 } 275 } 276