1 /* $NetBSD: sunxi_twi.c,v 1.10 2018/07/01 21:16:19 jmcneill 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.10 2018/07/01 21:16:19 jmcneill 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 55 static 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 64 static 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 73 static int sunxi_twi_match(device_t, cfdata_t, void *); 74 static void sunxi_twi_attach(device_t, device_t, void *); 75 76 struct sunxi_twi_config { 77 bool iflg_rwc; 78 }; 79 80 static const struct sunxi_twi_config sun4i_a10_i2c_config = { 81 .iflg_rwc = false, 82 }; 83 84 static const struct sunxi_twi_config sun6i_a31_i2c_config = { 85 .iflg_rwc = true, 86 }; 87 88 static 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 94 CFATTACH_DECL_NEW(sunxi_twi, sizeof(struct gttwsi_softc), 95 sunxi_twi_match, sunxi_twi_attach, NULL, NULL); 96 97 static i2c_tag_t 98 sunxi_twi_get_tag(device_t dev) 99 { 100 struct gttwsi_softc * const sc = device_private(dev); 101 102 return &sc->sc_i2c; 103 } 104 105 const struct fdtbus_i2c_controller_func sunxi_twi_funcs = { 106 .get_tag = sunxi_twi_get_tag, 107 }; 108 109 static uint32_t 110 sunxi_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 115 static void 116 sunxi_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 121 static u_int 122 sunxi_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 127 static void 128 sunxi_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 150 static int 151 sunxi_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 158 static void 159 sunxi_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 const int phandle = faa->faa_phandle; 165 bus_space_tag_t bst = faa->faa_bst; 166 bus_space_handle_t bsh; 167 struct fdtbus_reset *rst; 168 struct clk *clk; 169 char intrstr[128]; 170 bus_addr_t addr; 171 bus_size_t size; 172 void *ih; 173 174 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 175 aprint_error(": couldn't get registers\n"); 176 return; 177 } 178 179 if (bus_space_map(bst, addr, size, 0, &bsh) != 0) { 180 aprint_error(": couldn't map registers\n"); 181 return; 182 } 183 184 if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) { 185 aprint_error(": failed to decode interrupt\n"); 186 return; 187 } 188 189 if ((clk = fdtbus_clock_get_index(phandle, 0)) != NULL) 190 if (clk_enable(clk) != 0) { 191 aprint_error(": couldn't enable clock\n"); 192 return; 193 } 194 if ((rst = fdtbus_reset_get_index(phandle, 0)) != NULL) 195 if (fdtbus_reset_deassert(rst) != 0) { 196 aprint_error(": couldn't de-assert reset\n"); 197 return; 198 } 199 200 conf = (void *)of_search_compatible(phandle, compat_data)->data; 201 prop_dictionary_set_bool(device_properties(self), "iflg-rwc", 202 conf->iflg_rwc); 203 204 /* Attach gttwsi core */ 205 sc->sc_reg_read = sunxi_twi_reg_read; 206 sc->sc_reg_write = sunxi_twi_reg_write; 207 gttwsi_attach_subr(self, bst, bsh); 208 209 /* 210 * Set clock rate to 100kHz. 211 */ 212 if (clk != NULL) 213 sunxi_twi_set_clock(sc, clk_get_rate(clk), 100000); 214 215 ih = fdtbus_intr_establish(phandle, 0, IPL_VM, 0, gttwsi_intr, sc); 216 if (ih == NULL) { 217 aprint_error_dev(self, "couldn't establish interrupt on %s\n", 218 intrstr); 219 return; 220 } 221 aprint_normal_dev(self, "interrupting on %s\n", intrstr); 222 223 fdtbus_register_i2c_controller(self, phandle, &sunxi_twi_funcs); 224 225 fdtbus_attach_i2cbus(self, phandle, &sc->sc_i2c, iicbus_print); 226 } 227