1*34ee086fSskrll /* $NetBSD: exynos_i2c.c,v 1.22 2021/03/14 08:16:57 skrll Exp $ */
204bd7576Sreinoud
304bd7576Sreinoud /*
49c676a63Smarty * Copyright (c) 2015 Jared D. McNeill <jmcneill@invisible.ca>
504bd7576Sreinoud * All rights reserved.
604bd7576Sreinoud *
704bd7576Sreinoud * Redistribution and use in source and binary forms, with or without
804bd7576Sreinoud * modification, are permitted provided that the following conditions
904bd7576Sreinoud * are met:
1004bd7576Sreinoud * 1. Redistributions of source code must retain the above copyright
1104bd7576Sreinoud * notice, this list of conditions and the following disclaimer.
1204bd7576Sreinoud * 2. Redistributions in binary form must reproduce the above copyright
1304bd7576Sreinoud * notice, this list of conditions and the following disclaimer in the
1404bd7576Sreinoud * documentation and/or other materials provided with the distribution.
1504bd7576Sreinoud *
1604bd7576Sreinoud * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
1704bd7576Sreinoud * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
1804bd7576Sreinoud * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
1904bd7576Sreinoud * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
2004bd7576Sreinoud * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
2104bd7576Sreinoud * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
2204bd7576Sreinoud * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
2304bd7576Sreinoud * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
2404bd7576Sreinoud * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
2504bd7576Sreinoud * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
2604bd7576Sreinoud * POSSIBILITY OF SUCH DAMAGE.
2704bd7576Sreinoud *
2804bd7576Sreinoud */
2904bd7576Sreinoud
3004bd7576Sreinoud #include "opt_exynos.h"
3104bd7576Sreinoud #include "opt_arm_debug.h"
3204bd7576Sreinoud
3304bd7576Sreinoud #include <sys/cdefs.h>
34*34ee086fSskrll __KERNEL_RCSID(0, "$NetBSD: exynos_i2c.c,v 1.22 2021/03/14 08:16:57 skrll Exp $");
3504bd7576Sreinoud
3604bd7576Sreinoud #include <sys/param.h>
3704bd7576Sreinoud #include <sys/bus.h>
3804bd7576Sreinoud #include <sys/device.h>
3904bd7576Sreinoud #include <sys/intr.h>
4004bd7576Sreinoud #include <sys/systm.h>
419c676a63Smarty #include <sys/kernel.h>
4204bd7576Sreinoud #include <sys/kmem.h>
4304bd7576Sreinoud
4404bd7576Sreinoud #include <arm/samsung/exynos_reg.h>
459c676a63Smarty #include <arm/samsung/exynos_var.h>
4604bd7576Sreinoud #include <arm/samsung/exynos_intr.h>
4704bd7576Sreinoud
4804bd7576Sreinoud #include <sys/gpio.h>
4904bd7576Sreinoud #include <dev/gpio/gpiovar.h>
5004bd7576Sreinoud
5104bd7576Sreinoud #include <dev/i2c/i2cvar.h>
5204bd7576Sreinoud #include <dev/i2c/i2c_bitbang.h>
5304bd7576Sreinoud
5401323977Smarty #include <dev/fdt/fdtvar.h>
5504bd7576Sreinoud
5601323977Smarty struct exynos_i2c_softc {
5701323977Smarty device_t sc_dev;
5801323977Smarty bus_space_tag_t sc_bst;
5901323977Smarty bus_space_handle_t sc_bsh;
6001323977Smarty void * sc_ih;
619c676a63Smarty struct clk * sc_clk;
6204bd7576Sreinoud
639c676a63Smarty struct fdtbus_pinctrl_pin *sc_sda;
649c676a63Smarty struct fdtbus_pinctrl_pin *sc_scl;
6501323977Smarty bool sc_sda_is_output;
669c676a63Smarty
6701323977Smarty struct i2c_controller sc_ic;
6817465ae7Sthorpej kmutex_t sc_intr_lock;
6917465ae7Sthorpej kcondvar_t sc_intr_wait;
7004bd7576Sreinoud };
7104bd7576Sreinoud
7201323977Smarty static int exynos_i2c_intr(void *);
7304bd7576Sreinoud
7401323977Smarty static int exynos_i2c_send_start(void *, int);
7501323977Smarty static int exynos_i2c_send_stop(void *, int);
7601323977Smarty static int exynos_i2c_initiate_xfer(void *, i2c_addr_t, int);
7701323977Smarty static int exynos_i2c_read_byte(void *, uint8_t *, int);
7801323977Smarty static int exynos_i2c_write_byte(void *, uint8_t , int);
7904bd7576Sreinoud
809c676a63Smarty static int exynos_i2c_wait(struct exynos_i2c_softc *, int);
819c676a63Smarty
8204bd7576Sreinoud
8301323977Smarty static int exynos_i2c_match(device_t, cfdata_t, void *);
8401323977Smarty static void exynos_i2c_attach(device_t, device_t, void *);
8504bd7576Sreinoud
8601323977Smarty CFATTACH_DECL_NEW(exynos_i2c, sizeof(struct exynos_i2c_softc),
8701323977Smarty exynos_i2c_match, exynos_i2c_attach, NULL, NULL);
8804bd7576Sreinoud
89a8082a0bSmarty #define I2C_WRITE(sc, reg, val) \
909c676a63Smarty bus_space_write_1((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
91a8082a0bSmarty #define I2C_READ(sc, reg) \
929c676a63Smarty bus_space_read_1((sc)->sc_bst, (sc)->sc_bsh, (reg))
93a8082a0bSmarty
949c676a63Smarty #define IICCON 0x00
959c676a63Smarty #define IICSTAT 0x04
969c676a63Smarty #define IICADD 0x08
979c676a63Smarty #define IICDS 0x0C
989c676a63Smarty
999c676a63Smarty #define ACKENABLE (1<<7)
1009c676a63Smarty #define TXPRESCALE (1<<6)
1019c676a63Smarty #define INTENABLE (1<<5)
102a8082a0bSmarty #define IRQPEND (1<<4)
1039c676a63Smarty #define PRESCALE (0x0f)
1049c676a63Smarty
1059c676a63Smarty #define MODESELECT (3<<6)
1069c676a63Smarty #define BUSYSTART (1<<5)
1079c676a63Smarty #define BUSENABLE (1<<4)
1089c676a63Smarty #define ARBITRATION (1<<3)
1099c676a63Smarty #define SLAVESTATUS (1<<2)
1109c676a63Smarty #define ZEROSTATUS (1<<1)
1119c676a63Smarty #define LASTBIT (1<<0)
1129c676a63Smarty
1139c676a63Smarty #define READBIT (1<<7)
114a8082a0bSmarty
1156e54367aSthorpej static const struct device_compatible_entry compat_data[] = {
1166e54367aSthorpej { .compat = "samsung,s3c2440-i2c" },
1176e54367aSthorpej DEVICE_COMPAT_EOL
1186e54367aSthorpej };
1196e54367aSthorpej
12004bd7576Sreinoud static int
exynos_i2c_match(device_t self,cfdata_t cf,void * aux)12101323977Smarty exynos_i2c_match(device_t self, cfdata_t cf, void *aux)
12204bd7576Sreinoud {
12301323977Smarty struct fdt_attach_args * const faa = aux;
12404bd7576Sreinoud
1256e54367aSthorpej return of_compatible_match(faa->faa_phandle, compat_data);
12604bd7576Sreinoud }
12704bd7576Sreinoud
12804bd7576Sreinoud static void
exynos_i2c_attach(device_t parent,device_t self,void * aux)12901323977Smarty exynos_i2c_attach(device_t parent, device_t self, void *aux)
13004bd7576Sreinoud {
13101323977Smarty struct exynos_i2c_softc * const sc = device_private(self);
13201323977Smarty struct fdt_attach_args * const faa = aux;
13301323977Smarty const int phandle = faa->faa_phandle;
13401323977Smarty char intrstr[128];
13501323977Smarty bus_addr_t addr;
13601323977Smarty bus_size_t size;
13701323977Smarty int error;
13804bd7576Sreinoud
13901323977Smarty if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
14001323977Smarty aprint_error(": couldn't get registers\n");
14104bd7576Sreinoud return;
14204bd7576Sreinoud }
14304bd7576Sreinoud
14401323977Smarty sc->sc_dev = self;
14501323977Smarty sc->sc_bst = faa->faa_bst;
14601323977Smarty error = bus_space_map(sc->sc_bst, addr, size, 0, &sc->sc_bsh);
14701323977Smarty if (error) {
148d3f45d9fSskrll aprint_error(": couldn't map %#" PRIxBUSADDR ": %d", addr,
14901323977Smarty error);
15001323977Smarty return;
15104bd7576Sreinoud }
15204bd7576Sreinoud
15317465ae7Sthorpej mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_VM);
15417465ae7Sthorpej cv_init(&sc->sc_intr_wait, device_xname(self));
155a8082a0bSmarty aprint_normal(" @ 0x%08x\n", (uint)addr);
15604bd7576Sreinoud
15701323977Smarty if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
15801323977Smarty aprint_error_dev(self, "failed to decode interrupt\n");
15901323977Smarty return;
16004bd7576Sreinoud }
16104bd7576Sreinoud
162*34ee086fSskrll sc->sc_ih = fdtbus_intr_establish_xname(phandle, 0, IPL_VM,
163*34ee086fSskrll FDT_INTR_MPSAFE, exynos_i2c_intr, sc, device_xname(self));
16401323977Smarty if (sc->sc_ih == NULL) {
16501323977Smarty aprint_error_dev(self, "couldn't establish interrupt on %s\n",
16601323977Smarty intrstr);
16701323977Smarty return;
16801323977Smarty }
16901323977Smarty aprint_normal_dev(self, "interrupting on %s\n", intrstr);
17004bd7576Sreinoud
17117465ae7Sthorpej iic_tag_init(&sc->sc_ic);
1729c676a63Smarty sc->sc_ic.ic_cookie = sc;
1739c676a63Smarty sc->sc_ic.ic_send_start = exynos_i2c_send_start;
1749c676a63Smarty sc->sc_ic.ic_send_stop = exynos_i2c_send_stop;
1759c676a63Smarty sc->sc_ic.ic_initiate_xfer = exynos_i2c_initiate_xfer;
1769c676a63Smarty sc->sc_ic.ic_read_byte = exynos_i2c_read_byte;
1779c676a63Smarty sc->sc_ic.ic_write_byte = exynos_i2c_write_byte;
17801323977Smarty
17921b71bc0Sthorpej fdtbus_register_i2c_controller(&sc->sc_ic, phandle);
180ccafc8a1Sjmcneill
181c5030e66Sjmcneill fdtbus_attach_i2cbus(self, phandle, &sc->sc_ic, iicbus_print);
18201323977Smarty }
18301323977Smarty
18401323977Smarty static int
exynos_i2c_intr(void * priv)18501323977Smarty exynos_i2c_intr(void *priv)
18601323977Smarty {
18701323977Smarty struct exynos_i2c_softc * const sc = priv;
18801323977Smarty
1899c676a63Smarty uint8_t istatus = I2C_READ(sc, IICCON);
190a8082a0bSmarty if (!(istatus & IRQPEND))
191a8082a0bSmarty return 0;
192a8082a0bSmarty istatus &= ~IRQPEND;
1939c676a63Smarty I2C_WRITE(sc, IICCON, istatus);
19401323977Smarty
19517465ae7Sthorpej mutex_enter(&sc->sc_intr_lock);
19617465ae7Sthorpej cv_broadcast(&sc->sc_intr_wait);
19717465ae7Sthorpej mutex_exit(&sc->sc_intr_lock);
19801323977Smarty
19901323977Smarty return 1;
20001323977Smarty }
20104bd7576Sreinoud
20204bd7576Sreinoud static int
exynos_i2c_wait(struct exynos_i2c_softc * sc,int flags)2039c676a63Smarty exynos_i2c_wait(struct exynos_i2c_softc *sc, int flags)
2049c676a63Smarty {
2059c676a63Smarty int error, retry;
2069c676a63Smarty uint8_t stat = 0;
2079c676a63Smarty
2089c676a63Smarty retry = (flags & I2C_F_POLL) ? 100000 : 100;
2099c676a63Smarty
2109c676a63Smarty while (--retry > 0) {
2119c676a63Smarty if ((flags & I2C_F_POLL) == 0) {
21217465ae7Sthorpej error = cv_timedwait_sig(&sc->sc_intr_wait,
21317465ae7Sthorpej &sc->sc_intr_lock,
214d1579b2dSriastradh uimax(mstohz(10), 1));
2159c676a63Smarty if (error) {
2169c676a63Smarty return error;
2179c676a63Smarty }
2189c676a63Smarty }
2199c676a63Smarty stat = I2C_READ(sc, IICSTAT);
2209c676a63Smarty if (!(stat & BUSYSTART)) {
2219c676a63Smarty break;
2229c676a63Smarty }
2239c676a63Smarty if (flags & I2C_F_POLL) {
2249c676a63Smarty delay(10);
2259c676a63Smarty }
2269c676a63Smarty }
2279c676a63Smarty if (retry == 0) {
2289c676a63Smarty stat = I2C_READ(sc, IICSTAT);
2299c676a63Smarty device_printf(sc->sc_dev, "timed out, status = %#x\n", stat);
2309c676a63Smarty return ETIMEDOUT;
2319c676a63Smarty }
2329c676a63Smarty
2339c676a63Smarty return 0;
2349c676a63Smarty }
2359c676a63Smarty
2369c676a63Smarty
2379c676a63Smarty static int
exynos_i2c_send_start_locked(struct exynos_i2c_softc * sc,int flags)23817465ae7Sthorpej exynos_i2c_send_start_locked(struct exynos_i2c_softc *sc, int flags)
23917465ae7Sthorpej {
24017465ae7Sthorpej I2C_WRITE(sc, IICSTAT, 0xF0);
24117465ae7Sthorpej return 0;
24217465ae7Sthorpej }
24317465ae7Sthorpej
24417465ae7Sthorpej static int
exynos_i2c_send_stop_locked(struct exynos_i2c_softc * sc,int flags)24517465ae7Sthorpej exynos_i2c_send_stop_locked(struct exynos_i2c_softc *sc, int flags)
24617465ae7Sthorpej {
24717465ae7Sthorpej I2C_WRITE(sc, IICSTAT, 0xD0);
24817465ae7Sthorpej return 0;
24917465ae7Sthorpej }
25017465ae7Sthorpej
25117465ae7Sthorpej static int
exynos_i2c_write_byte_locked(struct exynos_i2c_softc * sc,uint8_t byte,int flags)25217465ae7Sthorpej exynos_i2c_write_byte_locked(struct exynos_i2c_softc *sc, uint8_t byte,
25317465ae7Sthorpej int flags)
25417465ae7Sthorpej {
25517465ae7Sthorpej int error = exynos_i2c_wait(sc, flags);
25617465ae7Sthorpej if (error) {
25717465ae7Sthorpej return error;
25817465ae7Sthorpej }
25917465ae7Sthorpej I2C_WRITE(sc, IICDS, byte);
26017465ae7Sthorpej return 0;
26117465ae7Sthorpej }
26217465ae7Sthorpej
26317465ae7Sthorpej static int
exynos_i2c_send_start(void * cookie,int flags)26401323977Smarty exynos_i2c_send_start(void *cookie, int flags)
26504bd7576Sreinoud {
2669c676a63Smarty struct exynos_i2c_softc *sc = cookie;
26717465ae7Sthorpej
26817465ae7Sthorpej mutex_enter(&sc->sc_intr_lock);
26917465ae7Sthorpej int error = exynos_i2c_send_start_locked(sc, flags);
27017465ae7Sthorpej mutex_exit(&sc->sc_intr_lock);
27117465ae7Sthorpej return error;
27204bd7576Sreinoud }
27304bd7576Sreinoud
27404bd7576Sreinoud static int
exynos_i2c_send_stop(void * cookie,int flags)27501323977Smarty exynos_i2c_send_stop(void *cookie, int flags)
27604bd7576Sreinoud {
2779c676a63Smarty struct exynos_i2c_softc *sc = cookie;
27817465ae7Sthorpej
27917465ae7Sthorpej mutex_enter(&sc->sc_intr_lock);
28017465ae7Sthorpej int error = exynos_i2c_send_stop_locked(sc, flags);
28117465ae7Sthorpej mutex_exit(&sc->sc_intr_lock);
28217465ae7Sthorpej return error;
28304bd7576Sreinoud }
28404bd7576Sreinoud
28504bd7576Sreinoud static int
exynos_i2c_initiate_xfer(void * cookie,i2c_addr_t addr,int flags)28601323977Smarty exynos_i2c_initiate_xfer(void *cookie, i2c_addr_t addr, int flags)
28704bd7576Sreinoud {
2889c676a63Smarty struct exynos_i2c_softc *sc = cookie;
2899c676a63Smarty uint8_t byte = addr & 0x7f;
29017465ae7Sthorpej int error;
29117465ae7Sthorpej
2929c676a63Smarty if (flags & I2C_F_READ)
2939c676a63Smarty byte |= READBIT;
2949c676a63Smarty else
2959c676a63Smarty byte &= ~READBIT;
29617465ae7Sthorpej
29717465ae7Sthorpej mutex_enter(&sc->sc_intr_lock);
2989c676a63Smarty I2C_WRITE(sc, IICADD, addr);
29917465ae7Sthorpej exynos_i2c_send_start_locked(sc, flags);
30017465ae7Sthorpej exynos_i2c_write_byte_locked(sc, byte, flags);
30117465ae7Sthorpej error = exynos_i2c_wait(cookie, flags);
30217465ae7Sthorpej mutex_exit(&sc->sc_intr_lock);
30317465ae7Sthorpej
30417465ae7Sthorpej return error;
30504bd7576Sreinoud }
30604bd7576Sreinoud
30704bd7576Sreinoud static int
exynos_i2c_read_byte(void * cookie,uint8_t * bytep,int flags)30801323977Smarty exynos_i2c_read_byte(void *cookie, uint8_t *bytep, int flags)
30904bd7576Sreinoud {
3109c676a63Smarty struct exynos_i2c_softc *sc = cookie;
31117465ae7Sthorpej
31217465ae7Sthorpej mutex_enter(&sc->sc_intr_lock);
3139c676a63Smarty int error = exynos_i2c_wait(sc, flags);
31417465ae7Sthorpej if (error) {
31517465ae7Sthorpej mutex_exit(&sc->sc_intr_lock);
3169c676a63Smarty return error;
31717465ae7Sthorpej }
3189c676a63Smarty *bytep = I2C_READ(sc, IICDS) & 0xff;
3199c676a63Smarty if (flags & I2C_F_STOP)
32017465ae7Sthorpej exynos_i2c_send_stop_locked(sc, flags);
32117465ae7Sthorpej mutex_exit(&sc->sc_intr_lock);
3229c676a63Smarty return 0;
32304bd7576Sreinoud }
32404bd7576Sreinoud
32504bd7576Sreinoud static int
exynos_i2c_write_byte(void * cookie,uint8_t byte,int flags)32601323977Smarty exynos_i2c_write_byte(void *cookie, uint8_t byte, int flags)
32704bd7576Sreinoud {
3289c676a63Smarty struct exynos_i2c_softc *sc = cookie;
32917465ae7Sthorpej
33017465ae7Sthorpej mutex_enter(&sc->sc_intr_lock);
33117465ae7Sthorpej int error = exynos_i2c_write_byte_locked(sc, byte, flags);
33217465ae7Sthorpej mutex_exit(&sc->sc_intr_lock);
3339c676a63Smarty return error;
33404bd7576Sreinoud }
335