xref: /openbsd-src/sys/dev/fdt/qciic_fdt.c (revision ddc4c9691a1753dc2aad5d05fe2ac8b9f3d90261)
1*ddc4c969Skettenis /*	$OpenBSD: qciic_fdt.c,v 1.2 2024/10/17 17:58:58 kettenis Exp $	*/
204270302Spatrick /*
304270302Spatrick  * Copyright (c) 2022 Mark Kettenis <kettenis@openbsd.org>
404270302Spatrick  *
504270302Spatrick  * Permission to use, copy, modify, and distribute this software for any
604270302Spatrick  * purpose with or without fee is hereby granted, provided that the above
704270302Spatrick  * copyright notice and this permission notice appear in all copies.
804270302Spatrick  *
904270302Spatrick  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1004270302Spatrick  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1104270302Spatrick  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1204270302Spatrick  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1304270302Spatrick  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1404270302Spatrick  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1504270302Spatrick  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1604270302Spatrick  */
1704270302Spatrick 
1804270302Spatrick #include <sys/param.h>
1904270302Spatrick #include <sys/systm.h>
2004270302Spatrick #include <sys/device.h>
2104270302Spatrick 
2204270302Spatrick #include <machine/bus.h>
2304270302Spatrick #include <machine/intr.h>
2404270302Spatrick #include <machine/fdt.h>
2504270302Spatrick 
2604270302Spatrick #include <dev/ofw/openfirm.h>
2704270302Spatrick #include <dev/ofw/ofw_gpio.h>
28*ddc4c969Skettenis #include <dev/ofw/ofw_pinctrl.h>
2904270302Spatrick #include <dev/ofw/fdt.h>
3004270302Spatrick 
3104270302Spatrick #define _I2C_PRIVATE
3204270302Spatrick #include <dev/i2c/i2cvar.h>
3304270302Spatrick 
3404270302Spatrick /* Registers */
3504270302Spatrick #define GENI_I2C_TX_TRANS_LEN		0x26c
3604270302Spatrick #define GENI_I2C_RX_TRANS_LEN		0x270
3704270302Spatrick #define GENI_M_CMD0			0x600
3804270302Spatrick #define  GENI_M_CMD0_OPCODE_I2C_WRITE	(0x1 << 27)
3904270302Spatrick #define  GENI_M_CMD0_OPCODE_I2C_READ	(0x2 << 27)
4004270302Spatrick #define  GENI_M_CMD0_SLV_ADDR_SHIFT	9
4104270302Spatrick #define  GENI_M_CMD0_STOP_STRETCH	(1 << 2)
4204270302Spatrick #define GENI_M_IRQ_STATUS		0x610
4304270302Spatrick #define GENI_M_IRQ_CLEAR		0x618
4404270302Spatrick #define  GENI_M_IRQ_CMD_DONE		(1 << 0)
4504270302Spatrick #define GENI_TX_FIFO			0x700
4604270302Spatrick #define GENI_RX_FIFO			0x780
4704270302Spatrick #define GENI_TX_FIFO_STATUS		0x800
4804270302Spatrick #define GENI_RX_FIFO_STATUS		0x804
4904270302Spatrick #define  GENI_RX_FIFO_STATUS_WC(val)	((val) & 0xffffff)
5004270302Spatrick 
5104270302Spatrick #define HREAD4(sc, reg)							\
5204270302Spatrick 	(bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg)))
5304270302Spatrick #define HWRITE4(sc, reg, val)						\
5404270302Spatrick 	bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val))
5504270302Spatrick 
5604270302Spatrick struct qciic_fdt_softc {
5704270302Spatrick 	struct device		sc_dev;
5804270302Spatrick 	bus_space_tag_t		sc_iot;
5904270302Spatrick 	bus_space_handle_t	sc_ioh;
6004270302Spatrick 
6104270302Spatrick 	int			sc_node;
6204270302Spatrick 	struct device		*sc_iic;
6304270302Spatrick 
6404270302Spatrick 	struct i2c_controller	sc_ic;
6504270302Spatrick };
6604270302Spatrick 
6704270302Spatrick int	qciic_fdt_match(struct device *, void *, void *);
6804270302Spatrick void	qciic_fdt_attach(struct device *, struct device *, void *);
6904270302Spatrick 
7004270302Spatrick const struct cfattach qciic_fdt_ca = {
7104270302Spatrick 	sizeof (struct qciic_fdt_softc), qciic_fdt_match, qciic_fdt_attach
7204270302Spatrick };
7304270302Spatrick 
7404270302Spatrick int	qciic_fdt_acquire_bus(void *, int);
7504270302Spatrick void	qciic_fdt_release_bus(void *, int);
7604270302Spatrick int	qciic_fdt_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t,
7704270302Spatrick 	    void *, size_t, int);
7804270302Spatrick 
7904270302Spatrick void	*qciic_fdt_i2c_intr_establish(void *, void *, int, int (*)(void *),
8004270302Spatrick 	    void *, const char *);
8104270302Spatrick void	qciic_fdt_i2c_intr_disestablish(void *, void *);
8204270302Spatrick const char *qciic_fdt_i2c_intr_string(void *, void *);
8304270302Spatrick 
8404270302Spatrick void	qciic_fdt_bus_scan(struct device *, struct i2cbus_attach_args *,
8504270302Spatrick 	    void *);
8604270302Spatrick 
8704270302Spatrick int
8804270302Spatrick qciic_fdt_match(struct device *parent, void *match, void *aux)
8904270302Spatrick {
9004270302Spatrick 	struct fdt_attach_args *faa = aux;
9104270302Spatrick 
9204270302Spatrick 	return OF_is_compatible(faa->fa_node, "qcom,geni-i2c");
9304270302Spatrick }
9404270302Spatrick 
9504270302Spatrick void
9604270302Spatrick qciic_fdt_attach(struct device *parent, struct device *self, void *aux)
9704270302Spatrick {
9804270302Spatrick 	struct qciic_fdt_softc *sc = (struct qciic_fdt_softc *)self;
9904270302Spatrick 	struct fdt_attach_args *faa = aux;
10004270302Spatrick 	struct i2cbus_attach_args iba;
10104270302Spatrick 
10204270302Spatrick 	sc->sc_node = faa->fa_node;
10304270302Spatrick 
10404270302Spatrick 	sc->sc_iot = faa->fa_iot;
10504270302Spatrick 	if (bus_space_map(sc->sc_iot, faa->fa_reg[0].addr, faa->fa_reg[0].size,
10604270302Spatrick 	    0, &sc->sc_ioh)) {
10704270302Spatrick 		printf(": can't map registers\n");
10804270302Spatrick 		return;
10904270302Spatrick 	}
11004270302Spatrick 
11104270302Spatrick 	printf("\n");
11204270302Spatrick 
113*ddc4c969Skettenis 	pinctrl_byname(sc->sc_node, "default");
114*ddc4c969Skettenis 
11504270302Spatrick 	sc->sc_ic.ic_cookie = sc;
11604270302Spatrick 	sc->sc_ic.ic_acquire_bus = qciic_fdt_acquire_bus;
11704270302Spatrick 	sc->sc_ic.ic_release_bus = qciic_fdt_release_bus;
11804270302Spatrick 	sc->sc_ic.ic_exec = qciic_fdt_exec;
11904270302Spatrick 	sc->sc_ic.ic_intr_establish = qciic_fdt_i2c_intr_establish;
12004270302Spatrick 	sc->sc_ic.ic_intr_disestablish = qciic_fdt_i2c_intr_disestablish;
12104270302Spatrick 	sc->sc_ic.ic_intr_string = qciic_fdt_i2c_intr_string;
12204270302Spatrick 
12304270302Spatrick 	memset(&iba, 0, sizeof(iba));
12404270302Spatrick 	iba.iba_name = "iic";
12504270302Spatrick 	iba.iba_tag = &sc->sc_ic;
12604270302Spatrick 	iba.iba_bus_scan = qciic_fdt_bus_scan;
12704270302Spatrick 	iba.iba_bus_scan_arg = &sc->sc_node;
12804270302Spatrick 	config_found(&sc->sc_dev, &iba, iicbus_print);
12904270302Spatrick }
13004270302Spatrick 
13104270302Spatrick int
13204270302Spatrick qciic_fdt_acquire_bus(void *cookie, int flags)
13304270302Spatrick {
13404270302Spatrick 	return 0;
13504270302Spatrick }
13604270302Spatrick 
13704270302Spatrick void
13804270302Spatrick qciic_fdt_release_bus(void *cookie, int flags)
13904270302Spatrick {
14004270302Spatrick }
14104270302Spatrick 
14204270302Spatrick int
14304270302Spatrick qciic_fdt_wait(struct qciic_fdt_softc *sc, uint32_t bits)
14404270302Spatrick {
14504270302Spatrick 	uint32_t stat;
14604270302Spatrick 	int timo;
14704270302Spatrick 
14804270302Spatrick 	for (timo = 50000; timo > 0; timo--) {
14904270302Spatrick 		stat = HREAD4(sc, GENI_M_IRQ_STATUS);
15004270302Spatrick 		if (stat & bits)
15104270302Spatrick 			break;
15204270302Spatrick 		delay(10);
15304270302Spatrick 	}
15404270302Spatrick 	if (timo == 0)
15504270302Spatrick 		return ETIMEDOUT;
15604270302Spatrick 
15704270302Spatrick 	return 0;
15804270302Spatrick }
15904270302Spatrick 
16004270302Spatrick int
16104270302Spatrick qciic_fdt_read(struct qciic_fdt_softc *sc, uint8_t *buf, size_t len)
16204270302Spatrick {
16304270302Spatrick 	uint32_t stat, word;
16404270302Spatrick 	int timo, i;
16504270302Spatrick 
16604270302Spatrick 	word = 0;
16704270302Spatrick 	for (i = 0; i < len; i++) {
16804270302Spatrick 		if ((i % 4) == 0) {
16904270302Spatrick 			for (timo = 50000; timo > 0; timo--) {
17004270302Spatrick 				stat = HREAD4(sc, GENI_RX_FIFO_STATUS);
17104270302Spatrick 				if (GENI_RX_FIFO_STATUS_WC(stat) > 0)
17204270302Spatrick 					break;
17304270302Spatrick 				delay(10);
17404270302Spatrick 			}
17504270302Spatrick 			if (timo == 0)
17604270302Spatrick 				return ETIMEDOUT;
17704270302Spatrick 			word = HREAD4(sc, GENI_RX_FIFO);
17804270302Spatrick 		}
17904270302Spatrick 		buf[i] = word >> ((i % 4) * 8);
18004270302Spatrick 	}
18104270302Spatrick 
18204270302Spatrick 	return 0;
18304270302Spatrick }
18404270302Spatrick 
18504270302Spatrick int
18604270302Spatrick qciic_fdt_write(struct qciic_fdt_softc *sc, const uint8_t *buf, size_t len)
18704270302Spatrick {
18804270302Spatrick 	uint32_t stat, word;
18904270302Spatrick 	int timo, i;
19004270302Spatrick 
19104270302Spatrick 	word = 0;
19204270302Spatrick 	for (i = 0; i < len; i++) {
19304270302Spatrick 		word |= buf[i] << ((i % 4) * 8);
19404270302Spatrick 		if ((i % 4) == 3 || i == (len - 1)) {
19504270302Spatrick 			for (timo = 50000; timo > 0; timo--) {
19604270302Spatrick 				stat = HREAD4(sc, GENI_TX_FIFO_STATUS);
19704270302Spatrick 				if (stat < 16)
19804270302Spatrick 					break;
19904270302Spatrick 				delay(10);
20004270302Spatrick 			}
20104270302Spatrick 			if (timo == 0)
20204270302Spatrick 				return ETIMEDOUT;
20304270302Spatrick 			HWRITE4(sc, GENI_TX_FIFO, word);
20404270302Spatrick 			word = 0;
20504270302Spatrick 		}
20604270302Spatrick 	}
20704270302Spatrick 
20804270302Spatrick 	return 0;
20904270302Spatrick }
21004270302Spatrick 
21104270302Spatrick int
21204270302Spatrick qciic_fdt_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, const void *cmd,
21304270302Spatrick     size_t cmdlen, void *buf, size_t buflen, int flags)
21404270302Spatrick {
21504270302Spatrick 	struct qciic_fdt_softc *sc = cookie;
21604270302Spatrick 	uint32_t m_cmd, m_param, stat;
21704270302Spatrick 	int error;
21804270302Spatrick 
21904270302Spatrick 	m_param = addr << GENI_M_CMD0_SLV_ADDR_SHIFT;
22004270302Spatrick 	m_param |= GENI_M_CMD0_STOP_STRETCH;
22104270302Spatrick 
22204270302Spatrick 	if (buflen == 0 && I2C_OP_STOP_P(op))
22304270302Spatrick 		m_param &= ~GENI_M_CMD0_STOP_STRETCH;
22404270302Spatrick 
22504270302Spatrick 	if (cmdlen > 0) {
22604270302Spatrick 		stat = HREAD4(sc, GENI_M_IRQ_STATUS);
22704270302Spatrick 		HWRITE4(sc, GENI_M_IRQ_CLEAR, stat);
22804270302Spatrick 		HWRITE4(sc, GENI_I2C_TX_TRANS_LEN, cmdlen);
22904270302Spatrick 		m_cmd = GENI_M_CMD0_OPCODE_I2C_WRITE | m_param;
23004270302Spatrick 		HWRITE4(sc, GENI_M_CMD0, m_cmd);
23104270302Spatrick 
23204270302Spatrick 		error = qciic_fdt_write(sc, cmd, cmdlen);
23304270302Spatrick 		if (error)
23404270302Spatrick 			return error;
23504270302Spatrick 
23604270302Spatrick 		error = qciic_fdt_wait(sc, GENI_M_IRQ_CMD_DONE);
23704270302Spatrick 		if (error)
23804270302Spatrick 			return error;
23904270302Spatrick 	}
24004270302Spatrick 
24104270302Spatrick 	if (buflen == 0)
24204270302Spatrick 		return 0;
24304270302Spatrick 
24404270302Spatrick 	if (I2C_OP_STOP_P(op))
24504270302Spatrick 		m_param &= ~GENI_M_CMD0_STOP_STRETCH;
24604270302Spatrick 
24704270302Spatrick 	if (I2C_OP_READ_P(op)) {
24804270302Spatrick 		stat = HREAD4(sc, GENI_M_IRQ_STATUS);
24904270302Spatrick 		HWRITE4(sc, GENI_M_IRQ_CLEAR, stat);
25004270302Spatrick 		HWRITE4(sc, GENI_I2C_RX_TRANS_LEN, buflen);
25104270302Spatrick 		m_cmd = GENI_M_CMD0_OPCODE_I2C_READ | m_param;
25204270302Spatrick 		HWRITE4(sc, GENI_M_CMD0, m_cmd);
25304270302Spatrick 
25404270302Spatrick 		error = qciic_fdt_read(sc, buf, buflen);
25504270302Spatrick 		if (error)
25604270302Spatrick 			return error;
25704270302Spatrick 
25804270302Spatrick 		error = qciic_fdt_wait(sc, GENI_M_IRQ_CMD_DONE);
25904270302Spatrick 		if (error)
26004270302Spatrick 			return error;
26104270302Spatrick 	} else {
26204270302Spatrick 		stat = HREAD4(sc, GENI_M_IRQ_STATUS);
26304270302Spatrick 		HWRITE4(sc, GENI_M_IRQ_CLEAR, stat);
26404270302Spatrick 		HWRITE4(sc, GENI_I2C_TX_TRANS_LEN, buflen);
26504270302Spatrick 		m_cmd = GENI_M_CMD0_OPCODE_I2C_WRITE | m_param;
26604270302Spatrick 		HWRITE4(sc, GENI_M_CMD0, m_cmd);
26704270302Spatrick 
26804270302Spatrick 		error = qciic_fdt_write(sc, buf, buflen);
26904270302Spatrick 		if (error)
27004270302Spatrick 			return error;
27104270302Spatrick 
27204270302Spatrick 		error = qciic_fdt_wait(sc, GENI_M_IRQ_CMD_DONE);
27304270302Spatrick 		if (error)
27404270302Spatrick 			return error;
27504270302Spatrick 	}
27604270302Spatrick 
27704270302Spatrick 	return 0;
27804270302Spatrick }
27904270302Spatrick 
28004270302Spatrick void *
28104270302Spatrick qciic_fdt_i2c_intr_establish(void *cookie, void *ih, int level,
28204270302Spatrick     int (*func)(void *), void *arg, const char *name)
28304270302Spatrick {
28404270302Spatrick 	int node = *(int *)ih;
28504270302Spatrick 
28604270302Spatrick 	return fdt_intr_establish(node, level, func, arg, (char *)name);
28704270302Spatrick }
28804270302Spatrick 
28904270302Spatrick void
29004270302Spatrick qciic_fdt_i2c_intr_disestablish(void *cookie, void *ih)
29104270302Spatrick {
29204270302Spatrick 	fdt_intr_disestablish(ih);
29304270302Spatrick }
29404270302Spatrick 
29504270302Spatrick const char *
29604270302Spatrick qciic_fdt_i2c_intr_string(void *cookie, void *ih)
29704270302Spatrick {
29804270302Spatrick 	static char irqstr[64];
29904270302Spatrick 
30004270302Spatrick 	snprintf(irqstr, sizeof(irqstr), "irq");
30104270302Spatrick 
30204270302Spatrick 	return irqstr;
30304270302Spatrick }
30404270302Spatrick 
30504270302Spatrick void
30604270302Spatrick qciic_fdt_bus_scan(struct device *self, struct i2cbus_attach_args *iba, void *aux)
30704270302Spatrick {
30804270302Spatrick 	int iba_node = *(int *)aux;
30904270302Spatrick 	extern int iic_print(void *, const char *);
31004270302Spatrick 	struct i2c_attach_args ia;
31104270302Spatrick 	char name[32], status[32];
31204270302Spatrick 	uint32_t reg[1];
31304270302Spatrick 	int node;
31404270302Spatrick 
31504270302Spatrick 	for (node = OF_child(iba_node); node; node = OF_peer(node)) {
31604270302Spatrick 		memset(name, 0, sizeof(name));
31704270302Spatrick 		memset(status, 0, sizeof(status));
31804270302Spatrick 		memset(reg, 0, sizeof(reg));
31904270302Spatrick 
32004270302Spatrick 		if (OF_getprop(node, "compatible", name, sizeof(name)) == -1)
32104270302Spatrick 			continue;
32204270302Spatrick 		if (name[0] == '\0')
32304270302Spatrick 			continue;
32404270302Spatrick 
32504270302Spatrick 		if (OF_getprop(node, "status", status, sizeof(status)) > 0 &&
32604270302Spatrick 		    strcmp(status, "disabled") == 0)
32704270302Spatrick 			continue;
32804270302Spatrick 
32904270302Spatrick 		if (OF_getprop(node, "reg", &reg, sizeof(reg)) != sizeof(reg))
33004270302Spatrick 			continue;
33104270302Spatrick 
33204270302Spatrick 		memset(&ia, 0, sizeof(ia));
33304270302Spatrick 		ia.ia_tag = iba->iba_tag;
33404270302Spatrick 		ia.ia_addr = bemtoh32(&reg[0]);
33504270302Spatrick 		ia.ia_name = name;
33604270302Spatrick 		ia.ia_cookie = &node;
33704270302Spatrick 		ia.ia_intr = &node;
33804270302Spatrick 
33904270302Spatrick 		/* Quirk for ihidev(4) */
34004270302Spatrick 		if (strcmp(name, "hid-over-i2c") == 0) {
34104270302Spatrick 			ia.ia_name = "ihidev";
34204270302Spatrick 			ia.ia_size = OF_getpropint(node, "hid-descr-addr", 0);
34304270302Spatrick 			ia.ia_cookie = name;
34404270302Spatrick 		}
34504270302Spatrick 
34604270302Spatrick 		config_found(self, &ia, iic_print);
34704270302Spatrick 	}
34804270302Spatrick }
349