Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/arm/freescale/imx/imx_iomux.c
39536 views
1
/*-
2
* Copyright (c) 2014 Ian Lepore
3
* All rights reserved.
4
*
5
* Redistribution and use in source and binary forms, with or without
6
* modification, are permitted provided that the following conditions
7
* are met:
8
* 1. Redistributions of source code must retain the above copyright
9
* notice, this list of conditions and the following disclaimer.
10
* 2. Redistributions in binary form must reproduce the above copyright
11
* notice, this list of conditions and the following disclaimer in the
12
* documentation and/or other materials provided with the distribution.
13
*
14
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24
* SUCH DAMAGE.
25
*/
26
27
/*
28
* Pin mux and pad control driver for imx5 and imx6.
29
*
30
* This driver implements the fdt_pinctrl interface for configuring the gpio and
31
* peripheral pins based on fdt configuration data.
32
*
33
* When the driver attaches, it walks the entire fdt tree and automatically
34
* configures the pins for each device which has a pinctrl-0 property and whose
35
* status is "okay". In addition it implements the fdt_pinctrl_configure()
36
* method which any other driver can call at any time to reconfigure its pins.
37
*
38
* The nature of the fsl,pins property in fdt data makes this driver's job very
39
* easy. Instead of representing each pin and pad configuration using symbolic
40
* properties such as pullup-enable="true" and so on, the data simply contains
41
* the addresses of the registers that control the pins, and the raw values to
42
* store in those registers.
43
*
44
* The imx5 and imx6 SoCs also have a small number of "general purpose
45
* registers" in the iomuxc device which are used to control an assortment
46
* of completely unrelated aspects of SoC behavior. This driver provides other
47
* drivers with direct access to those registers via simple accessor functions.
48
*/
49
50
#include <sys/param.h>
51
#include <sys/systm.h>
52
#include <sys/bus.h>
53
#include <sys/kernel.h>
54
#include <sys/module.h>
55
#include <sys/malloc.h>
56
#include <sys/rman.h>
57
58
#include <machine/bus.h>
59
60
#include <dev/ofw/openfirm.h>
61
#include <dev/ofw/ofw_bus.h>
62
#include <dev/ofw/ofw_bus_subr.h>
63
#include <dev/fdt/fdt_pinctrl.h>
64
65
#include <arm/freescale/imx/imx_iomuxvar.h>
66
#include <arm/freescale/imx/imx_machdep.h>
67
68
struct iomux_softc {
69
device_t dev;
70
struct resource *mem_res;
71
u_int last_gpregaddr;
72
};
73
74
static struct iomux_softc *iomux_sc;
75
76
static struct ofw_compat_data compat_data[] = {
77
{"fsl,imx8mq-iomuxc", true},
78
{"fsl,imx6dl-iomuxc", true},
79
{"fsl,imx6q-iomuxc", true},
80
{"fsl,imx6sl-iomuxc", true},
81
{"fsl,imx6ul-iomuxc", true},
82
{"fsl,imx6sx-iomuxc", true},
83
{"fsl,imx53-iomuxc", true},
84
{"fsl,imx51-iomuxc", true},
85
{NULL, false},
86
};
87
88
/*
89
* Each tuple in an fsl,pins property contains these fields.
90
*/
91
struct pincfg {
92
uint32_t mux_reg;
93
uint32_t padconf_reg;
94
uint32_t input_reg;
95
uint32_t mux_val;
96
uint32_t input_val;
97
uint32_t padconf_val;
98
};
99
100
#define PADCONF_NONE (1U << 31) /* Do not configure pad. */
101
#define PADCONF_SION (1U << 30) /* Force SION bit in mux register. */
102
#define PADMUX_SION (1U << 4) /* The SION bit in the mux register. */
103
104
static inline uint32_t
105
RD4(struct iomux_softc *sc, bus_size_t off)
106
{
107
108
return (bus_read_4(sc->mem_res, off));
109
}
110
111
static inline void
112
WR4(struct iomux_softc *sc, bus_size_t off, uint32_t val)
113
{
114
115
bus_write_4(sc->mem_res, off, val);
116
}
117
118
static void
119
iomux_configure_input(struct iomux_softc *sc, uint32_t reg, uint32_t val)
120
{
121
u_int select, mask, shift, width;
122
123
/* If register and value are zero, there is nothing to configure. */
124
if (reg == 0 && val == 0)
125
return;
126
127
/*
128
* If the config value has 0xff in the high byte it is encoded:
129
* 31 23 15 7 0
130
* | 0xff | shift | width | select |
131
* We need to mask out the old select value and OR in the new, using a
132
* mask of the given width and shifting the values up by shift.
133
*/
134
if ((val & 0xff000000) == 0xff000000) {
135
select = val & 0x000000ff;
136
width = (val & 0x0000ff00) >> 8;
137
shift = (val & 0x00ff0000) >> 16;
138
mask = ((1u << width) - 1) << shift;
139
val = (RD4(sc, reg) & ~mask) | (select << shift);
140
}
141
WR4(sc, reg, val);
142
}
143
144
static int
145
iomux_configure_pins(device_t dev, phandle_t cfgxref)
146
{
147
struct iomux_softc *sc;
148
struct pincfg *cfgtuples, *cfg;
149
phandle_t cfgnode;
150
int i, ntuples;
151
uint32_t sion;
152
153
sc = device_get_softc(dev);
154
cfgnode = OF_node_from_xref(cfgxref);
155
ntuples = OF_getencprop_alloc_multi(cfgnode, "fsl,pins",
156
sizeof(*cfgtuples), (void **)&cfgtuples);
157
if (ntuples < 0)
158
return (ENOENT);
159
if (ntuples == 0)
160
return (0); /* Empty property is not an error. */
161
for (i = 0, cfg = cfgtuples; i < ntuples; i++, cfg++) {
162
sion = (cfg->padconf_val & PADCONF_SION) ? PADMUX_SION : 0;
163
WR4(sc, cfg->mux_reg, cfg->mux_val | sion);
164
iomux_configure_input(sc, cfg->input_reg, cfg->input_val);
165
if ((cfg->padconf_val & PADCONF_NONE) == 0)
166
WR4(sc, cfg->padconf_reg, cfg->padconf_val);
167
if (bootverbose) {
168
char name[32];
169
OF_getprop(cfgnode, "name", &name, sizeof(name));
170
printf("%16s: muxreg 0x%04x muxval 0x%02x "
171
"inpreg 0x%04x inpval 0x%02x "
172
"padreg 0x%04x padval 0x%08x\n",
173
name, cfg->mux_reg, cfg->mux_val | sion,
174
cfg->input_reg, cfg->input_val,
175
cfg->padconf_reg, cfg->padconf_val);
176
}
177
}
178
OF_prop_free(cfgtuples);
179
return (0);
180
}
181
182
static int
183
iomux_probe(device_t dev)
184
{
185
186
if (!ofw_bus_status_okay(dev))
187
return (ENXIO);
188
189
if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
190
return (ENXIO);
191
192
device_set_desc(dev, "Freescale i.MX pin configuration");
193
return (BUS_PROBE_DEFAULT);
194
}
195
196
static int
197
iomux_detach(device_t dev)
198
{
199
200
/* This device is always present. */
201
return (EBUSY);
202
}
203
204
static int
205
iomux_attach(device_t dev)
206
{
207
struct iomux_softc * sc;
208
int rid;
209
210
sc = device_get_softc(dev);
211
sc->dev = dev;
212
213
switch (imx_soc_type()) {
214
case IMXSOC_51:
215
sc->last_gpregaddr = 1 * sizeof(uint32_t);
216
break;
217
case IMXSOC_53:
218
sc->last_gpregaddr = 2 * sizeof(uint32_t);
219
break;
220
case IMXSOC_6DL:
221
case IMXSOC_6S:
222
case IMXSOC_6SL:
223
case IMXSOC_6Q:
224
sc->last_gpregaddr = 13 * sizeof(uint32_t);
225
break;
226
case IMXSOC_6UL:
227
sc->last_gpregaddr = 14 * sizeof(uint32_t);
228
break;
229
default:
230
device_printf(dev, "Unknown SoC type\n");
231
return (ENXIO);
232
}
233
234
rid = 0;
235
sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
236
RF_ACTIVE);
237
if (sc->mem_res == NULL) {
238
device_printf(dev, "Cannot allocate memory resources\n");
239
return (ENXIO);
240
}
241
242
iomux_sc = sc;
243
244
/*
245
* Register as a pinctrl device, and call the convenience function that
246
* walks the entire device tree invoking FDT_PINCTRL_CONFIGURE() on any
247
* pinctrl-0 property cells whose xref phandle refers to a configuration
248
* that is a child node of our node in the tree.
249
*
250
* The pinctrl bindings documentation specifically mentions that the
251
* pinctrl device itself may have a pinctrl-0 property which contains
252
* static configuration to be applied at device init time. The tree
253
* walk will automatically handle this for us when it passes through our
254
* node in the tree.
255
*/
256
fdt_pinctrl_register(dev, "fsl,pins");
257
fdt_pinctrl_configure_tree(dev);
258
259
return (0);
260
}
261
262
uint32_t
263
imx_iomux_gpr_get(u_int regaddr)
264
{
265
struct iomux_softc *sc __diagused;
266
267
sc = iomux_sc;
268
KASSERT(sc != NULL, ("%s called before attach", __FUNCTION__));
269
KASSERT(regaddr >= 0 && regaddr <= sc->last_gpregaddr,
270
("%s bad regaddr %u, max %u", __FUNCTION__, regaddr,
271
sc->last_gpregaddr));
272
273
return (RD4(iomux_sc, regaddr));
274
}
275
276
void
277
imx_iomux_gpr_set(u_int regaddr, uint32_t val)
278
{
279
struct iomux_softc *sc __diagused;
280
281
sc = iomux_sc;
282
KASSERT(sc != NULL, ("%s called before attach", __FUNCTION__));
283
KASSERT(regaddr >= 0 && regaddr <= sc->last_gpregaddr,
284
("%s bad regaddr %u, max %u", __FUNCTION__, regaddr,
285
sc->last_gpregaddr));
286
287
WR4(iomux_sc, regaddr, val);
288
}
289
290
void
291
imx_iomux_gpr_set_masked(u_int regaddr, uint32_t clrbits, uint32_t setbits)
292
{
293
struct iomux_softc *sc __diagused;
294
uint32_t val;
295
296
sc = iomux_sc;
297
KASSERT(sc != NULL, ("%s called before attach", __FUNCTION__));
298
KASSERT(regaddr >= 0 && regaddr <= sc->last_gpregaddr,
299
("%s bad regaddr %u, max %u", __FUNCTION__, regaddr,
300
sc->last_gpregaddr));
301
302
val = RD4(iomux_sc, regaddr * 4);
303
val = (val & ~clrbits) | setbits;
304
WR4(iomux_sc, regaddr, val);
305
}
306
307
static device_method_t imx_iomux_methods[] = {
308
/* Device interface */
309
DEVMETHOD(device_probe, iomux_probe),
310
DEVMETHOD(device_attach, iomux_attach),
311
DEVMETHOD(device_detach, iomux_detach),
312
313
/* fdt_pinctrl interface */
314
DEVMETHOD(fdt_pinctrl_configure,iomux_configure_pins),
315
316
DEVMETHOD_END
317
};
318
319
static driver_t imx_iomux_driver = {
320
"imx_iomux",
321
imx_iomux_methods,
322
sizeof(struct iomux_softc),
323
};
324
325
EARLY_DRIVER_MODULE(imx_iomux, simplebus, imx_iomux_driver, 0, 0,
326
BUS_PASS_CPU + BUS_PASS_ORDER_LATE);
327
328