1271550Sian/*- 2271550Sian * Copyright (c) 2014 Ian Lepore 3271550Sian * All rights reserved. 4271550Sian * 5271550Sian * Redistribution and use in source and binary forms, with or without 6271550Sian * modification, are permitted provided that the following conditions 7271550Sian * are met: 8271550Sian * 1. Redistributions of source code must retain the above copyright 9271550Sian * notice, this list of conditions and the following disclaimer. 10271550Sian * 2. Redistributions in binary form must reproduce the above copyright 11271550Sian * notice, this list of conditions and the following disclaimer in the 12271550Sian * documentation and/or other materials provided with the distribution. 13271550Sian * 14271550Sian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15271550Sian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16271550Sian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17271550Sian * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18271550Sian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19271550Sian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20271550Sian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21271550Sian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22271550Sian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23271550Sian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24271550Sian * SUCH DAMAGE. 25271550Sian * 26271550Sian * $FreeBSD: stable/11/sys/arm/freescale/imx/imx_iomux.c 323468 2017-09-11 22:28:38Z ian $ 27271550Sian */ 28271550Sian 29271591Sian/* 30271591Sian * Pin mux and pad control driver for imx5 and imx6. 31271591Sian * 32271591Sian * This driver implements the fdt_pinctrl interface for configuring the gpio and 33271591Sian * peripheral pins based on fdt configuration data. 34271591Sian * 35271591Sian * When the driver attaches, it walks the entire fdt tree and automatically 36271591Sian * configures the pins for each device which has a pinctrl-0 property and whose 37271591Sian * status is "okay". In addition it implements the fdt_pinctrl_configure() 38271591Sian * method which any other driver can call at any time to reconfigure its pins. 39271591Sian * 40271591Sian * The nature of the fsl,pins property in fdt data makes this driver's job very 41271591Sian * easy. Instead of representing each pin and pad configuration using symbolic 42271591Sian * properties such as pullup-enable="true" and so on, the data simply contains 43271591Sian * the addresses of the registers that control the pins, and the raw values to 44271591Sian * store in those registers. 45271591Sian * 46271591Sian * The imx5 and imx6 SoCs also have a small number of "general purpose 47271591Sian * registers" in the iomuxc device which are used to control an assortment 48271591Sian * of completely unrelated aspects of SoC behavior. This driver provides other 49271591Sian * drivers with direct access to those registers via simple accessor functions. 50271591Sian */ 51271591Sian 52271550Sian#include <sys/param.h> 53271550Sian#include <sys/systm.h> 54271550Sian#include <sys/bus.h> 55271550Sian#include <sys/kernel.h> 56271550Sian#include <sys/module.h> 57271550Sian#include <sys/malloc.h> 58271550Sian#include <sys/rman.h> 59271550Sian 60271550Sian#include <machine/bus.h> 61271550Sian 62271550Sian#include <dev/fdt/fdt_common.h> 63271550Sian#include <dev/fdt/fdt_pinctrl.h> 64271550Sian#include <dev/ofw/openfirm.h> 65271550Sian#include <dev/ofw/ofw_bus.h> 66271550Sian#include <dev/ofw/ofw_bus_subr.h> 67271550Sian 68271550Sian#include <arm/freescale/imx/imx_iomuxvar.h> 69271550Sian#include <arm/freescale/imx/imx_machdep.h> 70271550Sian 71271550Sianstruct iomux_softc { 72271550Sian device_t dev; 73271550Sian struct resource *mem_res; 74323468Sian u_int last_gpregaddr; 75271550Sian}; 76271550Sian 77271550Sianstatic struct iomux_softc *iomux_sc; 78271550Sian 79271550Sianstatic struct ofw_compat_data compat_data[] = { 80271550Sian {"fsl,imx6dl-iomuxc", true}, 81271550Sian {"fsl,imx6q-iomuxc", true}, 82271550Sian {"fsl,imx6sl-iomuxc", true}, 83323410Sian {"fsl,imx6ul-iomuxc", true}, 84271550Sian {"fsl,imx6sx-iomuxc", true}, 85271550Sian {"fsl,imx53-iomuxc", true}, 86271550Sian {"fsl,imx51-iomuxc", true}, 87271550Sian {NULL, false}, 88271550Sian}; 89271550Sian 90271550Sian/* 91271550Sian * Each tuple in an fsl,pins property contains these fields. 92271550Sian */ 93271550Sianstruct pincfg { 94271550Sian uint32_t mux_reg; 95271550Sian uint32_t padconf_reg; 96271550Sian uint32_t input_reg; 97271550Sian uint32_t mux_val; 98271550Sian uint32_t input_val; 99271550Sian uint32_t padconf_val; 100271550Sian}; 101271550Sian 102274412Sian#define PADCONF_NONE (1U << 31) /* Do not configure pad. */ 103274412Sian#define PADCONF_SION (1U << 30) /* Force SION bit in mux register. */ 104274412Sian#define PADMUX_SION (1U << 4) /* The SION bit in the mux register. */ 105274412Sian 106271550Sianstatic inline uint32_t 107271550SianRD4(struct iomux_softc *sc, bus_size_t off) 108271550Sian{ 109271550Sian 110271550Sian return (bus_read_4(sc->mem_res, off)); 111271550Sian} 112271550Sian 113271550Sianstatic inline void 114271550SianWR4(struct iomux_softc *sc, bus_size_t off, uint32_t val) 115271550Sian{ 116271550Sian 117271550Sian bus_write_4(sc->mem_res, off, val); 118271550Sian} 119271550Sian 120277568Sianstatic void 121277568Sianiomux_configure_input(struct iomux_softc *sc, uint32_t reg, uint32_t val) 122277568Sian{ 123277568Sian u_int select, mask, shift, width; 124277568Sian 125277568Sian /* If register and value are zero, there is nothing to configure. */ 126277568Sian if (reg == 0 && val == 0) 127277568Sian return; 128277568Sian 129277568Sian /* 130277568Sian * If the config value has 0xff in the high byte it is encoded: 131277568Sian * 31 23 15 7 0 132277568Sian * | 0xff | shift | width | select | 133277568Sian * We need to mask out the old select value and OR in the new, using a 134277568Sian * mask of the given width and shifting the values up by shift. 135277568Sian */ 136277568Sian if ((val & 0xff000000) == 0xff000000) { 137277568Sian select = val & 0x000000ff; 138277568Sian width = (val & 0x0000ff00) >> 8; 139277568Sian shift = (val & 0x00ff0000) >> 16; 140277568Sian mask = ((1u << width) - 1) << shift; 141277568Sian val = (RD4(sc, reg) & ~mask) | (select << shift); 142277568Sian } 143277568Sian WR4(sc, reg, val); 144277568Sian} 145277568Sian 146271550Sianstatic int 147271550Sianiomux_configure_pins(device_t dev, phandle_t cfgxref) 148271550Sian{ 149277568Sian struct iomux_softc *sc; 150271550Sian struct pincfg *cfgtuples, *cfg; 151271550Sian phandle_t cfgnode; 152271550Sian int i, ntuples; 153274412Sian uint32_t sion; 154271550Sian 155271550Sian sc = device_get_softc(dev); 156271550Sian cfgnode = OF_node_from_xref(cfgxref); 157271550Sian ntuples = OF_getencprop_alloc(cfgnode, "fsl,pins", sizeof(*cfgtuples), 158271550Sian (void **)&cfgtuples); 159271550Sian if (ntuples < 0) 160271550Sian return (ENOENT); 161271550Sian if (ntuples == 0) 162271550Sian return (0); /* Empty property is not an error. */ 163271550Sian for (i = 0, cfg = cfgtuples; i < ntuples; i++, cfg++) { 164274412Sian sion = (cfg->padconf_val & PADCONF_SION) ? PADMUX_SION : 0; 165274412Sian WR4(sc, cfg->mux_reg, cfg->mux_val | sion); 166277568Sian iomux_configure_input(sc, cfg->input_reg, cfg->input_val); 167274414Sian if ((cfg->padconf_val & PADCONF_NONE) == 0) 168274412Sian WR4(sc, cfg->padconf_reg, cfg->padconf_val); 169274412Sian if (bootverbose) { 170274412Sian char name[32]; 171274412Sian OF_getprop(cfgnode, "name", &name, sizeof(name)); 172274412Sian printf("%16s: muxreg 0x%04x muxval 0x%02x " 173274412Sian "inpreg 0x%04x inpval 0x%02x " 174274412Sian "padreg 0x%04x padval 0x%08x\n", 175274412Sian name, cfg->mux_reg, cfg->mux_val | sion, 176274412Sian cfg->input_reg, cfg->input_val, 177274412Sian cfg->padconf_reg, cfg->padconf_val); 178274412Sian } 179271550Sian } 180299702Sgonzo OF_prop_free(cfgtuples); 181271550Sian return (0); 182271550Sian} 183271550Sian 184271550Sianstatic int 185271550Sianiomux_probe(device_t dev) 186271550Sian{ 187271550Sian 188271550Sian if (!ofw_bus_status_okay(dev)) 189271550Sian return (ENXIO); 190271550Sian 191271550Sian if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) 192271550Sian return (ENXIO); 193271550Sian 194271550Sian device_set_desc(dev, "Freescale i.MX pin configuration"); 195271550Sian return (BUS_PROBE_DEFAULT); 196271550Sian} 197271550Sian 198271550Sianstatic int 199271550Sianiomux_detach(device_t dev) 200271550Sian{ 201271550Sian 202271550Sian /* This device is always present. */ 203271550Sian return (EBUSY); 204271550Sian} 205271550Sian 206271550Sianstatic int 207271550Sianiomux_attach(device_t dev) 208271550Sian{ 209271550Sian struct iomux_softc * sc; 210271550Sian int rid; 211271550Sian 212271550Sian sc = device_get_softc(dev); 213271550Sian sc->dev = dev; 214271550Sian 215271550Sian switch (imx_soc_type()) { 216271550Sian case IMXSOC_51: 217323468Sian sc->last_gpregaddr = 1 * sizeof(uint32_t); 218271550Sian break; 219271550Sian case IMXSOC_53: 220323468Sian sc->last_gpregaddr = 2 * sizeof(uint32_t); 221271550Sian break; 222271550Sian case IMXSOC_6DL: 223271550Sian case IMXSOC_6S: 224271550Sian case IMXSOC_6SL: 225271550Sian case IMXSOC_6Q: 226323468Sian sc->last_gpregaddr = 13 * sizeof(uint32_t); 227271550Sian break; 228323410Sian case IMXSOC_6UL: 229323468Sian sc->last_gpregaddr = 14 * sizeof(uint32_t); 230323410Sian break; 231271550Sian default: 232271550Sian device_printf(dev, "Unknown SoC type\n"); 233271550Sian return (ENXIO); 234271550Sian } 235271550Sian 236271550Sian rid = 0; 237271550Sian sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 238271550Sian RF_ACTIVE); 239271550Sian if (sc->mem_res == NULL) { 240271550Sian device_printf(dev, "Cannot allocate memory resources\n"); 241271550Sian return (ENXIO); 242271550Sian } 243271550Sian 244271550Sian iomux_sc = sc; 245271550Sian 246271550Sian /* 247271550Sian * Register as a pinctrl device, and call the convenience function that 248271550Sian * walks the entire device tree invoking FDT_PINCTRL_CONFIGURE() on any 249271550Sian * pinctrl-0 property cells whose xref phandle refers to a configuration 250271550Sian * that is a child node of our node in the tree. 251271550Sian * 252271550Sian * The pinctrl bindings documentation specifically mentions that the 253271550Sian * pinctrl device itself may have a pinctrl-0 property which contains 254271550Sian * static configuration to be applied at device init time. The tree 255271550Sian * walk will automatically handle this for us when it passes through our 256271550Sian * node in the tree. 257271550Sian */ 258271550Sian fdt_pinctrl_register(dev, "fsl,pins"); 259271550Sian fdt_pinctrl_configure_tree(dev); 260271550Sian 261271550Sian return (0); 262271550Sian} 263271550Sian 264271550Sianuint32_t 265323468Sianimx_iomux_gpr_get(u_int regaddr) 266271550Sian{ 267271550Sian struct iomux_softc * sc; 268271550Sian 269271550Sian sc = iomux_sc; 270271550Sian KASSERT(sc != NULL, ("%s called before attach", __FUNCTION__)); 271323468Sian KASSERT(regaddr >= 0 && regaddr <= sc->last_gpregaddr, 272323468Sian ("%s bad regaddr %u, max %u", __FUNCTION__, regaddr, 273323468Sian sc->last_gpregaddr)); 274271550Sian 275323468Sian return (RD4(iomux_sc, regaddr)); 276271550Sian} 277271550Sian 278271550Sianvoid 279323468Sianimx_iomux_gpr_set(u_int regaddr, uint32_t val) 280271550Sian{ 281271550Sian struct iomux_softc * sc; 282271550Sian 283271550Sian sc = iomux_sc; 284271550Sian KASSERT(sc != NULL, ("%s called before attach", __FUNCTION__)); 285323468Sian KASSERT(regaddr >= 0 && regaddr <= sc->last_gpregaddr, 286323468Sian ("%s bad regaddr %u, max %u", __FUNCTION__, regaddr, 287323468Sian sc->last_gpregaddr)); 288271550Sian 289323468Sian WR4(iomux_sc, regaddr, val); 290271550Sian} 291271550Sian 292271550Sianvoid 293323468Sianimx_iomux_gpr_set_masked(u_int regaddr, uint32_t clrbits, uint32_t setbits) 294271550Sian{ 295271550Sian struct iomux_softc * sc; 296271550Sian uint32_t val; 297271550Sian 298271550Sian sc = iomux_sc; 299271550Sian KASSERT(sc != NULL, ("%s called before attach", __FUNCTION__)); 300323468Sian KASSERT(regaddr >= 0 && regaddr <= sc->last_gpregaddr, 301323468Sian ("%s bad regaddr %u, max %u", __FUNCTION__, regaddr, 302323468Sian sc->last_gpregaddr)); 303271550Sian 304323468Sian val = RD4(iomux_sc, regaddr * 4); 305271550Sian val = (val & ~clrbits) | setbits; 306323468Sian WR4(iomux_sc, regaddr, val); 307271550Sian} 308271550Sian 309271550Sianstatic device_method_t imx_iomux_methods[] = { 310271550Sian /* Device interface */ 311271550Sian DEVMETHOD(device_probe, iomux_probe), 312271550Sian DEVMETHOD(device_attach, iomux_attach), 313271550Sian DEVMETHOD(device_detach, iomux_detach), 314271550Sian 315271550Sian /* fdt_pinctrl interface */ 316271550Sian DEVMETHOD(fdt_pinctrl_configure,iomux_configure_pins), 317271550Sian 318271550Sian DEVMETHOD_END 319271550Sian}; 320271550Sian 321271550Sianstatic driver_t imx_iomux_driver = { 322271550Sian "imx_iomux", 323271550Sian imx_iomux_methods, 324271550Sian sizeof(struct iomux_softc), 325271550Sian}; 326271550Sian 327271550Sianstatic devclass_t imx_iomux_devclass; 328271550Sian 329271550SianEARLY_DRIVER_MODULE(imx_iomux, simplebus, imx_iomux_driver, 330271550Sian imx_iomux_devclass, 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_LATE); 331271550Sian 332