xref: /netbsd-src/sys/arch/arm/samsung/exynos_i2c.c (revision 34ee086f833fe6265aed19fb6b8064a2f7a8253e)
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