xref: /openbsd-src/sys/arch/arm64/dev/apliic.c (revision 97b909b357032a8ef8fbf06bc0516d33b1de443e)
1*97b909b3Skettenis /*	$OpenBSD: apliic.c,v 1.5 2022/12/10 18:43:48 kettenis Exp $	*/
23e05440bSpatrick /*
33e05440bSpatrick  * Copyright (c) 2021 Patrick Wildt <patrick@blueri.se>
43e05440bSpatrick  *
53e05440bSpatrick  * Permission to use, copy, modify, and distribute this software for any
63e05440bSpatrick  * purpose with or without fee is hereby granted, provided that the above
73e05440bSpatrick  * copyright notice and this permission notice appear in all copies.
83e05440bSpatrick  *
93e05440bSpatrick  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
103e05440bSpatrick  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
113e05440bSpatrick  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
123e05440bSpatrick  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
133e05440bSpatrick  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
143e05440bSpatrick  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
153e05440bSpatrick  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
163e05440bSpatrick  */
173e05440bSpatrick 
183e05440bSpatrick #include <sys/param.h>
193e05440bSpatrick #include <sys/systm.h>
203e05440bSpatrick #include <sys/device.h>
210163115dSkettenis #include <sys/malloc.h>
223e05440bSpatrick 
233e05440bSpatrick #include <machine/intr.h>
243e05440bSpatrick #include <machine/bus.h>
253e05440bSpatrick #include <machine/fdt.h>
263e05440bSpatrick 
273e05440bSpatrick #define _I2C_PRIVATE
283e05440bSpatrick #include <dev/i2c/i2cvar.h>
293e05440bSpatrick 
303e05440bSpatrick #include <dev/ofw/openfirm.h>
313e05440bSpatrick #include <dev/ofw/ofw_clock.h>
323e05440bSpatrick #include <dev/ofw/ofw_pinctrl.h>
333e05440bSpatrick #include <dev/ofw/ofw_power.h>
343e05440bSpatrick #include <dev/ofw/fdt.h>
353e05440bSpatrick 
363e05440bSpatrick /* Registers. */
373e05440bSpatrick #define I2C_MTXFIFO		0x00
383e05440bSpatrick #define  I2C_MTXFIFO_DATA_MASK		(0xff << 0)
393e05440bSpatrick #define  I2C_MTXFIFO_START		(1 << 8)
403e05440bSpatrick #define  I2C_MTXFIFO_STOP		(1 << 9)
413e05440bSpatrick #define  I2C_MTXFIFO_READ		(1 << 10)
423e05440bSpatrick #define I2C_MRXFIFO		0x04
433e05440bSpatrick #define  I2C_MRXFIFO_DATA_MASK		(0xff << 0)
443e05440bSpatrick #define  I2C_MRXFIFO_EMPTY		(1 << 8)
453e05440bSpatrick #define I2C_SMSTA		0x14
463e05440bSpatrick #define  I2C_SMSTA_MTN			(1 << 21)
473e05440bSpatrick #define  I2C_SMSTA_XEN			(1 << 27)
483e05440bSpatrick #define  I2C_SMSTA_XBUSY		(1 << 28)
493e05440bSpatrick #define I2C_CTL			0x1c
503e05440bSpatrick #define  I2C_CTL_CLK_MASK		(0xff << 0)
513e05440bSpatrick #define  I2C_CTL_MTR			(1 << 9)
523e05440bSpatrick #define  I2C_CTL_MRR			(1 << 10)
533e05440bSpatrick #define  I2C_CTL_EN			(1 << 11)
543e05440bSpatrick #define I2C_REV			0x28
553e05440bSpatrick 
563e05440bSpatrick #define HREAD4(sc, reg)							\
573e05440bSpatrick 	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
583e05440bSpatrick #define HWRITE4(sc, reg, val)						\
593e05440bSpatrick 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
603e05440bSpatrick #define HSET4(sc, reg, bits)						\
613e05440bSpatrick 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits))
623e05440bSpatrick #define HCLR4(sc, reg, bits)						\
633e05440bSpatrick 	HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits))
643e05440bSpatrick 
653e05440bSpatrick struct apliic_softc {
663e05440bSpatrick 	struct device		sc_dev;
673e05440bSpatrick 	bus_space_tag_t		sc_iot;
683e05440bSpatrick 	bus_space_handle_t	sc_ioh;
693e05440bSpatrick 
703e05440bSpatrick 	int			sc_node;
713e05440bSpatrick 	int			sc_hwrev;
723e05440bSpatrick 	uint32_t		sc_clkdiv;
733e05440bSpatrick 	struct i2c_controller	sc_ic;
743e05440bSpatrick };
753e05440bSpatrick 
763e05440bSpatrick int apliic_match(struct device *, void *, void *);
773e05440bSpatrick void apliic_attach(struct device *, struct device *, void *);
783e05440bSpatrick 
793e05440bSpatrick const struct cfattach	apliic_ca = {
803e05440bSpatrick 	sizeof (struct apliic_softc), apliic_match, apliic_attach
813e05440bSpatrick };
823e05440bSpatrick 
833e05440bSpatrick struct cfdriver apliic_cd = {
843e05440bSpatrick 	NULL, "apliic", DV_DULL
853e05440bSpatrick };
863e05440bSpatrick 
873e05440bSpatrick int	apliic_acquire_bus(void *, int);
883e05440bSpatrick void	apliic_release_bus(void *, int);
893e05440bSpatrick int	apliic_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t,
903e05440bSpatrick 	    void *, size_t, int);
913e05440bSpatrick 
923e05440bSpatrick void	apliic_bus_scan(struct device *, struct i2cbus_attach_args *, void *);
933e05440bSpatrick 
943e05440bSpatrick int
apliic_match(struct device * parent,void * match,void * aux)953e05440bSpatrick apliic_match(struct device *parent, void *match, void *aux)
963e05440bSpatrick {
973e05440bSpatrick 	struct fdt_attach_args *faa = aux;
983e05440bSpatrick 
993e05440bSpatrick 	return OF_is_compatible(faa->fa_node, "apple,i2c");
1003e05440bSpatrick }
1013e05440bSpatrick 
1023e05440bSpatrick void
apliic_attach(struct device * parent,struct device * self,void * aux)1033e05440bSpatrick apliic_attach(struct device *parent, struct device *self, void *aux)
1043e05440bSpatrick {
1053e05440bSpatrick 	struct apliic_softc *sc = (struct apliic_softc *)self;
1063e05440bSpatrick 	struct fdt_attach_args *faa = aux;
1073e05440bSpatrick 	struct i2cbus_attach_args iba;
1083e05440bSpatrick 	uint32_t clock_speed, bus_speed;
1093e05440bSpatrick 
1103e05440bSpatrick 	if (faa->fa_nreg < 1) {
1113e05440bSpatrick 		printf(": no registers\n");
1123e05440bSpatrick 		return;
1133e05440bSpatrick 	}
1143e05440bSpatrick 
1153e05440bSpatrick 	sc->sc_iot = faa->fa_iot;
1163e05440bSpatrick 	sc->sc_node = faa->fa_node;
1173e05440bSpatrick 
1183e05440bSpatrick 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr,
1193e05440bSpatrick 	    faa->fa_reg[0].size, 0, &sc->sc_ioh)) {
1203e05440bSpatrick 		printf(": can't map registers\n");
1213e05440bSpatrick 		return;
1223e05440bSpatrick 	}
1233e05440bSpatrick 
1243e05440bSpatrick 	printf("\n");
1253e05440bSpatrick 
1263e05440bSpatrick 	pinctrl_byname(sc->sc_node, "default");
1273e05440bSpatrick 	power_domain_enable(sc->sc_node);
1283e05440bSpatrick 	clock_enable_all(sc->sc_node);
1293e05440bSpatrick 
1303e05440bSpatrick 	clock_speed = clock_get_frequency(sc->sc_node, NULL);
1313e05440bSpatrick 	bus_speed = OF_getpropint(sc->sc_node, "clock-frequency", 100000) * 16;
1323e05440bSpatrick 	sc->sc_clkdiv = (clock_speed + bus_speed - 1) / bus_speed;
1333e05440bSpatrick 	KASSERT(sc->sc_clkdiv <= I2C_CTL_CLK_MASK);
1343e05440bSpatrick 
1353e05440bSpatrick 	sc->sc_hwrev = HREAD4(sc, I2C_REV);
1363e05440bSpatrick 
1373e05440bSpatrick 	HWRITE4(sc, I2C_CTL, sc->sc_clkdiv | I2C_CTL_MTR | I2C_CTL_MRR |
1383e05440bSpatrick 	    (sc->sc_hwrev >= 6 ? I2C_CTL_EN : 0));
1393e05440bSpatrick 
1403e05440bSpatrick 	sc->sc_ic.ic_cookie = sc;
1413e05440bSpatrick 	sc->sc_ic.ic_acquire_bus = apliic_acquire_bus;
1423e05440bSpatrick 	sc->sc_ic.ic_release_bus = apliic_release_bus;
1433e05440bSpatrick 	sc->sc_ic.ic_exec = apliic_exec;
1443e05440bSpatrick 
1453e05440bSpatrick 	/* Configure its children */
1463e05440bSpatrick 	memset(&iba, 0, sizeof(iba));
1473e05440bSpatrick 	iba.iba_name = "iic";
1483e05440bSpatrick 	iba.iba_tag = &sc->sc_ic;
1493e05440bSpatrick 	iba.iba_bus_scan = apliic_bus_scan;
1503e05440bSpatrick 	iba.iba_bus_scan_arg = &sc->sc_node;
1513e05440bSpatrick 
1523e05440bSpatrick 	config_found(&sc->sc_dev, &iba, iicbus_print);
1533e05440bSpatrick }
1543e05440bSpatrick 
1553e05440bSpatrick int
apliic_wait(struct apliic_softc * sc)156075e123eSkettenis apliic_wait(struct apliic_softc *sc)
157075e123eSkettenis {
158075e123eSkettenis 	uint32_t reg;
159075e123eSkettenis 	int timo;
160075e123eSkettenis 
161*97b909b3Skettenis 	for (timo = 100; timo > 0; timo--) {
162075e123eSkettenis 		reg = HREAD4(sc, I2C_SMSTA);
163075e123eSkettenis 		if (reg & I2C_SMSTA_XEN)
164075e123eSkettenis 			break;
165075e123eSkettenis 		delay(1000);
166075e123eSkettenis 	}
167075e123eSkettenis 	if (reg & I2C_SMSTA_MTN)
168075e123eSkettenis 		return ENXIO;
169075e123eSkettenis 	if (timo == 0) {
170075e123eSkettenis 		HWRITE4(sc, I2C_SMSTA, reg);
171075e123eSkettenis 		return ETIMEDOUT;
172075e123eSkettenis 	}
173075e123eSkettenis 
174075e123eSkettenis 	HWRITE4(sc, I2C_SMSTA, I2C_SMSTA_XEN);
175075e123eSkettenis 	return 0;
176075e123eSkettenis }
177075e123eSkettenis 
178075e123eSkettenis int
apliic_acquire_bus(void * cookie,int flags)1793e05440bSpatrick apliic_acquire_bus(void *cookie, int flags)
1803e05440bSpatrick {
1813e05440bSpatrick 	return 0;
1823e05440bSpatrick }
1833e05440bSpatrick 
1843e05440bSpatrick void
apliic_release_bus(void * cookie,int flags)1853e05440bSpatrick apliic_release_bus(void *cookie, int flags)
1863e05440bSpatrick {
1873e05440bSpatrick }
1883e05440bSpatrick 
1893e05440bSpatrick int
apliic_exec(void * cookie,i2c_op_t op,i2c_addr_t addr,const void * cmd,size_t cmdlen,void * buf,size_t buflen,int flags)1903e05440bSpatrick apliic_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmd,
1913e05440bSpatrick     size_t cmdlen, void *buf, size_t buflen, int flags)
1923e05440bSpatrick {
1933e05440bSpatrick 	struct apliic_softc *sc = cookie;
1943e05440bSpatrick 	uint32_t reg;
195075e123eSkettenis 	int error, i;
1963e05440bSpatrick 
1973e05440bSpatrick 	if (!I2C_OP_STOP_P(op))
1983e05440bSpatrick 		return EINVAL;
1993e05440bSpatrick 
200075e123eSkettenis 	reg = HREAD4(sc, I2C_SMSTA);
201075e123eSkettenis 	HWRITE4(sc, I2C_SMSTA, reg);
2023e05440bSpatrick 
2033e05440bSpatrick 	if (cmdlen > 0) {
2043e05440bSpatrick 		HWRITE4(sc, I2C_MTXFIFO, I2C_MTXFIFO_START | addr << 1);
2053e05440bSpatrick 		for (i = 0; i < cmdlen - 1; i++)
2063e05440bSpatrick 			HWRITE4(sc, I2C_MTXFIFO, ((uint8_t *)cmd)[i]);
2073e05440bSpatrick 		HWRITE4(sc, I2C_MTXFIFO, ((uint8_t *)cmd)[cmdlen - 1] |
2083e05440bSpatrick 		    (buflen == 0 ? I2C_MTXFIFO_STOP : 0));
2093e05440bSpatrick 	}
2103e05440bSpatrick 
2113e05440bSpatrick 	if (buflen == 0)
2123e05440bSpatrick 		return 0;
2133e05440bSpatrick 
2143e05440bSpatrick 	if (I2C_OP_READ_P(op)) {
2153e05440bSpatrick 		HWRITE4(sc, I2C_MTXFIFO, I2C_MTXFIFO_START | addr << 1 | 1);
2163e05440bSpatrick 		HWRITE4(sc, I2C_MTXFIFO, I2C_MTXFIFO_READ | buflen |
2173e05440bSpatrick 		    I2C_MTXFIFO_STOP);
218075e123eSkettenis 		error = apliic_wait(sc);
219075e123eSkettenis 		if (error)
220075e123eSkettenis 			return error;
2213e05440bSpatrick 		for (i = 0; i < buflen; i++) {
2223e05440bSpatrick 			reg = HREAD4(sc, I2C_MRXFIFO);
223585c4a5aSkettenis 			if (reg & I2C_MRXFIFO_EMPTY)
2243e05440bSpatrick 				return EIO;
2253e05440bSpatrick 			((uint8_t *)buf)[i] = reg & I2C_MRXFIFO_DATA_MASK;
2263e05440bSpatrick 		}
2273e05440bSpatrick 	} else {
2283e05440bSpatrick 		if (cmdlen == 0)
2293e05440bSpatrick 			HWRITE4(sc, I2C_MTXFIFO, I2C_MTXFIFO_START | addr << 1);
2303e05440bSpatrick 		for (i = 0; i < buflen - 1; i++)
2313e05440bSpatrick 			HWRITE4(sc, I2C_MTXFIFO, ((uint8_t *)buf)[i]);
2323e05440bSpatrick 		HWRITE4(sc, I2C_MTXFIFO, ((uint8_t *)buf)[buflen - 1] |
2333e05440bSpatrick 		    I2C_MTXFIFO_STOP);
234075e123eSkettenis 		error = apliic_wait(sc);
235075e123eSkettenis 		if (error)
236075e123eSkettenis 			return error;
2373e05440bSpatrick 	}
2383e05440bSpatrick 
2393e05440bSpatrick 	return 0;
2403e05440bSpatrick }
2413e05440bSpatrick 
2423e05440bSpatrick void
apliic_bus_scan(struct device * self,struct i2cbus_attach_args * iba,void * arg)2433e05440bSpatrick apliic_bus_scan(struct device *self, struct i2cbus_attach_args *iba, void *arg)
2443e05440bSpatrick {
2453e05440bSpatrick 	int iba_node = *(int *)arg;
2463e05440bSpatrick 	struct i2c_attach_args ia;
2470163115dSkettenis 	char status[32];
2480163115dSkettenis 	char *compat;
2493e05440bSpatrick 	uint32_t reg[1];
2503e05440bSpatrick 	int node;
2510163115dSkettenis 	int len;
2523e05440bSpatrick 
2533e05440bSpatrick 	for (node = OF_child(iba_node); node; node = OF_peer(node)) {
2543e05440bSpatrick 		memset(status, 0, sizeof(status));
2553e05440bSpatrick 		if (OF_getprop(node, "status", status, sizeof(status)) > 0 &&
2563e05440bSpatrick 		    strcmp(status, "disabled") == 0)
2573e05440bSpatrick 			continue;
2583e05440bSpatrick 
2590163115dSkettenis 		memset(reg, 0, sizeof(reg));
2603e05440bSpatrick 		if (OF_getprop(node, "reg", &reg, sizeof(reg)) != sizeof(reg))
2613e05440bSpatrick 			continue;
2623e05440bSpatrick 
2630163115dSkettenis 		len = OF_getproplen(node, "compatible");
2640163115dSkettenis 		if (len <= 0)
2650163115dSkettenis 			continue;
2660163115dSkettenis 
2670163115dSkettenis 		compat = malloc(len, M_TEMP, M_WAITOK);
2680163115dSkettenis 		OF_getprop(node, "compatible", compat, len);
2690163115dSkettenis 
2703e05440bSpatrick 		memset(&ia, 0, sizeof(ia));
2713e05440bSpatrick 		ia.ia_tag = iba->iba_tag;
2723e05440bSpatrick 		ia.ia_addr = bemtoh32(&reg[0]);
2730163115dSkettenis 		ia.ia_name = compat;
2740163115dSkettenis 		ia.ia_namelen = len;
2753e05440bSpatrick 		ia.ia_cookie = &node;
2763e05440bSpatrick 		config_found(self, &ia, iic_print);
2770163115dSkettenis 
2780163115dSkettenis 		free(compat, M_TEMP, len);
2793e05440bSpatrick 	}
2803e05440bSpatrick }
281