1 1.24 thorpej /* $NetBSD: exynos_i2c.c,v 1.24 2025/09/16 11:55:17 thorpej Exp $ */ 2 1.1 reinoud 3 1.1 reinoud /* 4 1.9 marty * Copyright (c) 2015 Jared D. McNeill <jmcneill (at) invisible.ca> 5 1.1 reinoud * All rights reserved. 6 1.1 reinoud * 7 1.1 reinoud * Redistribution and use in source and binary forms, with or without 8 1.1 reinoud * modification, are permitted provided that the following conditions 9 1.1 reinoud * are met: 10 1.1 reinoud * 1. Redistributions of source code must retain the above copyright 11 1.1 reinoud * notice, this list of conditions and the following disclaimer. 12 1.1 reinoud * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 reinoud * notice, this list of conditions and the following disclaimer in the 14 1.1 reinoud * documentation and/or other materials provided with the distribution. 15 1.1 reinoud * 16 1.1 reinoud * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 17 1.1 reinoud * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 1.1 reinoud * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 1.1 reinoud * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 20 1.1 reinoud * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 1.1 reinoud * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 1.1 reinoud * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 1.1 reinoud * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 1.1 reinoud * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 1.1 reinoud * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 1.1 reinoud * POSSIBILITY OF SUCH DAMAGE. 27 1.1 reinoud * 28 1.1 reinoud */ 29 1.1 reinoud 30 1.1 reinoud #include "opt_exynos.h" 31 1.1 reinoud #include "opt_arm_debug.h" 32 1.1 reinoud 33 1.1 reinoud #include <sys/cdefs.h> 34 1.24 thorpej __KERNEL_RCSID(0, "$NetBSD: exynos_i2c.c,v 1.24 2025/09/16 11:55:17 thorpej Exp $"); 35 1.1 reinoud 36 1.1 reinoud #include <sys/param.h> 37 1.1 reinoud #include <sys/bus.h> 38 1.1 reinoud #include <sys/device.h> 39 1.1 reinoud #include <sys/intr.h> 40 1.1 reinoud #include <sys/systm.h> 41 1.9 marty #include <sys/kernel.h> 42 1.1 reinoud #include <sys/kmem.h> 43 1.1 reinoud 44 1.1 reinoud #include <arm/samsung/exynos_reg.h> 45 1.9 marty #include <arm/samsung/exynos_var.h> 46 1.1 reinoud #include <arm/samsung/exynos_intr.h> 47 1.1 reinoud 48 1.1 reinoud #include <sys/gpio.h> 49 1.1 reinoud #include <dev/gpio/gpiovar.h> 50 1.1 reinoud 51 1.1 reinoud #include <dev/i2c/i2cvar.h> 52 1.1 reinoud #include <dev/i2c/i2c_bitbang.h> 53 1.1 reinoud 54 1.5 marty #include <dev/fdt/fdtvar.h> 55 1.1 reinoud 56 1.5 marty struct exynos_i2c_softc { 57 1.5 marty device_t sc_dev; 58 1.5 marty bus_space_tag_t sc_bst; 59 1.5 marty bus_space_handle_t sc_bsh; 60 1.5 marty void * sc_ih; 61 1.9 marty struct clk * sc_clk; 62 1.5 marty 63 1.9 marty struct fdtbus_pinctrl_pin *sc_sda; 64 1.9 marty struct fdtbus_pinctrl_pin *sc_scl; 65 1.5 marty bool sc_sda_is_output; 66 1.9 marty 67 1.5 marty struct i2c_controller sc_ic; 68 1.18 thorpej kmutex_t sc_intr_lock; 69 1.18 thorpej kcondvar_t sc_intr_wait; 70 1.1 reinoud }; 71 1.1 reinoud 72 1.5 marty static int exynos_i2c_intr(void *); 73 1.1 reinoud 74 1.5 marty static int exynos_i2c_send_start(void *, int); 75 1.5 marty static int exynos_i2c_send_stop(void *, int); 76 1.5 marty static int exynos_i2c_initiate_xfer(void *, i2c_addr_t, int); 77 1.5 marty static int exynos_i2c_read_byte(void *, uint8_t *, int); 78 1.5 marty static int exynos_i2c_write_byte(void *, uint8_t , int); 79 1.1 reinoud 80 1.9 marty static int exynos_i2c_wait(struct exynos_i2c_softc *, int); 81 1.9 marty 82 1.1 reinoud 83 1.5 marty static int exynos_i2c_match(device_t, cfdata_t, void *); 84 1.5 marty static void exynos_i2c_attach(device_t, device_t, void *); 85 1.1 reinoud 86 1.5 marty CFATTACH_DECL_NEW(exynos_i2c, sizeof(struct exynos_i2c_softc), 87 1.5 marty exynos_i2c_match, exynos_i2c_attach, NULL, NULL); 88 1.1 reinoud 89 1.8 marty #define I2C_WRITE(sc, reg, val) \ 90 1.9 marty bus_space_write_1((sc)->sc_bst, (sc)->sc_bsh, (reg), (val)) 91 1.8 marty #define I2C_READ(sc, reg) \ 92 1.9 marty bus_space_read_1((sc)->sc_bst, (sc)->sc_bsh, (reg)) 93 1.8 marty 94 1.9 marty #define IICCON 0x00 95 1.9 marty #define IICSTAT 0x04 96 1.9 marty #define IICADD 0x08 97 1.9 marty #define IICDS 0x0C 98 1.9 marty 99 1.9 marty #define ACKENABLE (1<<7) 100 1.9 marty #define TXPRESCALE (1<<6) 101 1.9 marty #define INTENABLE (1<<5) 102 1.9 marty #define IRQPEND (1<<4) 103 1.9 marty #define PRESCALE (0x0f) 104 1.9 marty 105 1.9 marty #define MODESELECT (3<<6) 106 1.9 marty #define BUSYSTART (1<<5) 107 1.9 marty #define BUSENABLE (1<<4) 108 1.9 marty #define ARBITRATION (1<<3) 109 1.9 marty #define SLAVESTATUS (1<<2) 110 1.9 marty #define ZEROSTATUS (1<<1) 111 1.9 marty #define LASTBIT (1<<0) 112 1.9 marty 113 1.9 marty #define READBIT (1<<7) 114 1.8 marty 115 1.21 thorpej static const struct device_compatible_entry compat_data[] = { 116 1.21 thorpej { .compat = "samsung,s3c2440-i2c" }, 117 1.21 thorpej DEVICE_COMPAT_EOL 118 1.21 thorpej }; 119 1.21 thorpej 120 1.1 reinoud static int 121 1.5 marty exynos_i2c_match(device_t self, cfdata_t cf, void *aux) 122 1.1 reinoud { 123 1.5 marty struct fdt_attach_args * const faa = aux; 124 1.1 reinoud 125 1.21 thorpej return of_compatible_match(faa->faa_phandle, compat_data); 126 1.1 reinoud } 127 1.1 reinoud 128 1.1 reinoud static void 129 1.5 marty exynos_i2c_attach(device_t parent, device_t self, void *aux) 130 1.1 reinoud { 131 1.5 marty struct exynos_i2c_softc * const sc = device_private(self); 132 1.5 marty struct fdt_attach_args * const faa = aux; 133 1.5 marty const int phandle = faa->faa_phandle; 134 1.5 marty char intrstr[128]; 135 1.5 marty bus_addr_t addr; 136 1.5 marty bus_size_t size; 137 1.5 marty int error; 138 1.5 marty 139 1.5 marty if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 140 1.5 marty aprint_error(": couldn't get registers\n"); 141 1.5 marty return; 142 1.5 marty } 143 1.5 marty 144 1.1 reinoud sc->sc_dev = self; 145 1.5 marty sc->sc_bst = faa->faa_bst; 146 1.5 marty error = bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh); 147 1.5 marty if (error) { 148 1.17 skrll aprint_error(": couldn't map %#" PRIxBUSADDR ": %d", addr, 149 1.5 marty error); 150 1.1 reinoud return; 151 1.1 reinoud } 152 1.1 reinoud 153 1.18 thorpej mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_VM); 154 1.18 thorpej cv_init(&sc->sc_intr_wait, device_xname(self)); 155 1.8 marty aprint_normal(" @ 0x%08x\n", (uint)addr); 156 1.1 reinoud 157 1.5 marty if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) { 158 1.5 marty aprint_error_dev(self, "failed to decode interrupt\n"); 159 1.5 marty return; 160 1.1 reinoud } 161 1.5 marty 162 1.22 skrll sc->sc_ih = fdtbus_intr_establish_xname(phandle, 0, IPL_VM, 163 1.22 skrll FDT_INTR_MPSAFE, exynos_i2c_intr, sc, device_xname(self)); 164 1.5 marty if (sc->sc_ih == NULL) { 165 1.5 marty aprint_error_dev(self, "couldn't establish interrupt on %s\n", 166 1.5 marty intrstr); 167 1.5 marty return; 168 1.5 marty } 169 1.5 marty aprint_normal_dev(self, "interrupting on %s\n", intrstr); 170 1.19 skrll 171 1.18 thorpej iic_tag_init(&sc->sc_ic); 172 1.9 marty sc->sc_ic.ic_cookie = sc; 173 1.9 marty sc->sc_ic.ic_send_start = exynos_i2c_send_start; 174 1.9 marty sc->sc_ic.ic_send_stop = exynos_i2c_send_stop; 175 1.9 marty sc->sc_ic.ic_initiate_xfer = exynos_i2c_initiate_xfer; 176 1.9 marty sc->sc_ic.ic_read_byte = exynos_i2c_read_byte; 177 1.9 marty sc->sc_ic.ic_write_byte = exynos_i2c_write_byte; 178 1.5 marty 179 1.23 thorpej iicbus_attach(self, &sc->sc_ic); 180 1.1 reinoud } 181 1.1 reinoud 182 1.5 marty static int 183 1.5 marty exynos_i2c_intr(void *priv) 184 1.5 marty { 185 1.5 marty struct exynos_i2c_softc * const sc = priv; 186 1.5 marty 187 1.9 marty uint8_t istatus = I2C_READ(sc, IICCON); 188 1.8 marty if (!(istatus & IRQPEND)) 189 1.8 marty return 0; 190 1.8 marty istatus &= ~IRQPEND; 191 1.9 marty I2C_WRITE(sc, IICCON, istatus); 192 1.5 marty 193 1.18 thorpej mutex_enter(&sc->sc_intr_lock); 194 1.18 thorpej cv_broadcast(&sc->sc_intr_wait); 195 1.18 thorpej mutex_exit(&sc->sc_intr_lock); 196 1.5 marty 197 1.5 marty return 1; 198 1.5 marty } 199 1.1 reinoud 200 1.1 reinoud static int 201 1.9 marty exynos_i2c_wait(struct exynos_i2c_softc *sc, int flags) 202 1.9 marty { 203 1.9 marty int error, retry; 204 1.9 marty uint8_t stat = 0; 205 1.9 marty 206 1.9 marty retry = (flags & I2C_F_POLL) ? 100000 : 100; 207 1.9 marty 208 1.9 marty while (--retry > 0) { 209 1.9 marty if ((flags & I2C_F_POLL) == 0) { 210 1.18 thorpej error = cv_timedwait_sig(&sc->sc_intr_wait, 211 1.18 thorpej &sc->sc_intr_lock, 212 1.18 thorpej uimax(mstohz(10), 1)); 213 1.9 marty if (error) { 214 1.9 marty return error; 215 1.9 marty } 216 1.9 marty } 217 1.9 marty stat = I2C_READ(sc, IICSTAT); 218 1.9 marty if (!(stat & BUSYSTART)) { 219 1.9 marty break; 220 1.9 marty } 221 1.9 marty if (flags & I2C_F_POLL) { 222 1.9 marty delay(10); 223 1.9 marty } 224 1.9 marty } 225 1.9 marty if (retry == 0) { 226 1.9 marty stat = I2C_READ(sc, IICSTAT); 227 1.9 marty device_printf(sc->sc_dev, "timed out, status = %#x\n", stat); 228 1.9 marty return ETIMEDOUT; 229 1.9 marty } 230 1.9 marty 231 1.9 marty return 0; 232 1.9 marty } 233 1.9 marty 234 1.9 marty 235 1.9 marty static int 236 1.18 thorpej exynos_i2c_send_start_locked(struct exynos_i2c_softc *sc, int flags) 237 1.18 thorpej { 238 1.18 thorpej I2C_WRITE(sc, IICSTAT, 0xF0); 239 1.18 thorpej return 0; 240 1.18 thorpej } 241 1.18 thorpej 242 1.18 thorpej static int 243 1.18 thorpej exynos_i2c_send_stop_locked(struct exynos_i2c_softc *sc, int flags) 244 1.18 thorpej { 245 1.18 thorpej I2C_WRITE(sc, IICSTAT, 0xD0); 246 1.18 thorpej return 0; 247 1.18 thorpej } 248 1.18 thorpej 249 1.18 thorpej static int 250 1.18 thorpej exynos_i2c_write_byte_locked(struct exynos_i2c_softc *sc, uint8_t byte, 251 1.18 thorpej int flags) 252 1.18 thorpej { 253 1.18 thorpej int error = exynos_i2c_wait(sc, flags); 254 1.18 thorpej if (error) { 255 1.18 thorpej return error; 256 1.18 thorpej } 257 1.18 thorpej I2C_WRITE(sc, IICDS, byte); 258 1.18 thorpej return 0; 259 1.18 thorpej } 260 1.18 thorpej 261 1.18 thorpej static int 262 1.5 marty exynos_i2c_send_start(void *cookie, int flags) 263 1.1 reinoud { 264 1.9 marty struct exynos_i2c_softc *sc = cookie; 265 1.18 thorpej 266 1.18 thorpej mutex_enter(&sc->sc_intr_lock); 267 1.18 thorpej int error = exynos_i2c_send_start_locked(sc, flags); 268 1.18 thorpej mutex_exit(&sc->sc_intr_lock); 269 1.18 thorpej return error; 270 1.1 reinoud } 271 1.1 reinoud 272 1.3 skrll static int 273 1.5 marty exynos_i2c_send_stop(void *cookie, int flags) 274 1.1 reinoud { 275 1.9 marty struct exynos_i2c_softc *sc = cookie; 276 1.18 thorpej 277 1.18 thorpej mutex_enter(&sc->sc_intr_lock); 278 1.18 thorpej int error = exynos_i2c_send_stop_locked(sc, flags); 279 1.18 thorpej mutex_exit(&sc->sc_intr_lock); 280 1.18 thorpej return error; 281 1.1 reinoud } 282 1.1 reinoud 283 1.3 skrll static int 284 1.5 marty exynos_i2c_initiate_xfer(void *cookie, i2c_addr_t addr, int flags) 285 1.1 reinoud { 286 1.9 marty struct exynos_i2c_softc *sc = cookie; 287 1.9 marty uint8_t byte = addr & 0x7f; 288 1.18 thorpej int error; 289 1.18 thorpej 290 1.9 marty if (flags & I2C_F_READ) 291 1.9 marty byte |= READBIT; 292 1.9 marty else 293 1.9 marty byte &= ~READBIT; 294 1.18 thorpej 295 1.18 thorpej mutex_enter(&sc->sc_intr_lock); 296 1.9 marty I2C_WRITE(sc, IICADD, addr); 297 1.18 thorpej exynos_i2c_send_start_locked(sc, flags); 298 1.18 thorpej exynos_i2c_write_byte_locked(sc, byte, flags); 299 1.18 thorpej error = exynos_i2c_wait(cookie, flags); 300 1.18 thorpej mutex_exit(&sc->sc_intr_lock); 301 1.18 thorpej 302 1.18 thorpej return error; 303 1.1 reinoud } 304 1.1 reinoud 305 1.1 reinoud static int 306 1.5 marty exynos_i2c_read_byte(void *cookie, uint8_t *bytep, int flags) 307 1.1 reinoud { 308 1.9 marty struct exynos_i2c_softc *sc = cookie; 309 1.18 thorpej 310 1.18 thorpej mutex_enter(&sc->sc_intr_lock); 311 1.9 marty int error = exynos_i2c_wait(sc, flags); 312 1.18 thorpej if (error) { 313 1.18 thorpej mutex_exit(&sc->sc_intr_lock); 314 1.9 marty return error; 315 1.18 thorpej } 316 1.9 marty *bytep = I2C_READ(sc, IICDS) & 0xff; 317 1.9 marty if (flags & I2C_F_STOP) 318 1.18 thorpej exynos_i2c_send_stop_locked(sc, flags); 319 1.18 thorpej mutex_exit(&sc->sc_intr_lock); 320 1.9 marty return 0; 321 1.1 reinoud } 322 1.1 reinoud 323 1.3 skrll static int 324 1.5 marty exynos_i2c_write_byte(void *cookie, uint8_t byte, int flags) 325 1.1 reinoud { 326 1.9 marty struct exynos_i2c_softc *sc = cookie; 327 1.18 thorpej 328 1.18 thorpej mutex_enter(&sc->sc_intr_lock); 329 1.18 thorpej int error = exynos_i2c_write_byte_locked(sc, byte, flags); 330 1.18 thorpej mutex_exit(&sc->sc_intr_lock); 331 1.18 thorpej return error; 332 1.1 reinoud } 333