sunxi_twi.c revision 1.9
1/* $NetBSD: sunxi_twi.c,v 1.9 2018/05/09 02:53:00 thorpej Exp $ */
2
3/*-
4 * Copyright (c) 2017 Jared McNeill <jmcneill@invisible.ca>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include "opt_gttwsi.h"
30#ifdef GTTWSI_ALLWINNER
31# error Do not define GTTWSI_ALLWINNER when using this driver
32#endif
33
34#include <sys/cdefs.h>
35
36__KERNEL_RCSID(0, "$NetBSD: sunxi_twi.c,v 1.9 2018/05/09 02:53:00 thorpej Exp $");
37
38#include <sys/param.h>
39#include <sys/bus.h>
40#include <sys/device.h>
41#include <sys/intr.h>
42#include <sys/systm.h>
43#include <sys/time.h>
44
45#include <dev/i2c/i2cvar.h>
46#include <dev/i2c/gttwsivar.h>
47#include <dev/i2c/gttwsireg.h>
48
49#include <dev/fdt/fdtvar.h>
50
51#define	TWI_CCR_REG	0x14
52#define	 TWI_CCR_CLK_M	__BITS(6,3)
53#define	 TWI_CCR_CLK_N	__BITS(2,0)
54
55static uint8_t sunxi_twi_regmap_rd[] = {
56	[TWSI_SLAVEADDR/4]		= 0x00,
57	[TWSI_EXTEND_SLAVEADDR/4]	= 0x04,
58	[TWSI_DATA/4]			= 0x08,
59	[TWSI_CONTROL/4]		= 0x0c,
60	[TWSI_STATUS/4]			= 0x10,
61	[TWSI_SOFTRESET/4]		= 0x18,
62};
63
64static uint8_t sunxi_twi_regmap_wr[] = {
65	[TWSI_SLAVEADDR/4]		= 0x00,
66	[TWSI_EXTEND_SLAVEADDR/4]	= 0x04,
67	[TWSI_DATA/4]			= 0x08,
68	[TWSI_CONTROL/4]		= 0x0c,
69	[TWSI_BAUDRATE/4]		= 0x14,
70	[TWSI_SOFTRESET/4]		= 0x18,
71};
72
73static int sunxi_twi_match(device_t, cfdata_t, void *);
74static void sunxi_twi_attach(device_t, device_t, void *);
75
76struct sunxi_twi_config {
77	bool		iflg_rwc;
78};
79
80static const struct sunxi_twi_config sun4i_a10_i2c_config = {
81	.iflg_rwc = false,
82};
83
84static const struct sunxi_twi_config sun6i_a31_i2c_config = {
85	.iflg_rwc = true,
86};
87
88static const struct of_compat_data compat_data[] = {
89	{ "allwinner,sun4i-a10-i2c",	(uintptr_t)&sun4i_a10_i2c_config },
90	{ "allwinner,sun6i-a31-i2c",	(uintptr_t)&sun6i_a31_i2c_config },
91	{ NULL }
92};
93
94CFATTACH_DECL_NEW(sunxi_twi, sizeof(struct gttwsi_softc),
95	sunxi_twi_match, sunxi_twi_attach, NULL, NULL);
96
97static i2c_tag_t
98sunxi_twi_get_tag(device_t dev)
99{
100	struct gttwsi_softc * const sc = device_private(dev);
101
102	return &sc->sc_i2c;
103}
104
105const struct fdtbus_i2c_controller_func sunxi_twi_funcs = {
106	.get_tag = sunxi_twi_get_tag,
107};
108
109static uint32_t
110sunxi_twi_reg_read(struct gttwsi_softc *sc, uint32_t reg)
111{
112	return bus_space_read_4(sc->sc_bust, sc->sc_bush, sunxi_twi_regmap_rd[reg/4]);
113}
114
115static void
116sunxi_twi_reg_write(struct gttwsi_softc *sc, uint32_t reg, uint32_t val)
117{
118	bus_space_write_4(sc->sc_bust, sc->sc_bush, sunxi_twi_regmap_wr[reg/4], val);
119}
120
121static u_int
122sunxi_twi_calc_rate(u_int parent_rate, u_int n, u_int m)
123{
124	return parent_rate / (10 * (m + 1) * (1 << n));
125}
126
127static void
128sunxi_twi_set_clock(struct gttwsi_softc *sc, u_int parent_rate, u_int rate)
129{
130	uint32_t baud;
131	u_int n, m, best_rate;
132
133	baud = sunxi_twi_reg_read(sc, TWSI_BAUDRATE);
134
135	for (best_rate = 0, n = 0; n < 8; n++) {
136		for (m = 0; m < 16; m++) {
137			const u_int tmp_rate = sunxi_twi_calc_rate(parent_rate, n, m);
138			if (tmp_rate <= rate && tmp_rate > best_rate) {
139				best_rate = tmp_rate;
140				baud = __SHIFTIN(n, TWI_CCR_CLK_N) |
141				       __SHIFTIN(m, TWI_CCR_CLK_M);
142			}
143		}
144	}
145
146	sunxi_twi_reg_write(sc, TWSI_BAUDRATE, baud);
147	delay(10000);
148}
149
150static int
151sunxi_twi_match(device_t parent, cfdata_t cf, void *aux)
152{
153	struct fdt_attach_args * const faa = aux;
154
155	return of_match_compat_data(faa->faa_phandle, compat_data);
156}
157
158static void
159sunxi_twi_attach(device_t parent, device_t self, void *aux)
160{
161	struct gttwsi_softc * const sc = device_private(self);
162	struct fdt_attach_args * const faa = aux;
163	const struct sunxi_twi_config *conf;
164	struct i2cbus_attach_args iba;
165	const int phandle = faa->faa_phandle;
166	bus_space_tag_t bst = faa->faa_bst;
167	bus_space_handle_t bsh;
168	prop_dictionary_t devs;
169	uint32_t address_cells;
170	struct fdtbus_reset *rst;
171	struct clk *clk;
172	char intrstr[128];
173	bus_addr_t addr;
174	bus_size_t size;
175	void *ih;
176
177	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
178		aprint_error(": couldn't get registers\n");
179		return;
180	}
181
182	if (bus_space_map(bst, addr, size, 0, &bsh) != 0) {
183		aprint_error(": couldn't map registers\n");
184		return;
185	}
186
187	if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
188		aprint_error(": failed to decode interrupt\n");
189		return;
190	}
191
192	if ((clk = fdtbus_clock_get_index(phandle, 0)) != NULL)
193		if (clk_enable(clk) != 0) {
194			aprint_error(": couldn't enable clock\n");
195			return;
196		}
197	if ((rst = fdtbus_reset_get_index(phandle, 0)) != NULL)
198		if (fdtbus_reset_deassert(rst) != 0) {
199			aprint_error(": couldn't de-assert reset\n");
200			return;
201		}
202
203	conf = (void *)of_search_compatible(phandle, compat_data)->data;
204	prop_dictionary_set_bool(device_properties(self), "iflg-rwc",
205	    conf->iflg_rwc);
206
207	/* Attach gttwsi core */
208	sc->sc_reg_read = sunxi_twi_reg_read;
209	sc->sc_reg_write = sunxi_twi_reg_write;
210	gttwsi_attach_subr(self, bst, bsh);
211
212	/*
213	 * Set clock rate to 100kHz.
214	 */
215	if (clk != NULL)
216		sunxi_twi_set_clock(sc, clk_get_rate(clk), 100000);
217
218	ih = fdtbus_intr_establish(phandle, 0, IPL_VM, 0, gttwsi_intr, sc);
219	if (ih == NULL) {
220		aprint_error_dev(self, "couldn't establish interrupt on %s\n",
221		    intrstr);
222		return;
223	}
224	aprint_normal_dev(self, "interrupting on %s\n", intrstr);
225
226	fdtbus_register_i2c_controller(self, phandle, &sunxi_twi_funcs);
227
228	devs = prop_dictionary_create();
229	if (of_getprop_uint32(phandle, "#address-cells", &address_cells))
230		address_cells = 1;
231
232	of_enter_i2c_devs(devs, phandle, address_cells * 4, 0);
233
234	memset(&iba, 0, sizeof(iba));
235	iba.iba_tag = &sc->sc_i2c;
236	iba.iba_child_devices = prop_dictionary_get(devs, "i2c-child-devices");
237	if (iba.iba_child_devices)
238		prop_object_retain(iba.iba_child_devices);
239	prop_object_release(devs);
240
241	config_found_ia(self, "i2cbus", &iba, iicbus_print);
242}
243