Thu Nov 30 20:41:21 2017 UTC ()
Set twi clock based on parent clock, and fix register remapping for writes.


(jmcneill)
diff -r1.6 -r1.7 src/sys/arch/arm/sunxi/sunxi_twi.c

cvs diff -r1.6 -r1.7 src/sys/arch/arm/sunxi/sunxi_twi.c (expand / switch to unified diff)

--- src/sys/arch/arm/sunxi/sunxi_twi.c 2017/10/29 15:00:00 1.6
+++ src/sys/arch/arm/sunxi/sunxi_twi.c 2017/11/30 20:41:21 1.7
@@ -1,14 +1,14 @@ @@ -1,14 +1,14 @@
1/* $NetBSD: sunxi_twi.c,v 1.6 2017/10/29 15:00:00 jmcneill Exp $ */ 1/* $NetBSD: sunxi_twi.c,v 1.7 2017/11/30 20:41:21 jmcneill Exp $ */
2 2
3/*- 3/*-
4 * Copyright (c) 2017 Jared McNeill <jmcneill@invisible.ca> 4 * Copyright (c) 2017 Jared McNeill <jmcneill@invisible.ca>
5 * All rights reserved. 5 * All rights reserved.
6 * 6 *
7 * Redistribution and use in source and binary forms, with or without 7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions 8 * modification, are permitted provided that the following conditions
9 * are met: 9 * are met:
10 * 1. Redistributions of source code must retain the above copyright 10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer. 11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright 12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the 13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution. 14 * documentation and/or other materials provided with the distribution.
@@ -23,53 +23,61 @@ @@ -23,53 +23,61 @@
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 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 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE. 26 * POSSIBILITY OF SUCH DAMAGE.
27 */ 27 */
28 28
29#include "opt_gttwsi.h" 29#include "opt_gttwsi.h"
30#ifdef GTTWSI_ALLWINNER 30#ifdef GTTWSI_ALLWINNER
31# error Do not define GTTWSI_ALLWINNER when using this driver 31# error Do not define GTTWSI_ALLWINNER when using this driver
32#endif 32#endif
33 33
34#include <sys/cdefs.h> 34#include <sys/cdefs.h>
35 35
36__KERNEL_RCSID(0, "$NetBSD: sunxi_twi.c,v 1.6 2017/10/29 15:00:00 jmcneill Exp $"); 36__KERNEL_RCSID(0, "$NetBSD: sunxi_twi.c,v 1.7 2017/11/30 20:41:21 jmcneill Exp $");
37 37
38#include <sys/param.h> 38#include <sys/param.h>
39#include <sys/bus.h> 39#include <sys/bus.h>
40#include <sys/device.h> 40#include <sys/device.h>
41#include <sys/intr.h> 41#include <sys/intr.h>
42#include <sys/systm.h> 42#include <sys/systm.h>
43#include <sys/time.h> 43#include <sys/time.h>
44 44
45#include <dev/i2c/i2cvar.h> 45#include <dev/i2c/i2cvar.h>
46#include <dev/i2c/gttwsivar.h> 46#include <dev/i2c/gttwsivar.h>
47#include <dev/i2c/gttwsireg.h> 47#include <dev/i2c/gttwsireg.h>
48 48
49#include <dev/fdt/fdtvar.h> 49#include <dev/fdt/fdtvar.h>
50 50
51#define TWI_CCR_REG 0x14 51#define TWI_CCR_REG 0x14
52#define TWI_CCR_CLK_M __BITS(6,3) 52#define TWI_CCR_CLK_M __BITS(6,3)
53#define TWI_CCR_CLK_N __BITS(2,0) 53#define TWI_CCR_CLK_N __BITS(2,0)
54 54
55static uint8_t sunxi_twi_regmap[] = { 55static uint8_t sunxi_twi_regmap_rd[] = {
56 [TWSI_SLAVEADDR] = 0x00, 56 [TWSI_SLAVEADDR/4] = 0x00,
57 [TWSI_EXTEND_SLAVEADDR] = 0x04, 57 [TWSI_EXTEND_SLAVEADDR/4] = 0x04,
58 [TWSI_DATA] = 0x08, 58 [TWSI_DATA/4] = 0x08,
59 [TWSI_CONTROL] = 0x0c, 59 [TWSI_CONTROL/4] = 0x0c,
60 [TWSI_STATUS] = 0x10, 60 [TWSI_STATUS/4] = 0x10,
61 [TWSI_BAUDRATE] = 0x14, 61 [TWSI_SOFTRESET/4] = 0x18,
62 [TWSI_SOFTRESET] = 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,
63}; 71};
64 72
65static int sunxi_twi_match(device_t, cfdata_t, void *); 73static int sunxi_twi_match(device_t, cfdata_t, void *);
66static void sunxi_twi_attach(device_t, device_t, void *); 74static void sunxi_twi_attach(device_t, device_t, void *);
67 75
68struct sunxi_twi_config { 76struct sunxi_twi_config {
69 bool iflg_rwc; 77 bool iflg_rwc;
70}; 78};
71 79
72static const struct sunxi_twi_config sun4i_a10_i2c_config = { 80static const struct sunxi_twi_config sun4i_a10_i2c_config = {
73 .iflg_rwc = false, 81 .iflg_rwc = false,
74}; 82};
75 83
@@ -91,33 +99,62 @@ sunxi_twi_get_tag(device_t dev) @@ -91,33 +99,62 @@ sunxi_twi_get_tag(device_t dev)
91{ 99{
92 struct gttwsi_softc * const sc = device_private(dev); 100 struct gttwsi_softc * const sc = device_private(dev);
93 101
94 return &sc->sc_i2c; 102 return &sc->sc_i2c;
95} 103}
96 104
97const struct fdtbus_i2c_controller_func sunxi_twi_funcs = { 105const struct fdtbus_i2c_controller_func sunxi_twi_funcs = {
98 .get_tag = sunxi_twi_get_tag, 106 .get_tag = sunxi_twi_get_tag,
99}; 107};
100 108
101static uint32_t 109static uint32_t
102sunxi_twi_reg_read(struct gttwsi_softc *sc, uint32_t reg) 110sunxi_twi_reg_read(struct gttwsi_softc *sc, uint32_t reg)
103{ 111{
104 return bus_space_read_4(sc->sc_bust, sc->sc_bush, sunxi_twi_regmap[reg]); 112 return bus_space_read_4(sc->sc_bust, sc->sc_bush, sunxi_twi_regmap_rd[reg/4]);
105} 113}
106 114
107static void 115static void
108sunxi_twi_reg_write(struct gttwsi_softc *sc, uint32_t reg, uint32_t val) 116sunxi_twi_reg_write(struct gttwsi_softc *sc, uint32_t reg, uint32_t val)
109{ 117{
110 bus_space_write_4(sc->sc_bust, sc->sc_bush, sunxi_twi_regmap[reg], val); 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);
111} 148}
112 149
113static int 150static int
114sunxi_twi_match(device_t parent, cfdata_t cf, void *aux) 151sunxi_twi_match(device_t parent, cfdata_t cf, void *aux)
115{ 152{
116 struct fdt_attach_args * const faa = aux; 153 struct fdt_attach_args * const faa = aux;
117 154
118 return of_match_compat_data(faa->faa_phandle, compat_data); 155 return of_match_compat_data(faa->faa_phandle, compat_data);
119} 156}
120 157
121static void 158static void
122sunxi_twi_attach(device_t parent, device_t self, void *aux) 159sunxi_twi_attach(device_t parent, device_t self, void *aux)
123{ 160{
@@ -142,56 +179,51 @@ sunxi_twi_attach(device_t parent, device @@ -142,56 +179,51 @@ sunxi_twi_attach(device_t parent, device
142 return; 179 return;
143 } 180 }
144 181
145 if (bus_space_map(bst, addr, size, 0, &bsh) != 0) { 182 if (bus_space_map(bst, addr, size, 0, &bsh) != 0) {
146 aprint_error(": couldn't map registers\n"); 183 aprint_error(": couldn't map registers\n");
147 return; 184 return;
148 } 185 }
149 186
150 if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) { 187 if (!fdtbus_intr_str(phandle, 0, intrstr, sizeof(intrstr))) {
151 aprint_error(": failed to decode interrupt\n"); 188 aprint_error(": failed to decode interrupt\n");
152 return; 189 return;
153 } 190 }
154 191
155 if ((clk = fdtbus_clock_get_index(phandle, 0)) != NULL) 192 clk = fdtbus_clock_get_index(phandle, 0);
156 if (clk_enable(clk) != 0) { 193 if (clk == NULL || clk_enable(clk) != 0) {
157 aprint_error(": couldn't enable clock\n"); 194 aprint_error(": couldn't enable clock\n");
158 return; 195 return;
159 } 196 }
160 if ((rst = fdtbus_reset_get_index(phandle, 0)) != NULL) 197 if ((rst = fdtbus_reset_get_index(phandle, 0)) != NULL)
161 if (fdtbus_reset_deassert(rst) != 0) { 198 if (fdtbus_reset_deassert(rst) != 0) {
162 aprint_error(": couldn't de-assert reset\n"); 199 aprint_error(": couldn't de-assert reset\n");
163 return; 200 return;
164 } 201 }
165 202
166 conf = (void *)of_search_compatible(phandle, compat_data)->data; 203 conf = (void *)of_search_compatible(phandle, compat_data)->data;
167 prop_dictionary_set_bool(device_properties(self), "iflg-rwc", 204 prop_dictionary_set_bool(device_properties(self), "iflg-rwc",
168 conf->iflg_rwc); 205 conf->iflg_rwc);
169 206
170 /* 207 /* Attach gttwsi core */
171 * Set clock rate to 100kHz. From the datasheet: 
172 * For 100Khz standard speed 2Wire, CLK_N=2, CLK_M=11 
173 * F0=48M/2^2=12Mhz, F1=F0/(10*(11+1)) = 0.1Mhz 
174 */ 
175 const u_int m = 11, n = 2; 
176 const uint32_t ccr = __SHIFTIN(n, TWI_CCR_CLK_N) | 
177 __SHIFTIN(m, TWI_CCR_CLK_M);  
178 bus_space_write_4(bst, bsh, TWI_CCR_REG, ccr); 
179 
180 sc->sc_reg_read = sunxi_twi_reg_read; 208 sc->sc_reg_read = sunxi_twi_reg_read;
181 sc->sc_reg_write = sunxi_twi_reg_write; 209 sc->sc_reg_write = sunxi_twi_reg_write;
182 
183 gttwsi_attach_subr(self, bst, bsh); 210 gttwsi_attach_subr(self, bst, bsh);
184 211
 212 /*
 213 * Set clock rate to 100kHz.
 214 */
 215 sunxi_twi_set_clock(sc, clk_get_rate(clk), 100000);
 216
185 ih = fdtbus_intr_establish(phandle, 0, IPL_VM, 0, gttwsi_intr, sc); 217 ih = fdtbus_intr_establish(phandle, 0, IPL_VM, 0, gttwsi_intr, sc);
186 if (ih == NULL) { 218 if (ih == NULL) {
187 aprint_error_dev(self, "couldn't establish interrupt on %s\n", 219 aprint_error_dev(self, "couldn't establish interrupt on %s\n",
188 intrstr); 220 intrstr);
189 return; 221 return;
190 } 222 }
191 aprint_normal_dev(self, "interrupting on %s\n", intrstr); 223 aprint_normal_dev(self, "interrupting on %s\n", intrstr);
192 224
193 fdtbus_register_i2c_controller(self, phandle, &sunxi_twi_funcs); 225 fdtbus_register_i2c_controller(self, phandle, &sunxi_twi_funcs);
194 226
195 devs = prop_dictionary_create(); 227 devs = prop_dictionary_create();
196 if (of_getprop_uint32(phandle, "#address-cells", &address_cells)) 228 if (of_getprop_uint32(phandle, "#address-cells", &address_cells))
197 address_cells = 1; 229 address_cells = 1;