Back to home page

LXR

 
 

    


File indexing completed on 2025-05-11 08:23:05

0001 /*-
0002  * Copyright (c) 2014 Ian Lepore
0003  * All rights reserved.
0004  *
0005  * Redistribution and use in source and binary forms, with or without
0006  * modification, are permitted provided that the following conditions
0007  * are met:
0008  * 1. Redistributions of source code must retain the above copyright
0009  *    notice, this list of conditions and the following disclaimer.
0010  * 2. Redistributions in binary form must reproduce the above copyright
0011  *    notice, this list of conditions and the following disclaimer in the
0012  *    documentation and/or other materials provided with the distribution.
0013  *
0014  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
0015  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
0016  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
0017  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
0018  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
0019  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
0020  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
0021  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
0022  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
0023  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
0024  * SUCH DAMAGE.
0025  *
0026  * $FreeBSD: head/sys/arm/freescale/imx/imx_iomux.c 321938 2017-08-02 18:28:06Z ian $
0027  */
0028 
0029 /*
0030  * Pin mux and pad control driver for imx5 and imx6.
0031  *
0032  * This driver implements the fdt_pinctrl interface for configuring the gpio and
0033  * peripheral pins based on fdt configuration data.
0034  *
0035  * When the driver attaches, it walks the entire fdt tree and automatically
0036  * configures the pins for each device which has a pinctrl-0 property and whose
0037  * status is "okay".  In addition it implements the fdt_pinctrl_configure()
0038  * method which any other driver can call at any time to reconfigure its pins.
0039  *
0040  * The nature of the fsl,pins property in fdt data makes this driver's job very
0041  * easy.  Instead of representing each pin and pad configuration using symbolic
0042  * properties such as pullup-enable="true" and so on, the data simply contains
0043  * the addresses of the registers that control the pins, and the raw values to
0044  * store in those registers.
0045  *
0046  * The imx5 and imx6 SoCs also have a small number of "general purpose
0047  * registers" in the iomuxc device which are used to control an assortment
0048  * of completely unrelated aspects of SoC behavior.  This driver provides other
0049  * drivers with direct access to those registers via simple accessor functions.
0050  */
0051 
0052 #include <sys/param.h>
0053 #ifndef __rtems__
0054 #include <sys/systm.h>
0055 #include <sys/bus.h>
0056 #include <sys/kernel.h>
0057 #include <sys/module.h>
0058 #include <sys/malloc.h>
0059 #include <sys/rman.h>
0060 
0061 #include <machine/bus.h>
0062 
0063 #include <dev/ofw/openfirm.h>
0064 #include <dev/ofw/ofw_bus.h>
0065 #include <dev/ofw/ofw_bus_subr.h>
0066 #include <dev/fdt/fdt_pinctrl.h>
0067 #endif /* __rtems__ */
0068 
0069 #include <arm/freescale/imx/imx_iomuxvar.h>
0070 #ifndef __rtems__
0071 #include <arm/freescale/imx/imx_machdep.h>
0072 #else /* __rtems__ */
0073 #include <bsp.h>
0074 #include <bsp/fdt.h>
0075 #include <bsp/imx-iomux.h>
0076 #include <rtems/sysinit.h>
0077 #include <errno.h>
0078 #include <libfdt.h>
0079 #include <stdlib.h>
0080 
0081 typedef size_t bus_size_t;
0082 typedef int phandle_t;
0083 #endif /* __rtems__ */
0084 
0085 struct iomux_softc {
0086 #ifndef __rtems__
0087     device_t    dev;
0088     struct resource *mem_res;
0089     u_int       last_gpregaddr;
0090 #else /* __rtems__ */
0091     volatile uint32_t *regs;
0092 #endif /* __rtems__ */
0093 };
0094 
0095 #ifndef __rtems__
0096 static struct iomux_softc *iomux_sc;
0097 
0098 static struct ofw_compat_data compat_data[] = {
0099     {"fsl,imx6dl-iomuxc",   true},
0100     {"fsl,imx6q-iomuxc",    true},
0101     {"fsl,imx6sl-iomuxc",   true},
0102     {"fsl,imx6ul-iomuxc",   true},
0103     {"fsl,imx6sx-iomuxc",   true},
0104     {"fsl,imx53-iomuxc",    true},
0105     {"fsl,imx51-iomuxc",    true},
0106     {NULL,          false},
0107 };
0108 #else /* __rtems__ */
0109 static struct iomux_softc iomux_sc_instance;
0110 
0111 #define iomux_sc (&iomux_sc_instance);
0112 
0113 /* Return true if there is no status or status is "okay" or "ok". */
0114 static bool
0115 imx_fdt_node_status_okay(const void *fdt, int node)
0116 {
0117     const void *status;
0118     int len;
0119 
0120     status = fdt_getprop(fdt, node, "status", &len);
0121     if ((status == NULL) ||
0122         (strncmp(status, "okay", MIN(5, len)) == 0) ||
0123         (strncmp(status, "ok", MIN(3, len)) == 0)) {
0124         return true;
0125     }
0126 
0127     return false;
0128 }
0129 
0130 /*
0131  * Walk through all subnodes and handle "pinctrl-0" if the node is enabled. This
0132  * does roughly the same as FreeBSDs fdt_pinctrl_configure_tree but without the
0133  * whole open firmware (OF*) functions.
0134  */
0135 static void
0136 imx_pinctrl_configure_children(const void *fdt, int parent)
0137 {
0138     int node;
0139     const uint32_t *phandle;
0140     int len;
0141 
0142     fdt_for_each_subnode(node, fdt, parent) {
0143         if (imx_fdt_node_status_okay(fdt, node)) {
0144             imx_pinctrl_configure_children(fdt, node);
0145             phandle = fdt_getprop(fdt, node, "pinctrl-0", &len);
0146             if (phandle != NULL && len == sizeof(*phandle)) {
0147                 imx_iomux_configure_pins(fdt,
0148                     fdt32_to_cpu(*phandle));
0149             }
0150         }
0151     }
0152 }
0153 
0154 static void
0155 imx_iomux_init(void)
0156 {
0157     const void *fdt;
0158     int node;
0159     struct iomux_softc *sc;
0160 
0161     fdt = bsp_fdt_get();
0162     node = fdt_node_offset_by_compatible(fdt, -1, "fsl,imx7d-iomuxc");
0163     if (node < 0) {
0164         node = fdt_node_offset_by_compatible(fdt, -1,
0165             "fsl,imx6ul-iomuxc");
0166     }
0167     if (node < 0) {
0168         node = fdt_node_offset_by_compatible(fdt, -1,
0169             "nxp,imxrt1050-iomuxc");
0170     }
0171     sc = iomux_sc;
0172     sc->regs = imx_get_reg_of_node(fdt, node);
0173 
0174     node = fdt_path_offset(fdt, "/");
0175     imx_pinctrl_configure_children(fdt, node);
0176 }
0177 
0178 RTEMS_SYSINIT_ITEM(imx_iomux_init, RTEMS_SYSINIT_BSP_START,
0179     RTEMS_SYSINIT_ORDER_MIDDLE);
0180 
0181 #define OF_node_from_xref(phandle) fdt_node_offset_by_phandle(fdt, phandle)
0182 
0183 static int
0184 imx_iomux_getencprop_alloc(const char *fdt, int node, const char *name,
0185     size_t elsz, void **buf)
0186 {
0187     int len;
0188     const uint32_t *val;
0189     int i;
0190     uint32_t *cell;
0191 
0192     val = fdt_getprop(fdt, node, "fsl,pins", &len);
0193     if (val == NULL || len < 0 || len % elsz != 0) {
0194         return (-1);
0195     }
0196 
0197     cell = malloc((size_t)len);
0198     *buf = cell;
0199     if (cell == NULL) {
0200         return (-1);
0201     }
0202 
0203     for (i = 0; i < len / 4; ++i) {
0204         cell[i] = fdt32_to_cpu(val[i]);
0205     }
0206 
0207     return (len / (int)elsz);
0208 }
0209 
0210 #define OF_getencprop_alloc(node, name, elsz, buf) \
0211     imx_iomux_getencprop_alloc(fdt, node, name, elsz, buf)
0212 
0213 #define OF_prop_free(buf) free(buf)
0214 #endif /* __rtems__ */
0215 
0216 /*
0217  * Each tuple in an fsl,pins property contains these fields.
0218  */
0219 struct pincfg {
0220     uint32_t mux_reg;
0221     uint32_t padconf_reg;
0222     uint32_t input_reg;
0223     uint32_t mux_val;
0224     uint32_t input_val;
0225     uint32_t padconf_val;
0226 };
0227 
0228 #define PADCONF_NONE    (1U << 31)  /* Do not configure pad. */
0229 #define PADCONF_SION    (1U << 30)  /* Force SION bit in mux register. */
0230 #define PADMUX_SION (1U <<  4)  /* The SION bit in the mux register. */
0231 
0232 static inline uint32_t
0233 RD4(struct iomux_softc *sc, bus_size_t off)
0234 {
0235 
0236 #ifndef __rtems__
0237     return (bus_read_4(sc->mem_res, off));
0238 #else /* __rtems__ */
0239     return (sc->regs[off / 4]);
0240 #endif /* __rtems__ */
0241 }
0242 
0243 static inline void
0244 WR4(struct iomux_softc *sc, bus_size_t off, uint32_t val)
0245 {
0246 
0247 #ifndef __rtems__
0248     bus_write_4(sc->mem_res, off, val);
0249 #else /* __rtems__ */
0250     sc->regs[off / 4] = val;
0251 #endif /* __rtems__ */
0252 }
0253 
0254 static void
0255 iomux_configure_input(struct iomux_softc *sc, uint32_t reg, uint32_t val)
0256 {
0257     u_int select, mask, shift, width;
0258 
0259     /* If register and value are zero, there is nothing to configure. */
0260     if (reg == 0 && val == 0)
0261         return;
0262 
0263     /*
0264      * If the config value has 0xff in the high byte it is encoded:
0265      *  31     23      15      7        0
0266      *      | 0xff | shift | width | select |
0267      * We need to mask out the old select value and OR in the new, using a
0268      * mask of the given width and shifting the values up by shift.
0269      */
0270     if ((val & 0xff000000) == 0xff000000) {
0271         select = val & 0x000000ff;
0272         width = (val & 0x0000ff00) >> 8;
0273         shift = (val & 0x00ff0000) >> 16;
0274         mask  = ((1u << width) - 1) << shift;
0275         val = (RD4(sc, reg) & ~mask) | (select << shift);
0276     }
0277     WR4(sc, reg, val);
0278 }
0279 
0280 #ifndef __rtems__
0281 static int
0282 iomux_configure_pins(device_t dev, phandle_t cfgxref)
0283 #else /* __rtems__ */
0284 int imx_iomux_configure_pins(const void *fdt, uint32_t cfgxref)
0285 #endif /* __rtems__ */
0286 {
0287     struct iomux_softc *sc;
0288     struct pincfg *cfgtuples, *cfg;
0289     phandle_t cfgnode;
0290     int i, ntuples;
0291     uint32_t sion;
0292 
0293 #ifndef __rtems__
0294     sc = device_get_softc(dev);
0295 #else /* __rtems__ */
0296     sc = iomux_sc;
0297 #endif /* __rtems__ */
0298     cfgnode = OF_node_from_xref(cfgxref);
0299     ntuples = OF_getencprop_alloc(cfgnode, "fsl,pins", sizeof(*cfgtuples),
0300         (void **)&cfgtuples);
0301     if (ntuples < 0)
0302         return (ENOENT);
0303     if (ntuples == 0)
0304         return (0); /* Empty property is not an error. */
0305     for (i = 0, cfg = cfgtuples; i < ntuples; i++, cfg++) {
0306         sion = (cfg->padconf_val & PADCONF_SION) ? PADMUX_SION : 0;
0307         WR4(sc, cfg->mux_reg, cfg->mux_val | sion);
0308         iomux_configure_input(sc, cfg->input_reg, cfg->input_val);
0309         if ((cfg->padconf_val & PADCONF_NONE) == 0)
0310 #ifndef __rtems__
0311             WR4(sc, cfg->padconf_reg, cfg->padconf_val);
0312 #else /* __rtems__ */
0313             /*
0314              * Need to mask the flags. On (for example) i.MXRT1166
0315              * they are used for domain write protection. On other
0316              * i.MX* these are Reserved.
0317              */
0318             WR4(sc, cfg->padconf_reg, cfg->padconf_val
0319                 & ~(PADCONF_SION | PADCONF_NONE));
0320 #endif /* __rtems__ */
0321 #ifndef __rtems__
0322         if (bootverbose) {
0323             char name[32]; 
0324             OF_getprop(cfgnode, "name", &name, sizeof(name));
0325             printf("%16s: muxreg 0x%04x muxval 0x%02x "
0326                 "inpreg 0x%04x inpval 0x%02x "
0327                 "padreg 0x%04x padval 0x%08x\n",
0328                 name, cfg->mux_reg, cfg->mux_val | sion,
0329                 cfg->input_reg, cfg->input_val,
0330                 cfg->padconf_reg, cfg->padconf_val);
0331         }
0332 #endif /* __rtems__ */
0333     }
0334     OF_prop_free(cfgtuples);
0335     return (0);
0336 }
0337 
0338 #ifndef __rtems__
0339 static int
0340 iomux_probe(device_t dev)
0341 {
0342 
0343     if (!ofw_bus_status_okay(dev))
0344         return (ENXIO);
0345 
0346     if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data)
0347         return (ENXIO);
0348 
0349     device_set_desc(dev, "Freescale i.MX pin configuration");
0350     return (BUS_PROBE_DEFAULT);
0351 }
0352 
0353 static int
0354 iomux_detach(device_t dev)
0355 {
0356 
0357         /* This device is always present. */
0358     return (EBUSY);
0359 }
0360 
0361 static int
0362 iomux_attach(device_t dev)
0363 {
0364     struct iomux_softc * sc;
0365     int rid;
0366 
0367     sc = device_get_softc(dev);
0368     sc->dev = dev;
0369 
0370     switch (imx_soc_type()) {
0371     case IMXSOC_51:
0372         sc->last_gpregaddr = 1 * sizeof(uint32_t);
0373         break;
0374     case IMXSOC_53:
0375         sc->last_gpregaddr = 2 * sizeof(uint32_t);
0376         break;
0377     case IMXSOC_6DL:
0378     case IMXSOC_6S:
0379     case IMXSOC_6SL:
0380     case IMXSOC_6Q:
0381         sc->last_gpregaddr = 13 * sizeof(uint32_t);
0382         break;
0383     case IMXSOC_6UL:
0384         sc->last_gpregaddr = 14 * sizeof(uint32_t);
0385         break;
0386     default:
0387         device_printf(dev, "Unknown SoC type\n");
0388         return (ENXIO);
0389     }
0390 
0391     rid = 0;
0392     sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
0393         RF_ACTIVE);
0394     if (sc->mem_res == NULL) {
0395         device_printf(dev, "Cannot allocate memory resources\n");
0396         return (ENXIO);
0397     }
0398 
0399     iomux_sc = sc;
0400 
0401     /*
0402      * Register as a pinctrl device, and call the convenience function that
0403      * walks the entire device tree invoking FDT_PINCTRL_CONFIGURE() on any
0404      * pinctrl-0 property cells whose xref phandle refers to a configuration
0405      * that is a child node of our node in the tree.
0406      *
0407      * The pinctrl bindings documentation specifically mentions that the
0408      * pinctrl device itself may have a pinctrl-0 property which contains
0409      * static configuration to be applied at device init time.  The tree
0410      * walk will automatically handle this for us when it passes through our
0411      * node in the tree.
0412      */
0413     fdt_pinctrl_register(dev, "fsl,pins");
0414     fdt_pinctrl_configure_tree(dev);
0415 
0416     return (0);
0417 }
0418 
0419 uint32_t
0420 imx_iomux_gpr_get(u_int regaddr)
0421 {
0422     struct iomux_softc * sc;
0423 
0424     sc = iomux_sc;
0425     KASSERT(sc != NULL, ("%s called before attach", __FUNCTION__));
0426     KASSERT(regaddr >= 0 && regaddr <= sc->last_gpregaddr, 
0427         ("%s bad regaddr %u, max %u", __FUNCTION__, regaddr,
0428         sc->last_gpregaddr));
0429 
0430     return (RD4(iomux_sc, regaddr));
0431 }
0432 
0433 void
0434 imx_iomux_gpr_set(u_int regaddr, uint32_t val)
0435 {
0436     struct iomux_softc * sc;
0437 
0438     sc = iomux_sc;
0439     KASSERT(sc != NULL, ("%s called before attach", __FUNCTION__));
0440     KASSERT(regaddr >= 0 && regaddr <= sc->last_gpregaddr, 
0441         ("%s bad regaddr %u, max %u", __FUNCTION__, regaddr,
0442         sc->last_gpregaddr));
0443 
0444     WR4(iomux_sc, regaddr, val);
0445 }
0446 
0447 void
0448 imx_iomux_gpr_set_masked(u_int regaddr, uint32_t clrbits, uint32_t setbits)
0449 {
0450     struct iomux_softc * sc;
0451     uint32_t val;
0452 
0453     sc = iomux_sc;
0454     KASSERT(sc != NULL, ("%s called before attach", __FUNCTION__));
0455     KASSERT(regaddr >= 0 && regaddr <= sc->last_gpregaddr, 
0456         ("%s bad regaddr %u, max %u", __FUNCTION__, regaddr,
0457         sc->last_gpregaddr));
0458 
0459     val = RD4(iomux_sc, regaddr * 4);
0460     val = (val & ~clrbits) | setbits;
0461     WR4(iomux_sc, regaddr, val);
0462 }
0463 
0464 static device_method_t imx_iomux_methods[] = {
0465     /* Device interface */
0466     DEVMETHOD(device_probe,         iomux_probe),
0467     DEVMETHOD(device_attach,        iomux_attach),
0468     DEVMETHOD(device_detach,        iomux_detach),
0469 
0470         /* fdt_pinctrl interface */
0471     DEVMETHOD(fdt_pinctrl_configure,iomux_configure_pins),
0472 
0473     DEVMETHOD_END
0474 };
0475 
0476 static driver_t imx_iomux_driver = {
0477     "imx_iomux",
0478     imx_iomux_methods,
0479     sizeof(struct iomux_softc),
0480 };
0481 
0482 static devclass_t imx_iomux_devclass;
0483 
0484 EARLY_DRIVER_MODULE(imx_iomux, simplebus, imx_iomux_driver, 
0485     imx_iomux_devclass, 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_LATE);
0486 
0487 #endif /* __rtems__ */