Index: head/sys/arm/conf/IMX6 =================================================================== --- head/sys/arm/conf/IMX6 (revision 289536) +++ head/sys/arm/conf/IMX6 (revision 289537) @@ -1,140 +1,142 @@ # # Kernel configuration for Freescale i.MX6 systems. # # For more information on this file, please read the config(5) manual page, # and/or the handbook section on Kernel Configuration Files: # # http://www.FreeBSD.org/doc/en_US.ISO8859-1/books/handbook/kernelconfig-config.html # # The handbook is also available locally in /usr/share/doc/handbook # if you've installed the doc distribution, otherwise always see the # FreeBSD World Wide Web server (http://www.FreeBSD.org/) for the # latest information. # # An exhaustive list of options and more detailed explanations of the # device lines is also present in the ../../conf/NOTES and NOTES files. # If you are in doubt as to the purpose or necessity of a line, check first # in NOTES. # # $FreeBSD$ ident IMX6 include "std.armv6" include "../freescale/imx/std.imx6" +options ARM_INTRNG + options SOC_IMX6 options HZ=500 # Scheduling quantum is 2 milliseconds. options SCHED_ULE # ULE scheduler #options NFSD # Network Filesystem Server options INCLUDE_CONFIG_FILE # Include this file in kernel options PLATFORM options SMP # Enable multiple cores # Debugging for use in -current makeoptions DEBUG=-g # Build kernel with gdb(1) debug symbols options KDB # Enable kernel debugger support # For minimum debugger support (stable branch) use: #options KDB_TRACE # Print a stack trace for a panic # For full debugger support use this instead: options DDB # Enable the kernel debugger #options GDB # Support remote GDB. # Other debugging options... options ALT_BREAK_TO_DEBUGGER # Use to enter debugger. #options DEADLKRES # Enable the deadlock resolver #options INVARIANTS # Enable calls of extra sanity checking #options INVARIANT_SUPPORT # Extra sanity checks of internal structures, required by INVARIANTS #options WITNESS # Enable checks to detect deadlocks and cycles #options WITNESS_SKIPSPIN # Don't run witness on spinlocks for speed #options DIAGNOSTIC # NFS root from boopt/dhcp #options BOOTP #options BOOTP_NFSROOT #options BOOTP_COMPAT #options BOOTP_NFSV3 #options BOOTP_WIRED_TO=ffec0 # U-Boot stuff lives on slice 1, FreeBSD on slice 2. options ROOTDEVNAME=\"ufs:mmcsd0s2a\" # Interrupt controller device gic # Pseudo devices. device loop # Network loopback device random # Entropy device device vlan # 802.1Q VLAN support device tun # Packet tunnel. device md # Memory "disks" #device gif # IPv6 and IPv4 tunneling #device firmware # firmware assist module device ether # Ethernet support device miibus # Required for ethernet device bpf # Berkeley packet filter (required for DHCP) # General-purpose input/output device gpio # Serial (COM) ports device uart # Multi-uart driver # SDCard device sdhci # SD controller device mmc # SD/MMC protocol device mmcsd # SDCard disk device # SCSI peripherals device scbus # SCSI bus (required for ATA/SCSI) device da # Direct Access (disks) device cd # CD device pass # Passthrough device (direct ATA/SCSI access) # USB support #options USB_DEBUG # enable debug msgs device ehci # OHCI USB interface device usb # USB Bus (required) device umass # Disks/Mass storage - Requires scbus and da device uhid # "Human Interface Devices" device u3g # USB modems #device ukbd # Allow keyboard like HIDs to control console #device ums # USB mouse # USB Ethernet, requires miibus #device aue # ADMtek USB Ethernet #device axe # ASIX Electronics USB Ethernet #device cdce # Generic USB over Ethernet #device cue # CATC USB Ethernet #device kue # Kawasaki LSI USB Ethernet #device rue # RealTek RTL8150 USB Ethernet #device udav # Davicom DM9601E USB # USB Wireless #device rum # Ralink Technology RT2501USB wireless NICs # Wireless NIC cards #device wlan # 802.11 support #device wlan_wep # 802.11 WEP support #device wlan_ccmp # 802.11 CCMP support #device wlan_tkip # 802.11 TKIP support #device wlan_amrr # AMRR transmit rate control algorithm # NOTE: serial console will be disabled if syscons enabled # Uncomment following lines for framebuffer/syscons support # Wandboard has no video console support yet. #device sc #device kbdmux #options SC_DFLT_FONT # compile font in #makeoptions SC_DFLT_FONT=cp437 # Flattened Device Tree options FDT # Configure using FDT/DTB data makeoptions MODULES_EXTRA=dtb/imx6 # SoC-specific devices device ffec # Freescale Fast Ethernet Controller device fsliic # Freescale i2c/iic device iic # iic protocol device iicbus # iic bus device imxwdt # Watchdog. WARNING: can't be disabled!!! Index: head/sys/arm/freescale/imx/imx6_machdep.c =================================================================== --- head/sys/arm/freescale/imx/imx6_machdep.c (revision 289536) +++ head/sys/arm/freescale/imx/imx6_machdep.c (revision 289537) @@ -1,283 +1,285 @@ /*- * Copyright (c) 2013 Ian Lepore * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "opt_platform.h" #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "platform_if.h" struct fdt_fixup_entry fdt_fixup_table[] = { { NULL, NULL } }; static uint32_t gpio1_node; +#ifndef ARM_INTRNG /* * Work around the linux workaround for imx6 erratum 006687, in which some * ethernet interrupts don't go to the GPC and thus won't wake the system from * Wait mode. We don't use Wait mode (which halts the GIC, leaving only GPC * interrupts able to wake the system), so we don't experience the bug at all. * The linux workaround is to reconfigure GPIO1_6 as the ENET interrupt by * writing magic values to an undocumented IOMUX register, then letting the gpio * interrupt driver notify the ethernet driver. We'll be able to do all that * (even though we don't need to) once the INTRNG project is committed and the * imx_gpio driver becomes an interrupt driver. Until then, this crazy little * workaround watches for requests to map an interrupt 6 with the interrupt * controller node referring to gpio1, and it substitutes the proper ffec * interrupt number. */ static int imx6_decode_fdt(uint32_t iparent, uint32_t *intr, int *interrupt, int *trig, int *pol) { if (fdt32_to_cpu(intr[0]) == 6 && OF_node_from_xref(iparent) == gpio1_node) { *interrupt = 150; *trig = INTR_TRIGGER_CONFORM; *pol = INTR_POLARITY_CONFORM; return (0); } return (gic_decode_fdt(iparent, intr, interrupt, trig, pol)); } fdt_pic_decode_t fdt_pic_table[] = { &imx6_decode_fdt, NULL }; +#endif static vm_offset_t imx6_lastaddr(platform_t plat) { return (arm_devmap_lastaddr()); } static int imx6_attach(platform_t plat) { /* Inform the MPCore timer driver that its clock is variable. */ arm_tmr_change_frequency(ARM_TMR_FREQUENCY_VARIES); return (0); } static void imx6_late_init(platform_t plat) { /* Cache the gpio1 node handle for imx6_decode_fdt() workaround code. */ gpio1_node = OF_node_from_xref( OF_finddevice("/soc/aips-bus@02000000/gpio@0209c000")); } /* * Set up static device mappings. * * This attempts to cover the most-used devices with 1MB section mappings, which * is good for performance (uses fewer TLB entries for device access). * * ARMMP covers the interrupt controller, MPCore timers, global timer, and the * L2 cache controller. Most of the 1MB range is unused reserved space. * * AIPS1/AIPS2 cover most of the on-chip devices such as uart, spi, i2c, etc. * * Notably not mapped right now are HDMI, GPU, and other devices below ARMMP in * the memory map. When we get support for graphics it might make sense to * static map some of that area. Be careful with other things in that area such * as OCRAM that probably shouldn't be mapped as PTE_DEVICE memory. */ static int imx6_devmap_init(platform_t plat) { const uint32_t IMX6_ARMMP_PHYS = 0x00a00000; const uint32_t IMX6_ARMMP_SIZE = 0x00100000; const uint32_t IMX6_AIPS1_PHYS = 0x02000000; const uint32_t IMX6_AIPS1_SIZE = 0x00100000; const uint32_t IMX6_AIPS2_PHYS = 0x02100000; const uint32_t IMX6_AIPS2_SIZE = 0x00100000; arm_devmap_add_entry(IMX6_ARMMP_PHYS, IMX6_ARMMP_SIZE); arm_devmap_add_entry(IMX6_AIPS1_PHYS, IMX6_AIPS1_SIZE); arm_devmap_add_entry(IMX6_AIPS2_PHYS, IMX6_AIPS2_SIZE); return (0); } void cpu_reset(void) { const uint32_t IMX6_WDOG_CR_PHYS = 0x020bc000; imx_wdog_cpu_reset(IMX6_WDOG_CR_PHYS); } /* * Determine what flavor of imx6 we're running on. * * This code is based on the way u-boot does it. Information found on the web * indicates that Freescale themselves were the original source of this logic, * including the strange check for number of CPUs in the SCU configuration * register, which is apparently needed on some revisions of the SOLO. * * According to the documentation, there is such a thing as an i.MX6 Dual * (non-lite flavor). However, Freescale doesn't seem to have assigned it a * number or provided any logic to handle it in their detection code. * * Note that the ANALOG_DIGPROG and SCU configuration registers are not * documented in the chip reference manual. (SCU configuration is mentioned, * but not mapped out in detail.) I think the bottom two bits of the scu config * register may be ncpu-1. * * This hasn't been tested yet on a dual[-lite]. * * On a solo: * digprog = 0x00610001 * hwsoc = 0x00000062 * scu config = 0x00000500 * On a quad: * digprog = 0x00630002 * hwsoc = 0x00000063 * scu config = 0x00005503 */ u_int imx_soc_type() { uint32_t digprog, hwsoc; uint32_t *pcr; static u_int soctype; const vm_offset_t SCU_CONFIG_PHYSADDR = 0x00a00004; #define HWSOC_MX6SL 0x60 #define HWSOC_MX6DL 0x61 #define HWSOC_MX6SOLO 0x62 #define HWSOC_MX6Q 0x63 if (soctype != 0) return (soctype); digprog = imx6_anatop_read_4(IMX6_ANALOG_DIGPROG_SL); hwsoc = (digprog >> IMX6_ANALOG_DIGPROG_SOCTYPE_SHIFT) & IMX6_ANALOG_DIGPROG_SOCTYPE_MASK; if (hwsoc != HWSOC_MX6SL) { digprog = imx6_anatop_read_4(IMX6_ANALOG_DIGPROG); hwsoc = (digprog & IMX6_ANALOG_DIGPROG_SOCTYPE_MASK) >> IMX6_ANALOG_DIGPROG_SOCTYPE_SHIFT; /*printf("digprog = 0x%08x\n", digprog);*/ if (hwsoc == HWSOC_MX6DL) { pcr = arm_devmap_ptov(SCU_CONFIG_PHYSADDR, 4); if (pcr != NULL) { /*printf("scu config = 0x%08x\n", *pcr);*/ if ((*pcr & 0x03) == 0) { hwsoc = HWSOC_MX6SOLO; } } } } /* printf("hwsoc 0x%08x\n", hwsoc); */ switch (hwsoc) { case HWSOC_MX6SL: soctype = IMXSOC_6SL; break; case HWSOC_MX6SOLO: soctype = IMXSOC_6S; break; case HWSOC_MX6DL: soctype = IMXSOC_6DL; break; case HWSOC_MX6Q : soctype = IMXSOC_6Q; break; default: printf("imx_soc_type: Don't understand hwsoc 0x%02x, " "digprog 0x%08x; assuming IMXSOC_6Q\n", hwsoc, digprog); soctype = IMXSOC_6Q; break; } return (soctype); } /* * Early putc routine for EARLY_PRINTF support. To use, add to kernel config: * option SOCDEV_PA=0x02000000 * option SOCDEV_VA=0x02000000 * option EARLY_PRINTF * Resist the temptation to change the #if 0 to #ifdef EARLY_PRINTF here. It * makes sense now, but if multiple SOCs do that it will make early_putc another * duplicate symbol to be eliminated on the path to a generic kernel. */ #if 0 static void imx6_early_putc(int c) { volatile uint32_t * UART_STAT_REG = (uint32_t *)0x02020098; volatile uint32_t * UART_TX_REG = (uint32_t *)0x02020040; const uint32_t UART_TXRDY = (1 << 3); while ((*UART_STAT_REG & UART_TXRDY) == 0) continue; *UART_TX_REG = c; } early_putc_t *early_putc = imx6_early_putc; #endif static platform_method_t imx6_methods[] = { PLATFORMMETHOD(platform_attach, imx6_attach), PLATFORMMETHOD(platform_lastaddr, imx6_lastaddr), PLATFORMMETHOD(platform_devmap_init, imx6_devmap_init), PLATFORMMETHOD(platform_late_init, imx6_late_init), PLATFORMMETHOD_END, }; FDT_PLATFORM_DEF2(imx6, imx6s, "i.MX6 Solo", 0, "fsl,imx6s"); FDT_PLATFORM_DEF2(imx6, imx6d, "i.MX6 Dual", 0, "fsl,imx6d"); FDT_PLATFORM_DEF2(imx6, imx6q, "i.MX6 Quad", 0, "fsl,imx6q"); Index: head/sys/arm/freescale/imx/imx_gpio.c =================================================================== --- head/sys/arm/freescale/imx/imx_gpio.c (revision 289536) +++ head/sys/arm/freescale/imx/imx_gpio.c (revision 289537) @@ -1,500 +1,729 @@ /*- * Copyright (c) 2012, 2013 The FreeBSD Foundation * All rights reserved. * * This software was developed by Oleksandr Rybalko under sponsorship * from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * Freescale i.MX515 GPIO driver. */ #include __FBSDID("$FreeBSD$"); +#include "opt_platform.h" + #include #include #include #include #include #include #include #include #include +#include #include +#include #include #include #include #include #include #include #include "gpio_if.h" -#define GPIO_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) -#define GPIO_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) -#define GPIO_LOCK_INIT(_sc) mtx_init(&_sc->sc_mtx, \ - device_get_nameunit(_sc->sc_dev), "imx_gpio", MTX_DEF) -#define GPIO_LOCK_DESTROY(_sc) mtx_destroy(&_sc->sc_mtx); -#define GPIO_ASSERT_LOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_OWNED); -#define GPIO_ASSERT_UNLOCKED(_sc) mtx_assert(&_sc->sc_mtx, MA_NOTOWNED); +#ifdef ARM_INTRNG +#include "pic_if.h" +#endif #define WRITE4(_sc, _r, _v) \ bus_space_write_4((_sc)->sc_iot, (_sc)->sc_ioh, (_r), (_v)) #define READ4(_sc, _r) \ bus_space_read_4((_sc)->sc_iot, (_sc)->sc_ioh, (_r)) #define SET4(_sc, _r, _m) \ WRITE4((_sc), (_r), READ4((_sc), (_r)) | (_m)) #define CLEAR4(_sc, _r, _m) \ WRITE4((_sc), (_r), READ4((_sc), (_r)) & ~(_m)) /* Registers definition for Freescale i.MX515 GPIO controller */ #define IMX_GPIO_DR_REG 0x000 /* Pin Data */ #define IMX_GPIO_OE_REG 0x004 /* Set Pin Output */ #define IMX_GPIO_PSR_REG 0x008 /* Pad Status */ #define IMX_GPIO_ICR1_REG 0x00C /* Interrupt Configuration */ #define IMX_GPIO_ICR2_REG 0x010 /* Interrupt Configuration */ #define GPIO_ICR_COND_LOW 0 #define GPIO_ICR_COND_HIGH 1 #define GPIO_ICR_COND_RISE 2 #define GPIO_ICR_COND_FALL 3 #define IMX_GPIO_IMR_REG 0x014 /* Interrupt Mask Register */ #define IMX_GPIO_ISR_REG 0x018 /* Interrupt Status Register */ #define IMX_GPIO_EDGE_REG 0x01C /* Edge Detect Register */ #define DEFAULT_CAPS (GPIO_PIN_INPUT | GPIO_PIN_OUTPUT) #define NGPIO 32 struct imx51_gpio_softc { device_t dev; device_t sc_busdev; struct mtx sc_mtx; - struct resource *sc_res[11]; /* 1 x mem, 2 x IRQ, 8 x IRQ */ - void *gpio_ih[11]; /* 1 ptr is not a big waste */ - int sc_l_irq; /* Last irq resource */ + struct resource *sc_res[3]; /* 1 x mem, 2 x IRQ */ + void *gpio_ih[2]; bus_space_tag_t sc_iot; bus_space_handle_t sc_ioh; int gpio_npins; struct gpio_pin gpio_pins[NGPIO]; + struct arm_irqsrc *gpio_pic_irqsrc[NGPIO]; }; static struct ofw_compat_data compat_data[] = { {"fsl,imx6q-gpio", 1}, {"fsl,imx53-gpio", 1}, {"fsl,imx51-gpio", 1}, {NULL, 0} }; static struct resource_spec imx_gpio_spec[] = { { SYS_RES_MEMORY, 0, RF_ACTIVE }, { SYS_RES_IRQ, 0, RF_ACTIVE }, { SYS_RES_IRQ, 1, RF_ACTIVE }, { -1, 0 } }; -static struct resource_spec imx_gpio0irq_spec[] = { - { SYS_RES_IRQ, 2, RF_ACTIVE }, - { SYS_RES_IRQ, 3, RF_ACTIVE }, - { SYS_RES_IRQ, 4, RF_ACTIVE }, - { SYS_RES_IRQ, 5, RF_ACTIVE }, - { SYS_RES_IRQ, 6, RF_ACTIVE }, - { SYS_RES_IRQ, 7, RF_ACTIVE }, - { SYS_RES_IRQ, 8, RF_ACTIVE }, - { SYS_RES_IRQ, 9, RF_ACTIVE }, - { -1, 0 } -}; - /* * Helpers */ static void imx51_gpio_pin_configure(struct imx51_gpio_softc *, struct gpio_pin *, uint32_t); /* * Driver stuff */ static int imx51_gpio_probe(device_t); static int imx51_gpio_attach(device_t); static int imx51_gpio_detach(device_t); -static int imx51_gpio_intr(void *); /* * GPIO interface */ static device_t imx51_gpio_get_bus(device_t); static int imx51_gpio_pin_max(device_t, int *); static int imx51_gpio_pin_getcaps(device_t, uint32_t, uint32_t *); static int imx51_gpio_pin_getflags(device_t, uint32_t, uint32_t *); static int imx51_gpio_pin_getname(device_t, uint32_t, char *); static int imx51_gpio_pin_setflags(device_t, uint32_t, uint32_t); static int imx51_gpio_pin_set(device_t, uint32_t, unsigned int); static int imx51_gpio_pin_get(device_t, uint32_t, unsigned int *); static int imx51_gpio_pin_toggle(device_t, uint32_t pin); +#ifdef ARM_INTRNG +/* + * this is teardown_intr + */ static void +gpio_pic_disable_intr(device_t dev, struct arm_irqsrc *isrc) +{ + struct imx51_gpio_softc *sc; + u_int irq; + + sc = device_get_softc(dev); + irq = isrc->isrc_data; + + // XXX Not sure this is necessary + mtx_lock_spin(&sc->sc_mtx); + CLEAR4(sc, IMX_GPIO_IMR_REG, (1U << irq)); + WRITE4(sc, IMX_GPIO_ISR_REG, (1U << irq)); + mtx_unlock_spin(&sc->sc_mtx); +} + +/* + * this is mask_intr + */ +static void +gpio_pic_disable_source(device_t dev, struct arm_irqsrc *isrc) +{ + struct imx51_gpio_softc *sc; + + sc = device_get_softc(dev); + + mtx_lock_spin(&sc->sc_mtx); + CLEAR4(sc, IMX_GPIO_IMR_REG, (1U << isrc->isrc_data)); + mtx_unlock_spin(&sc->sc_mtx); +} + +/* + * this is setup_intr + */ +static void +gpio_pic_enable_intr(device_t dev, struct arm_irqsrc *isrc) +{ + struct imx51_gpio_softc *sc; + int icfg; + u_int irq, reg, shift, wrk; + + sc = device_get_softc(dev); + + if (isrc->isrc_trig == INTR_TRIGGER_LEVEL) { + if (isrc->isrc_pol == INTR_POLARITY_LOW) + icfg = GPIO_ICR_COND_LOW; + else + icfg = GPIO_ICR_COND_HIGH; + } else { + if (isrc->isrc_pol == INTR_POLARITY_HIGH) + icfg = GPIO_ICR_COND_FALL; + else + icfg = GPIO_ICR_COND_RISE; + } + + irq = isrc->isrc_data; + if (irq < 16) { + reg = IMX_GPIO_ICR1_REG; + shift = 2 * irq; + } else { + reg = IMX_GPIO_ICR2_REG; + shift = 2 * (irq - 16); + } + + mtx_lock_spin(&sc->sc_mtx); + CLEAR4(sc, IMX_GPIO_IMR_REG, (1U << irq)); + WRITE4(sc, IMX_GPIO_ISR_REG, (1U << irq)); + wrk = READ4(sc, reg); + wrk &= ~(0x03 << shift); + wrk |= icfg << shift; + WRITE4(sc, reg, wrk); + mtx_unlock_spin(&sc->sc_mtx); +} + +/* + * this is unmask_intr + */ +static void +gpio_pic_enable_source(device_t dev, struct arm_irqsrc *isrc) +{ + struct imx51_gpio_softc *sc; + + sc = device_get_softc(dev); + + mtx_lock_spin(&sc->sc_mtx); + SET4(sc, IMX_GPIO_IMR_REG, (1U << isrc->isrc_data)); + mtx_unlock_spin(&sc->sc_mtx); +} + +static void +gpio_pic_post_filter(device_t dev, struct arm_irqsrc *isrc) +{ + struct imx51_gpio_softc *sc; + + sc = device_get_softc(dev); + + arm_irq_memory_barrier(0); + /* EOI. W1C reg so no r-m-w, no locking needed. */ + WRITE4(sc, IMX_GPIO_ISR_REG, (1U << isrc->isrc_data)); +} + +static void +gpio_pic_post_ithread(device_t dev, struct arm_irqsrc *isrc) +{ + + arm_irq_memory_barrier(0); + gpio_pic_enable_source(dev, isrc); +} + +static void +gpio_pic_pre_ithread(device_t dev, struct arm_irqsrc *isrc) +{ + + gpio_pic_disable_source(dev, isrc); +} + +/* + * intrng calls this to make a new isrc known to us. + */ +static int +gpio_pic_register(device_t dev, struct arm_irqsrc *isrc, boolean_t *is_percpu) +{ + struct imx51_gpio_softc *sc; + u_int irq, tripol; + + sc = device_get_softc(dev); + + /* + * From devicetree/bindings/gpio/fsl-imx-gpio.txt: + * #interrupt-cells: 2. The first cell is the GPIO number. The second + * cell bits[3:0] is used to specify trigger type and level flags: + * 1 = low-to-high edge triggered. + * 2 = high-to-low edge triggered. + * 4 = active high level-sensitive. + * 8 = active low level-sensitive. + * We can do any single one of these modes, but nothing in combo. + */ + + if (isrc->isrc_ncells != 2) { + device_printf(sc->dev, "Invalid #interrupt-cells"); + return (EINVAL); + } + + irq = isrc->isrc_cells[0]; + tripol = isrc->isrc_cells[1]; + if (irq >= sc->gpio_npins) { + device_printf(sc->dev, "Invalid interrupt number %d", irq); + return (EINVAL); + } + switch (tripol) + { + case 1: + isrc->isrc_trig = INTR_TRIGGER_EDGE; + isrc->isrc_pol = INTR_POLARITY_HIGH; + break; + case 2: + isrc->isrc_trig = INTR_TRIGGER_EDGE; + isrc->isrc_pol = INTR_POLARITY_LOW; + break; + case 4: + isrc->isrc_trig = INTR_TRIGGER_LEVEL; + isrc->isrc_pol = INTR_POLARITY_HIGH; + break; + case 8: + isrc->isrc_trig = INTR_TRIGGER_LEVEL; + isrc->isrc_pol = INTR_POLARITY_LOW; + break; + default: + device_printf(sc->dev, "unsupported trigger/polarity 0x%2x\n", + tripol); + return (ENOTSUP); + } + isrc->isrc_nspc_type = ARM_IRQ_NSPC_PLAIN; + isrc->isrc_nspc_num = irq; + + /* + * 1. The link between ISRC and controller must be set atomically. + * 2. Just do things only once in rare case when consumers + * of shared interrupt came here at the same moment. + */ + mtx_lock_spin(&sc->sc_mtx); + if (sc->gpio_pic_irqsrc[irq] != NULL) { + mtx_unlock_spin(&sc->sc_mtx); + return (sc->gpio_pic_irqsrc[irq] == isrc ? 0 : EEXIST); + } + sc->gpio_pic_irqsrc[irq] = isrc; + isrc->isrc_data = irq; + mtx_unlock_spin(&sc->sc_mtx); + + arm_irq_set_name(isrc, "%s,%u", device_get_nameunit(sc->dev), irq); + return (0); +} + +static int +gpio_pic_unregister(device_t dev, struct arm_irqsrc *isrc) +{ + struct imx51_gpio_softc *sc; + u_int irq; + + sc = device_get_softc(dev); + + mtx_lock_spin(&sc->sc_mtx); + irq = isrc->isrc_data; + if (sc->gpio_pic_irqsrc[irq] != isrc) { + mtx_unlock_spin(&sc->sc_mtx); + return (sc->gpio_pic_irqsrc[irq] == NULL ? 0 : EINVAL); + } + sc->gpio_pic_irqsrc[irq] = NULL; + isrc->isrc_data = 0; + mtx_unlock_spin(&sc->sc_mtx); + + arm_irq_set_name(isrc, ""); + return (0); +} + +static int +gpio_pic_filter(void *arg) +{ + struct imx51_gpio_softc *sc; + uint32_t i, interrupts; + + sc = arg; + mtx_lock_spin(&sc->sc_mtx); + interrupts = READ4(sc, IMX_GPIO_ISR_REG) & READ4(sc, IMX_GPIO_IMR_REG); + mtx_unlock_spin(&sc->sc_mtx); + + for (i = 0; interrupts != 0; i++, interrupts >>= 1) { + if ((interrupts & 0x1) == 0) + continue; + if (sc->gpio_pic_irqsrc[i]) + arm_irq_dispatch(sc->gpio_pic_irqsrc[i], curthread->td_intr_frame); + else + device_printf(sc->dev, "spurious interrupt %d\n", i); + } + + return (FILTER_HANDLED); +} +#endif + +/* + * + */ +static void imx51_gpio_pin_configure(struct imx51_gpio_softc *sc, struct gpio_pin *pin, unsigned int flags) { - GPIO_LOCK(sc); + mtx_lock_spin(&sc->sc_mtx); /* * Manage input/output */ if (flags & (GPIO_PIN_INPUT|GPIO_PIN_OUTPUT)) { pin->gp_flags &= ~(GPIO_PIN_INPUT|GPIO_PIN_OUTPUT); if (flags & GPIO_PIN_OUTPUT) { pin->gp_flags |= GPIO_PIN_OUTPUT; - SET4(sc, IMX_GPIO_OE_REG, (1 << pin->gp_pin)); + SET4(sc, IMX_GPIO_OE_REG, (1U << pin->gp_pin)); } else { pin->gp_flags |= GPIO_PIN_INPUT; - CLEAR4(sc, IMX_GPIO_OE_REG, (1 << pin->gp_pin)); + CLEAR4(sc, IMX_GPIO_OE_REG, (1U << pin->gp_pin)); } } - GPIO_UNLOCK(sc); + mtx_unlock_spin(&sc->sc_mtx); } static device_t imx51_gpio_get_bus(device_t dev) { struct imx51_gpio_softc *sc; sc = device_get_softc(dev); return (sc->sc_busdev); } static int imx51_gpio_pin_max(device_t dev, int *maxpin) { + struct imx51_gpio_softc *sc; - *maxpin = NGPIO - 1; + sc = device_get_softc(dev); + *maxpin = sc->gpio_npins - 1; + return (0); } static int imx51_gpio_pin_getcaps(device_t dev, uint32_t pin, uint32_t *caps) { struct imx51_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); - GPIO_LOCK(sc); + mtx_lock_spin(&sc->sc_mtx); *caps = sc->gpio_pins[i].gp_caps; - GPIO_UNLOCK(sc); + mtx_unlock_spin(&sc->sc_mtx); return (0); } static int imx51_gpio_pin_getflags(device_t dev, uint32_t pin, uint32_t *flags) { struct imx51_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); - GPIO_LOCK(sc); + mtx_lock_spin(&sc->sc_mtx); *flags = sc->gpio_pins[i].gp_flags; - GPIO_UNLOCK(sc); + mtx_unlock_spin(&sc->sc_mtx); return (0); } static int imx51_gpio_pin_getname(device_t dev, uint32_t pin, char *name) { struct imx51_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); - GPIO_LOCK(sc); + mtx_lock_spin(&sc->sc_mtx); memcpy(name, sc->gpio_pins[i].gp_name, GPIOMAXNAME); - GPIO_UNLOCK(sc); + mtx_unlock_spin(&sc->sc_mtx); return (0); } static int imx51_gpio_pin_setflags(device_t dev, uint32_t pin, uint32_t flags) { struct imx51_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); imx51_gpio_pin_configure(sc, &sc->gpio_pins[i], flags); return (0); } static int imx51_gpio_pin_set(device_t dev, uint32_t pin, unsigned int value) { struct imx51_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); - GPIO_LOCK(sc); + mtx_lock_spin(&sc->sc_mtx); if (value) - SET4(sc, IMX_GPIO_DR_REG, (1 << i)); + SET4(sc, IMX_GPIO_DR_REG, (1U << i)); else - CLEAR4(sc, IMX_GPIO_DR_REG, (1 << i)); - GPIO_UNLOCK(sc); + CLEAR4(sc, IMX_GPIO_DR_REG, (1U << i)); + mtx_unlock_spin(&sc->sc_mtx); return (0); } static int imx51_gpio_pin_get(device_t dev, uint32_t pin, unsigned int *val) { struct imx51_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); - GPIO_LOCK(sc); + mtx_lock_spin(&sc->sc_mtx); *val = (READ4(sc, IMX_GPIO_DR_REG) >> i) & 1; - GPIO_UNLOCK(sc); + mtx_unlock_spin(&sc->sc_mtx); return (0); } static int imx51_gpio_pin_toggle(device_t dev, uint32_t pin) { struct imx51_gpio_softc *sc; int i; sc = device_get_softc(dev); for (i = 0; i < sc->gpio_npins; i++) { if (sc->gpio_pins[i].gp_pin == pin) break; } if (i >= sc->gpio_npins) return (EINVAL); - GPIO_LOCK(sc); + mtx_lock_spin(&sc->sc_mtx); WRITE4(sc, IMX_GPIO_DR_REG, - (READ4(sc, IMX_GPIO_DR_REG) ^ (1 << i))); - GPIO_UNLOCK(sc); + (READ4(sc, IMX_GPIO_DR_REG) ^ (1U << i))); + mtx_unlock_spin(&sc->sc_mtx); return (0); } static int -imx51_gpio_intr(void *arg) -{ - struct imx51_gpio_softc *sc; - uint32_t input, value; - - sc = arg; - input = READ4(sc, IMX_GPIO_ISR_REG); - value = input & READ4(sc, IMX_GPIO_IMR_REG); - WRITE4(sc, IMX_GPIO_ISR_REG, input); - - if (!value) - goto intr_done; - - /* TODO: interrupt handling */ - -intr_done: - return (FILTER_HANDLED); -} - -static int imx51_gpio_probe(device_t dev) { if (!ofw_bus_status_okay(dev)) return (ENXIO); if (ofw_bus_search_compatible(dev, compat_data)->ocd_data != 0) { device_set_desc(dev, "Freescale i.MX GPIO Controller"); return (BUS_PROBE_DEFAULT); } return (ENXIO); } static int imx51_gpio_attach(device_t dev) { struct imx51_gpio_softc *sc; - int i, irq; + int i, irq, unit; sc = device_get_softc(dev); - mtx_init(&sc->sc_mtx, device_get_nameunit(dev), NULL, MTX_DEF); + sc->dev = dev; + sc->gpio_npins = NGPIO; + mtx_init(&sc->sc_mtx, device_get_nameunit(sc->dev), NULL, MTX_SPIN); + if (bus_alloc_resources(dev, imx_gpio_spec, sc->sc_res)) { device_printf(dev, "could not allocate resources\n"); bus_release_resources(dev, imx_gpio_spec, sc->sc_res); mtx_destroy(&sc->sc_mtx); return (ENXIO); } - sc->dev = dev; - sc->gpio_npins = NGPIO; - sc->sc_l_irq = 2; sc->sc_iot = rman_get_bustag(sc->sc_res[0]); sc->sc_ioh = rman_get_bushandle(sc->sc_res[0]); - - if (bus_alloc_resources(dev, imx_gpio0irq_spec, &sc->sc_res[3]) == 0) { - /* - * First GPIO unit able to serve +8 interrupts for 8 first - * pins. - */ - sc->sc_l_irq = 10; - } - - for (irq = 1; irq <= sc->sc_l_irq; irq ++) { - if ((bus_setup_intr(dev, sc->sc_res[irq], INTR_TYPE_MISC, - imx51_gpio_intr, NULL, sc, &sc->gpio_ih[irq]))) { + /* + * Mask off all interrupts in hardware, then set up interrupt handling. + */ + WRITE4(sc, IMX_GPIO_IMR_REG, 0); + for (irq = 0; irq < 2; irq++) { +#ifdef ARM_INTRNG + if ((bus_setup_intr(dev, sc->sc_res[1 + irq], INTR_TYPE_CLK, + gpio_pic_filter, NULL, sc, &sc->gpio_ih[irq]))) { device_printf(dev, "WARNING: unable to register interrupt handler\n"); imx51_gpio_detach(dev); return (ENXIO); } +#endif } + unit = device_get_unit(dev); for (i = 0; i < sc->gpio_npins; i++) { sc->gpio_pins[i].gp_pin = i; sc->gpio_pins[i].gp_caps = DEFAULT_CAPS; sc->gpio_pins[i].gp_flags = - (READ4(sc, IMX_GPIO_OE_REG) & (1 << i)) ? GPIO_PIN_OUTPUT: + (READ4(sc, IMX_GPIO_OE_REG) & (1U << i)) ? GPIO_PIN_OUTPUT : GPIO_PIN_INPUT; snprintf(sc->gpio_pins[i].gp_name, GPIOMAXNAME, - "imx_gpio%d.%d", device_get_unit(dev), i); + "imx_gpio%d.%d", unit, i); } + +#ifdef ARM_INTRNG + arm_pic_register(dev, OF_xref_from_node(ofw_bus_get_node(dev))); +#endif sc->sc_busdev = gpiobus_attach_bus(dev); + if (sc->sc_busdev == NULL) { imx51_gpio_detach(dev); return (ENXIO); } return (0); } static int imx51_gpio_detach(device_t dev) { int irq; struct imx51_gpio_softc *sc; sc = device_get_softc(dev); - KASSERT(mtx_initialized(&sc->sc_mtx), ("gpio mutex not initialized")); - gpiobus_detach_bus(dev); - for (irq = 1; irq <= sc->sc_l_irq; irq ++) { + for (irq = 1; irq <= 2; irq++) { if (sc->gpio_ih[irq]) bus_teardown_intr(dev, sc->sc_res[irq], sc->gpio_ih[irq]); } - bus_release_resources(dev, imx_gpio0irq_spec, &sc->sc_res[3]); bus_release_resources(dev, imx_gpio_spec, sc->sc_res); mtx_destroy(&sc->sc_mtx); return(0); } static device_method_t imx51_gpio_methods[] = { DEVMETHOD(device_probe, imx51_gpio_probe), DEVMETHOD(device_attach, imx51_gpio_attach), DEVMETHOD(device_detach, imx51_gpio_detach), + +#ifdef ARM_INTRNG + /* Interrupt controller interface */ + DEVMETHOD(pic_disable_intr, gpio_pic_disable_intr), + DEVMETHOD(pic_disable_source, gpio_pic_disable_source), + DEVMETHOD(pic_enable_intr, gpio_pic_enable_intr), + DEVMETHOD(pic_enable_source, gpio_pic_enable_source), + DEVMETHOD(pic_post_filter, gpio_pic_post_filter), + DEVMETHOD(pic_post_ithread, gpio_pic_post_ithread), + DEVMETHOD(pic_pre_ithread, gpio_pic_pre_ithread), + DEVMETHOD(pic_register, gpio_pic_register), + DEVMETHOD(pic_unregister, gpio_pic_unregister), +#endif /* GPIO protocol */ DEVMETHOD(gpio_get_bus, imx51_gpio_get_bus), DEVMETHOD(gpio_pin_max, imx51_gpio_pin_max), DEVMETHOD(gpio_pin_getname, imx51_gpio_pin_getname), DEVMETHOD(gpio_pin_getflags, imx51_gpio_pin_getflags), DEVMETHOD(gpio_pin_getcaps, imx51_gpio_pin_getcaps), DEVMETHOD(gpio_pin_setflags, imx51_gpio_pin_setflags), DEVMETHOD(gpio_pin_get, imx51_gpio_pin_get), DEVMETHOD(gpio_pin_set, imx51_gpio_pin_set), DEVMETHOD(gpio_pin_toggle, imx51_gpio_pin_toggle), {0, 0}, }; static driver_t imx51_gpio_driver = { "gpio", imx51_gpio_methods, sizeof(struct imx51_gpio_softc), }; static devclass_t imx51_gpio_devclass; DRIVER_MODULE(imx51_gpio, simplebus, imx51_gpio_driver, imx51_gpio_devclass, 0, 0);