xref: /netbsd-src/sys/arch/arm/sunxi/sunxi_twi.c (revision 6e54367a22fbc89a1139d033e95bec0c0cf0975b)
1*6e54367aSthorpej /* $NetBSD: sunxi_twi.c,v 1.17 2021/01/27 03:10:20 thorpej Exp $ */
2f9f219ddSjmcneill 
3f9f219ddSjmcneill /*-
4f9f219ddSjmcneill  * Copyright (c) 2017 Jared McNeill <jmcneill@invisible.ca>
5f9f219ddSjmcneill  * All rights reserved.
6f9f219ddSjmcneill  *
7f9f219ddSjmcneill  * Redistribution and use in source and binary forms, with or without
8f9f219ddSjmcneill  * modification, are permitted provided that the following conditions
9f9f219ddSjmcneill  * are met:
10f9f219ddSjmcneill  * 1. Redistributions of source code must retain the above copyright
11f9f219ddSjmcneill  *    notice, this list of conditions and the following disclaimer.
12f9f219ddSjmcneill  * 2. Redistributions in binary form must reproduce the above copyright
13f9f219ddSjmcneill  *    notice, this list of conditions and the following disclaimer in the
14f9f219ddSjmcneill  *    documentation and/or other materials provided with the distribution.
15f9f219ddSjmcneill  *
16f9f219ddSjmcneill  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17f9f219ddSjmcneill  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18f9f219ddSjmcneill  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19f9f219ddSjmcneill  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20f9f219ddSjmcneill  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21f9f219ddSjmcneill  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22f9f219ddSjmcneill  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23f9f219ddSjmcneill  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24f9f219ddSjmcneill  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25f9f219ddSjmcneill  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26f9f219ddSjmcneill  * POSSIBILITY OF SUCH DAMAGE.
27f9f219ddSjmcneill  */
28f9f219ddSjmcneill 
29f9f219ddSjmcneill #include <sys/cdefs.h>
30f9f219ddSjmcneill 
31*6e54367aSthorpej __KERNEL_RCSID(0, "$NetBSD: sunxi_twi.c,v 1.17 2021/01/27 03:10:20 thorpej Exp $");
32f9f219ddSjmcneill 
33f9f219ddSjmcneill #include <sys/param.h>
34f9f219ddSjmcneill #include <sys/bus.h>
35f9f219ddSjmcneill #include <sys/device.h>
36f9f219ddSjmcneill #include <sys/intr.h>
37f9f219ddSjmcneill #include <sys/systm.h>
38f9f219ddSjmcneill #include <sys/time.h>
39f9f219ddSjmcneill 
40f9f219ddSjmcneill #include <dev/i2c/i2cvar.h>
41f9f219ddSjmcneill #include <dev/i2c/gttwsivar.h>
42f7148f3cSjmcneill #include <dev/i2c/gttwsireg.h>
43f9f219ddSjmcneill 
44f9f219ddSjmcneill #include <dev/fdt/fdtvar.h>
45f9f219ddSjmcneill 
46e12a7b5cSjmcneill #define	TWI_CCR_REG	0x14
47e12a7b5cSjmcneill #define	 TWI_CCR_CLK_M	__BITS(6,3)
48e12a7b5cSjmcneill #define	 TWI_CCR_CLK_N	__BITS(2,0)
49e12a7b5cSjmcneill 
5092b1a40bSthorpej static const bus_size_t sunxi_twi_regmap[] = {
5192b1a40bSthorpej 	[TWSI_SLAVEADDR]	= TWSI_ALLWINNER_SLAVEADDR,
5292b1a40bSthorpej 	[TWSI_EXTEND_SLAVEADDR]	= TWSI_ALLWINNER_EXTEND_SLAVEADDR,
5392b1a40bSthorpej 	[TWSI_DATA]		= TWSI_ALLWINNER_DATA,
5492b1a40bSthorpej 	[TWSI_CONTROL]		= TWSI_ALLWINNER_CONTROL,
5592b1a40bSthorpej 	[TWSI_STATUS]		= TWSI_ALLWINNER_STATUS,
5692b1a40bSthorpej 	[TWSI_BAUDRATE]		= TWSI_ALLWINNER_BAUDRATE,
5792b1a40bSthorpej 	[TWSI_SOFTRESET]	= TWSI_ALLWINNER_SOFTRESET,
58f7148f3cSjmcneill };
59f7148f3cSjmcneill 
60f9f219ddSjmcneill static int sunxi_twi_match(device_t, cfdata_t, void *);
61f9f219ddSjmcneill static void sunxi_twi_attach(device_t, device_t, void *);
62f9f219ddSjmcneill 
6355c5ba04Sjmcneill struct sunxi_twi_config {
6455c5ba04Sjmcneill 	bool		iflg_rwc;
6555c5ba04Sjmcneill };
6655c5ba04Sjmcneill 
6755c5ba04Sjmcneill static const struct sunxi_twi_config sun4i_a10_i2c_config = {
6855c5ba04Sjmcneill 	.iflg_rwc = false,
6955c5ba04Sjmcneill };
7055c5ba04Sjmcneill 
7155c5ba04Sjmcneill static const struct sunxi_twi_config sun6i_a31_i2c_config = {
7255c5ba04Sjmcneill 	.iflg_rwc = true,
7355c5ba04Sjmcneill };
7455c5ba04Sjmcneill 
75646c0f59Sthorpej static const struct device_compatible_entry compat_data[] = {
76646c0f59Sthorpej 	{ .compat = "allwinner,sun4i-a10-i2c",	.data = &sun4i_a10_i2c_config },
77646c0f59Sthorpej 	{ .compat = "allwinner,sun6i-a31-i2c",	.data = &sun6i_a31_i2c_config },
78ec189949Sthorpej 	DEVICE_COMPAT_EOL
79f9f219ddSjmcneill };
80f9f219ddSjmcneill 
81f9f219ddSjmcneill CFATTACH_DECL_NEW(sunxi_twi, sizeof(struct gttwsi_softc),
82f9f219ddSjmcneill 	sunxi_twi_match, sunxi_twi_attach, NULL, NULL);
83f9f219ddSjmcneill 
845a3c5ae4Sjmcneill static u_int
sunxi_twi_calc_rate(u_int parent_rate,u_int n,u_int m)855a3c5ae4Sjmcneill sunxi_twi_calc_rate(u_int parent_rate, u_int n, u_int m)
865a3c5ae4Sjmcneill {
875a3c5ae4Sjmcneill 	return parent_rate / (10 * (m + 1) * (1 << n));
885a3c5ae4Sjmcneill }
895a3c5ae4Sjmcneill 
905a3c5ae4Sjmcneill static void
sunxi_twi_set_clock(struct gttwsi_softc * sc,u_int parent_rate,u_int rate)915a3c5ae4Sjmcneill sunxi_twi_set_clock(struct gttwsi_softc *sc, u_int parent_rate, u_int rate)
925a3c5ae4Sjmcneill {
935a3c5ae4Sjmcneill 	uint32_t baud;
945a3c5ae4Sjmcneill 	u_int n, m, best_rate;
955a3c5ae4Sjmcneill 
9692b1a40bSthorpej 	baud = gttwsi_read_4(sc, TWSI_BAUDRATE);
975a3c5ae4Sjmcneill 
985a3c5ae4Sjmcneill 	for (best_rate = 0, n = 0; n < 8; n++) {
995a3c5ae4Sjmcneill 		for (m = 0; m < 16; m++) {
10092b1a40bSthorpej 			const u_int tmp_rate =
10192b1a40bSthorpej 			    sunxi_twi_calc_rate(parent_rate, n, m);
1025a3c5ae4Sjmcneill 			if (tmp_rate <= rate && tmp_rate > best_rate) {
1035a3c5ae4Sjmcneill 				best_rate = tmp_rate;
1045a3c5ae4Sjmcneill 				baud = __SHIFTIN(n, TWI_CCR_CLK_N) |
1055a3c5ae4Sjmcneill 				       __SHIFTIN(m, TWI_CCR_CLK_M);
1065a3c5ae4Sjmcneill 			}
1075a3c5ae4Sjmcneill 		}
1085a3c5ae4Sjmcneill 	}
1095a3c5ae4Sjmcneill 
11092b1a40bSthorpej 	gttwsi_write_4(sc, TWSI_BAUDRATE, baud);
1115a3c5ae4Sjmcneill 	delay(10000);
112f7148f3cSjmcneill }
113f7148f3cSjmcneill 
114f9f219ddSjmcneill static int
sunxi_twi_match(device_t parent,cfdata_t cf,void * aux)115f9f219ddSjmcneill sunxi_twi_match(device_t parent, cfdata_t cf, void *aux)
116f9f219ddSjmcneill {
117f9f219ddSjmcneill 	struct fdt_attach_args * const faa = aux;
118f9f219ddSjmcneill 
119*6e54367aSthorpej 	return of_compatible_match(faa->faa_phandle, compat_data);
120f9f219ddSjmcneill }
121f9f219ddSjmcneill 
122f9f219ddSjmcneill static void
sunxi_twi_attach(device_t parent,device_t self,void * aux)123f9f219ddSjmcneill sunxi_twi_attach(device_t parent, device_t self, void *aux)
124f9f219ddSjmcneill {
125f9f219ddSjmcneill 	struct gttwsi_softc * const sc = device_private(self);
126f9f219ddSjmcneill 	struct fdt_attach_args * const faa = aux;
12755c5ba04Sjmcneill 	const struct sunxi_twi_config *conf;
128f9f219ddSjmcneill 	const int phandle = faa->faa_phandle;
129f9f219ddSjmcneill 	bus_space_tag_t bst = faa->faa_bst;
130f9f219ddSjmcneill 	bus_space_handle_t bsh;
131f9f219ddSjmcneill 	struct fdtbus_reset *rst;
132f9f219ddSjmcneill 	struct clk *clk;
133f9f219ddSjmcneill 	char intrstr[128];
134f9f219ddSjmcneill 	bus_addr_t addr;
135f9f219ddSjmcneill 	bus_size_t size;
136f9f219ddSjmcneill 	void *ih;
137f9f219ddSjmcneill 
138f9f219ddSjmcneill 	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
139f9f219ddSjmcneill 		aprint_error(": couldn't get registers\n");
140f9f219ddSjmcneill 		return;
141f9f219ddSjmcneill 	}
142f9f219ddSjmcneill 
143f9f219ddSjmcneill 	if (bus_space_map(bst, addr, size, 0, &bsh) != 0) {
144f9f219ddSjmcneill 		aprint_error(": couldn't map registers\n");
145f9f219ddSjmcneill 		return;
146f9f219ddSjmcneill 	}
147f9f219ddSjmcneill 
148f9f219ddSjmcneill 	if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
149f9f219ddSjmcneill 		aprint_error(": failed to decode interrupt\n");
150f9f219ddSjmcneill 		return;
151f9f219ddSjmcneill 	}
152f9f219ddSjmcneill 
1539e63c0a9Sjmcneill 	if ((clk = fdtbus_clock_get_index(phandle, 0)) != NULL)
1549e63c0a9Sjmcneill 		if (clk_enable(clk) != 0) {
155f9f219ddSjmcneill 			aprint_error(": couldn't enable clock\n");
156f9f219ddSjmcneill 			return;
157f9f219ddSjmcneill 		}
158f9f219ddSjmcneill 	if ((rst = fdtbus_reset_get_index(phandle, 0)) != NULL)
159f9f219ddSjmcneill 		if (fdtbus_reset_deassert(rst) != 0) {
160f9f219ddSjmcneill 			aprint_error(": couldn't de-assert reset\n");
161f9f219ddSjmcneill 			return;
162f9f219ddSjmcneill 		}
163f9f219ddSjmcneill 
164*6e54367aSthorpej 	conf = of_compatible_lookup(phandle, compat_data)->data;
16555c5ba04Sjmcneill 	prop_dictionary_set_bool(device_properties(self), "iflg-rwc",
16655c5ba04Sjmcneill 	    conf->iflg_rwc);
16755c5ba04Sjmcneill 
1685a3c5ae4Sjmcneill 	/* Attach gttwsi core */
16992b1a40bSthorpej 	gttwsi_attach_subr(self, bst, bsh, sunxi_twi_regmap);
170f9f219ddSjmcneill 
1715a3c5ae4Sjmcneill 	/*
1725a3c5ae4Sjmcneill 	 * Set clock rate to 100kHz.
1735a3c5ae4Sjmcneill 	 */
1749e63c0a9Sjmcneill 	if (clk != NULL)
1755a3c5ae4Sjmcneill 		sunxi_twi_set_clock(sc, clk_get_rate(clk), 100000);
1765a3c5ae4Sjmcneill 
177076a1169Sjmcneill 	ih = fdtbus_intr_establish_xname(phandle, 0, IPL_VM, 0, gttwsi_intr,
178076a1169Sjmcneill 	    sc, device_xname(self));
179f9f219ddSjmcneill 	if (ih == NULL) {
180f9f219ddSjmcneill 		aprint_error_dev(self, "couldn't establish interrupt on %s\n",
181f9f219ddSjmcneill 		    intrstr);
182f9f219ddSjmcneill 		return;
183f9f219ddSjmcneill 	}
184f9f219ddSjmcneill 	aprint_normal_dev(self, "interrupting on %s\n", intrstr);
185f9f219ddSjmcneill 
18621b71bc0Sthorpej 	fdtbus_register_i2c_controller(&sc->sc_i2c, phandle);
187f9f219ddSjmcneill 
1887d854132Sjmcneill 	fdtbus_attach_i2cbus(self, phandle, &sc->sc_i2c, iicbus_print);
189f9f219ddSjmcneill }
190