]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - sys/dev/iicbus/twsi/mv_twsi.c
Add pnpinfo.
[FreeBSD/FreeBSD.git] / sys / dev / iicbus / twsi / mv_twsi.c
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 #ifdef EXT_RESOURCES
65 #include <dev/extres/clk/clk.h>
66 #endif
67
68 #include <arm/mv/mvreg.h>
69 #include <arm/mv/mvvar.h>
70 #include <dev/iicbus/twsi/twsi.h>
71
72 #include "iicbus_if.h"
73
74 #define MV_TWSI_NAME            "twsi"
75 #define IICBUS_DEVNAME          "iicbus"
76
77 #define TWSI_ADDR       0x00
78 #define TWSI_DATA       0x04
79 #define TWSI_CNTR       0x08
80 #define TWSI_XADDR      0x10
81 #define TWSI_STAT       0x0c
82 #define TWSI_BAUD_RATE  0x0c
83 #define TWSI_SRST       0x1c
84
85 #define TWSI_BAUD_RATE_RAW(C,M,N)       ((C)/((10*(M+1))<<(N+1)))
86 #define TWSI_BAUD_RATE_SLOW             50000   /* 50kHz */
87 #define TWSI_BAUD_RATE_FAST             100000  /* 100kHz */
88
89 #define TWSI_DEBUG
90 #undef TWSI_DEBUG
91
92 #ifdef  TWSI_DEBUG
93 #define debugf(fmt, args...) do { printf("%s(): ", __func__); printf(fmt,##args); } while (0)
94 #else
95 #define debugf(fmt, args...)
96 #endif
97
98 static phandle_t mv_twsi_get_node(device_t, device_t);
99 static int mv_twsi_probe(device_t);
100 static int mv_twsi_attach(device_t);
101
102 static struct ofw_compat_data compat_data[] = {
103         { "mrvl,twsi",                  true },
104         { "marvell,mv64xxx-i2c",        true },
105         { "marvell,mv78230-i2c",        true },
106         { NULL,                         false }
107 };
108
109 static device_method_t mv_twsi_methods[] = {
110         /* device interface */
111         DEVMETHOD(device_probe,         mv_twsi_probe),
112         DEVMETHOD(device_attach,        mv_twsi_attach),
113
114         /* ofw_bus interface */
115         DEVMETHOD(ofw_bus_get_node,     mv_twsi_get_node),
116
117         DEVMETHOD_END
118 };
119
120 DEFINE_CLASS_1(twsi, mv_twsi_driver, mv_twsi_methods,
121     sizeof(struct twsi_softc), twsi_driver);
122
123 static devclass_t mv_twsi_devclass;
124
125 DRIVER_MODULE(twsi, simplebus, mv_twsi_driver, mv_twsi_devclass, 0, 0);
126 DRIVER_MODULE(iicbus, twsi, iicbus_driver, iicbus_devclass, 0, 0);
127 MODULE_DEPEND(twsi, iicbus, 1, 1, 1);
128 SIMPLEBUS_PNP_INFO(compat_data);
129
130 static phandle_t
131 mv_twsi_get_node(device_t bus, device_t dev)
132 {
133
134         /* Used by ofw_iicbus. */
135         return (ofw_bus_get_node(bus));
136 }
137
138 static int
139 mv_twsi_probe(device_t dev)
140 {
141         struct twsi_softc *sc;
142
143         sc = device_get_softc(dev);
144         if (!ofw_bus_status_okay(dev))
145                 return (ENXIO);
146
147         if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
148                 return (ENXIO);
149
150         device_set_desc(dev, "Marvell Integrated I2C Bus Controller");
151         return (BUS_PROBE_DEFAULT);
152 }
153
154 #define ABSSUB(a,b)     (((a) > (b)) ? (a) - (b) : (b) - (a))
155 static void
156 mv_twsi_cal_baud_rate(struct twsi_softc *sc, const uint32_t target,
157     struct twsi_baud_rate *rate)
158 {
159         uint64_t clk;
160         uint32_t cur, diff, diff0;
161         int m, n, m0, n0;
162
163         /* Calculate baud rate. */
164         m0 = n0 = 4;    /* Default values on reset */
165         diff0 = 0xffffffff;
166 #ifdef __aarch64__
167         clk_get_freq(sc->clk_core, &clk);
168 #else
169         clk = get_tclk();
170 #endif
171
172         for (n = 0; n < 8; n++) {
173                 for (m = 0; m < 16; m++) {
174                         cur = TWSI_BAUD_RATE_RAW(clk,m,n);
175                         diff = ABSSUB(target, cur);
176                         if (diff < diff0) {
177                                 m0 = m;
178                                 n0 = n;
179                                 diff0 = diff;
180                         }
181                 }
182         }
183         rate->raw = TWSI_BAUD_RATE_RAW(clk, m0, n0);
184         rate->param = TWSI_BAUD_RATE_PARAM(m0, n0);
185         rate->m = m0;
186         rate->n = n0;
187 }
188
189 static int
190 mv_twsi_attach(device_t dev)
191 {
192         struct twsi_softc *sc;
193 #ifdef __aarch64__
194         int error;
195 #endif
196
197         sc = device_get_softc(dev);
198         sc->dev = dev;
199
200 #ifdef __aarch64__
201         /* Activate clock */
202         error = clk_get_by_ofw_index(dev, 0, 0, &sc->clk_core);
203         if (error != 0) {
204                 device_printf(dev, "could not find core clock\n");
205                 return (error);
206         }
207         error = clk_enable(sc->clk_core);
208         if (error != 0) {
209                 device_printf(dev, "could not enable core clock\n");
210                 return (error);
211         }
212
213         if (clk_get_by_ofw_index(dev, 0, 1, &sc->clk_reg) == 0) {
214                 error = clk_enable(sc->clk_reg);
215                 if (error != 0) {
216                         device_printf(dev, "could not enable core clock\n");
217                         return (error);
218                 }
219         }
220 #endif
221
222         mv_twsi_cal_baud_rate(sc, TWSI_BAUD_RATE_SLOW, &sc->baud_rate[IIC_SLOW]);
223         mv_twsi_cal_baud_rate(sc, TWSI_BAUD_RATE_FAST, &sc->baud_rate[IIC_FAST]);
224         if (bootverbose)
225                 device_printf(dev, "calculated baud rates are:\n"
226                     " %" PRIu32 " kHz (M=%d, N=%d) for slow,\n"
227                     " %" PRIu32 " kHz (M=%d, N=%d) for fast.\n",
228                     sc->baud_rate[IIC_SLOW].raw / 1000,
229                     sc->baud_rate[IIC_SLOW].m,
230                     sc->baud_rate[IIC_SLOW].n,
231                     sc->baud_rate[IIC_FAST].raw / 1000,
232                     sc->baud_rate[IIC_FAST].m,
233                     sc->baud_rate[IIC_FAST].n);
234
235         sc->reg_data = TWSI_DATA;
236         sc->reg_slave_addr = TWSI_ADDR;
237         sc->reg_slave_ext_addr = TWSI_XADDR;
238         sc->reg_control = TWSI_CNTR;
239         sc->reg_status = TWSI_STAT;
240         sc->reg_baud_rate = TWSI_BAUD_RATE;
241         sc->reg_soft_reset = TWSI_SRST;
242
243         return (twsi_attach(dev));
244 }