1 /*-
2 * Copyright (C) 2008 MARVELL INTERNATIONAL LTD.
3 * All rights reserved.
4 *
5 * Developed by Semihalf.
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 * 3. Neither the name of MARVELL nor the names of contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 /*
33 * Driver for the TWSI (aka I2C, aka IIC) bus controller found on Marvell
34 * and Allwinner SoCs. Supports master operation only, and works in polling mode.
35 *
36 * Calls to DELAY() are needed per Application Note AN-179 "TWSI Software
37 * Guidelines for Discovery(TM), Horizon (TM) and Feroceon(TM) Devices".
38 */
39
40 #include <sys/cdefs.h>
41 __FBSDID("$FreeBSD$");
42
43 #include <sys/param.h>
44 #include <sys/systm.h>
45 #include <sys/bus.h>
46 #include <sys/kernel.h>
47 #include <sys/module.h>
48 #include <sys/resource.h>
49
50 #include <machine/_inttypes.h>
51 #include <machine/bus.h>
52 #include <machine/resource.h>
53
54 #include <sys/rman.h>
55
56 #include <sys/lock.h>
57 #include <sys/mutex.h>
58
59 #include <dev/iicbus/iiconf.h>
60 #include <dev/iicbus/iicbus.h>
61 #include <dev/ofw/ofw_bus.h>
62 #include <dev/ofw/ofw_bus_subr.h>
63
64 #include <dev/extres/clk/clk.h>
65
66 #include <arm/mv/mvreg.h>
67 #include <arm/mv/mvvar.h>
68 #include <dev/iicbus/twsi/twsi.h>
69
70 #include "iicbus_if.h"
71
72 #define MV_TWSI_NAME "twsi"
73 #define IICBUS_DEVNAME "iicbus"
74
75 #define TWSI_ADDR 0x00
76 #define TWSI_DATA 0x04
77 #define TWSI_CNTR 0x08
78 #define TWSI_XADDR 0x10
79 #define TWSI_STAT 0x0c
80 #define TWSI_BAUD_RATE 0x0c
81 #define TWSI_SRST 0x1c
82
83 #define TWSI_BAUD_RATE_RAW(C,M,N) ((C)/((10*(M+1))<<(N+1)))
84 #define TWSI_BAUD_RATE_SLOW 50000 /* 50kHz */
85 #define TWSI_BAUD_RATE_FAST 100000 /* 100kHz */
86
87 #define TWSI_DEBUG
88 #undef TWSI_DEBUG
89
90 #ifdef TWSI_DEBUG
91 #define debugf(fmt, args...) do { printf("%s(): ", __func__); printf(fmt,##args); } while (0)
92 #else
93 #define debugf(fmt, args...)
94 #endif
95
96 static phandle_t mv_twsi_get_node(device_t, device_t);
97 static int mv_twsi_probe(device_t);
98 static int mv_twsi_attach(device_t);
99
100 static struct ofw_compat_data compat_data[] = {
101 { "mrvl,twsi", true },
102 { "marvell,mv64xxx-i2c", true },
103 { "marvell,mv78230-i2c", true },
104 { NULL, false }
105 };
106
107 static device_method_t mv_twsi_methods[] = {
108 /* device interface */
109 DEVMETHOD(device_probe, mv_twsi_probe),
110 DEVMETHOD(device_attach, mv_twsi_attach),
111
112 /* ofw_bus interface */
113 DEVMETHOD(ofw_bus_get_node, mv_twsi_get_node),
114
115 DEVMETHOD_END
116 };
117
118 DEFINE_CLASS_1(twsi, mv_twsi_driver, mv_twsi_methods,
119 sizeof(struct twsi_softc), twsi_driver);
120
121 DRIVER_MODULE(twsi, simplebus, mv_twsi_driver, 0, 0);
122 DRIVER_MODULE(iicbus, twsi, iicbus_driver, 0, 0);
123 MODULE_DEPEND(twsi, iicbus, 1, 1, 1);
124 SIMPLEBUS_PNP_INFO(compat_data);
125
126 static phandle_t
127 mv_twsi_get_node(device_t bus, device_t dev)
128 {
129
130 /* Used by ofw_iicbus. */
131 return (ofw_bus_get_node(bus));
132 }
133
134 static int
135 mv_twsi_probe(device_t dev)
136 {
137
138 if (!ofw_bus_status_okay(dev))
139 return (ENXIO);
140
141 if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
142 return (ENXIO);
143
144 device_set_desc(dev, "Marvell Integrated I2C Bus Controller");
145 return (BUS_PROBE_DEFAULT);
146 }
147
148 #define ABSSUB(a,b) (((a) > (b)) ? (a) - (b) : (b) - (a))
149 static void
150 mv_twsi_cal_baud_rate(struct twsi_softc *sc, const uint32_t target,
151 struct twsi_baud_rate *rate)
152 {
153 uint64_t clk;
154 uint32_t cur, diff, diff0;
155 int m, n, m0, n0;
156
157 /* Calculate baud rate. */
158 m0 = n0 = 4; /* Default values on reset */
159 diff0 = 0xffffffff;
160 clk_get_freq(sc->clk_core, &clk);
161
162 for (n = 0; n < 8; n++) {
163 for (m = 0; m < 16; m++) {
164 cur = TWSI_BAUD_RATE_RAW(clk,m,n);
165 diff = ABSSUB(target, cur);
166 if (diff < diff0) {
167 m0 = m;
168 n0 = n;
169 diff0 = diff;
170 }
171 }
172 }
173 rate->raw = TWSI_BAUD_RATE_RAW(clk, m0, n0);
174 rate->param = TWSI_BAUD_RATE_PARAM(m0, n0);
175 rate->m = m0;
176 rate->n = n0;
177 }
178
179 static int
180 mv_twsi_attach(device_t dev)
181 {
182 struct twsi_softc *sc;
183 int error;
184
185 sc = device_get_softc(dev);
186 sc->dev = dev;
187
188 /* Activate clock */
189 error = clk_get_by_ofw_index(dev, 0, 0, &sc->clk_core);
190 if (error != 0) {
191 device_printf(dev, "could not find core clock\n");
192 return (error);
193 }
194 error = clk_enable(sc->clk_core);
195 if (error != 0) {
196 device_printf(dev, "could not enable core clock\n");
197 return (error);
198 }
199
200 if (clk_get_by_ofw_index(dev, 0, 1, &sc->clk_reg) == 0) {
201 error = clk_enable(sc->clk_reg);
202 if (error != 0) {
203 device_printf(dev, "could not enable core clock\n");
204 return (error);
205 }
206 }
207
208 mv_twsi_cal_baud_rate(sc, TWSI_BAUD_RATE_SLOW, &sc->baud_rate[IIC_SLOW]);
209 mv_twsi_cal_baud_rate(sc, TWSI_BAUD_RATE_FAST, &sc->baud_rate[IIC_FAST]);
210 if (bootverbose)
211 device_printf(dev, "calculated baud rates are:\n"
212 " %" PRIu32 " kHz (M=%d, N=%d) for slow,\n"
213 " %" PRIu32 " kHz (M=%d, N=%d) for fast.\n",
214 sc->baud_rate[IIC_SLOW].raw / 1000,
215 sc->baud_rate[IIC_SLOW].m,
216 sc->baud_rate[IIC_SLOW].n,
217 sc->baud_rate[IIC_FAST].raw / 1000,
218 sc->baud_rate[IIC_FAST].m,
219 sc->baud_rate[IIC_FAST].n);
220
221 sc->reg_data = TWSI_DATA;
222 sc->reg_slave_addr = TWSI_ADDR;
223 sc->reg_slave_ext_addr = TWSI_XADDR;
224 sc->reg_control = TWSI_CNTR;
225 sc->reg_status = TWSI_STAT;
226 sc->reg_baud_rate = TWSI_BAUD_RATE;
227 sc->reg_soft_reset = TWSI_SRST;
228
229 return (twsi_attach(dev));
230 }
Cache object: 813ee5cd6917837f0c28510257ef15f0
|