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: releng/10.3/sys/arm/freescale/imx/imx_iomux.c 278732 2015-02-13 23:34:40Z ian $ 27271550Sian */ 28271550Sian 29273670Sian/* 30273670Sian * Pin mux and pad control driver for imx5 and imx6. 31273670Sian * 32273670Sian * This driver implements the fdt_pinctrl interface for configuring the gpio and 33273670Sian * peripheral pins based on fdt configuration data. 34273670Sian * 35273670Sian * When the driver attaches, it walks the entire fdt tree and automatically 36273670Sian * configures the pins for each device which has a pinctrl-0 property and whose 37273670Sian * status is "okay". In addition it implements the fdt_pinctrl_configure() 38273670Sian * method which any other driver can call at any time to reconfigure its pins. 39273670Sian * 40273670Sian * The nature of the fsl,pins property in fdt data makes this driver's job very 41273670Sian * easy. Instead of representing each pin and pad configuration using symbolic 42273670Sian * properties such as pullup-enable="true" and so on, the data simply contains 43273670Sian * the addresses of the registers that control the pins, and the raw values to 44273670Sian * store in those registers. 45273670Sian * 46273670Sian * The imx5 and imx6 SoCs also have a small number of "general purpose 47273670Sian * registers" in the iomuxc device which are used to control an assortment 48273670Sian * of completely unrelated aspects of SoC behavior. This driver provides other 49273670Sian * drivers with direct access to those registers via simple accessor functions. 50273670Sian */ 51273670Sian 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#include <machine/fdt.h> 62271550Sian 63271550Sian#include <dev/fdt/fdt_common.h> 64271550Sian#include <dev/fdt/fdt_pinctrl.h> 65271550Sian#include <dev/ofw/openfirm.h> 66271550Sian#include <dev/ofw/ofw_bus.h> 67271550Sian#include <dev/ofw/ofw_bus_subr.h> 68271550Sian 69271550Sian#include <arm/freescale/imx/imx_iomuxvar.h> 70271550Sian#include <arm/freescale/imx/imx_machdep.h> 71271550Sian 72271550Sianstruct iomux_softc { 73271550Sian device_t dev; 74271550Sian struct resource *mem_res; 75271550Sian u_int last_gpreg; 76271550Sian}; 77271550Sian 78271550Sianstatic struct iomux_softc *iomux_sc; 79271550Sian 80271550Sianstatic struct ofw_compat_data compat_data[] = { 81271550Sian {"fsl,imx6dl-iomuxc", true}, 82271550Sian {"fsl,imx6q-iomuxc", true}, 83271550Sian {"fsl,imx6sl-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 102276275Sian#define PADCONF_NONE (1U << 31) /* Do not configure pad. */ 103276275Sian#define PADCONF_SION (1U << 30) /* Force SION bit in mux register. */ 104276275Sian#define PADMUX_SION (1U << 4) /* The SION bit in the mux register. */ 105276275Sian 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 120278732Sianstatic void 121278732Sianiomux_configure_input(struct iomux_softc *sc, uint32_t reg, uint32_t val) 122278732Sian{ 123278732Sian u_int select, mask, shift, width; 124278732Sian 125278732Sian /* If register and value are zero, there is nothing to configure. */ 126278732Sian if (reg == 0 && val == 0) 127278732Sian return; 128278732Sian 129278732Sian /* 130278732Sian * If the config value has 0xff in the high byte it is encoded: 131278732Sian * 31 23 15 7 0 132278732Sian * | 0xff | shift | width | select | 133278732Sian * We need to mask out the old select value and OR in the new, using a 134278732Sian * mask of the given width and shifting the values up by shift. 135278732Sian */ 136278732Sian if ((val & 0xff000000) == 0xff000000) { 137278732Sian select = val & 0x000000ff; 138278732Sian width = (val & 0x0000ff00) >> 8; 139278732Sian shift = (val & 0x00ff0000) >> 16; 140278732Sian mask = ((1u << width) - 1) << shift; 141278732Sian val = (RD4(sc, reg) & ~mask) | (select << shift); 142278732Sian } 143278732Sian WR4(sc, reg, val); 144278732Sian} 145278732Sian 146271550Sianstatic int 147271550Sianiomux_configure_pins(device_t dev, phandle_t cfgxref) 148271550Sian{ 149278732Sian struct iomux_softc *sc; 150271550Sian struct pincfg *cfgtuples, *cfg; 151271550Sian phandle_t cfgnode; 152271550Sian int i, ntuples; 153276275Sian 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++) { 164276275Sian sion = (cfg->padconf_val & PADCONF_SION) ? PADMUX_SION : 0; 165276275Sian WR4(sc, cfg->mux_reg, cfg->mux_val | sion); 166278732Sian iomux_configure_input(sc, cfg->input_reg, cfg->input_val); 167276275Sian if ((cfg->padconf_val & PADCONF_NONE) == 0) 168276275Sian WR4(sc, cfg->padconf_reg, cfg->padconf_val); 169276275Sian if (bootverbose) { 170276275Sian char name[32]; 171276275Sian OF_getprop(cfgnode, "name", &name, sizeof(name)); 172276275Sian printf("%16s: muxreg 0x%04x muxval 0x%02x " 173276275Sian "inpreg 0x%04x inpval 0x%02x " 174276275Sian "padreg 0x%04x padval 0x%08x\n", 175276275Sian name, cfg->mux_reg, cfg->mux_val | sion, 176276275Sian cfg->input_reg, cfg->input_val, 177276275Sian cfg->padconf_reg, cfg->padconf_val); 178276275Sian } 179271550Sian } 180271550Sian free(cfgtuples, M_OFWPROP); 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: 217271550Sian sc->last_gpreg = 1; 218271550Sian break; 219271550Sian case IMXSOC_53: 220271550Sian sc->last_gpreg = 2; 221271550Sian break; 222271550Sian case IMXSOC_6DL: 223271550Sian case IMXSOC_6S: 224271550Sian case IMXSOC_6SL: 225271550Sian case IMXSOC_6Q: 226271550Sian sc->last_gpreg = 13; 227271550Sian break; 228271550Sian default: 229271550Sian device_printf(dev, "Unknown SoC type\n"); 230271550Sian return (ENXIO); 231271550Sian } 232271550Sian 233271550Sian rid = 0; 234271550Sian sc->mem_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, 235271550Sian RF_ACTIVE); 236271550Sian if (sc->mem_res == NULL) { 237271550Sian device_printf(dev, "Cannot allocate memory resources\n"); 238271550Sian return (ENXIO); 239271550Sian } 240271550Sian 241271550Sian iomux_sc = sc; 242271550Sian 243271550Sian /* 244271550Sian * Register as a pinctrl device, and call the convenience function that 245271550Sian * walks the entire device tree invoking FDT_PINCTRL_CONFIGURE() on any 246271550Sian * pinctrl-0 property cells whose xref phandle refers to a configuration 247271550Sian * that is a child node of our node in the tree. 248271550Sian * 249271550Sian * The pinctrl bindings documentation specifically mentions that the 250271550Sian * pinctrl device itself may have a pinctrl-0 property which contains 251271550Sian * static configuration to be applied at device init time. The tree 252271550Sian * walk will automatically handle this for us when it passes through our 253271550Sian * node in the tree. 254271550Sian */ 255271550Sian fdt_pinctrl_register(dev, "fsl,pins"); 256271550Sian fdt_pinctrl_configure_tree(dev); 257271550Sian 258271550Sian return (0); 259271550Sian} 260271550Sian 261271550Sianuint32_t 262271550Sianimx_iomux_gpr_get(u_int regnum) 263271550Sian{ 264271550Sian struct iomux_softc * sc; 265271550Sian 266271550Sian sc = iomux_sc; 267271550Sian KASSERT(sc != NULL, ("%s called before attach", __FUNCTION__)); 268271550Sian KASSERT(regnum >= 0 && regnum <= sc->last_gpreg, 269271550Sian ("%s bad regnum %u, max %u", __FUNCTION__, regnum, sc->last_gpreg)); 270271550Sian 271271550Sian return (RD4(iomux_sc, regnum * 4)); 272271550Sian} 273271550Sian 274271550Sianvoid 275271550Sianimx_iomux_gpr_set(u_int regnum, uint32_t val) 276271550Sian{ 277271550Sian struct iomux_softc * sc; 278271550Sian 279271550Sian sc = iomux_sc; 280271550Sian KASSERT(sc != NULL, ("%s called before attach", __FUNCTION__)); 281271550Sian KASSERT(regnum >= 0 && regnum <= sc->last_gpreg, 282271550Sian ("%s bad regnum %u, max %u", __FUNCTION__, regnum, sc->last_gpreg)); 283271550Sian 284271550Sian WR4(iomux_sc, regnum * 4, val); 285271550Sian} 286271550Sian 287271550Sianvoid 288271550Sianimx_iomux_gpr_set_masked(u_int regnum, uint32_t clrbits, uint32_t setbits) 289271550Sian{ 290271550Sian struct iomux_softc * sc; 291271550Sian uint32_t val; 292271550Sian 293271550Sian sc = iomux_sc; 294271550Sian KASSERT(sc != NULL, ("%s called before attach", __FUNCTION__)); 295271550Sian KASSERT(regnum >= 0 && regnum <= sc->last_gpreg, 296271550Sian ("%s bad regnum %u, max %u", __FUNCTION__, regnum, sc->last_gpreg)); 297271550Sian 298271550Sian val = RD4(iomux_sc, regnum * 4); 299271550Sian val = (val & ~clrbits) | setbits; 300271550Sian WR4(iomux_sc, regnum * 4, val); 301271550Sian} 302271550Sian 303271550Sianstatic device_method_t imx_iomux_methods[] = { 304271550Sian /* Device interface */ 305271550Sian DEVMETHOD(device_probe, iomux_probe), 306271550Sian DEVMETHOD(device_attach, iomux_attach), 307271550Sian DEVMETHOD(device_detach, iomux_detach), 308271550Sian 309271550Sian /* fdt_pinctrl interface */ 310271550Sian DEVMETHOD(fdt_pinctrl_configure,iomux_configure_pins), 311271550Sian 312271550Sian DEVMETHOD_END 313271550Sian}; 314271550Sian 315271550Sianstatic driver_t imx_iomux_driver = { 316271550Sian "imx_iomux", 317271550Sian imx_iomux_methods, 318271550Sian sizeof(struct iomux_softc), 319271550Sian}; 320271550Sian 321271550Sianstatic devclass_t imx_iomux_devclass; 322271550Sian 323271550SianEARLY_DRIVER_MODULE(imx_iomux, simplebus, imx_iomux_driver, 324271550Sian imx_iomux_devclass, 0, 0, BUS_PASS_CPU + BUS_PASS_ORDER_LATE); 325271550Sian 326