sunxi_twi.c revision 1.6
11.6Sjmcneill/* $NetBSD: sunxi_twi.c,v 1.6 2017/10/29 15:00:00 jmcneill Exp $ */
21.1Sjmcneill
31.1Sjmcneill/*-
41.1Sjmcneill * Copyright (c) 2017 Jared McNeill <jmcneill@invisible.ca>
51.1Sjmcneill * All rights reserved.
61.1Sjmcneill *
71.1Sjmcneill * Redistribution and use in source and binary forms, with or without
81.1Sjmcneill * modification, are permitted provided that the following conditions
91.1Sjmcneill * are met:
101.1Sjmcneill * 1. Redistributions of source code must retain the above copyright
111.1Sjmcneill *    notice, this list of conditions and the following disclaimer.
121.1Sjmcneill * 2. Redistributions in binary form must reproduce the above copyright
131.1Sjmcneill *    notice, this list of conditions and the following disclaimer in the
141.1Sjmcneill *    documentation and/or other materials provided with the distribution.
151.1Sjmcneill *
161.1Sjmcneill * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
171.1Sjmcneill * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
181.1Sjmcneill * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
191.1Sjmcneill * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
201.1Sjmcneill * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
211.1Sjmcneill * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
221.1Sjmcneill * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
231.1Sjmcneill * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
241.1Sjmcneill * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
251.1Sjmcneill * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
261.1Sjmcneill * POSSIBILITY OF SUCH DAMAGE.
271.1Sjmcneill */
281.1Sjmcneill
291.6Sjmcneill#include "opt_gttwsi.h"
301.6Sjmcneill#ifdef GTTWSI_ALLWINNER
311.6Sjmcneill# error Do not define GTTWSI_ALLWINNER when using this driver
321.6Sjmcneill#endif
331.6Sjmcneill
341.1Sjmcneill#include <sys/cdefs.h>
351.1Sjmcneill
361.6Sjmcneill__KERNEL_RCSID(0, "$NetBSD: sunxi_twi.c,v 1.6 2017/10/29 15:00:00 jmcneill Exp $");
371.1Sjmcneill
381.1Sjmcneill#include <sys/param.h>
391.1Sjmcneill#include <sys/bus.h>
401.1Sjmcneill#include <sys/device.h>
411.1Sjmcneill#include <sys/intr.h>
421.1Sjmcneill#include <sys/systm.h>
431.1Sjmcneill#include <sys/time.h>
441.1Sjmcneill
451.1Sjmcneill#include <dev/i2c/i2cvar.h>
461.1Sjmcneill#include <dev/i2c/gttwsivar.h>
471.6Sjmcneill#include <dev/i2c/gttwsireg.h>
481.1Sjmcneill
491.1Sjmcneill#include <dev/fdt/fdtvar.h>
501.1Sjmcneill
511.5Sjmcneill#define	TWI_CCR_REG	0x14
521.5Sjmcneill#define	 TWI_CCR_CLK_M	__BITS(6,3)
531.5Sjmcneill#define	 TWI_CCR_CLK_N	__BITS(2,0)
541.5Sjmcneill
551.6Sjmcneillstatic uint8_t sunxi_twi_regmap[] = {
561.6Sjmcneill	[TWSI_SLAVEADDR]	= 0x00,
571.6Sjmcneill	[TWSI_EXTEND_SLAVEADDR]	= 0x04,
581.6Sjmcneill	[TWSI_DATA]		= 0x08,
591.6Sjmcneill	[TWSI_CONTROL]		= 0x0c,
601.6Sjmcneill	[TWSI_STATUS]		= 0x10,
611.6Sjmcneill	[TWSI_BAUDRATE]		= 0x14,
621.6Sjmcneill	[TWSI_SOFTRESET]	= 0x18,
631.6Sjmcneill};
641.6Sjmcneill
651.1Sjmcneillstatic int sunxi_twi_match(device_t, cfdata_t, void *);
661.1Sjmcneillstatic void sunxi_twi_attach(device_t, device_t, void *);
671.1Sjmcneill
681.4Sjmcneillstruct sunxi_twi_config {
691.4Sjmcneill	bool		iflg_rwc;
701.4Sjmcneill};
711.4Sjmcneill
721.4Sjmcneillstatic const struct sunxi_twi_config sun4i_a10_i2c_config = {
731.4Sjmcneill	.iflg_rwc = false,
741.4Sjmcneill};
751.4Sjmcneill
761.4Sjmcneillstatic const struct sunxi_twi_config sun6i_a31_i2c_config = {
771.4Sjmcneill	.iflg_rwc = true,
781.4Sjmcneill};
791.4Sjmcneill
801.4Sjmcneillstatic const struct of_compat_data compat_data[] = {
811.4Sjmcneill	{ "allwinner,sun4i-a10-i2c",	(uintptr_t)&sun4i_a10_i2c_config },
821.4Sjmcneill	{ "allwinner,sun6i-a31-i2c",	(uintptr_t)&sun6i_a31_i2c_config },
831.4Sjmcneill	{ NULL }
841.1Sjmcneill};
851.1Sjmcneill
861.1SjmcneillCFATTACH_DECL_NEW(sunxi_twi, sizeof(struct gttwsi_softc),
871.1Sjmcneill	sunxi_twi_match, sunxi_twi_attach, NULL, NULL);
881.1Sjmcneill
891.1Sjmcneillstatic i2c_tag_t
901.1Sjmcneillsunxi_twi_get_tag(device_t dev)
911.1Sjmcneill{
921.1Sjmcneill	struct gttwsi_softc * const sc = device_private(dev);
931.1Sjmcneill
941.1Sjmcneill	return &sc->sc_i2c;
951.1Sjmcneill}
961.1Sjmcneill
971.1Sjmcneillconst struct fdtbus_i2c_controller_func sunxi_twi_funcs = {
981.1Sjmcneill	.get_tag = sunxi_twi_get_tag,
991.1Sjmcneill};
1001.1Sjmcneill
1011.6Sjmcneillstatic uint32_t
1021.6Sjmcneillsunxi_twi_reg_read(struct gttwsi_softc *sc, uint32_t reg)
1031.6Sjmcneill{
1041.6Sjmcneill	return bus_space_read_4(sc->sc_bust, sc->sc_bush, sunxi_twi_regmap[reg]);
1051.6Sjmcneill}
1061.6Sjmcneill
1071.6Sjmcneillstatic void
1081.6Sjmcneillsunxi_twi_reg_write(struct gttwsi_softc *sc, uint32_t reg, uint32_t val)
1091.6Sjmcneill{
1101.6Sjmcneill	bus_space_write_4(sc->sc_bust, sc->sc_bush, sunxi_twi_regmap[reg], val);
1111.6Sjmcneill}
1121.6Sjmcneill
1131.1Sjmcneillstatic int
1141.1Sjmcneillsunxi_twi_match(device_t parent, cfdata_t cf, void *aux)
1151.1Sjmcneill{
1161.1Sjmcneill	struct fdt_attach_args * const faa = aux;
1171.1Sjmcneill
1181.4Sjmcneill	return of_match_compat_data(faa->faa_phandle, compat_data);
1191.1Sjmcneill}
1201.1Sjmcneill
1211.1Sjmcneillstatic void
1221.1Sjmcneillsunxi_twi_attach(device_t parent, device_t self, void *aux)
1231.1Sjmcneill{
1241.1Sjmcneill	struct gttwsi_softc * const sc = device_private(self);
1251.1Sjmcneill	struct fdt_attach_args * const faa = aux;
1261.4Sjmcneill	const struct sunxi_twi_config *conf;
1271.1Sjmcneill	struct i2cbus_attach_args iba;
1281.1Sjmcneill	const int phandle = faa->faa_phandle;
1291.1Sjmcneill	bus_space_tag_t bst = faa->faa_bst;
1301.1Sjmcneill	bus_space_handle_t bsh;
1311.1Sjmcneill	prop_dictionary_t devs;
1321.1Sjmcneill	uint32_t address_cells;
1331.1Sjmcneill	struct fdtbus_reset *rst;
1341.1Sjmcneill	struct clk *clk;
1351.1Sjmcneill	char intrstr[128];
1361.1Sjmcneill	bus_addr_t addr;
1371.1Sjmcneill	bus_size_t size;
1381.1Sjmcneill	void *ih;
1391.1Sjmcneill
1401.1Sjmcneill	if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) {
1411.1Sjmcneill		aprint_error(": couldn't get registers\n");
1421.1Sjmcneill		return;
1431.1Sjmcneill	}
1441.1Sjmcneill
1451.1Sjmcneill	if (bus_space_map(bst, addr, size, 0, &bsh) != 0) {
1461.1Sjmcneill		aprint_error(": couldn't map registers\n");
1471.1Sjmcneill		return;
1481.1Sjmcneill	}
1491.1Sjmcneill
1501.1Sjmcneill	if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
1511.1Sjmcneill		aprint_error(": failed to decode interrupt\n");
1521.1Sjmcneill		return;
1531.1Sjmcneill	}
1541.1Sjmcneill
1551.1Sjmcneill	if ((clk = fdtbus_clock_get_index(phandle, 0)) != NULL)
1561.1Sjmcneill		if (clk_enable(clk) != 0) {
1571.1Sjmcneill			aprint_error(": couldn't enable clock\n");
1581.1Sjmcneill			return;
1591.1Sjmcneill		}
1601.1Sjmcneill	if ((rst = fdtbus_reset_get_index(phandle, 0)) != NULL)
1611.1Sjmcneill		if (fdtbus_reset_deassert(rst) != 0) {
1621.1Sjmcneill			aprint_error(": couldn't de-assert reset\n");
1631.1Sjmcneill			return;
1641.1Sjmcneill		}
1651.1Sjmcneill
1661.4Sjmcneill	conf = (void *)of_search_compatible(phandle, compat_data)->data;
1671.4Sjmcneill	prop_dictionary_set_bool(device_properties(self), "iflg-rwc",
1681.4Sjmcneill	    conf->iflg_rwc);
1691.4Sjmcneill
1701.5Sjmcneill	/*
1711.5Sjmcneill	 * Set clock rate to 100kHz. From the datasheet:
1721.5Sjmcneill	 *   For 100Khz standard speed 2Wire, CLK_N=2, CLK_M=11
1731.5Sjmcneill	 *   F0=48M/2^2=12Mhz, F1=F0/(10*(11+1)) = 0.1Mhz
1741.5Sjmcneill	 */
1751.5Sjmcneill	const u_int m = 11, n = 2;
1761.5Sjmcneill	const uint32_t ccr = __SHIFTIN(n, TWI_CCR_CLK_N) |
1771.5Sjmcneill			     __SHIFTIN(m, TWI_CCR_CLK_M);
1781.5Sjmcneill	bus_space_write_4(bst, bsh, TWI_CCR_REG, ccr);
1791.5Sjmcneill
1801.6Sjmcneill	sc->sc_reg_read = sunxi_twi_reg_read;
1811.6Sjmcneill	sc->sc_reg_write = sunxi_twi_reg_write;
1821.6Sjmcneill
1831.1Sjmcneill	gttwsi_attach_subr(self, bst, bsh);
1841.1Sjmcneill
1851.1Sjmcneill	ih = fdtbus_intr_establish(phandle, 0, IPL_VM, 0, gttwsi_intr, sc);
1861.1Sjmcneill	if (ih == NULL) {
1871.1Sjmcneill		aprint_error_dev(self, "couldn't establish interrupt on %s\n",
1881.1Sjmcneill		    intrstr);
1891.1Sjmcneill		return;
1901.1Sjmcneill	}
1911.1Sjmcneill	aprint_normal_dev(self, "interrupting on %s\n", intrstr);
1921.1Sjmcneill
1931.1Sjmcneill	fdtbus_register_i2c_controller(self, phandle, &sunxi_twi_funcs);
1941.1Sjmcneill
1951.1Sjmcneill	devs = prop_dictionary_create();
1961.1Sjmcneill	if (of_getprop_uint32(phandle, "#address-cells", &address_cells))
1971.1Sjmcneill		address_cells = 1;
1981.1Sjmcneill
1991.1Sjmcneill	of_enter_i2c_devs(devs, phandle, address_cells * 4, 0);
2001.1Sjmcneill
2011.1Sjmcneill	memset(&iba, 0, sizeof(iba));
2021.1Sjmcneill	iba.iba_tag = &sc->sc_i2c;
2031.1Sjmcneill	iba.iba_child_devices = prop_dictionary_get(devs, "i2c-child-devices");
2041.1Sjmcneill	if (iba.iba_child_devices)
2051.1Sjmcneill		prop_object_retain(iba.iba_child_devices);
2061.1Sjmcneill	else
2071.1Sjmcneill		iba.iba_child_devices = prop_array_create();
2081.1Sjmcneill	prop_object_release(devs);
2091.1Sjmcneill
2101.1Sjmcneill	config_found_ia(self, "i2cbus", &iba, iicbus_print);
2111.1Sjmcneill}
212