diff options
author | Patrick Wildt <patrick@cvs.openbsd.org> | 2013-09-06 20:45:55 +0000 |
---|---|---|
committer | Patrick Wildt <patrick@cvs.openbsd.org> | 2013-09-06 20:45:55 +0000 |
commit | cbccf2bd320a00fd2d28064002c74254167e613c (patch) | |
tree | 323c94f9972ad75f3f2318d9cac70847e3400a66 /sys | |
parent | aba4f7ffe403094c2f9d96695a052a49d4a82890 (diff) |
Support for FreeScale's i.MX6 SoC.
Diffstat (limited to 'sys')
27 files changed, 7133 insertions, 1 deletions
diff --git a/sys/arch/armv7/conf/GENERIC-IMX b/sys/arch/armv7/conf/GENERIC-IMX new file mode 100644 index 00000000000..951f469aaf3 --- /dev/null +++ b/sys/arch/armv7/conf/GENERIC-IMX @@ -0,0 +1,147 @@ +# $OpenBSD: GENERIC-IMX,v 1.1 2013/09/06 20:45:53 patrick Exp $ +# +# GENERIC machine description file +# +# This machine description file is used to generate the default OpenBSD +# kernel. The generic kernel does not include all options, subsystems +# and device drivers, but should be useful for most applications. +# +# The machine description file can be customised for your specific +# machine to reduce the kernel size and improve its performance. +# +# For further information on compiling OpenBSD kernels, see the config(8) +# man page. +# +# For further information on hardware support for this architecture, see +# the intro(4) man page. For further information about kernel options +# for this architecture, see the options(4) man page. For an explanation +# of each device driver in this file see the section 4 man page for the +# device. + +machine armv7 arm +include "../../../conf/GENERIC" +options TIMEZONE=0 # time zone to adjust RTC time by +options DST=0 # daylight saving time used by RTC +#options NFSCLIENT # NFS + +makeoptions KERNEL_BASE_PHYS="0x10800000" +makeoptions KERNEL_BASE_VIRT="0xc0800000" +options BUILD_STARTUP_PAGETABLE +options STARTUP_PAGETABLE_ADDR=0x10200000 +options SDRAM_START="0x10000000" + +#options APERTURE + +#options BOOT_CONFIG # boot-time kernel config + +# estimated number of users + +maxusers 32 + +# CPU options +options CPU_ARMv7 # Support the ARMv7 +makeoptions CPUFLAGS="-mcpu=armv5" # dont have gcc v7 support yet. + +#option WSDISPLAY_COMPAT_USL # VT handling +#option WSDISPLAY_COMPAT_RAWKBD # can get raw scancodes +option WSDISPLAY_DEFAULTSCREENS=1 +#option WSDISPLAY_COMPAT_PCVT # emulate some ioctls + +config bsd root on sd0a swap on sd0b + +# The main bus device +mainbus0 at root +cpu0 at mainbus? + +# Cortex-A9 (iMX6) +cortex0 at mainbus? +ampintc* at cortex? +amptimer* at cortex? +armliicc* at cortex? + +# iMX SoC +imx0 at mainbus? + +# iMX on-chip devices +imxccm* at imx? # clock control module +imxiomuxc* at imx? # iomux controller +imxdog* at imx? # watchdog timer +imxocotp* at imx? # on-chip otp controller +imxgpio* at imx? # user-visible GPIO pins? +imxesdhc* at imx? # SDHC controller +imxenet* at imx? # ethernet +imxuart* at imx? # onboard uarts +imxiic* at imx? # i2c + +ahci* at imx? # AHCI/SATA + +ehci* at imx? # EHCI (shim) +usb* at ehci? + +# USB bus support +usb* at ehci? flags 0x1 +uhub* at usb? +uhub* at uhub? + +uhub* at usb? # USB Hubs +uhub* at uhub? # USB Hubs +umodem* at uhub? # USB Modems/Serial +ucom* at umodem? +uvisor* at uhub? # Handspring Visor +ucom* at uvisor? +uvscom* at uhub? # SUNTAC Slipper U VS-10U serial +ucom* at uvscom? +ubsa* at uhub? # Belkin serial adapter +ucom* at ubsa? +uftdi* at uhub? # FTDI FT8U100AX serial adapter +ucom* at uftdi? +uplcom* at uhub? # I/O DATA USB-RSAQ2 serial adapter +ucom* at uplcom? +umct* at uhub? # MCT USB-RS232 serial adapter +ucom* at umct? +uaudio* at uhub? # USB Audio +audio* at uaudio? +umidi* at uhub? # USB MIDI +midi* at umidi? +ulpt* at uhub? # USB Printers +umass* at uhub? # USB Mass Storage devices +uhidev* at uhub? # Human Interface Devices +ums* at uhidev? # USB mouse +wsmouse* at ums? mux 0 +ukbd* at uhidev? # USB keyboard +wskbd* at ukbd? mux 1 +uhid* at uhidev? # USB generic HID support +aue* at uhub? # ADMtek AN986 Pegasus Ethernet +atu* at uhub? # Atmel AT76c50x based 802.11b +axe* at uhub? # ASIX Electronics AX88172 USB Ethernet +cue* at uhub? # CATC USB-EL1201A based Ethernet +kue* at uhub? # Kawasaki KL5KUSB101B based Ethernet +smsc* at uhub? # SMSC LAN95xx Ethernet +cdce* at uhub? # CDC Ethernet +upl* at uhub? # Prolific PL2301/PL2302 host-to-host `network' +udav* at uhub? # Davicom DM9601 based Ethernet +url* at uhub? # Realtek RTL8150L based adapters +wi* at uhub? # WaveLAN IEEE 802.11DS +urio* at uhub? # Diamond Multimedia Rio 500 +#uyap* at uhub? # Y@P firmware loader +udsbr* at uhub? # D-Link DSB-R100 radio +radio* at udsbr? # USB radio +#ubt* at uhub? disable # USB Bluetooth +ugen* at uhub? # USB Generic driver + +ukphy* at mii? # "unknown" PHYs + +scsibus* at scsi? +sd* at scsibus? +st* at scsibus? +cd* at scsibus? +ch* at scsibus? +uk* at scsibus? + +sdmmc* at imxesdhc? # SD/MMC bus + +# I2C bus support +iic* at imxiic? + +# Pseudo-Devices +pseudo-device hotplug 1 # devices hot plugging diff --git a/sys/arch/armv7/conf/RAMDISK-IMX b/sys/arch/armv7/conf/RAMDISK-IMX new file mode 100644 index 00000000000..73345e25195 --- /dev/null +++ b/sys/arch/armv7/conf/RAMDISK-IMX @@ -0,0 +1,164 @@ +# $OpenBSD: RAMDISK-IMX,v 1.1 2013/09/06 20:45:53 patrick Exp $ +# +# GENERIC machine description file +# +# This machine description file is used to generate the default OpenBSD +# kernel. The generic kernel does not include all options, subsystems +# and device drivers, but should be useful for most applications. +# +# The machine description file can be customised for your specific +# machine to reduce the kernel size and improve its performance. +# +# For further information on compiling OpenBSD kernels, see the config(8) +# man page. +# +# For further information on hardware support for this architecture, see +# the intro(4) man page. For further information about kernel options +# for this architecture, see the options(4) man page. For an explanation +# of each device driver in this file see the section 4 man page for the +# device. + +machine armv7 arm + +options TIMEZONE=0 # time zone to adjust RTC time by +options DST=0 # daylight saving time used by RTC +options FFS # UFS +#options CD9660 # ISO 9660 + Rock Ridge file system +options MSDOSFS # MS-DOS file system +options INET # IP + ICMP + TCP + UDP +options INET6 # IPv6 (needs INET) +options DDB # kernel debugger +options DIAGNOSTIC +option DDB_SAFE_CONSOLE +options FIFO # FIFOs; RECOMMENDED +#options NFSCLIENT # NFS + +makeoptions KERNEL_BASE_PHYS="0x10800000" +makeoptions KERNEL_BASE_VIRT="0xc0800000" +options BUILD_STARTUP_PAGETABLE +options STARTUP_PAGETABLE_ADDR=0x10200000 +options SDRAM_START="0x10000000" + +options APERTURE + +# estimated number of users + +maxusers 32 + +# CPU options +options CPU_ARMv7 # Support the ARMv7 +makeoptions CPUFLAGS="-mcpu=armv5" # dont have gcc v7 support yet. + +#option SYSCALL_DEBUG + +#option WSDISPLAY_COMPAT_USL # VT handling +#option WSDISPLAY_COMPAT_RAWKBD # can get raw scancodes +option WSDISPLAY_DEFAULTSCREENS=1 +#option WSDISPLAY_COMPAT_PCVT # emulate some ioctls + +config bsd root on rd0a swap on rd0b + +# The main bus device +mainbus0 at root +cpu0 at mainbus? + +# Cortex-A9 (iMX6) +cortex0 at mainbus? +ampintc* at cortex? +amptimer* at cortex? +armliicc* at cortex? + +# iMX SoC +imx0 at mainbus? + +# iMX on-chip devices +imxccm* at imx? # clock control module +imxiomuxc* at imx? # iomux controller +imxdog* at imx? # watchdog timer +imxocotp* at imx? # on-chip otp controller +imxgpio* at imx? # user-visible GPIO pins? +imxesdhc* at imx? # SDHC controller +imxenet* at imx? # ethernet +imxuart* at imx? # onboard uarts +imxiic* at imx? # i2c + +ahci* at imx? # AHCI/SATA + +ehci* at imx? # EHCI (shim) +usb* at ehci? + +# USB bus support +usb* at ehci? flags 0x1 +uhub* at usb? +uhub* at uhub? + +uhub* at usb? # USB Hubs +uhub* at uhub? # USB Hubs +umodem* at uhub? # USB Modems/Serial +ucom* at umodem? +uvisor* at uhub? # Handspring Visor +ucom* at uvisor? +uvscom* at uhub? # SUNTAC Slipper U VS-10U serial +ucom* at uvscom? +ubsa* at uhub? # Belkin serial adapter +ucom* at ubsa? +uftdi* at uhub? # FTDI FT8U100AX serial adapter +ucom* at uftdi? +uplcom* at uhub? # I/O DATA USB-RSAQ2 serial adapter +ucom* at uplcom? +umct* at uhub? # MCT USB-RS232 serial adapter +ucom* at umct? +uaudio* at uhub? # USB Audio +audio* at uaudio? +umidi* at uhub? # USB MIDI +midi* at umidi? +ulpt* at uhub? # USB Printers +umass* at uhub? # USB Mass Storage devices +uhidev* at uhub? # Human Interface Devices +ums* at uhidev? # USB mouse +wsmouse* at ums? mux 0 +ukbd* at uhidev? # USB keyboard +wskbd* at ukbd? mux 1 +uhid* at uhidev? # USB generic HID support +aue* at uhub? # ADMtek AN986 Pegasus Ethernet +atu* at uhub? # Atmel AT76c50x based 802.11b +axe* at uhub? # ASIX Electronics AX88172 USB Ethernet +cue* at uhub? # CATC USB-EL1201A based Ethernet +kue* at uhub? # Kawasaki KL5KUSB101B based Ethernet +smsc* at uhub? # SMSC LAN95xx Ethernet +cdce* at uhub? # CDC Ethernet +upl* at uhub? # Prolific PL2301/PL2302 host-to-host `network' +udav* at uhub? # Davicom DM9601 based Ethernet +url* at uhub? # Realtek RTL8150L based adapters +wi* at uhub? # WaveLAN IEEE 802.11DS +urio* at uhub? # Diamond Multimedia Rio 500 +#uyap* at uhub? # Y@P firmware loader +udsbr* at uhub? # D-Link DSB-R100 radio +radio* at udsbr? # USB radio +#ubt* at uhub? disable # USB Bluetooth +ugen* at uhub? # USB Generic driver + +ukphy* at mii? # "unknown" PHYs + +scsibus* at scsi? +sd* at scsibus? +st* at scsibus? +cd* at scsibus? +ch* at scsibus? +uk* at scsibus? + +sdmmc* at imxesdhc? # SD/MMC bus + +# I2C bus support +iic* at imxiic? + +# Pseudo-Devices +#pseudo-device wsmux 2 # mouse & keyboard multiplexor +#pseudo-device crypto 1 +pseudo-device loop 1 # network loopback +pseudo-device bpfilter 1 # packet filter +pseudo-device rd 1 # ram disk + +# RAMDISK stuff +options MINIROOTSIZE=5120 +options RAMDISK_HOOKS diff --git a/sys/arch/armv7/conf/files.armv7 b/sys/arch/armv7/conf/files.armv7 index 596d07ad382..9aeee82b9b2 100644 --- a/sys/arch/armv7/conf/files.armv7 +++ b/sys/arch/armv7/conf/files.armv7 @@ -1,4 +1,4 @@ -# $OpenBSD: files.armv7,v 1.2 2013/09/05 11:48:42 patrick Exp $ +# $OpenBSD: files.armv7,v 1.3 2013/09/06 20:45:53 patrick Exp $ maxpartitions 16 maxusers 2 8 64 @@ -57,4 +57,5 @@ include "dev/bluetooth/files.bluetooth" include "dev/gpio/files.gpio" # SoC includes +include "arch/armv7/imx/files.imx" include "arch/armv7/omap/files.omap" diff --git a/sys/arch/armv7/imx/files.imx b/sys/arch/armv7/imx/files.imx new file mode 100644 index 00000000000..a75f7414958 --- /dev/null +++ b/sys/arch/armv7/imx/files.imx @@ -0,0 +1,51 @@ +# $OpenBSD: files.imx,v 1.1 2013/09/06 20:45:53 patrick Exp $ + +define imx {} +device imx: imx +attach imx at mainbus +file arch/armv7/imx/imx_machdep.c imx +file arch/armv7/imx/imx.c imx +file arch/armv7/imx/imx6.c imx + +# serial ports +device imxuart +attach imxuart at imx +file arch/armv7/imx/imxuart.c imxuart + +device imxccm +attach imxccm at imx +file arch/armv7/imx/imxccm.c imxccm + +device imxiomuxc +attach imxiomuxc at imx +file arch/armv7/imx/imxiomuxc.c imxiomuxc + +device imxdog +attach imxdog at imx +file arch/armv7/imx/imxdog.c imxdog + +device imxocotp +attach imxocotp at imx +file arch/armv7/imx/imxocotp.c imxocotp + +device imxgpio +attach imxgpio at imx +file arch/armv7/imx/imxgpio.c imxgpio + +device imxiic: i2cbus +attach imxiic at imx +file arch/armv7/imx/imxiic.c imxiic + +device imxenet: ether, ifnet, mii, ifmedia +attach imxenet at imx +file arch/armv7/imx/imxenet.c imxenet + +attach ehci at imx with imxehci +file arch/armv7/imx/imxehci.c imxehci + +device imxesdhc: sdmmcbus +attach imxesdhc at imx +file arch/armv7/imx/imxesdhc.c imxesdhc + +attach ahci at imx with imxahci +file arch/armv7/imx/imxahci.c imxahci diff --git a/sys/arch/armv7/imx/imx.c b/sys/arch/armv7/imx/imx.c new file mode 100644 index 00000000000..99014634976 --- /dev/null +++ b/sys/arch/armv7/imx/imx.c @@ -0,0 +1,201 @@ +/* $OpenBSD: imx.c,v 1.1 2013/09/06 20:45:53 patrick Exp $ */ +/* + * Copyright (c) 2005,2008 Dale Rahn <drahn@openbsd.com> + * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/malloc.h> +#include <sys/reboot.h> +#define _ARM32_BUS_DMA_PRIVATE +#include <machine/bus.h> +#include <arch/arm/armv7/armv7var.h> +#include <armv7/imx/imxvar.h> + +struct arm32_bus_dma_tag imx_bus_dma_tag = { + 0, + 0, + NULL, + _bus_dmamap_create, + _bus_dmamap_destroy, + _bus_dmamap_load, + _bus_dmamap_load_mbuf, + _bus_dmamap_load_uio, + _bus_dmamap_load_raw, + _bus_dmamap_unload, + _bus_dmamap_sync, + _bus_dmamem_alloc, + _bus_dmamem_free, + _bus_dmamem_map, + _bus_dmamem_unmap, + _bus_dmamem_mmap, +}; + +struct board_dev { + char *name; + int unit; +}; + +struct board_dev phyflex_imx6_devs[] = { + { "imxccm", 0 }, + { "imxiomuxc", 0 }, + { "imxdog", 0 }, + { "imxocotp", 0 }, + { "imxuart", 3 }, + { "imxgpio", 0 }, + { "imxgpio", 1 }, + { "imxgpio", 2 }, + { "imxgpio", 3 }, + { "imxgpio", 4 }, + { "imxgpio", 5 }, + { "imxgpio", 6 }, + { "imxesdhc", 1 }, + { "imxesdhc", 2 }, + { "ehci", 0 }, + { "imxenet", 0 }, + { "ahci", 0 }, + { NULL, 0 } +}; + +struct board_dev sabrelite_devs[] = { + { "imxccm", 0 }, + { "imxiomuxc", 0 }, + { "imxdog", 0 }, + { "imxocotp", 0 }, + { "imxuart", 1 }, + { "imxgpio", 0 }, + { "imxgpio", 1 }, + { "imxgpio", 2 }, + { "imxgpio", 3 }, + { "imxgpio", 4 }, + { "imxgpio", 5 }, + { "imxgpio", 6 }, + { "imxesdhc", 2 }, + { "imxesdhc", 3 }, + { "ehci", 0 }, + { "imxenet", 0 }, + { "ahci", 0 }, + { NULL, 0 } +}; + +struct board_dev *board_devs; + +struct imx_dev *imx_devs = NULL; + +struct imx_softc { + struct device sc_dv; +}; + +int imx_match(struct device *, void *, void *); +void imx_attach(struct device *, struct device *, void *); +int imx_submatch(struct device *, void *, void *); + +struct cfattach imx_ca = { + sizeof(struct imx_softc), imx_match, imx_attach, NULL, + config_activate_children +}; + +struct cfdriver imx_cd = { + NULL, "imx", DV_DULL +}; + +int +imx_match(struct device *parent, void *cfdata, void *aux) +{ + return (1); +} + +void +imx_attach(struct device *parent, struct device *self, void *aux) +{ + struct board_dev *bd; + + switch (board_id) { + case BOARD_ID_IMX6_PHYFLEX: + printf(": PhyFLEX-i.MX6\n"); + imx6_init(); + board_devs = phyflex_imx6_devs; + break; + case BOARD_ID_IMX6_SABRELITE: + printf(": i.MX6 SABRE Lite\n"); + imx6_init(); + board_devs = sabrelite_devs; + break; + default: + printf("\n"); + panic("%s: board type 0x%x unknown", __func__, board_id); + } + + /* Directly configure on-board devices (dev* in config file). */ + for (bd = board_devs; bd->name != NULL; bd++) { + struct imx_dev *id = imx_find_dev(bd->name, bd->unit); + struct imx_attach_args ia; + + if (id == NULL) + printf("%s: device %s unit %d not found\n", + self->dv_xname, bd->name, bd->unit); + + memset(&ia, 0, sizeof(ia)); + ia.ia_dev = id; + ia.ia_iot = &armv7_bs_tag; + ia.ia_dmat = &imx_bus_dma_tag; + + if (config_found_sm(self, &ia, NULL, imx_submatch) == NULL) + printf("%s: device %s unit %d not configured\n", + self->dv_xname, bd->name, bd->unit); + } +} + +/* + * We do direct configuration of devices on this SoC "bus", so we + * never call the child device's match function at all (it can be + * NULL in the struct cfattach). + */ +int +imx_submatch(struct device *parent, void *child, void *aux) +{ + struct cfdata *cf = child; + struct imx_attach_args *ia = aux; + + if (strcmp(cf->cf_driver->cd_name, ia->ia_dev->name) == 0) + return (1); + + /* "These are not the droids you are looking for." */ + return (0); +} + +void +imx_set_devs(struct imx_dev *devs) +{ + imx_devs = devs; +} + +struct imx_dev * +imx_find_dev(const char *name, int unit) +{ + struct imx_dev *id; + + if (imx_devs == NULL) + panic("%s: imx_devs == NULL", __func__); + + for (id = imx_devs; id->name != NULL; id++) { + if (id->unit == unit && strcmp(id->name, name) == 0) + return (id); + } + + return (NULL); +} diff --git a/sys/arch/armv7/imx/imx6.c b/sys/arch/armv7/imx/imx6.c new file mode 100644 index 00000000000..75e69a3aebd --- /dev/null +++ b/sys/arch/armv7/imx/imx6.c @@ -0,0 +1,345 @@ +/* $OpenBSD: imx6.c,v 1.1 2013/09/06 20:45:53 patrick Exp $ */ +/* + * Copyright (c) 2011 Uwe Stuehler <uwe@openbsd.org> + * Copyright (c) 2012 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <machine/bus.h> +#include <arch/arm/armv7/armv7var.h> + +#include <armv7/imx/imxvar.h> + +/* IRQs are defined without the 32 cpu IRQs */ + +#define CCM_ADDR 0x020c4000 +#define CCM_SIZE 0x5000 + +#define CCM_IRQ1 87 +#define CCM_IRQ2 88 + +#define ANALOG_ADDR 0x020c8000 +#define ANALOG_SIZE 0x1000 + +#define IOMUXC_ADDR 0x020e0000 +#define IOMUXC_SIZE 0x4000 + +#define WD1_ADDR 0x020bc000 +#define WD1_SIZE 0x400 +#define WD2_ADDR 0x020c0000 +#define WD2_SIZE 0x400 + +#define OCOTP_ADDR 0x021bc000 +#define OCOTP_SIZE 0x4000 + +#define UARTx_SIZE 0x4000 +#define UART1_ADDR 0x02020000 +#define UART2_ADDR 0x021e8000 +#define UART3_ADDR 0x021ec000 +#define UART4_ADDR 0x021f0000 +#define UART5_ADDR 0x021f4000 + +#define UART1_IRQ 26 +#define UART2_IRQ 27 +#define UART3_IRQ 28 +#define UART4_IRQ 29 +#define UART5_IRQ 30 + +#define USBPHYx_SIZE 0x1000 +#define USBPHY1_ADDR 0x020c9000 +#define USBPHY2_ADDR 0x020ca000 +#define USBOTG_ADDR 0x02184000 +#define USBOTG_EHCI_ADDR 0x02184100 +#define USBUH1_ADDR 0x02184200 +#define USBUH1_EHCI_ADDR 0x02184300 +#define USBUH2_ADDR 0x02184400 +#define USBUH2_EHCI_ADDR 0x02184500 +#define USBUH3_ADDR 0x02184600 +#define USBUH3_EHCI_ADDR 0x02184700 +#define USBNC_ADDR 0x02184800 +#define USBx_SIZE 0x100 + +#define USBH1_IRQ 40 +#define USBH2_IRQ 41 +#define USBH3_IRQ 42 +#define USBOTG_IRQ 43 +#define USBPHY0_IRQ 44 +#define USBPHY1_IRQ 45 + +#define GPIOx_SIZE 0x4000 +#define GPIO1_ADDR 0x0209c000 +#define GPIO2_ADDR 0x020a0000 +#define GPIO3_ADDR 0x020a4000 +#define GPIO4_ADDR 0x020a8000 +#define GPIO5_ADDR 0x020ac000 +#define GPIO6_ADDR 0x020b0000 +#define GPIO7_ADDR 0x020b4000 + +#define GPIO1_IRQ7 58 +#define GPIO1_IRQ6 59 +#define GPIO1_IRQ5 60 +#define GPIO1_IRQ4 61 +#define GPIO1_IRQ3 62 +#define GPIO1_IRQ2 63 +#define GPIO1_IRQ1 64 +#define GPIO1_IRQ0 65 +#define GPIO1_IRQ16 66 +#define GPIO1_IRQ32 67 +#define GPIO2_IRQ16 68 +#define GPIO2_IRQ32 69 +#define GPIO3_IRQ16 70 +#define GPIO3_IRQ32 71 +#define GPIO4_IRQ16 72 +#define GPIO4_IRQ32 73 +#define GPIO5_IRQ16 74 +#define GPIO5_IRQ32 75 +#define GPIO6_IRQ16 76 +#define GPIO6_IRQ32 77 +#define GPIO7_IRQ16 78 +#define GPIO7_IRQ32 79 + +#define I2Cx_SIZE 0x4000 +#define I2C1_ADDR 0x021a0000 +#define I2C2_ADDR 0x021a4000 +#define I2C3_ADDR 0x021a8000 + +#define I2C1_IRQ 36 +#define I2C2_IRQ 37 +#define I2C3_IRQ 38 + +#define ESDHCx_SIZE 0x4000 +#define ESDHC1_ADDR 0x02190000 +#define ESDHC2_ADDR 0x02194000 +#define ESDHC3_ADDR 0x02198000 +#define ESDHC4_ADDR 0x0219c000 + +#define ESDHC1_IRQ 22 +#define ESDHC2_IRQ 23 +#define ESDHC3_IRQ 24 +#define ESDHC4_IRQ 25 + +#define ENET_ADDR 0x02188000 +#define ENET_SIZE 0x4000 + +#define ENET_IRQ0 118 +#define ENET_IRQ1 119 + +#define SATA_ADDR 0x02200000 +#define SATA_SIZE 0x4000 + +#define SATA_IRQ 39 + +#define PCIE_REG_ADDR 0x01ffc000 +#define PCIE_REG_SIZE 0x4000 +#define PCIE_MAP_ADDR 0x01000000 +#define PCIE_MAP_SIZE 0xffc000 + +#define PCIE_IRQ0 120 +#define PCIE_IRQ1 121 +#define PCIE_IRQ2 122 +#define PCIE_IRQ3 123 + +struct imx_dev imx6_devs[] = { + + /* + * Clock Control Module + */ + { .name = "imxccm", + .unit = 0, + .mem = { { CCM_ADDR, CCM_SIZE } }, + }, + + /* + * IOMUX Controller + */ + { .name = "imxiomuxc", + .unit = 0, + .mem = { { IOMUXC_ADDR, IOMUXC_SIZE } }, + }, + + /* + * Watchdog Timer + */ + { .name = "imxdog", + .unit = 0, + .mem = { + { WD1_ADDR, WD1_SIZE }, + { WD2_ADDR, WD2_SIZE }, + }, + }, + + /* + * On-Chip OTP Controller + */ + { .name = "imxocotp", + .unit = 0, + .mem = { { OCOTP_ADDR, OCOTP_SIZE } }, + }, + + /* + * UART + */ + { .name = "imxuart", + .unit = 0, + .mem = { { UART1_ADDR, UARTx_SIZE } }, + .irq = { UART1_IRQ } + }, + { .name = "imxuart", + .unit = 1, + .mem = { { UART2_ADDR, UARTx_SIZE } }, + .irq = { UART2_IRQ } + }, + { .name = "imxuart", + .unit = 2, + .mem = { { UART3_ADDR, UARTx_SIZE } }, + .irq = { UART3_IRQ } + }, + { .name = "imxuart", + .unit = 3, + .mem = { { UART4_ADDR, UARTx_SIZE } }, + .irq = { UART4_IRQ } + }, + { .name = "imxuart", + .unit = 4, + .mem = { { UART5_ADDR, UARTx_SIZE } }, + .irq = { UART5_IRQ } + }, + + /* + * GPIO + */ + { .name = "imxgpio", + .unit = 0, + .mem = { { GPIO1_ADDR, GPIOx_SIZE } }, + }, + + { .name = "imxgpio", + .unit = 1, + .mem = { { GPIO2_ADDR, GPIOx_SIZE } }, + }, + + { .name = "imxgpio", + .unit = 2, + .mem = { { GPIO3_ADDR, GPIOx_SIZE } }, + }, + + { .name = "imxgpio", + .unit = 3, + .mem = { { GPIO4_ADDR, GPIOx_SIZE } }, + }, + + { .name = "imxgpio", + .unit = 4, + .mem = { { GPIO5_ADDR, GPIOx_SIZE } }, + }, + + { .name = "imxgpio", + .unit = 5, + .mem = { { GPIO6_ADDR, GPIOx_SIZE } }, + }, + + { .name = "imxgpio", + .unit = 6, + .mem = { { GPIO7_ADDR, GPIOx_SIZE } }, + }, + + /* + * I2C + */ + { .name = "imxiic", + .unit = 0, + .mem = { { I2C1_ADDR, I2Cx_SIZE } }, + .irq = { I2C1_IRQ }, + }, + + { .name = "imxiic", + .unit = 1, + .mem = { { I2C2_ADDR, I2Cx_SIZE } }, + .irq = { I2C2_IRQ }, + }, + + { .name = "imxiic", + .unit = 2, + .mem = { { I2C3_ADDR, I2Cx_SIZE } }, + .irq = { I2C3_IRQ }, + }, + + /* + * ESDHC + */ + { .name = "imxesdhc", + .unit = 0, + .mem = { { ESDHC1_ADDR, ESDHCx_SIZE } }, + .irq = { ESDHC1_IRQ }, + }, + + { .name = "imxesdhc", + .unit = 1, + .mem = { { ESDHC2_ADDR, ESDHCx_SIZE } }, + .irq = { ESDHC2_IRQ }, + }, + + { .name = "imxesdhc", + .unit = 2, + .mem = { { ESDHC3_ADDR, ESDHCx_SIZE } }, + .irq = { ESDHC3_IRQ }, + }, + + { .name = "imxesdhc", + .unit = 3, + .mem = { { ESDHC4_ADDR, ESDHCx_SIZE } }, + .irq = { ESDHC4_IRQ }, + }, + + /* + * USB + */ + { .name = "ehci", + .unit = 0, + .mem = { + { USBUH1_EHCI_ADDR, USBx_SIZE }, + { USBUH1_ADDR, USBx_SIZE }, + { USBPHY2_ADDR, USBPHYx_SIZE }, + { USBNC_ADDR, USBx_SIZE }, + }, + .irq = { USBH1_IRQ } + }, + + /* + * Ethernet + */ + { .name = "imxenet", + .unit = 0, + .mem = { { ENET_ADDR, ENET_SIZE } }, + .irq = { ENET_IRQ0, ENET_IRQ1 } + }, + + /* + * AHCI compatible SATA controller + */ + { .name = "ahci", + .unit = 0, + .mem = { { SATA_ADDR, SATA_SIZE } }, + .irq = { SATA_IRQ } + }, +}; + +void +imx6_init(void) +{ + imx_set_devs(imx6_devs); +} diff --git a/sys/arch/armv7/imx/imx_machdep.c b/sys/arch/armv7/imx/imx_machdep.c new file mode 100644 index 00000000000..a5a135253c1 --- /dev/null +++ b/sys/arch/armv7/imx/imx_machdep.c @@ -0,0 +1,982 @@ +/* $OpenBSD: imx_machdep.c,v 1.1 2013/09/06 20:45:53 patrick Exp $ */ +/* $NetBSD: lubbock_machdep.c,v 1.2 2003/07/15 00:25:06 lukem Exp $ */ + +/* + * Copyright (c) 2002, 2003 Genetec Corporation. All rights reserved. + * Written by Hiroyuki Bessho for Genetec Corporation. + * + * 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. + * 3. The name of Genetec Corporation may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY GENETEC CORPORATION ``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 GENETEC CORPORATION + * 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. + * + * Machine dependant functions for kernel setup for + * Intel DBPXA250 evaluation board (a.k.a. Lubbock). + * Based on iq80310_machhdep.c + */ +/* + * Copyright (c) 2001 Wasabi Systems, Inc. + * All rights reserved. + * + * Written by Jason R. Thorpe for Wasabi Systems, Inc. + * + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project by + * Wasabi Systems, Inc. + * 4. The name of Wasabi Systems, Inc. may not be used to endorse + * or promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``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 WASABI SYSTEMS, INC + * 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. + */ + +/* + * Copyright (c) 1997,1998 Mark Brinicombe. + * Copyright (c) 1997,1998 Causality Limited. + * 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. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Mark Brinicombe + * for the NetBSD Project. + * 4. The name of the company nor the name of the author may be used to + * endorse or promote products derived from this software without specific + * prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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. + * + * Machine dependant functions for kernel setup for Intel IQ80310 evaluation + * boards using RedBoot firmware. + */ + +/* + * DIP switches: + * + * S19: no-dot: set RB_KDB. enter kgdb session. + * S20: no-dot: set RB_SINGLE. don't go multi user mode. + */ + +#include <sys/param.h> +#include <sys/device.h> +#include <sys/systm.h> +#include <sys/kernel.h> +#include <sys/exec.h> +#include <sys/proc.h> +#include <sys/msgbuf.h> +#include <sys/reboot.h> +#include <sys/termios.h> + +#include <uvm/uvm_extern.h> + +#include <sys/conf.h> +#include <sys/queue.h> +#include <sys/device.h> +#include <dev/cons.h> +#include <dev/ic/smc91cxxreg.h> + +#include <machine/db_machdep.h> +#include <ddb/db_sym.h> +#include <ddb/db_extern.h> + +#include <machine/bootconfig.h> +#include <machine/bus.h> +#include <machine/cpu.h> +#include <machine/frame.h> +#include <arm/undefined.h> +#include <arm/machdep.h> + +#include <arm/armv7/armv7reg.h> +#include <arm/armv7/armv7var.h> + +#include <arm/cortex/smc.h> +#include <machine/machine_reg.h> +#include <armv7/imx/imxvar.h> + +#include "wsdisplay.h" + +/* Kernel text starts 2MB in from the bottom of the kernel address space. */ +#define KERNEL_TEXT_BASE (KERNEL_BASE + 0x00000000) +#define KERNEL_VM_BASE (KERNEL_BASE + 0x04000000) + +/* + * The range 0xc1000000 - 0xccffffff is available for kernel VM space + * Core-logic registers and I/O mappings occupy 0xfd000000 - 0xffffffff + */ +/* +#define KERNEL_VM_SIZE 0x0C000000 +*/ +#define KERNEL_VM_SIZE 0x10000000 + + +/* + * Address to call from cpu_reset() to reset the machine. + * This is machine architecture dependant as it varies depending + * on where the ROM appears when you turn the MMU off. + */ + +u_int cpu_reset_address = 0; + +/* Define various stack sizes in pages */ +#define IRQ_STACK_SIZE 1 +#define ABT_STACK_SIZE 1 +#ifdef IPKDB +#define UND_STACK_SIZE 2 +#else +#define UND_STACK_SIZE 1 +#endif + +BootConfig bootconfig; /* Boot config storage */ +char *boot_args = NULL; +char *boot_file = ""; + +vaddr_t physical_start; +vaddr_t physical_freestart; +vaddr_t physical_freeend; +vaddr_t physical_end; +u_int free_pages; +vaddr_t pagetables_start; +int physmem = 0; + +/*int debug_flags;*/ +#ifndef PMAP_STATIC_L1S +int max_processes = 64; /* Default number */ +#endif /* !PMAP_STATIC_L1S */ + +/* Physical and virtual addresses for some global pages */ +pv_addr_t systempage; +pv_addr_t irqstack; +pv_addr_t undstack; +pv_addr_t abtstack; +extern pv_addr_t kernelstack; +pv_addr_t minidataclean; + +vaddr_t msgbufphys; + +extern u_int data_abort_handler_address; +extern u_int prefetch_abort_handler_address; +extern u_int undefined_handler_address; + +#ifdef PMAP_DEBUG +extern int pmap_debug_level; +#endif + +uint32_t board_id; + +#define KERNEL_PT_SYS 0 /* Page table for mapping proc0 zero page */ +#define KERNEL_PT_KERNEL 1 /* Page table for mapping kernel */ +#define KERNEL_PT_KERNEL_NUM 32 +#define KERNEL_PT_VMDATA (KERNEL_PT_KERNEL+KERNEL_PT_KERNEL_NUM) + /* Page tables for mapping kernel VM */ +#define KERNEL_PT_VMDATA_NUM 8 /* start with 16MB of KVM */ +#define NUM_KERNEL_PTS (KERNEL_PT_VMDATA + KERNEL_PT_VMDATA_NUM) + +pv_addr_t kernel_pt_table[NUM_KERNEL_PTS]; + +extern struct user *proc0paddr; + +/* + * safepri is a safe priority for sleep to set for a spin-wait + * during autoconfiguration or after a panic. + */ +int safepri = 0; + +/* Prototypes */ + +void imxdog_reset(void); +void imx_powerdown(void); +void imx_reset(void); + +char bootargs[MAX_BOOT_STRING]; +void process_kernel_args(char *); + +void consinit(void); +void early_clkman(u_int, int); +void kgdb_port_init(void); +void change_clock(uint32_t v); + +bs_protos(bs_notimpl); + +#include <armv7/imx/imxuartvar.h> +#include "com.h" +#if NCOM > 0 +#include <dev/ic/comreg.h> +#include <dev/ic/comvar.h> +#endif + +#ifndef CONSPEED +#define CONSPEED B115200 /* What u-boot */ +#endif +#ifndef CONMODE +#define CONMODE ((TTYDEF_CFLAG & ~(CSIZE | CSTOPB | PARENB)) | CS8) /* 8N1 */ +#endif + +int comcnspeed = CONSPEED; +int comcnmode = CONMODE; + +extern int32_t amptimer_frequency; + +/* + * + */ +void +imx_powerdown(void) +{ +} + +/* + * void boot(int howto, char *bootstr) + * + * Reboots the system + * + * Deal with any syncing, unmounting, dumping and shutdown hooks, + * then reset the CPU. + */ +void +boot(int howto) +{ +#ifdef DIAGNOSTIC + /* info */ + printf("boot: howto=%08x curproc=%p\n", howto, curproc); +#endif + + /* + * If we are still cold then hit the air brakes + * and crash to earth fast + */ + if (cold) { + doshutdownhooks(); + if ((howto & (RB_HALT | RB_USERREQ)) != RB_USERREQ) { + printf("The operating system has halted.\n"); + printf("Please press any key to reboot.\n\n"); + cngetc(); + } + printf("rebooting...\n"); + delay(500000); + imxdog_reset(); + printf("reboot failed; spinning\n"); + while(1); + /*NOTREACHED*/ + } + + /* Disable console buffering */ +/* cnpollc(1);*/ + + /* + * If RB_NOSYNC was not specified sync the discs. + * Note: Unless cold is set to 1 here, syslogd will die during the + * unmount. It looks like syslogd is getting woken up only to find + * that it cannot page part of the binary in as the filesystem has + * been unmounted. + */ + if (!(howto & RB_NOSYNC)) + bootsync(howto); + + /* Say NO to interrupts */ + splhigh(); + + /* Do a dump if requested. */ + if ((howto & (RB_DUMP | RB_HALT)) == RB_DUMP) + dumpsys(); + + /* Run any shutdown hooks */ + doshutdownhooks(); + config_suspend(TAILQ_FIRST(&alldevs), DVACT_POWERDOWN); + + /* Make sure IRQ's are disabled */ + IRQdisable; + + if (howto & RB_HALT) { + if (howto & RB_POWERDOWN) { + + printf("\nAttempting to power down...\n"); + delay(500000); + imx_powerdown(); + } + + printf("The operating system has halted.\n"); + printf("Please press any key to reboot.\n\n"); + cngetc(); + } + + printf("rebooting...\n"); + delay(500000); + imxdog_reset(); + printf("reboot failed; spinning\n"); + while(1); + /*NOTREACHED*/ +} + +static __inline +pd_entry_t * +read_ttb(void) +{ + long ttb; + + __asm __volatile("mrc p15, 0, %0, c2, c0, 0" : "=r" (ttb)); + + + return (pd_entry_t *)(ttb & ~((1<<14)-1)); +} + +#if 1 +#define VERBOSE_INIT_ARM +#endif + +/* + * simple memory mapping function used in early bootstrap stage + * before pmap is initialized. + * size and cacheability are ignored and map one section with nocache. + */ +static vaddr_t section_free = 0xfd000000; /* XXX - huh */ + +int bootstrap_bs_map(void *t, bus_addr_t bpa, bus_size_t size, + int flags, bus_space_handle_t *bshp); +int +bootstrap_bs_map(void *t, bus_addr_t bpa, bus_size_t size, + int flags, bus_space_handle_t *bshp) +{ + u_long startpa; + vaddr_t va; + pd_entry_t *pagedir = read_ttb(); + /* This assumes PA==VA for page directory */ + + va = section_free; + section_free += L1_S_SIZE; + + /* + startpa = trunc_page(bpa); + */ + startpa = bpa & ~L1_S_OFFSET; + pmap_map_section((vaddr_t)pagedir, va, startpa, + VM_PROT_READ | VM_PROT_WRITE, PTE_NOCACHE); + cpu_tlb_flushD(); + + *bshp = (bus_space_handle_t)(va + (bpa - startpa)); + + return(0); +} + +static void +copy_io_area_map(pd_entry_t *new_pd) +{ + pd_entry_t *cur_pd = read_ttb(); + vaddr_t va; + + for (va = MACHINE_IO_AREA_VBASE; + (cur_pd[va>>L1_S_SHIFT] & L1_TYPE_MASK) == L1_TYPE_S; + va += L1_S_SIZE) { + + new_pd[va>>L1_S_SHIFT] = cur_pd[va>>L1_S_SHIFT]; + if (va == (ARM_VECTORS_HIGH & ~(0x00400000 - 1))) + break; /* STUPID */ + + } +} + +void parse_uboot_tags(void *); +void imx_reset() { + uint16_t* wcr = (uint16_t*)0x020bc000; + uint16_t* wsr = (uint16_t*)0x020bc002; + *wcr = 0; + *wsr = 0x5555; + *wsr = 0xaaaa; + *wcr = 1; + *wcr = 1; +} + +/* + * u_int initarm(...) + * + * Initial entry point on startup. This gets called before main() is + * entered. + * It should be responsible for setting up everything that must be + * in place when main is called. + * This includes + * Taking a copy of the boot configuration structure. + * Initialising the physical console so characters can be printed. + * Setting up page tables for the kernel + * Relocating the kernel to the bottom of physical memory + */ +u_int +initarm(void *arg0, void *arg1, void *arg2) +{ + int loop; + int loop1; + u_int l1pagetable; + pv_addr_t kernel_l1pt; + paddr_t memstart; + psize_t memsize; + +#if 0 + int led_data = 0; +#endif + /* early bus_space_map support */ + struct bus_space tmp_bs_tag; + int (*map_func_save)(void *, bus_addr_t, bus_size_t, int, + bus_space_handle_t *); + + board_id = (uint32_t)arg1; + + /* + * Heads up ... Setup the CPU / MMU / TLB functions + */ + if (set_cpufuncs()) + panic("cpu not recognized!"); + +#if 0 + /* Calibrate the delay loop. */ +#endif + + /* + * Temporarily replace bus_space_map() functions so that + * console devices can get mapped. + * + * Note that this relies upon the fact that both regular + * and a4x bus_space tags use the same map function. + */ +#if defined(CPU_ARMv7) + tmp_bs_tag = armv7_bs_tag; + map_func_save = armv7_bs_tag.bs_map; + armv7_bs_tag.bs_map = bootstrap_bs_map; + armv7_a4x_bs_tag.bs_map = bootstrap_bs_map; +#endif + tmp_bs_tag.bs_map = bootstrap_bs_map; + + /* setup a serial console for very early boot */ + consinit(); + + /* Talk to the user */ + printf("\nOpenBSD/imx booting ...\n"); + + printf("arg0 %p arg1 %p arg2 %p\n", arg0, arg1, arg2); + parse_uboot_tags(arg2); + + /* + * Examine the boot args string for options we need to know about + * now. + */ + process_kernel_args(bootconfig.bootstring); +#ifdef RAMDISK_HOOKS + boothowto |= RB_DFLTROOT; +#endif /* RAMDISK_HOOKS */ + + /* normally u-boot will set up bootconfig.dramblocks */ + if (bootconfig.dramblocks == 0) { + memstart = SDRAM_START; + memsize = 0x10000000; /* 256 MB */ + /* Fake bootconfig structure for the benefit of pmap.c */ + /* XXX must make the memory description h/w independant */ + bootconfig.dram[0].address = memstart; + bootconfig.dram[0].pages = memsize / PAGE_SIZE; + bootconfig.dramblocks = 1; + } else { + memstart = bootconfig.dram[0].address; + memsize = bootconfig.dram[0].pages * PAGE_SIZE; + } + + /* + * Set up the variables that define the availablilty of + * physical memory. For now, we're going to set + * physical_freestart to 0xa0200000 (where the kernel + * was loaded), and allocate the memory we need downwards. + * If we get too close to the page tables that RedBoot + * set up, we will panic. We will update physical_freestart + * and physical_freeend later to reflect what pmap_bootstrap() + * wants to see. + * + * XXX pmap_bootstrap() needs an enema. + */ + physical_start = bootconfig.dram[0].address; + physical_end = physical_start + (bootconfig.dram[0].pages * PAGE_SIZE); + + { + extern char _end[]; + physical_freestart = (((unsigned long)_end - KERNEL_TEXT_BASE +0xfff) & ~0xfff) + memstart; + physical_freeend = memstart+memsize; + } + + physmem = (physical_end - physical_start) / PAGE_SIZE; + +#ifdef DEBUG + /* Tell the user about the memory */ + printf("physmemory: %d pages at 0x%08lx -> 0x%08lx\n", physmem, + physical_start, physical_end - 1); +#endif + + /* + * Okay, the kernel starts 2MB in from the bottom of physical + * memory. We are going to allocate our bootstrap pages downwards + * from there. + * + * We need to allocate some fixed page tables to get the kernel + * going. We allocate one page directory and a number of page + * tables and store the physical addresses in the kernel_pt_table + * array. + * + * The kernel page directory must be on a 16K boundary. The page + * tables must be on 4K bounaries. What we do is allocate the + * page directory on the first 16K boundary that we encounter, and + * the page tables on 4K boundaries otherwise. Since we allocate + * at least 3 L2 page tables, we are guaranteed to encounter at + * least one 16K aligned region. + */ + +#ifdef VERBOSE_INIT_ARM + printf("Allocating page tables\n"); +#endif + + free_pages = (physical_freeend - physical_freestart) / PAGE_SIZE; + +#ifdef VERBOSE_INIT_ARM + printf("freestart = 0x%08lx, free_pages = %d (0x%08x)\n", + physical_freestart, free_pages, free_pages); +#endif + + /* Define a macro to simplify memory allocation */ +#define valloc_pages(var, np) \ + alloc_pages((var).pv_pa, (np)); \ + (var).pv_va = KERNEL_BASE + (var).pv_pa - physical_start; + +#define alloc_pages(var, np) \ + (var) = physical_freestart; \ + physical_freestart += ((np) * PAGE_SIZE); \ + if (physical_freeend < physical_freestart) \ + panic("initarm: out of memory"); \ + free_pages -= (np); \ + memset((char *)(var), 0, ((np) * PAGE_SIZE)); + + loop1 = 0; + kernel_l1pt.pv_pa = 0; + for (loop = 0; loop <= NUM_KERNEL_PTS; ++loop) { + /* Are we 16KB aligned for an L1 ? */ + if (((physical_freestart) & (L1_TABLE_SIZE - 1)) == 0 + && kernel_l1pt.pv_pa == 0) { + valloc_pages(kernel_l1pt, L1_TABLE_SIZE / PAGE_SIZE); + } else { + valloc_pages(kernel_pt_table[loop1], + L2_TABLE_SIZE / PAGE_SIZE); + ++loop1; + } + } + + /* This should never be able to happen but better confirm that. */ + if (!kernel_l1pt.pv_pa || (kernel_l1pt.pv_pa & (L1_TABLE_SIZE-1)) != 0) + panic("initarm: Failed to align the kernel page directory"); + + /* + * Allocate a page for the system page mapped to V0x00000000 + * This page will just contain the system vectors and can be + * shared by all processes. + */ + vector_page = ARM_VECTORS_HIGH; + alloc_pages(systempage.pv_pa, 1); + systempage.pv_va = vector_page; + + /* Allocate stacks for all modes */ + valloc_pages(irqstack, IRQ_STACK_SIZE); + valloc_pages(abtstack, ABT_STACK_SIZE); + valloc_pages(undstack, UND_STACK_SIZE); + valloc_pages(kernelstack, UPAGES); + + /* Allocate enough pages for cleaning the Mini-Data cache. */ + +#ifdef VERBOSE_INIT_ARM + printf("IRQ stack: p0x%08lx v0x%08lx\n", irqstack.pv_pa, + irqstack.pv_va); + printf("ABT stack: p0x%08lx v0x%08lx\n", abtstack.pv_pa, + abtstack.pv_va); + printf("UND stack: p0x%08lx v0x%08lx\n", undstack.pv_pa, + undstack.pv_va); + printf("SVC stack: p0x%08lx v0x%08lx\n", kernelstack.pv_pa, + kernelstack.pv_va); +#endif + + /* + * XXX Defer this to later so that we can reclaim the memory + * XXX used by the RedBoot page tables. + */ + alloc_pages(msgbufphys, round_page(MSGBUFSIZE) / PAGE_SIZE); + + /* + * Ok we have allocated physical pages for the primary kernel + * page tables + */ + +#ifdef VERBOSE_INIT_ARM + printf("Creating L1 page table at 0x%08lx\n", kernel_l1pt.pv_pa); +#endif + + /* + * Now we start construction of the L1 page table + * We start by mapping the L2 page tables into the L1. + * This means that we can replace L1 mappings later on if necessary + */ + l1pagetable = kernel_l1pt.pv_pa; + + /* Map the L2 pages tables in the L1 page table */ + pmap_link_l2pt(l1pagetable, vector_page & ~(0x00400000 - 1), + &kernel_pt_table[KERNEL_PT_SYS]); + + for (loop = 0; loop < KERNEL_PT_KERNEL_NUM; loop++) + pmap_link_l2pt(l1pagetable, KERNEL_BASE + loop * 0x00400000, + &kernel_pt_table[KERNEL_PT_KERNEL + loop]); + + for (loop = 0; loop < KERNEL_PT_VMDATA_NUM; loop++) + pmap_link_l2pt(l1pagetable, KERNEL_VM_BASE + loop * 0x00400000, + &kernel_pt_table[KERNEL_PT_VMDATA + loop]); + + /* update the top of the kernel VM */ + pmap_curmaxkvaddr = + KERNEL_VM_BASE + (KERNEL_PT_VMDATA_NUM * 0x00400000); + +#ifdef VERBOSE_INIT_ARM + printf("Mapping kernel\n"); +#endif + + /* Now we fill in the L2 pagetable for the kernel static code/data */ + { + extern char etext[], _end[]; + size_t textsize = (u_int32_t) etext - KERNEL_TEXT_BASE; + size_t totalsize = (u_int32_t) _end - KERNEL_TEXT_BASE; + u_int logical; + + textsize = (textsize + PGOFSET) & ~PGOFSET; + totalsize = (totalsize + PGOFSET) & ~PGOFSET; + + logical = 0x00000000; /* offset of kernel in RAM */ + + logical += pmap_map_chunk(l1pagetable, KERNEL_BASE + logical, + physical_start + logical, textsize, + VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE, PTE_CACHE); + logical += pmap_map_chunk(l1pagetable, KERNEL_BASE + logical, + physical_start + logical, totalsize - textsize, + VM_PROT_READ|VM_PROT_WRITE, PTE_CACHE); + } + +#ifdef VERBOSE_INIT_ARM + printf("Constructing L2 page tables\n"); +#endif + + /* Map the stack pages */ + pmap_map_chunk(l1pagetable, irqstack.pv_va, irqstack.pv_pa, + IRQ_STACK_SIZE * PAGE_SIZE, VM_PROT_READ|VM_PROT_WRITE, PTE_CACHE); + pmap_map_chunk(l1pagetable, abtstack.pv_va, abtstack.pv_pa, + ABT_STACK_SIZE * PAGE_SIZE, VM_PROT_READ|VM_PROT_WRITE, PTE_CACHE); + pmap_map_chunk(l1pagetable, undstack.pv_va, undstack.pv_pa, + UND_STACK_SIZE * PAGE_SIZE, VM_PROT_READ|VM_PROT_WRITE, PTE_CACHE); + pmap_map_chunk(l1pagetable, kernelstack.pv_va, kernelstack.pv_pa, + UPAGES * PAGE_SIZE, VM_PROT_READ | VM_PROT_WRITE, PTE_CACHE); + + pmap_map_chunk(l1pagetable, kernel_l1pt.pv_va, kernel_l1pt.pv_pa, + L1_TABLE_SIZE, VM_PROT_READ | VM_PROT_WRITE, PTE_PAGETABLE); + + for (loop = 0; loop < NUM_KERNEL_PTS; ++loop) { + pmap_map_chunk(l1pagetable, kernel_pt_table[loop].pv_va, + kernel_pt_table[loop].pv_pa, L2_TABLE_SIZE, + VM_PROT_READ|VM_PROT_WRITE, PTE_PAGETABLE); + } + + /* Map the Mini-Data cache clean area. */ + + /* Map the vector page. */ +#if 0 + /* MULTI-ICE requires that page 0 is NC/NB so that it can download the + * cache-clean code there. */ + pmap_map_entry(l1pagetable, vector_page, systempage.pv_pa, + VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE, PTE_NOCACHE); +#else + pmap_map_entry(l1pagetable, vector_page, systempage.pv_pa, + VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE, PTE_CACHE); +#endif + + /* + * map integrated peripherals at same address in l1pagetable + * so that we can continue to use console. + */ + copy_io_area_map((pd_entry_t *)l1pagetable); + + + /* + * Now we have the real page tables in place so we can switch to them. + * Once this is done we will be running with the REAL kernel page + * tables. + */ + + /* be a client to all domains */ + cpu_domains(0x55555555); + /* Switch tables */ + + cpu_domains((DOMAIN_CLIENT << (PMAP_DOMAIN_KERNEL*2)) | DOMAIN_CLIENT); + setttb(kernel_l1pt.pv_pa); + cpu_tlb_flushID(); + cpu_domains(DOMAIN_CLIENT << (PMAP_DOMAIN_KERNEL*2)); + + /* + * Moved from cpu_startup() as data_abort_handler() references + * this during uvm init + */ + proc0paddr = (struct user *)kernelstack.pv_va; + proc0.p_addr = proc0paddr; + + arm32_vector_init(vector_page, ARM_VEC_ALL); + + /* + * Pages were allocated during the secondary bootstrap for the + * stacks for different CPU modes. + * We must now set the r13 registers in the different CPU modes to + * point to these stacks. + * Since the ARM stacks use STMFD etc. we must set r13 to the top end + * of the stack memory. + */ + + set_stackptr(PSR_IRQ32_MODE, + irqstack.pv_va + IRQ_STACK_SIZE * PAGE_SIZE); + set_stackptr(PSR_ABT32_MODE, + abtstack.pv_va + ABT_STACK_SIZE * PAGE_SIZE); + set_stackptr(PSR_UND32_MODE, + undstack.pv_va + UND_STACK_SIZE * PAGE_SIZE); + + /* + * Well we should set a data abort handler. + * Once things get going this will change as we will need a proper + * handler. + * Until then we will use a handler that just panics but tells us + * why. + * Initialisation of the vectors will just panic on a data abort. + * This just fills in a slighly better one. + */ + + data_abort_handler_address = (u_int)data_abort_handler; + prefetch_abort_handler_address = (u_int)prefetch_abort_handler; + undefined_handler_address = (u_int)undefinedinstruction_bounce; + + /* Initialise the undefined instruction handlers */ +#ifdef VERBOSE_INIT_ARM + printf("undefined "); +#endif + undefined_init(); + + /* Load memory into UVM. */ +#ifdef VERBOSE_INIT_ARM + printf("page "); +#endif + uvm_setpagesize(); /* initialize PAGE_SIZE-dependent variables */ + uvm_page_physload(atop(physical_freestart), atop(physical_freeend), + atop(physical_freestart), atop(physical_freeend), 0); + + /* Boot strap pmap telling it where the kernel page table is */ +#ifdef VERBOSE_INIT_ARM + printf("pmap "); +#endif + pmap_bootstrap((pd_entry_t *)kernel_l1pt.pv_va, KERNEL_VM_BASE, + KERNEL_VM_BASE + KERNEL_VM_SIZE); + +#ifdef IPKDB + /* Initialise ipkdb */ + ipkdb_init(); + if (boothowto & RB_KDB) + ipkdb_connect(0); +#endif + + /* + * Restore proper bus_space operation, now that pmap is initialized. + */ +#if defined(CPU_ARMv7) + armv7_bs_tag.bs_map = map_func_save; + armv7_a4x_bs_tag.bs_map = map_func_save; +#endif + +#ifdef DDB + db_machine_init(); + + /* Firmware doesn't load symbols. */ + ddb_init(); + + if (boothowto & RB_KDB) + Debugger(); +#endif + + switch (board_id) { + case BOARD_ID_IMX6_PHYFLEX: + amptimer_frequency = 396 * 1000 * 1000; + printf("board type: phyFLEX-i.MX6\n"); + break; + case BOARD_ID_IMX6_SABRELITE: + amptimer_frequency = 396 * 1000 * 1000; + printf("board type: SABRE Lite\n"); + break; + default: + printf("board type %x unknown\n", board_id); + } + + /* We return the new stack pointer address */ + return(kernelstack.pv_va + USPACE_SVC_STACK_TOP); +} + + +void +process_kernel_args(char *args) +{ + char *cp = args; + + if (cp == NULL) { + boothowto = RB_AUTOBOOT; + return; + } + + boothowto = 0; + + /* Make a local copy of the bootargs */ + strncpy(bootargs, cp, MAX_BOOT_STRING - sizeof(int)); + + cp = bootargs; + boot_file = bootargs; + + /* Skip the kernel image filename */ + while (*cp != ' ' && *cp != 0) + ++cp; + + if (*cp != 0) + *cp++ = 0; + + while (*cp == ' ') + ++cp; + + boot_args = cp; + + printf("bootfile: %s\n", boot_file); + printf("bootargs: %s\n", boot_args); + + /* Setup pointer to boot flags */ + while (*cp != '-') + if (*cp++ == '\0') + return; + + for (;*++cp;) { + int fl; + + fl = 0; + switch(*cp) { + case 'a': + fl |= RB_ASKNAME; + break; + case 'c': + fl |= RB_CONFIG; + break; + case 'd': + fl |= RB_KDB; + break; + case 's': + fl |= RB_SINGLE; + break; + default: + printf("unknown option `%c'\n", *cp); + break; + } + boothowto |= fl; + } +} + +void +consinit(void) +{ + static int consinit_called = 0; + paddr_t paddr; + + if (consinit_called != 0) + return; + + consinit_called = 1; + + switch (board_id) { + case BOARD_ID_IMX6_PHYFLEX: + paddr = 0x021f0000; + break; + case BOARD_ID_IMX6_SABRELITE: + paddr = 0x021e8000; + break; + default: + printf("board type %x unknown", board_id); + return; + /* XXX - HELP */ + } + imxuartcnattach(&armv7_bs_tag, paddr, comcnspeed, comcnmode); +} + + +//int glass_console = 0; + +void +board_startup(void) +{ + if (boothowto & RB_CONFIG) { +#ifdef BOOT_CONFIG + user_config(); +#else + printf("kernel does not support -c; continuing..\n"); +#endif + } +} + +void +platform_smc_write(bus_space_tag_t iot, bus_space_handle_t ioh, bus_size_t off, + uint32_t op, uint32_t val) +{ + bus_space_write_4(iot, ioh, off, val); +} diff --git a/sys/arch/armv7/imx/imxahci.c b/sys/arch/armv7/imx/imxahci.c new file mode 100644 index 00000000000..ffe6f88845e --- /dev/null +++ b/sys/arch/armv7/imx/imxahci.c @@ -0,0 +1,173 @@ +/* $OpenBSD: imxahci.c,v 1.1 2013/09/06 20:45:53 patrick Exp $ */ +/* + * Copyright (c) 2013 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/buf.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/device.h> +#include <sys/queue.h> + +#include <machine/bus.h> + +#include <dev/ic/ahcivar.h> + +#include <armv7/imx/imxvar.h> +#include <armv7/imx/imxccmvar.h> +#include <armv7/imx/imxiomuxcvar.h> + +/* registers */ +#define SATA_CAP 0x000 +#define SATA_GHC 0x004 +#define SATA_IS 0x008 +#define SATA_PI 0x00C +#define SATA_VS 0x010 +#define SATA_CCC_CTL 0x014 +#define SATA_CCC_PORTS 0x018 +#define SATA_CAP2 0x024 +#define SATA_BISTAFR 0x0A0 +#define SATA_BISTCR 0x0A4 +#define SATA_BISTFCTR 0x0A8 +#define SATA_BSTSR 0x0AC +#define SATA_OOBR 0x0BC +#define SATA_GPCR 0x0D0 +#define SATA_GPSR 0x0D4 +#define SATA_TIMER1MS 0x0E0 +#define SATA_TESTR 0x0F4 +#define SATA_VERSIONR 0x0F8 +#define SATA_P0CLB 0x100 +#define SATA_P0FB 0x108 +#define SATA_P0IS 0x110 +#define SATA_P0IE 0x114 +#define SATA_P0CMD 0x118 +#define SATA_P0TFD 0x120 +#define SATA_P0SIG 0x124 +#define SATA_P0SSTS 0x128 +#define SATA_P0SCTL 0x12C +#define SATA_P0SERR 0x130 +#define SATA_P0SACT 0x134 +#define SATA_P0CI 0x138 +#define SATA_P0SNTF 0x13C +#define SATA_P0DMACR 0x170 +#define SATA_P0PHYCR 0x178 +#define SATA_P0PHYSR 0x17C + +#define SATA_CAP_SSS (1 << 27) +#define SATA_GHC_HR (1 << 0) +#define SATA_P0PHYCR_TEST_PDDQ (1 << 20) + +void imxahci_attach(struct device *, struct device *, void *); +int imxahci_detach(struct device *, int); +int imxahci_activate(struct device *, int); + +extern int ahci_intr(void *); + +struct imxahci_softc { + struct ahci_softc sc; +}; + +struct cfattach imxahci_ca = { + sizeof(struct imxahci_softc), + NULL, + imxahci_attach, + imxahci_detach, + imxahci_activate +}; + +struct cfdriver imxahci_cd = { + NULL, "ahci", DV_DULL +}; + +void +imxahci_attach(struct device *parent, struct device *self, void *args) +{ + struct imx_attach_args *ia = args; + struct imxahci_softc *imxsc = (struct imxahci_softc *) self; + struct ahci_softc *sc = &imxsc->sc; + uint32_t timeout = 0x100000; + + sc->sc_iot = ia->ia_iot; + sc->sc_ios = ia->ia_dev->mem[0].size; + sc->sc_dmat = ia->ia_dmat; + + if (bus_space_map(sc->sc_iot, ia->ia_dev->mem[0].addr, + ia->ia_dev->mem[0].size, 0, &sc->sc_ioh)) + panic("imxahci_attach: bus_space_map failed!"); + + sc->sc_ih = arm_intr_establish(ia->ia_dev->irq[0], IPL_BIO, + ahci_intr, sc, sc->sc_dev.dv_xname); + if (sc->sc_ih == NULL) { + printf(": unable to establish interrupt\n"); + goto unmap; + } + + /* power it up */ + imxccm_enable_sata(); + delay(100); + + /* power phy up */ + imxiomuxc_enable_sata(); + + /* setup */ + bus_space_write_4(sc->sc_iot, sc->sc_ioh, SATA_P0PHYCR, + bus_space_read_4(sc->sc_iot, sc->sc_ioh, SATA_P0PHYCR) & ~SATA_P0PHYCR_TEST_PDDQ); + + bus_space_write_4(sc->sc_iot, sc->sc_ioh, SATA_GHC, SATA_GHC_HR); + + while (!bus_space_read_4(sc->sc_iot, sc->sc_ioh, SATA_VERSIONR)); + + bus_space_write_4(sc->sc_iot, sc->sc_ioh, SATA_CAP, + bus_space_read_4(sc->sc_iot, sc->sc_ioh, SATA_CAP) | SATA_CAP_SSS); + + bus_space_write_4(sc->sc_iot, sc->sc_ioh, SATA_PI, 1); + + bus_space_write_4(sc->sc_iot, sc->sc_ioh, SATA_TIMER1MS, imxccm_get_ahbclk()); + + while (!(bus_space_read_4(sc->sc_iot, sc->sc_ioh, SATA_P0SSTS) & 0xF) && timeout--); + + if (ahci_attach(sc) != 0) { + /* error printed by ahci_attach */ + goto irq; + } + + return; +irq: + arm_intr_disestablish(sc->sc_ih); +unmap: + bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios); +} + +int +imxahci_detach(struct device *self, int flags) +{ + struct imxahci_softc *imxsc = (struct imxahci_softc *) self; + struct ahci_softc *sc = &imxsc->sc; + + ahci_detach(sc, flags); + bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios); + return 0; +} + +int +imxahci_activate(struct device *self, int act) +{ + struct imxahci_softc *imxsc = (struct imxahci_softc *) self; + struct ahci_softc *sc = &imxsc->sc; + + return ahci_activate((struct device *)sc, act); +} diff --git a/sys/arch/armv7/imx/imxccm.c b/sys/arch/armv7/imx/imxccm.c new file mode 100644 index 00000000000..dc2daee6e58 --- /dev/null +++ b/sys/arch/armv7/imx/imxccm.c @@ -0,0 +1,563 @@ +/* $OpenBSD: imxccm.c,v 1.1 2013/09/06 20:45:53 patrick Exp $ */ +/* + * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/queue.h> +#include <sys/malloc.h> +#include <sys/sysctl.h> +#include <sys/device.h> +#include <sys/evcount.h> +#include <sys/socket.h> +#include <sys/timeout.h> +#include <machine/intr.h> +#include <machine/bus.h> +#include <armv7/imx/imxvar.h> + +/* registers */ +#define CCM_CCR 0x00 +#define CCM_CCDR 0x04 +#define CCM_CSR 0x08 +#define CCM_CCSR 0x0c +#define CCM_CACRR 0x10 +#define CCM_CBCDR 0x14 +#define CCM_CBCMR 0x18 +#define CCM_CSCMR1 0x1c +#define CCM_CSCMR2 0x20 +#define CCM_CSCDR1 0x24 +#define CCM_CS1CDR 0x28 +#define CCM_CS2CDR 0x2c +#define CCM_CDCDR 0x30 +#define CCM_CHSCCDR 0x34 +#define CCM_CSCDR2 0x38 +#define CCM_CSCDR3 0x3c +#define CCM_CSCDR4 0x40 +#define CCM_CDHIPR 0x48 +#define CCM_CDCR 0x4c +#define CCM_CTOR 0x50 +#define CCM_CLPCR 0x54 +#define CCM_CISR 0x58 +#define CCM_CIMR 0x5c +#define CCM_CCOSR 0x60 +#define CCM_CGPR 0x64 +#define CCM_CCGR0 0x68 +#define CCM_CCGR1 0x6c +#define CCM_CCGR2 0x70 +#define CCM_CCGR3 0x74 +#define CCM_CCGR4 0x78 +#define CCM_CCGR5 0x7c +#define CCM_CCGR6 0x80 +#define CCM_CCGR7 0x84 +#define CCM_CMEOR 0x88 + +/* ANALOG */ +#define CCM_ANALOG_PLL_ARM 0x4000 +#define CCM_ANALOG_PLL_ARM_SET 0x4004 +#define CCM_ANALOG_PLL_ARM_CLR 0x4008 +#define CCM_ANALOG_PLL_USB1 0x4010 +#define CCM_ANALOG_PLL_USB1_SET 0x4014 +#define CCM_ANALOG_PLL_USB1_CLR 0x4018 +#define CCM_ANALOG_PLL_USB2 0x4020 +#define CCM_ANALOG_PLL_USB2_SET 0x4024 +#define CCM_ANALOG_PLL_USB2_CLR 0x4028 +#define CCM_ANALOG_PLL_SYS 0x4030 +#define CCM_ANALOG_USB2_CHRG_DETECT 0x4210 +#define CCM_ANALOG_USB2_CHRG_DETECT_SET 0x4214 +#define CCM_ANALOG_USB2_CHRG_DETECT_CLR 0x4218 +#define CCM_ANALOG_DIGPROG 0x4260 +#define CCM_ANALOG_PLL_ENET 0x40e0 +#define CCM_ANALOG_PLL_ENET_SET 0x40e4 +#define CCM_ANALOG_PLL_ENET_CLR 0x40e8 +#define CCM_ANALOG_PFD_480 0x40f0 +#define CCM_ANALOG_PFD_480_SET 0x40f4 +#define CCM_ANALOG_PFD_480_CLR 0x40f8 +#define CCM_ANALOG_PFD_528 0x4100 +#define CCM_ANALOG_PFD_528_SET 0x4104 +#define CCM_ANALOG_PFD_528_CLR 0x4108 +#define CCM_PMU_MISC1 0x4160 + +/* bits and bytes */ +#define CCM_CCSR_PLL3_SW_CLK_SEL (1 << 0) +#define CCM_CCSR_PLL2_SW_CLK_SEL (1 << 1) +#define CCM_CCSR_PLL1_SW_CLK_SEL (1 << 2) +#define CCM_CCSR_STEP_SEL (1 << 8) +#define CCM_CBCDR_IPG_PODF_SHIFT 8 +#define CCM_CBCDR_IPG_PODF_MASK 0x3 +#define CCM_CBCDR_AHB_PODF_SHIFT 10 +#define CCM_CBCDR_AHB_PODF_MASK 0x7 +#define CCM_CBCDR_PERIPH_CLK_SEL_SHIFT 25 +#define CCM_CBCDR_PERIPH_CLK_SEL_MASK 0x1 +#define CCM_CBCMR_PERIPH_CLK2_SEL_SHIFT 12 +#define CCM_CBCMR_PERIPH_CLK2_SEL_MASK 0x3 +#define CCM_CBCMR_PRE_PERIPH_CLK_SEL_SHIFT 18 +#define CCM_CBCMR_PRE_PERIPH_CLK_SEL_MASK 0x3 +#define CCM_CSCDR1_USDHCx_CLK_SEL_SHIFT(x) ((x) + 15) +#define CCM_CSCDR1_USDHCx_CLK_SEL_MASK 0x1 +#define CCM_CSCDR1_USDHCx_PODF_MASK 0x7 +#define CCM_CSCDR1_UART_PODF_MASK 0x7 +#define CCM_CCGR1_ENET (3 << 10) +#define CCM_CCGR2_I2C(x) (3 << (6 + 2*x)) +#define CCM_CCGR4_125M_PCIE (3 << 0) +#define CCM_CCGR5_100M_SATA (3 << 4) +#define CCM_CCGR6_USBOH3 (3 << 0) +#define CCM_CSCMR1_PERCLK_CLK_SEL_MASK 0x1f +#define CCM_ANALOG_PLL_ARM_DIV_SELECT_MASK 0x7f +#define CCM_ANALOG_PLL_ARM_BYPASS (1 << 16) +#define CCM_ANALOG_PLL_USB2_DIV_SELECT_MASK 0x1 +#define CCM_ANALOG_PLL_USB2_EN_USB_CLKS (1 << 6) +#define CCM_ANALOG_PLL_USB2_POWER (1 << 12) +#define CCM_ANALOG_PLL_USB2_ENABLE (1 << 13) +#define CCM_ANALOG_PLL_USB2_BYPASS (1 << 16) +#define CCM_ANALOG_PLL_USB2_LOCK (1 << 31) +#define CCM_ANALOG_PLL_SYS_DIV_SELECT_MASK 0x1 +#define CCM_ANALOG_USB2_CHRG_DETECT_CHK_CHRG_B (1 << 19) +#define CCM_ANALOG_USB2_CHRG_DETECT_EN_B (1 << 20) +#define CCM_ANALOG_DIGPROG_MINOR_MASK 0xff +#define CCM_ANALOG_PLL_ENET_DIV_125M (1 << 11) +#define CCM_ANALOG_PLL_ENET_POWERDOWN (1 << 12) +#define CCM_ANALOG_PLL_ENET_ENABLE (1 << 13) +#define CCM_ANALOG_PLL_ENET_BYPASS (1 << 16) +#define CCM_ANALOG_PLL_ENET_125M_PCIE (1 << 19) +#define CCM_ANALOG_PLL_ENET_100M_SATA (1 << 20) +#define CCM_ANALOG_PLL_ENET_LOCK (1 << 31) +#define CCM_ANALOG_PFD_480_PFDx_FRAC(x, y) (((x) >> ((y) << 3)) & 0x3f) +#define CCM_ANALOG_PFD_528_PFDx_FRAC(x, y) (((x) >> ((y) << 3)) & 0x3f) +#define CCM_PMU_MISC1_LVDSCLK1_CLK_SEL_SATA (0xB << 0) +#define CCM_PMU_MISC1_LVDSCLK1_CLK_SEL_MASK (0x1f << 0) +#define CCM_PMU_MISC1_LVDSCLK1_OBEN (1 << 10) +#define CCM_PMU_MISC1_LVDSCLK1_IBEN (1 << 12) + +#define HCLK_FREQ 24000 +#define PLL3_80M 80000 + +#define HREAD4(sc, reg) \ + (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) +#define HWRITE4(sc, reg, val) \ + bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) +#define HSET4(sc, reg, bits) \ + HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) +#define HCLR4(sc, reg, bits) \ + HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) + +struct imxccm_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; +}; + +enum clocks { + /* OSC */ + OSC, /* 24 MHz OSC */ + + /* PLLs */ + ARM_PLL1, /* ARM core PLL */ + SYS_PLL2, /* System PLL: 528 MHz */ + USB1_PLL3, /* OTG USB PLL: 480 MHz */ + USB2_PLL, /* Host USB PLL: 480 MHz */ + AUD_PLL4, /* Audio PLL */ + VID_PLL5, /* Video PLL */ + ENET_PLL6, /* ENET PLL */ + MLB_PLL, /* MLB PLL */ + + /* SYS_PLL2 PFDs */ + SYS_PLL2_PFD0, /* 352 MHz */ + SYS_PLL2_PFD1, /* 594 MHz */ + SYS_PLL2_PFD2, /* 396 MHz */ + + /* USB1_PLL3 PFDs */ + USB1_PLL3_PFD0, /* 720 MHz */ + USB1_PLL3_PFD1, /* 540 MHz */ + USB1_PLL3_PFD2, /* 508.2 MHz */ + USB1_PLL3_PFD3, /* 454.7 MHz */ +}; + +struct imxccm_softc *imxccm_sc; + +void imxccm_attach(struct device *parent, struct device *self, void *args); +int imxccm_cpuspeed(int *); +unsigned int imxccm_decode_pll(enum clocks, unsigned int); +unsigned int imxccm_get_pll2_pfd(unsigned int); +unsigned int imxccm_get_pll3_pfd(unsigned int); +unsigned int imxccm_get_armclk(void); +void imxccm_armclk_set_parent(enum clocks); +void imxccm_armclk_set_freq(unsigned int); +unsigned int imxccm_get_usdhx(int x); +unsigned int imxccm_get_periphclk(void); +unsigned int imxccm_get_fecclk(void); +unsigned int imxccm_get_ahbclk(void); +unsigned int imxccm_get_ipgclk(void); +unsigned int imxccm_get_ipg_perclk(void); +unsigned int imxccm_get_uartclk(void); +void imxccm_enable_i2c(int x); +void imxccm_enable_usboh3(void); +void imxccm_disable_usb2_chrg_detect(void); +void imxccm_enable_pll_usb2(void); +void imxccm_enable_pll_enet(void); +void imxccm_enable_enet(void); +void imxccm_enable_sata(void); +void imxccm_enable_pcie(void); + +struct cfattach imxccm_ca = { + sizeof (struct imxccm_softc), NULL, imxccm_attach +}; + +struct cfdriver imxccm_cd = { + NULL, "imxccm", DV_DULL +}; + +void +imxccm_attach(struct device *parent, struct device *self, void *args) +{ + struct imx_attach_args *ia = args; + struct imxccm_softc *sc = (struct imxccm_softc *) self; + + imxccm_sc = sc; + sc->sc_iot = ia->ia_iot; + if (bus_space_map(sc->sc_iot, ia->ia_dev->mem[0].addr, + ia->ia_dev->mem[0].size, 0, &sc->sc_ioh)) + panic("imxccm_attach: bus_space_map failed!"); + + printf(": imx6 rev 1.%d CPU freq: %d MHz", + HREAD4(sc, CCM_ANALOG_DIGPROG) & CCM_ANALOG_DIGPROG_MINOR_MASK, + imxccm_get_armclk() / 1000); + + printf("\n"); + + cpu_cpuspeed = imxccm_cpuspeed; +} + +int +imxccm_cpuspeed(int *freq) +{ + *freq = imxccm_get_armclk() / 1000; + return (0); +} + +unsigned int +imxccm_decode_pll(enum clocks pll, unsigned int freq) +{ + struct imxccm_softc *sc = imxccm_sc; + uint32_t div; + + switch (pll) { + case ARM_PLL1: + if (HREAD4(sc, CCM_ANALOG_PLL_ARM) + & CCM_ANALOG_PLL_ARM_BYPASS) + return freq; + div = HREAD4(sc, CCM_ANALOG_PLL_ARM) + & CCM_ANALOG_PLL_ARM_DIV_SELECT_MASK; + return (freq * div) / 2; + case SYS_PLL2: + div = HREAD4(sc, CCM_ANALOG_PLL_SYS) + & CCM_ANALOG_PLL_SYS_DIV_SELECT_MASK; + return freq * (20 + (div << 1)); + case USB1_PLL3: + div = HREAD4(sc, CCM_ANALOG_PLL_USB2) + & CCM_ANALOG_PLL_USB2_DIV_SELECT_MASK; + return freq * (20 + (div << 1)); + default: + return 0; + } +} + +unsigned int +imxccm_get_pll2_pfd(unsigned int pfd) +{ + struct imxccm_softc *sc = imxccm_sc; + + return imxccm_decode_pll(SYS_PLL2, HCLK_FREQ) * 18 + / CCM_ANALOG_PFD_528_PFDx_FRAC(HREAD4(sc, CCM_ANALOG_PFD_528), pfd); +} + +unsigned int +imxccm_get_pll3_pfd(unsigned int pfd) +{ + struct imxccm_softc *sc = imxccm_sc; + + return imxccm_decode_pll(USB1_PLL3, HCLK_FREQ) * 18 + / CCM_ANALOG_PFD_480_PFDx_FRAC(HREAD4(sc, CCM_ANALOG_PFD_480), pfd); +} + +unsigned int +imxccm_get_armclk() +{ + struct imxccm_softc *sc = imxccm_sc; + + uint32_t ccsr = HREAD4(sc, CCM_CCSR); + + if (!(ccsr & CCM_CCSR_PLL1_SW_CLK_SEL)) + return imxccm_decode_pll(ARM_PLL1, HCLK_FREQ); + else if (ccsr & CCM_CCSR_STEP_SEL) + return imxccm_get_pll2_pfd(2); + else + return HCLK_FREQ; +} + +void +imxccm_armclk_set_parent(enum clocks clock) +{ + struct imxccm_softc *sc = imxccm_sc; + + switch (clock) + { + case ARM_PLL1: + /* jump onto pll1 */ + HCLR4(sc, CCM_CCSR, CCM_CCSR_PLL1_SW_CLK_SEL); + /* put step clk on OSC, power saving */ + HCLR4(sc, CCM_CCSR, CCM_CCSR_STEP_SEL); + break; + case OSC: + /* put step clk on OSC */ + HCLR4(sc, CCM_CCSR, CCM_CCSR_STEP_SEL); + /* jump onto step clk */ + HSET4(sc, CCM_CCSR, CCM_CCSR_PLL1_SW_CLK_SEL); + break; + case SYS_PLL2_PFD2: + /* put step clk on pll2-pfd2 400 MHz */ + HSET4(sc, CCM_CCSR, CCM_CCSR_STEP_SEL); + /* jump onto step clk */ + HSET4(sc, CCM_CCSR, CCM_CCSR_PLL1_SW_CLK_SEL); + break; + default: + panic("%s: parent not possible for arm clk", __func__); + } +} + +void +imxccm_armclk_set_freq(unsigned int freq) +{ + if (freq > 1296000 || freq < 648000) + panic("%s: frequency must be between 648MHz and 1296MHz!", + __func__); +} + +unsigned int +imxccm_get_usdhx(int x) +{ + struct imxccm_softc *sc = imxccm_sc; + + uint32_t cscmr1 = HREAD4(sc, CCM_CSCMR1); + uint32_t cscdr1 = HREAD4(sc, CCM_CSCDR1); + uint32_t podf, clkroot; + + // Odd bitsetting. Damn you. + if (x == 1) + podf = ((cscdr1 >> 11) & CCM_CSCDR1_USDHCx_PODF_MASK); + else + podf = ((cscdr1 >> (10 + 3*x)) & CCM_CSCDR1_USDHCx_PODF_MASK); + + if (cscmr1 & (1 << CCM_CSCDR1_USDHCx_CLK_SEL_SHIFT(x))) + clkroot = imxccm_get_pll2_pfd(0); // 352 MHz + else + clkroot = imxccm_get_pll2_pfd(2); // 396 MHz + + return clkroot / (podf + 1); +} + +unsigned int +imxccm_get_uartclk() +{ + struct imxccm_softc *sc = imxccm_sc; + + uint32_t clkroot = PLL3_80M; + uint32_t podf = HREAD4(sc, CCM_CSCDR1) & CCM_CSCDR1_UART_PODF_MASK; + + return clkroot / (podf + 1); +} + +unsigned int +imxccm_get_periphclk() +{ + struct imxccm_softc *sc = imxccm_sc; + + if ((HREAD4(sc, CCM_CBCDR) >> CCM_CBCDR_PERIPH_CLK_SEL_SHIFT) + & CCM_CBCDR_PERIPH_CLK_SEL_MASK) { + switch((HREAD4(sc, CCM_CBCMR) + >> CCM_CBCMR_PERIPH_CLK2_SEL_SHIFT) & CCM_CBCMR_PERIPH_CLK2_SEL_MASK) { + case 0: + return imxccm_decode_pll(USB1_PLL3, HCLK_FREQ); + case 1: + case 2: + return HCLK_FREQ; + default: + return 0; + } + + } else { + switch((HREAD4(sc, CCM_CBCMR) + >> CCM_CBCMR_PRE_PERIPH_CLK_SEL_SHIFT) & CCM_CBCMR_PRE_PERIPH_CLK_SEL_MASK) { + default: + case 0: + return imxccm_decode_pll(SYS_PLL2, HCLK_FREQ); + case 1: + return imxccm_get_pll2_pfd(2); // 396 MHz + case 2: + return imxccm_get_pll2_pfd(0); // 352 MHz + case 3: + return imxccm_get_pll2_pfd(2) / 2; // 198 MHz + } + } +} + +unsigned int +imxccm_get_fecclk() +{ + struct imxccm_softc *sc = imxccm_sc; + uint32_t div = 0; + + switch (HREAD4(sc, CCM_ANALOG_PLL_ENET) & 0x3) + { + case 0: + div = 20; + case 1: + div = 10; + case 2: + div = 5; + case 3: + div = 4; + } + + return 500000 / div ; +} + +unsigned int +imxccm_get_ahbclk() +{ + struct imxccm_softc *sc = imxccm_sc; + uint32_t ahb_podf; + + ahb_podf = (HREAD4(sc, CCM_CBCDR) >> CCM_CBCDR_AHB_PODF_SHIFT) + & CCM_CBCDR_AHB_PODF_MASK; + return imxccm_get_periphclk() / (ahb_podf + 1); +} + +unsigned int +imxccm_get_ipgclk() +{ + struct imxccm_softc *sc = imxccm_sc; + uint32_t ipg_podf; + + ipg_podf = (HREAD4(sc, CCM_CBCDR) >> CCM_CBCDR_IPG_PODF_SHIFT) + & CCM_CBCDR_IPG_PODF_MASK; + return imxccm_get_ahbclk() / (ipg_podf + 1); +} + +unsigned int +imxccm_get_ipg_perclk() +{ + struct imxccm_softc *sc = imxccm_sc; + uint32_t ipg_podf; + + ipg_podf = HREAD4(sc, CCM_CSCMR1) & CCM_CSCMR1_PERCLK_CLK_SEL_MASK; + + return imxccm_get_ipgclk() / (ipg_podf + 1); +} + +void +imxccm_enable_i2c(int x) +{ + struct imxccm_softc *sc = imxccm_sc; + + HSET4(sc, CCM_CCGR2, CCM_CCGR2_I2C(x)); +} + +void +imxccm_enable_usboh3(void) +{ + struct imxccm_softc *sc = imxccm_sc; + + HSET4(sc, CCM_CCGR6, CCM_CCGR6_USBOH3); +} + +void +imxccm_enable_pll_enet(void) +{ + struct imxccm_softc *sc = imxccm_sc; + + if (HREAD4(sc, CCM_ANALOG_PLL_ENET) & CCM_ANALOG_PLL_ENET_ENABLE) + return; + + HCLR4(sc, CCM_ANALOG_PLL_ENET, CCM_ANALOG_PLL_ENET_POWERDOWN); + + HSET4(sc, CCM_ANALOG_PLL_ENET, CCM_ANALOG_PLL_ENET_ENABLE); + + while(!(HREAD4(sc, CCM_ANALOG_PLL_ENET) & CCM_ANALOG_PLL_ENET_LOCK)); + + HCLR4(sc, CCM_ANALOG_PLL_ENET, CCM_ANALOG_PLL_ENET_BYPASS); +} + +void +imxccm_enable_enet(void) +{ + struct imxccm_softc *sc = imxccm_sc; + + imxccm_enable_pll_enet(); + HWRITE4(sc, CCM_ANALOG_PLL_ENET_SET, CCM_ANALOG_PLL_ENET_DIV_125M); + + HSET4(sc, CCM_CCGR1, CCM_CCGR1_ENET); +} + +void +imxccm_enable_sata(void) +{ + struct imxccm_softc *sc = imxccm_sc; + + imxccm_enable_pll_enet(); + HWRITE4(sc, CCM_ANALOG_PLL_ENET_SET, CCM_ANALOG_PLL_ENET_100M_SATA); + + HSET4(sc, CCM_CCGR5, CCM_CCGR5_100M_SATA); +} + +void +imxccm_enable_pcie(void) +{ + struct imxccm_softc *sc = imxccm_sc; + + HWRITE4(sc, CCM_PMU_MISC1, + (HREAD4(sc, CCM_PMU_MISC1) & ~CCM_PMU_MISC1_LVDSCLK1_CLK_SEL_MASK) + | CCM_PMU_MISC1_LVDSCLK1_CLK_SEL_SATA + | CCM_PMU_MISC1_LVDSCLK1_OBEN + | CCM_PMU_MISC1_LVDSCLK1_IBEN); + + imxccm_enable_pll_enet(); + HWRITE4(sc, CCM_ANALOG_PLL_ENET_SET, CCM_ANALOG_PLL_ENET_125M_PCIE); + + HSET4(sc, CCM_CCGR4, CCM_CCGR4_125M_PCIE); +} + +void +imxccm_disable_usb2_chrg_detect(void) +{ + struct imxccm_softc *sc = imxccm_sc; + + HWRITE4(sc, CCM_ANALOG_USB2_CHRG_DETECT_SET, + CCM_ANALOG_USB2_CHRG_DETECT_CHK_CHRG_B + | CCM_ANALOG_USB2_CHRG_DETECT_EN_B); +} + +void +imxccm_enable_pll_usb2(void) +{ + struct imxccm_softc *sc = imxccm_sc; + + HWRITE4(sc, CCM_ANALOG_PLL_USB2_CLR, CCM_ANALOG_PLL_USB2_BYPASS); + + HWRITE4(sc, CCM_ANALOG_PLL_USB2_SET, + CCM_ANALOG_PLL_USB2_ENABLE + | CCM_ANALOG_PLL_USB2_POWER + | CCM_ANALOG_PLL_USB2_EN_USB_CLKS); +} diff --git a/sys/arch/armv7/imx/imxccmvar.h b/sys/arch/armv7/imx/imxccmvar.h new file mode 100644 index 00000000000..decfc2ae4b7 --- /dev/null +++ b/sys/arch/armv7/imx/imxccmvar.h @@ -0,0 +1,34 @@ +/* $OpenBSD: imxccmvar.h,v 1.1 2013/09/06 20:45:53 patrick Exp $ */ +/* + * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IMXCCMVAR_H +#define IMXCCMVAR_H + +unsigned int imxccm_get_usdhx(int x); +unsigned int imxccm_get_fecclk(void); +unsigned int imxccm_get_uartclk(void); +unsigned int imxccm_get_ipg_perclk(void); +unsigned int imxccm_get_ahbclk(void); +void imxccm_enable_i2c(int x); +void imxccm_enable_usboh3(void); +void imxccm_disable_usb2_chrg_detect(void); +void imxccm_enable_pll_usb2(void); +void imxccm_enable_enet(void); +void imxccm_enable_sata(void); +void imxccm_enable_pcie(void); + +#endif /* IMXCCMVAR_H */ diff --git a/sys/arch/armv7/imx/imxdog.c b/sys/arch/armv7/imx/imxdog.c new file mode 100644 index 00000000000..a6b32478d70 --- /dev/null +++ b/sys/arch/armv7/imx/imxdog.c @@ -0,0 +1,90 @@ +/* $OpenBSD: imxdog.c,v 1.1 2013/09/06 20:45:53 patrick Exp $ */ +/* + * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/queue.h> +#include <sys/malloc.h> +#include <sys/device.h> +#include <sys/evcount.h> +#include <sys/socket.h> +#include <sys/timeout.h> +#include <machine/intr.h> +#include <machine/bus.h> +#include <armv7/imx/imxvar.h> + +/* registers */ +#define WCR 0x00 +#define WSR 0x02 +#define WRSR 0x04 +#define WICR 0x06 +#define WMCR 0x08 + +struct imxdog_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; +}; + +struct imxdog_softc *imxdog_sc; + +void imxdog_attach(struct device *parent, struct device *self, void *args); +void imxdog_reset(void); + +struct cfattach imxdog_ca = { + sizeof (struct imxdog_softc), NULL, imxdog_attach +}; + +struct cfdriver imxdog_cd = { + NULL, "imxdog", DV_DULL +}; + +void +imxdog_attach(struct device *parent, struct device *self, void *args) +{ + struct imx_attach_args *ia = args; + struct imxdog_softc *sc = (struct imxdog_softc *) self; + + sc->sc_iot = ia->ia_iot; + if (bus_space_map(sc->sc_iot, ia->ia_dev->mem[0].addr, + ia->ia_dev->mem[0].size, 0, &sc->sc_ioh)) + panic("imxdog_attach: bus_space_map failed!"); + + printf("\n"); + imxdog_sc = sc; +} + +void +imxdog_reset() +{ + if (imxdog_sc == NULL) + return; + + /* disable watchdog and set timeout to 0 */ + bus_space_write_2(imxdog_sc->sc_iot, imxdog_sc->sc_ioh, WCR, 0); + + /* sequence to reset timeout counter */ + bus_space_write_2(imxdog_sc->sc_iot, imxdog_sc->sc_ioh, WSR, 0x5555); + bus_space_write_2(imxdog_sc->sc_iot, imxdog_sc->sc_ioh, WSR, 0xaaaa); + + /* enable watchdog */ + bus_space_write_2(imxdog_sc->sc_iot, imxdog_sc->sc_ioh, WCR, 1); + /* errata TKT039676 */ + bus_space_write_2(imxdog_sc->sc_iot, imxdog_sc->sc_ioh, WCR, 1); + + delay(100000); +} diff --git a/sys/arch/armv7/imx/imxehci.c b/sys/arch/armv7/imx/imxehci.c new file mode 100644 index 00000000000..065316d4684 --- /dev/null +++ b/sys/arch/armv7/imx/imxehci.c @@ -0,0 +1,250 @@ +/* $OpenBSD: imxehci.c,v 1.1 2013/09/06 20:45:53 patrick Exp $ */ +/* + * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/device.h> +#include <sys/kernel.h> +#include <sys/rwlock.h> +#include <sys/timeout.h> + +#include <machine/intr.h> +#include <machine/bus.h> + +#include <dev/usb/usb.h> +#include <dev/usb/usbdi.h> +#include <dev/usb/usbdivar.h> +#include <dev/usb/usb_mem.h> + +#include <armv7/imx/imxvar.h> +#include <armv7/imx/imxccmvar.h> +#include <armv7/imx/imxgpiovar.h> + +#include <dev/usb/ehcireg.h> +#include <dev/usb/ehcivar.h> + +/* usb phy */ +#define USBPHY_PWD 0x00 +#define USBPHY_CTRL 0x30 +#define USBPHY_CTRL_SET 0x34 +#define USBPHY_CTRL_CLR 0x38 +#define USBPHY_CTRL_TOG 0x3c + +#define USBPHY_CTRL_ENUTMILEVEL2 (1 << 14) +#define USBPHY_CTRL_ENUTMILEVEL3 (1 << 15) +#define USBPHY_CTRL_CLKGATE (1 << 30) +#define USBPHY_CTRL_SFTRST (1 << 31) + +/* ehci */ +#define EHCI_USBMODE 0x68 + +#define EHCI_USBMODE_HOST (3 << 0) +#define EHCI_PS_PTS_UTMI_MASK ((1 << 25) | (3 << 30)) + +/* usb non-core */ +#define USBNC_USB_UH1_CTRL 0x04 + +#define USBNC_USB_UH1_CTRL_OVER_CUR_POL (1 << 8) +#define USBNC_USB_UH1_CTRL_OVER_CUR_DIS (1 << 7) + +/* board specific */ +#define EHCI_PHYFLEX_USB_H1_PWR 0 +#define EHCI_PHYFLEX_USB_OTG_PWR 111 + +void imxehci_attach(struct device *, struct device *, void *); +int imxehci_detach(struct device *, int); + +struct imxehci_softc { + struct ehci_softc sc; + void *sc_ih; + bus_space_handle_t uh_ioh; + bus_space_handle_t ph_ioh; + bus_space_handle_t nc_ioh; +}; + +struct cfattach imxehci_ca = { + sizeof (struct imxehci_softc), NULL, imxehci_attach, + imxehci_detach, NULL +}; + +void +imxehci_attach(struct device *parent, struct device *self, void *aux) +{ + struct imxehci_softc *sc = (struct imxehci_softc *)self; + struct imx_attach_args *ia = aux; + usbd_status r; + char *devname = sc->sc.sc_bus.bdev.dv_xname; + + sc->sc.iot = ia->ia_iot; + sc->sc.sc_bus.dmatag = ia->ia_dmat; + sc->sc.sc_size = ia->ia_dev->mem[0].size; + + /* Map I/O space */ + if (bus_space_map(sc->sc.iot, ia->ia_dev->mem[0].addr, + ia->ia_dev->mem[0].size, 0, &sc->sc.ioh)) { + printf(": cannot map mem space\n"); + goto out; + } + + if (bus_space_map(sc->sc.iot, ia->ia_dev->mem[1].addr, + ia->ia_dev->mem[1].size, 0, &sc->uh_ioh)) { + printf(": cannot map mem space\n"); + goto mem0; + } + + if (bus_space_map(sc->sc.iot, ia->ia_dev->mem[2].addr, + ia->ia_dev->mem[2].size, 0, &sc->ph_ioh)) { + printf(": cannot map mem space\n"); + goto mem1; + } + + if (bus_space_map(sc->sc.iot, ia->ia_dev->mem[3].addr, + ia->ia_dev->mem[3].size, 0, &sc->nc_ioh)) { + printf(": cannot map mem space\n"); + goto mem2; + } + + printf("\n"); + + /* enable usb port power */ + switch (board_id) + { + case BOARD_ID_IMX6_PHYFLEX: + imxgpio_set_dir(EHCI_PHYFLEX_USB_H1_PWR, IMXGPIO_DIR_OUT); + delay(10); + imxgpio_set_bit(EHCI_PHYFLEX_USB_H1_PWR); + delay(10); + break; + } + + imxccm_enable_usboh3(); + delay(1000); + /* disable the carger detection, else signal on DP will be poor */ + imxccm_disable_usb2_chrg_detect(); + /* power host 1 */ + imxccm_enable_pll_usb2(); + + /* over current and polarity setting */ + bus_space_write_4(sc->sc.iot, sc->nc_ioh, USBNC_USB_UH1_CTRL, + bus_space_read_4(sc->sc.iot, sc->nc_ioh, USBNC_USB_UH1_CTRL) | + (USBNC_USB_UH1_CTRL_OVER_CUR_POL | USBNC_USB_UH1_CTRL_OVER_CUR_DIS)); + + bus_space_write_4(sc->sc.iot, sc->ph_ioh, USBPHY_CTRL_CLR, + USBPHY_CTRL_CLKGATE); + + /* Disable interrupts, so we don't get any spurious ones. */ + sc->sc.sc_offs = EREAD1(&sc->sc, EHCI_CAPLENGTH); + EOWRITE2(&sc->sc, EHCI_USBINTR, 0); + + /* Stop then Reset */ + uint32_t val = EOREAD4(&sc->sc, EHCI_USBCMD); + val &= ~EHCI_CMD_RS; + EOWRITE4(&sc->sc, EHCI_USBCMD, val); + + while (EOREAD4(&sc->sc, EHCI_USBCMD) & EHCI_CMD_RS) + ; + + val = EOREAD4(&sc->sc, EHCI_USBCMD); + val |= EHCI_CMD_HCRESET; + EOWRITE4(&sc->sc, EHCI_USBCMD, val); + + while (EOREAD4(&sc->sc, EHCI_USBCMD) & EHCI_CMD_HCRESET) + ; + + /* Reset USBPHY module */ + bus_space_write_4(sc->sc.iot, sc->ph_ioh, USBPHY_CTRL_SET, USBPHY_CTRL_SFTRST); + + delay(10); + + /* Remove CLKGATE and SFTRST */ + bus_space_write_4(sc->sc.iot, sc->ph_ioh, USBPHY_CTRL_CLR, + USBPHY_CTRL_CLKGATE | USBPHY_CTRL_SFTRST); + + delay(10); + + /* Power up the PHY */ + bus_space_write_4(sc->sc.iot, sc->ph_ioh, USBPHY_PWD, 0); + + /* enable FS/LS device */ + bus_space_write_4(sc->sc.iot, sc->ph_ioh, USBPHY_CTRL_SET, + USBPHY_CTRL_ENUTMILEVEL2 | USBPHY_CTRL_ENUTMILEVEL3); + + /* set host mode */ + EWRITE4(&sc->sc, EHCI_USBMODE, + EREAD4(&sc->sc, EHCI_USBMODE) | EHCI_USBMODE_HOST); + + /* set to UTMI mode */ + EOWRITE4(&sc->sc, EHCI_PORTSC(1), + EOREAD4(&sc->sc, EHCI_PORTSC(1)) & ~EHCI_PS_PTS_UTMI_MASK); + + sc->sc_ih = arm_intr_establish(ia->ia_dev->irq[0], IPL_USB, + ehci_intr, &sc->sc, devname); + if (sc->sc_ih == NULL) { + printf(": unable to establish interrupt\n"); + goto mem3; + } + + strlcpy(sc->sc.sc_vendor, "i.MX6", sizeof(sc->sc.sc_vendor)); + r = ehci_init(&sc->sc); + if (r != USBD_NORMAL_COMPLETION) { + printf("%s: init failed, error=%d\n", devname, r); + goto intr; + } + + sc->sc.sc_child = config_found((void *)sc, &sc->sc.sc_bus, + usbctlprint); + + goto out; + +intr: + arm_intr_disestablish(sc->sc_ih); + sc->sc_ih = NULL; +mem3: + bus_space_unmap(sc->sc.iot, sc->nc_ioh, ia->ia_dev->mem[3].addr); +mem2: + bus_space_unmap(sc->sc.iot, sc->ph_ioh, ia->ia_dev->mem[2].addr); +mem1: + bus_space_unmap(sc->sc.iot, sc->uh_ioh, ia->ia_dev->mem[1].addr); +mem0: + bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size); + sc->sc.sc_size = 0; +out: + return; +} + +int +imxehci_detach(struct device *self, int flags) +{ + struct imxehci_softc *sc = (struct imxehci_softc *)self; + int rv; + + rv = ehci_detach(&sc->sc, flags); + if (rv) + return (rv); + + if (sc->sc_ih != NULL) { + arm_intr_disestablish(sc->sc_ih); + sc->sc_ih = NULL; + } + + if (sc->sc.sc_size) { + bus_space_unmap(sc->sc.iot, sc->sc.ioh, sc->sc.sc_size); + sc->sc.sc_size = 0; + } + + return (0); +} diff --git a/sys/arch/armv7/imx/imxenet.c b/sys/arch/armv7/imx/imxenet.c new file mode 100644 index 00000000000..b1426b5285d --- /dev/null +++ b/sys/arch/armv7/imx/imxenet.c @@ -0,0 +1,964 @@ +/* $OpenBSD: imxenet.c,v 1.1 2013/09/06 20:45:53 patrick Exp $ */ +/* + * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/sockio.h> +#include <sys/queue.h> +#include <sys/malloc.h> +#include <sys/device.h> +#include <sys/evcount.h> +#include <sys/socket.h> +#include <sys/timeout.h> +#include <sys/mbuf.h> +#include <machine/intr.h> +#include <machine/bus.h> + +#include "bpfilter.h" + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#if NBPFILTER > 0 +#include <net/bpf.h> +#endif + +#ifdef INET +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#include <netinet/if_ether.h> +#endif + +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> + +#include <armv7/imx/imxvar.h> +#include <armv7/imx/imxenet.h> +#include <armv7/imx/imxccmvar.h> +#include <armv7/imx/imxgpiovar.h> +#include <armv7/imx/imxocotpvar.h> + +/* configuration registers */ +#define ENET_EIR 0x004 +#define ENET_EIMR 0x008 +#define ENET_RDAR 0x010 +#define ENET_TDAR 0x014 +#define ENET_ECR 0x024 +#define ENET_MMFR 0x040 +#define ENET_MSCR 0x044 +#define ENET_MIBC 0x064 +#define ENET_RCR 0x084 +#define ENET_TCR 0x0C4 +#define ENET_PALR 0x0E4 +#define ENET_PAUR 0x0E8 +#define ENET_OPD 0x0EC +#define ENET_IAUR 0x118 +#define ENET_IALR 0x11C +#define ENET_GAUR 0x120 +#define ENET_GALR 0x124 +#define ENET_TFWR 0x144 +#define ENET_RDSR 0x180 +#define ENET_TDSR 0x184 +#define ENET_MRBR 0x188 +#define ENET_RSFL 0x190 +#define ENET_RSEM 0x194 +#define ENET_RAEM 0x198 +#define ENET_RAFL 0x19C +#define ENET_TSEM 0x1A0 +#define ENET_TAEM 0x1A4 +#define ENET_TAFL 0x1A8 +#define ENET_TIPG 0x1AC +#define ENET_FTRL 0x1B0 +#define ENET_TACC 0x1C0 +#define ENET_RACC 0x1C4 + +#define ENET_RDAR_RDAR (1 << 24) +#define ENET_TDAR_TDAR (1 << 24) +#define ENET_ECR_RESET (1 << 0) +#define ENET_ECR_ETHEREN (1 << 1) +#define ENET_ECR_EN1588 (1 << 4) +#define ENET_ECR_SPEED (1 << 5) +#define ENET_ECR_DBSWP (1 << 8) +#define ENET_MMFR_TA (2 << 16) +#define ENET_MMFR_RA_SHIFT 18 +#define ENET_MMFR_PA_SHIFT 23 +#define ENET_MMFR_OP_WR (1 << 28) +#define ENET_MMFR_OP_RD (2 << 28) +#define ENET_MMFR_ST (1 << 30) +#define ENET_RCR_MII_MODE (1 << 2) +#define ENET_RCR_PROM (1 << 3) +#define ENET_RCR_FCE (1 << 5) +#define ENET_RCR_RGMII_MODE (1 << 6) +#define ENET_RCR_MAX_FL(x) (((x) & 0x3fff) << 16) +#define ENET_TCR_FDEN (1 << 2) +#define ENET_EIR_MII (1 << 23) +#define ENET_EIR_RXF (1 << 25) +#define ENET_EIR_TXF (1 << 27) +#define ENET_TFWR_STRFWD (1 << 8) + +/* statistics counters */ + +/* 1588 control */ +#define ENET_ATCR 0x400 +#define ENET_ATVR 0x404 +#define ENET_ATOFF 0x408 +#define ENET_ATPER 0x40C +#define ENET_ATCOR 0x410 +#define ENET_ATINC 0x414 +#define ENET_ATSTMP 0x418 + +/* capture / compare block */ +#define ENET_TGSR 0x604 +#define ENET_TCSR0 0x608 +#define ENET_TCCR0 0x60C +#define ENET_TCSR1 0x610 +#define ENET_TCCR1 0x614 +#define ENET_TCSR2 0x618 +#define ENET_TCCR2 0x61C +#define ENET_TCSR3 0x620 +#define ENET_TCCR3 0x624 + +#define ENET_MII_CLK 2500 +#define ENET_ALIGNMENT 16 + +#define ENET_SABRELITE_PHY 6 +#define ENET_PHYFLEX_PHY 3 +#define ENET_PHYFLEX_PHY_RST 87 + +#define HREAD4(sc, reg) \ + (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) +#define HWRITE4(sc, reg, val) \ + bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) +#define HSET4(sc, reg, bits) \ + HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) +#define HCLR4(sc, reg, bits) \ + HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) + +struct imxenet_softc { + struct device sc_dev; + struct arpcom sc_ac; + struct mii_data sc_mii; + int sc_phyno; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + void *sc_ih; /* Interrupt handler */ + bus_dma_tag_t sc_dma_tag; + uint32_t intr_status; /* soft interrupt status */ + struct imxenet_dma_alloc txdma; /* bus_dma glue for tx desc */ + struct imxenet_buf_desc *tx_desc_base; + struct imxenet_dma_alloc rxdma; /* bus_dma glue for rx desc */ + struct imxenet_buf_desc *rx_desc_base; + struct imxenet_dma_alloc tbdma; /* bus_dma glue for packets */ + struct imxenet_buffer *tx_buffer_base; + struct imxenet_dma_alloc rbdma; /* bus_dma glue for packets */ + struct imxenet_buffer *rx_buffer_base; + int cur_tx; + int cur_rx; +}; + +struct imxenet_softc *imxenet_sc; + +void imxenet_attach(struct device *, struct device *, void *); +void imxenet_chip_init(struct imxenet_softc *); +int imxenet_ioctl(struct ifnet *, u_long, caddr_t); +void imxenet_start(struct ifnet *); +int imxenet_encap(struct imxenet_softc *, struct mbuf *); +void imxenet_init_txd(struct imxenet_softc *); +void imxenet_init_rxd(struct imxenet_softc *); +void imxenet_init(struct imxenet_softc *); +void imxenet_stop(struct imxenet_softc *); +void imxenet_iff(struct imxenet_softc *); +struct mbuf * imxenet_newbuf(void); +int imxenet_intr(void *); +void imxenet_recv(struct imxenet_softc *); +int imxenet_wait_intr(struct imxenet_softc *, int, int); +int imxenet_miibus_readreg(struct device *, int, int); +void imxenet_miibus_writereg(struct device *, int, int, int); +void imxenet_miibus_statchg(struct device *); +int imxenet_ifmedia_upd(struct ifnet *); +void imxenet_ifmedia_sts(struct ifnet *, struct ifmediareq *); +int imxenet_dma_malloc(struct imxenet_softc *, bus_size_t, struct imxenet_dma_alloc *); +void imxenet_dma_free(struct imxenet_softc *, struct imxenet_dma_alloc *); + +struct cfattach imxenet_ca = { + sizeof (struct imxenet_softc), NULL, imxenet_attach +}; + +struct cfdriver imxenet_cd = { + NULL, "imxenet", DV_DULL +}; + +void +imxenet_attach(struct device *parent, struct device *self, void *args) +{ + struct imx_attach_args *ia = args; + struct imxenet_softc *sc = (struct imxenet_softc *) self; + struct mii_data *mii; + struct ifnet *ifp; + int tsize, rsize, tbsize, rbsize, s; + + sc->sc_iot = ia->ia_iot; + if (bus_space_map(sc->sc_iot, ia->ia_dev->mem[0].addr, + ia->ia_dev->mem[0].size, 0, &sc->sc_ioh)) + panic("imxenet_attach: bus_space_map failed!"); + + sc->sc_dma_tag = ia->ia_dmat; + + /* power it up */ + imxccm_enable_enet(); + + switch (board_id) + { + case BOARD_ID_IMX6_PHYFLEX: + case BOARD_ID_IMX6_SABRELITE: + /* phyFLEX i.MX6 and SABRE Lite PHY reset */ + imxgpio_set_dir(ENET_PHYFLEX_PHY_RST, IMXGPIO_DIR_OUT); + delay(10); + imxgpio_set_bit(ENET_PHYFLEX_PHY_RST); + delay(10); + break; + } + + /* reset the controller */ + HSET4(sc, ENET_ECR, ENET_ECR_RESET); + while(HREAD4(sc, ENET_ECR) & ENET_ECR_RESET); + + HWRITE4(sc, ENET_EIMR, 0); + HWRITE4(sc, ENET_EIR, 0xffffffff); + + sc->sc_ih = arm_intr_establish(ia->ia_dev->irq[0], IPL_NET, + imxenet_intr, sc, sc->sc_dev.dv_xname); + + tsize = ENET_MAX_TXD * sizeof(struct imxenet_buf_desc); + tsize = ENET_ROUNDUP(tsize, PAGE_SIZE); + + if (imxenet_dma_malloc(sc, tsize, &sc->txdma)) { + printf("%s: Unable to allocate tx_desc memory\n", + sc->sc_dev.dv_xname); + goto bad; + } + sc->tx_desc_base = (struct imxenet_buf_desc *)sc->txdma.dma_vaddr; + + rsize = ENET_MAX_RXD * sizeof(struct imxenet_buf_desc); + rsize = ENET_ROUNDUP(rsize, PAGE_SIZE); + + if (imxenet_dma_malloc(sc, rsize, &sc->rxdma)) { + printf("%s: Unable to allocate rx_desc memory\n", + sc->sc_dev.dv_xname); + goto txdma; + } + sc->rx_desc_base = (struct imxenet_buf_desc *)sc->rxdma.dma_vaddr; + + tbsize = ENET_MAX_TXD * ENET_MAX_PKT_SIZE; + tbsize = ENET_ROUNDUP(tbsize, PAGE_SIZE); + + if (imxenet_dma_malloc(sc, tbsize, &sc->tbdma)) { + printf("%s: Unable to allocate tx_buffer memory\n", + sc->sc_dev.dv_xname); + goto rxdma; + } + sc->tx_buffer_base = (struct imxenet_buffer *)sc->tbdma.dma_vaddr; + + rbsize = ENET_MAX_RXD * ENET_MAX_PKT_SIZE; + rbsize = ENET_ROUNDUP(rbsize, PAGE_SIZE); + + if (imxenet_dma_malloc(sc, rbsize, &sc->rbdma)) { + printf("%s: Unable to allocate rx_buffer memory\n", + sc->sc_dev.dv_xname); + goto tbdma; + } + sc->rx_buffer_base = (struct imxenet_buffer *)sc->rbdma.dma_vaddr; + + sc->cur_tx = 0; + sc->cur_rx = 0; + + printf("\n"); + + s = splnet(); + + ifp = &sc->sc_ac.ac_if; + ifp->if_softc = sc; + strlcpy(ifp->if_xname, sc->sc_dev.dv_xname, IFNAMSIZ); + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_ioctl = imxenet_ioctl; + ifp->if_start = imxenet_start; + ifp->if_capabilities = IFCAP_VLAN_MTU; + + memset(sc->sc_ac.ac_enaddr, 0xff, ETHER_ADDR_LEN); + imxocotp_get_ethernet_address(sc->sc_ac.ac_enaddr); + + printf("%s: address %s\n", sc->sc_dev.dv_xname, + ether_sprintf(sc->sc_ac.ac_enaddr)); + + /* initialize the chip */ + imxenet_chip_init(sc); + + IFQ_SET_READY(&ifp->if_snd); + + /* Initialize MII/media info. */ + mii = &sc->sc_mii; + mii->mii_ifp = ifp; + mii->mii_readreg = imxenet_miibus_readreg; + mii->mii_writereg = imxenet_miibus_writereg; + mii->mii_statchg = imxenet_miibus_statchg; + mii->mii_flags = MIIF_AUTOTSLEEP; + + ifmedia_init(&mii->mii_media, 0, imxenet_ifmedia_upd, imxenet_ifmedia_sts); + mii_attach(self, mii, 0xffffffff, MII_PHY_ANY, MII_OFFSET_ANY, 0); + + if (LIST_FIRST(&mii->mii_phys) == NULL) { + ifmedia_add(&mii->mii_media, IFM_ETHER | IFM_NONE, 0, NULL); + ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_NONE); + } else + ifmedia_set(&mii->mii_media, IFM_ETHER | IFM_AUTO); + + if_attach(ifp); + ether_ifattach(ifp); + splx(s); + + imxenet_sc = sc; + return; + +tbdma: + imxenet_dma_free(sc, &sc->tbdma); +rxdma: + imxenet_dma_free(sc, &sc->rxdma); +txdma: + imxenet_dma_free(sc, &sc->txdma); +bad: + bus_space_unmap(sc->sc_iot, sc->sc_ioh, ia->ia_dev->mem[0].size); +} + +void +imxenet_chip_init(struct imxenet_softc *sc) +{ + struct device *dev = (struct device *) sc; + int phy = 0; + + bus_space_write_4(sc->sc_iot, sc->sc_ioh, ENET_MSCR, + (((imxccm_get_fecclk() + (ENET_MII_CLK << 2) - 1) / (ENET_MII_CLK << 2)) << 1) | 0x100); + + switch (board_id) + { + case BOARD_ID_IMX6_SABRELITE: + phy = ENET_SABRELITE_PHY; + break; + case BOARD_ID_IMX6_PHYFLEX: + phy = ENET_PHYFLEX_PHY; + break; + } + + switch (board_id) + { + case BOARD_ID_IMX6_PHYFLEX: + case BOARD_ID_IMX6_SABRELITE: + /* prefer master mode */ + imxenet_miibus_writereg(dev, phy, 0x9, 0x1f00); + + /* min rx data delay */ + imxenet_miibus_writereg(dev, phy, 0x0b, 0x8105); + imxenet_miibus_writereg(dev, phy, 0x0c, 0x0000); + + /* min tx data delay */ + imxenet_miibus_writereg(dev, phy, 0x0b, 0x8106); + imxenet_miibus_writereg(dev, phy, 0x0c, 0x0000); + + /* max rx/tx clock delay, min rx/tx control delay */ + imxenet_miibus_writereg(dev, phy, 0x0b, 0x8104); + imxenet_miibus_writereg(dev, phy, 0x0c, 0xf0f0); + imxenet_miibus_writereg(dev, phy, 0x0b, 0x104); + + /* enable all interrupts */ + imxenet_miibus_writereg(dev, phy, 0x1b, 0xff00); + break; + } +} + +void +imxenet_init_rxd(struct imxenet_softc *sc) +{ + int i; + + memset(sc->rx_desc_base, 0, ENET_MAX_RXD * sizeof(struct imxenet_buf_desc)); + + for (i = 0; i < ENET_MAX_RXD; i++) + { + sc->rx_desc_base[i].status = ENET_RXD_EMPTY; + sc->rx_desc_base[i].data_pointer = sc->rbdma.dma_paddr + i * ENET_MAX_PKT_SIZE; +#ifdef ENET_ENHANCED_BD + sc->rx_desc_base[i].enhanced_status = ENET_RXD_INT; +#endif + } + + sc->rx_desc_base[i - 1].status |= ENET_RXD_WRAP; +} + +void +imxenet_init_txd(struct imxenet_softc *sc) +{ + int i; + + memset(sc->tx_desc_base, 0, ENET_MAX_TXD * sizeof(struct imxenet_buf_desc)); + + for (i = 0; i < ENET_MAX_TXD; i++) + { + sc->tx_desc_base[i].data_pointer = sc->tbdma.dma_paddr + i * ENET_MAX_PKT_SIZE; + } + + sc->tx_desc_base[i - 1].status |= ENET_TXD_WRAP; +} + +void +imxenet_init(struct imxenet_softc *sc) +{ + struct ifnet *ifp = &sc->sc_ac.ac_if; + int speed = 0; + + /* reset the controller */ + HSET4(sc, ENET_ECR, ENET_ECR_RESET); + while(HREAD4(sc, ENET_ECR) & ENET_ECR_RESET); + + /* set hw address */ + HWRITE4(sc, ENET_PALR, + (sc->sc_ac.ac_enaddr[0] << 24) | + (sc->sc_ac.ac_enaddr[1] << 16) | + (sc->sc_ac.ac_enaddr[2] << 8) | + sc->sc_ac.ac_enaddr[3]); + HWRITE4(sc, ENET_PAUR, + (sc->sc_ac.ac_enaddr[4] << 24) | + (sc->sc_ac.ac_enaddr[5] << 16)); + + /* clear outstanding interrupts */ + HWRITE4(sc, ENET_EIR, 0xffffffff); + + /* set address filter */ + HWRITE4(sc, ENET_GAUR, 0); + HWRITE4(sc, ENET_GALR, 0); + + /* set max receive buffer size, 3-0 bits always zero for alignment */ + HWRITE4(sc, ENET_MRBR, ENET_MAX_PKT_SIZE); + + /* set descriptor */ + HWRITE4(sc, ENET_TDSR, sc->txdma.dma_paddr); + HWRITE4(sc, ENET_RDSR, sc->rxdma.dma_paddr); + + /* init descriptor */ + imxenet_init_txd(sc); + imxenet_init_rxd(sc); + + /* set it to full-duplex */ + HWRITE4(sc, ENET_TCR, ENET_TCR_FDEN); + + /* + * Set max frame length to 1518 or 1522 with VLANs, + * pause frames and promisc mode. + * XXX: RGMII mode - phy dependant + */ + HWRITE4(sc, ENET_RCR, + ENET_RCR_MAX_FL(1522) | ENET_RCR_RGMII_MODE | ENET_RCR_MII_MODE | + ENET_RCR_FCE); + + bus_space_write_4(sc->sc_iot, sc->sc_ioh, ENET_MSCR, + (((imxccm_get_fecclk() + (ENET_MII_CLK << 2) - 1) / (ENET_MII_CLK << 2)) << 1) | 0x100); + + /* RX FIFO treshold and pause */ + HWRITE4(sc, ENET_RSEM, 0x84); + HWRITE4(sc, ENET_RSFL, 16); + HWRITE4(sc, ENET_RAEM, 8); + HWRITE4(sc, ENET_RAFL, 8); + HWRITE4(sc, ENET_OPD, 0xFFF0); + + /* do store and forward, only i.MX6, needs to be set correctly else */ + HWRITE4(sc, ENET_TFWR, ENET_TFWR_STRFWD); + + /* enable gigabit-ethernet and set it to support little-endian */ + switch (IFM_SUBTYPE(sc->sc_mii.mii_media_active)) { + case IFM_1000_T: /* Gigabit */ + speed |= ENET_ECR_SPEED; + break; + default: + speed &= ~ENET_ECR_SPEED; + } + HWRITE4(sc, ENET_ECR, ENET_ECR_ETHEREN | speed | ENET_ECR_DBSWP); + +#ifdef ENET_ENHANCED_BD + HSET4(sc, ENET_ECR, ENET_ECR_EN1588); +#endif + + /* rx descriptors are ready */ + HWRITE4(sc, ENET_RDAR, ENET_RDAR_RDAR); + + /* enable interrupts for tx/rx */ + HWRITE4(sc, ENET_EIMR, ENET_EIR_TXF | ENET_EIR_RXF); + HWRITE4(sc, ENET_EIMR, 0xffffffff); + + /* Indicate we are up and running. */ + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; +} + +void +imxenet_stop(struct imxenet_softc *sc) +{ + /* reset the controller */ + HSET4(sc, ENET_ECR, ENET_ECR_RESET); + while(HREAD4(sc, ENET_ECR) & ENET_ECR_RESET); +} + +void +imxenet_iff(struct imxenet_softc *sc) +{ + // Set interface features +} + +int +imxenet_ioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +{ + struct imxenet_softc *sc = ifp->if_softc; + struct ifreq *ifr = (struct ifreq *)data; + struct ifaddr *ifa = (struct ifaddr *)data; + int s, error = 0; + + s = splnet(); + + switch (cmd) { + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + if (!(ifp->if_flags & IFF_RUNNING)) + imxenet_init(sc); +#ifdef INET + if (ifa->ifa_addr->sa_family == AF_INET) + arp_ifinit(&sc->sc_ac, ifa); +#endif + break; + + case SIOCSIFFLAGS: + if (ifp->if_flags & IFF_UP) { + if (ifp->if_flags & IFF_RUNNING) + error = ENETRESET; + else + imxenet_init(sc); + } else { + if (ifp->if_flags & IFF_RUNNING) + imxenet_stop(sc); + } + break; + + case SIOCGIFMEDIA: + case SIOCSIFMEDIA: + error = ifmedia_ioctl(ifp, ifr, &sc->sc_mii.mii_media, cmd); + break; + + default: + error = ether_ioctl(ifp, &sc->sc_ac, cmd, data); + } + + if (error == ENETRESET) { + if (ifp->if_flags & IFF_RUNNING) + imxenet_iff(sc); + error = 0; + } + + splx(s); + return(error); +} + +void +imxenet_start(struct ifnet *ifp) +{ + struct imxenet_softc *sc = ifp->if_softc; + struct mbuf *m_head = NULL; + + if ((ifp->if_flags & (IFF_OACTIVE | IFF_RUNNING)) != IFF_RUNNING) + return; + + for (;;) { + IFQ_POLL(&ifp->if_snd, m_head); + if (m_head == NULL) + break; + + if (imxenet_encap(sc, m_head)) { + ifp->if_flags |= IFF_OACTIVE; + break; + } + + IFQ_DEQUEUE(&ifp->if_snd, m_head); + + ifp->if_opackets++; + +#if NBPFILTER > 0 + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, m_head, BPF_DIRECTION_OUT); +#endif + + m_freem(m_head); + } +} + +int +imxenet_encap(struct imxenet_softc *sc, struct mbuf *m) +{ + if (sc->tx_desc_base[sc->cur_tx].status & ENET_TXD_READY) { + printf("imxenet: tx queue full!\n"); + return EIO; + } + + if (m->m_pkthdr.len > ENET_MAX_PKT_SIZE) { + printf("imxenet: packet too big\n"); + return EIO; + } + + /* copy in the actual packet */ + m_copydata(m, 0, m->m_pkthdr.len, (caddr_t)sc->tx_buffer_base[sc->cur_tx].data); + + sc->tx_desc_base[sc->cur_tx].data_length = m->m_pkthdr.len; + + sc->tx_desc_base[sc->cur_tx].status &= ~ENET_TXD_STATUS_MASK; + sc->tx_desc_base[sc->cur_tx].status |= (ENET_TXD_READY | ENET_TXD_LAST | ENET_TXD_TC); + +#ifdef ENET_ENHANCED_BD + sc->tx_desc_base[sc->cur_tx].enhanced_status = ENET_TXD_INT; + sc->tx_desc_base[sc->cur_tx].update_done = 0; +#endif + + bus_dmamap_sync(sc->tbdma.dma_tag, sc->tbdma.dma_map, + ENET_MAX_PKT_SIZE * sc->cur_tx, ENET_MAX_PKT_SIZE, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + + bus_dmamap_sync(sc->txdma.dma_tag, sc->txdma.dma_map, + sizeof(struct imxenet_buf_desc) * sc->cur_tx, + sizeof(struct imxenet_buf_desc), + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + + + /* tx descriptors are ready */ + HWRITE4(sc, ENET_TDAR, ENET_TDAR_TDAR); + + if (sc->tx_desc_base[sc->cur_tx].status & ENET_TXD_WRAP) + sc->cur_tx = 0; + else + sc->cur_tx++; + + return 0; +} + +struct mbuf * +imxenet_newbuf(void) +{ + struct mbuf *m; + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + return (NULL); + + MCLGET(m, M_DONTWAIT); + if (!(m->m_flags & M_EXT)) { + m_freem(m); + return (NULL); + } + + return (m); +} + +/* + * Established by attachment driver at interrupt priority IPL_NET. + */ +int +imxenet_intr(void *arg) +{ + struct imxenet_softc *sc = arg; + struct ifnet *ifp = &sc->sc_ac.ac_if; + u_int32_t status; + + /* Find out which interrupts are pending. */ + status = HREAD4(sc, ENET_EIR); + + /* Acknowledge the interrupts we are about to handle. */ + HWRITE4(sc, ENET_EIR, status); + + /* + * Wake up the blocking process to service command + * related interrupt(s). + */ + if (ISSET(status, ENET_EIR_MII)) { + sc->intr_status |= status; + wakeup(&sc->intr_status); + } + + /* + * Handle incoming packets. + */ + if (ISSET(status, ENET_EIR_RXF)) { + if (ifp->if_flags & IFF_RUNNING) + imxenet_recv(sc); + } + + return 1; +} + +void +imxenet_recv(struct imxenet_softc *sc) +{ + struct ifnet *ifp = &sc->sc_ac.ac_if; + + bus_dmamap_sync(sc->rbdma.dma_tag, sc->rbdma.dma_map, + 0, sc->rbdma.dma_size, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + bus_dmamap_sync(sc->rxdma.dma_tag, sc->rxdma.dma_map, + 0, sc->rxdma.dma_size, + BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); + + while (!(sc->rx_desc_base[sc->cur_rx].status & ENET_RXD_EMPTY)) + { + struct mbuf *m; + m = imxenet_newbuf(); + + if (m == NULL) { + ifp->if_ierrors++; + goto done; + } + + ifp->if_ipackets++; + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = m->m_len = sc->rx_desc_base[sc->cur_rx].data_length; + m_adj(m, ETHER_ALIGN); + + memcpy(mtod(m, char *), sc->rx_buffer_base[sc->cur_rx].data, + sc->rx_desc_base[sc->cur_rx].data_length); + + sc->rx_desc_base[sc->cur_rx].status |= ENET_RXD_EMPTY; + sc->rx_desc_base[sc->cur_rx].data_length = 0; + + bus_dmamap_sync(sc->rbdma.dma_tag, sc->rbdma.dma_map, + ENET_MAX_PKT_SIZE * sc->cur_rx, ENET_MAX_PKT_SIZE, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + + bus_dmamap_sync(sc->rxdma.dma_tag, sc->rxdma.dma_map, + sizeof(struct imxenet_buf_desc) * sc->cur_rx, + sizeof(struct imxenet_buf_desc), + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + + if (sc->rx_desc_base[sc->cur_rx].status & ENET_RXD_WRAP) + sc->cur_rx = 0; + else + sc->cur_rx++; + + /* push the packet up */ +#if NBPFILTER > 0 + if (ifp->if_bpf) + bpf_mtap(ifp->if_bpf, m, BPF_DIRECTION_IN); +#endif + ether_input_mbuf(ifp, m); + } + +done: + /* rx descriptors are ready */ + HWRITE4(sc, ENET_RDAR, ENET_RDAR_RDAR); +} + +int +imxenet_wait_intr(struct imxenet_softc *sc, int mask, int timo) +{ + int status; + int s; + + s = splnet(); + + status = sc->intr_status; + while (status == 0) { + if (tsleep(&sc->intr_status, PWAIT, "hcintr", timo) + == EWOULDBLOCK) { + break; + } + status = sc->intr_status; + } + sc->intr_status &= ~status; + + splx(s); + return status; +} + +/* + * MII + * Interrupts need ENET_ECR_ETHEREN to be set, + * so we just read the interrupt status registers. + */ +int +imxenet_miibus_readreg(struct device *dev, int phy, int reg) +{ + int r = 0; + struct imxenet_softc *sc = (struct imxenet_softc *)dev; + + HSET4(sc, ENET_EIR, ENET_EIR_MII); + + bus_space_write_4(sc->sc_iot, sc->sc_ioh, ENET_MMFR, + ENET_MMFR_ST | ENET_MMFR_OP_RD | ENET_MMFR_TA | + phy << ENET_MMFR_PA_SHIFT | reg << ENET_MMFR_RA_SHIFT); + + while(!(HREAD4(sc, ENET_EIR) & ENET_EIR_MII)); + + r = bus_space_read_4(sc->sc_iot, sc->sc_ioh, ENET_MMFR); + + return (r & 0xffff); +} + +void +imxenet_miibus_writereg(struct device *dev, int phy, int reg, int val) +{ + struct imxenet_softc *sc = (struct imxenet_softc *)dev; + + HSET4(sc, ENET_EIR, ENET_EIR_MII); + + bus_space_write_4(sc->sc_iot, sc->sc_ioh, ENET_MMFR, + ENET_MMFR_ST | ENET_MMFR_OP_WR | ENET_MMFR_TA | + phy << ENET_MMFR_PA_SHIFT | reg << ENET_MMFR_RA_SHIFT | + (val & 0xffff)); + + while(!(HREAD4(sc, ENET_EIR) & ENET_EIR_MII)); + + return; +} + +void +imxenet_miibus_statchg(struct device *dev) +{ + struct imxenet_softc *sc = (struct imxenet_softc *)dev; + int ecr; + + ecr = HREAD4(sc, ENET_ECR); + switch (IFM_SUBTYPE(sc->sc_mii.mii_media_active)) { + case IFM_1000_T: /* Gigabit */ + ecr |= ENET_ECR_SPEED; + break; + default: + ecr &= ~ENET_ECR_SPEED; + } + HWRITE4(sc, ENET_ECR, ecr); + + return; +} + +int +imxenet_ifmedia_upd(struct ifnet *ifp) +{ + struct imxenet_softc *sc = ifp->if_softc; + struct mii_data *mii = &sc->sc_mii; + int err; + if (mii->mii_instance) { + struct mii_softc *miisc; + + LIST_FOREACH(miisc, &mii->mii_phys, mii_list) + mii_phy_reset(miisc); + } + err = mii_mediachg(mii); + return (err); +} + +void +imxenet_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct imxenet_softc *sc = ifp->if_softc; + struct mii_data *mii = &sc->sc_mii; + + mii_pollstat(mii); + + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; +} + +/* + * Manage DMA'able memory. + */ +int +imxenet_dma_malloc(struct imxenet_softc *sc, bus_size_t size, + struct imxenet_dma_alloc *dma) +{ + int r; + + dma->dma_tag = sc->sc_dma_tag; + r = bus_dmamem_alloc(dma->dma_tag, size, ENET_ALIGNMENT, 0, &dma->dma_seg, + 1, &dma->dma_nseg, BUS_DMA_NOWAIT); + if (r != 0) { + printf("%s: imxenet_dma_malloc: bus_dmammem_alloc failed; " + "size %lu, error %d\n", sc->sc_dev.dv_xname, + (unsigned long)size, r); + goto fail_0; + } + + r = bus_dmamem_map(dma->dma_tag, &dma->dma_seg, dma->dma_nseg, size, + &dma->dma_vaddr, BUS_DMA_NOWAIT|BUS_DMA_COHERENT); + if (r != 0) { + printf("%s: imxenet_dma_malloc: bus_dmammem_map failed; " + "size %lu, error %d\n", sc->sc_dev.dv_xname, + (unsigned long)size, r); + goto fail_1; + } + + r = bus_dmamap_create(dma->dma_tag, size, 1, + size, 0, BUS_DMA_NOWAIT, &dma->dma_map); + if (r != 0) { + printf("%s: imxenet_dma_malloc: bus_dmamap_create failed; " + "error %u\n", sc->sc_dev.dv_xname, r); + goto fail_2; + } + + r = bus_dmamap_load(dma->dma_tag, dma->dma_map, + dma->dma_vaddr, size, NULL, + BUS_DMA_NOWAIT); + if (r != 0) { + printf("%s: imxenet_dma_malloc: bus_dmamap_load failed; " + "error %u\n", sc->sc_dev.dv_xname, r); + goto fail_3; + } + + dma->dma_size = size; + dma->dma_paddr = dma->dma_map->dm_segs[0].ds_addr; + return (0); + +fail_3: + bus_dmamap_destroy(dma->dma_tag, dma->dma_map); +fail_2: + bus_dmamem_unmap(dma->dma_tag, dma->dma_vaddr, size); +fail_1: + bus_dmamem_free(dma->dma_tag, &dma->dma_seg, dma->dma_nseg); +fail_0: + dma->dma_map = NULL; + dma->dma_tag = NULL; + + return (r); +} + +void +imxenet_dma_free(struct imxenet_softc *sc, struct imxenet_dma_alloc *dma) +{ + if (dma->dma_tag == NULL) + return; + + if (dma->dma_map != NULL) { + bus_dmamap_sync(dma->dma_tag, dma->dma_map, 0, + dma->dma_map->dm_mapsize, + BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE); + bus_dmamap_unload(dma->dma_tag, dma->dma_map); + bus_dmamem_unmap(dma->dma_tag, dma->dma_vaddr, dma->dma_size); + bus_dmamem_free(dma->dma_tag, &dma->dma_seg, dma->dma_nseg); + bus_dmamap_destroy(dma->dma_tag, dma->dma_map); + } + dma->dma_tag = NULL; +} diff --git a/sys/arch/armv7/imx/imxenet.h b/sys/arch/armv7/imx/imxenet.h new file mode 100644 index 00000000000..880454b7aa6 --- /dev/null +++ b/sys/arch/armv7/imx/imxenet.h @@ -0,0 +1,83 @@ +/* $OpenBSD: imxenet.h,v 1.1 2013/09/06 20:45:53 patrick Exp $ */ +/* + * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* what should we use? */ +#define ENET_MAX_TXD 32 +#define ENET_MAX_RXD 32 + +#define ENET_MAX_PKT_SIZE 1536 + +#define ENET_ROUNDUP(size, unit) (((size) + (unit) - 1) & ~((unit) - 1)) + +/* buffer descriptor status bits */ +#define ENET_RXD_EMPTY (1 << 15) +#define ENET_RXD_WRAP (1 << 13) +#define ENET_RXD_LAST (1 << 11) +#define ENET_RXD_MISS (1 << 8) +#define ENET_RXD_BC (1 << 7) +#define ENET_RXD_MC (1 << 6) +#define ENET_RXD_LG (1 << 5) +#define ENET_RXD_NO (1 << 4) +#define ENET_RXD_CR (1 << 2) +#define ENET_RXD_OV (1 << 1) +#define ENET_RXD_TR (1 << 0) + +#define ENET_TXD_READY (1 << 15) +#define ENET_TXD_WRAP (1 << 13) +#define ENET_TXD_LAST (1 << 11) +#define ENET_TXD_TC (1 << 10) +#define ENET_TXD_ABC (1 << 9) +#define ENET_TXD_STATUS_MASK 0x3ff + +#ifdef ENET_ENHANCED_BD +/* enhanced */ +#define ENET_RXD_INT (1 << 23) + +#define ENET_TXD_INT (1 << 30) +#endif + + +/* + * Bus dma allocation structure used by + * imxenet_dma_malloc and imxenet_dma_free. + */ +struct imxenet_dma_alloc { + bus_addr_t dma_paddr; + caddr_t dma_vaddr; + bus_dma_tag_t dma_tag; + bus_dmamap_t dma_map; + bus_dma_segment_t dma_seg; + bus_size_t dma_size; + int dma_nseg; +}; + +struct imxenet_buf_desc { + uint16_t data_length; /* payload's length in bytes */ + uint16_t status; /* BD's status (see datasheet) */ + uint32_t data_pointer; /* payload's buffer address */ +#ifdef ENET_ENHANCED_BD + uint32_t enhanced_status; /* enhanced status with IEEE 1588 */ + uint32_t reserved0; /* reserved */ + uint32_t update_done; /* buffer descriptor update done */ + uint32_t timestamp; /* IEEE 1588 timestamp */ + uint32_t reserved1[2]; /* reserved */ +#endif +}; + +struct imxenet_buffer { + uint8_t data[ENET_MAX_PKT_SIZE]; +}; diff --git a/sys/arch/armv7/imx/imxesdhc.c b/sys/arch/armv7/imx/imxesdhc.c new file mode 100644 index 00000000000..7fec46214e8 --- /dev/null +++ b/sys/arch/armv7/imx/imxesdhc.c @@ -0,0 +1,963 @@ +/* $OpenBSD: imxesdhc.c,v 1.1 2013/09/06 20:45:53 patrick Exp $ */ +/* + * Copyright (c) 2009 Dale Rahn <drahn@openbsd.org> + * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org> + * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* i.MX SD/MMC support derived from /sys/dev/sdmmc/sdhc.c */ + + +#include <sys/param.h> +#include <sys/device.h> +#include <sys/kernel.h> +#include <sys/kthread.h> +#include <sys/malloc.h> +#include <sys/systm.h> +#include <machine/bus.h> + +#include <dev/sdmmc/sdmmcchip.h> +#include <dev/sdmmc/sdmmcvar.h> + +#include <armv7/imx/imxvar.h> +#include <armv7/imx/imxccmvar.h> +#include <armv7/imx/imxgpiovar.h> + +/* registers */ +#define SDHC_DS_ADDR 0x00 +#define SDHC_BLK_ATT 0x04 +#define SDHC_CMD_ARG 0x08 +#define SDHC_CMD_XFR_TYP 0x0c +#define SDHC_CMD_RSP0 0x10 +#define SDHC_CMD_RSP1 0x14 +#define SDHC_CMD_RSP2 0x18 +#define SDHC_CMD_RSP3 0x1c +#define SDHC_DATA_BUFF_ACC_PORT 0x20 +#define SDHC_PRES_STATE 0x24 +#define SDHC_PROT_CTRL 0x28 +#define SDHC_SYS_CTRL 0x2c +#define SDHC_INT_STATUS 0x30 +#define SDHC_INT_STATUS_EN 0x34 +#define SDHC_INT_SIGNAL_EN 0x38 +#define SDHC_AUTOCMD12_ERR_STATUS 0x3c +#define SDHC_HOST_CTRL_CAP 0x40 +#define SDHC_WTMK_LVL 0x44 +#define SDHC_MIX_CTRL 0x48 +#define SDHC_FORCE_EVENT 0x50 +#define SDHC_ADMA_ERR_STATUS 0x54 +#define SDHC_ADMA_SYS_ADDR 0x58 +#define SDHC_DLL_CTRL 0x60 +#define SDHC_DLL_STATUS 0x64 +#define SDHC_CLK_TUNE_CTRL_STATUS 0x68 +#define SDHC_VEND_SPEC 0xc0 +#define SDHC_MMC_BOOT 0xc4 +#define SDHC_VEND_SPEC2 0xc8 +#define SDHC_HOST_CTRL_VER 0xfc + +/* bits and bytes */ +#define SDHC_BLK_ATT_BLKCNT_MAX 0xffff +#define SDHC_BLK_ATT_BLKCNT_SHIFT 16 +#define SDHC_BLK_ATT_BLKSIZE_SHIFT 0 +#define SDHC_CMD_XFR_TYP_CMDINDX_SHIFT 24 +#define SDHC_CMD_XFR_TYP_CMDINDX_SHIFT_MASK (0x3f << SDHC_CMD_XFR_TYP_CMDINDX_SHIFT) +#define SDHC_CMD_XFR_TYP_CMDTYP_SHIFT 22 +#define SDHC_CMD_XFR_TYP_DPSEL_SHIFT 21 +#define SDHC_CMD_XFR_TYP_DPSEL (1 << SDHC_CMD_XFR_TYP_DPSEL_SHIFT) +#define SDHC_CMD_XFR_TYP_CICEN_SHIFT 20 +#define SDHC_CMD_XFR_TYP_CICEN (1 << SDHC_CMD_XFR_TYP_CICEN_SHIFT) +#define SDHC_CMD_XFR_TYP_CCCEN_SHIFT 19 +#define SDHC_CMD_XFR_TYP_CCCEN (1 << SDHC_CMD_XFR_TYP_CCCEN_SHIFT) +#define SDHC_CMD_XFR_TYP_RSPTYP_SHIFT 16 +#define SDHC_CMD_XFR_TYP_RSP_NONE (0x0 << SDHC_CMD_XFR_TYP_RSPTYP_SHIFT) +#define SDHC_CMD_XFR_TYP_RSP136 (0x1 << SDHC_CMD_XFR_TYP_RSPTYP_SHIFT) +#define SDHC_CMD_XFR_TYP_RSP48 (0x2 << SDHC_CMD_XFR_TYP_RSPTYP_SHIFT) +#define SDHC_CMD_XFR_TYP_RSP48B (0x3 << SDHC_CMD_XFR_TYP_RSPTYP_SHIFT) +#define SDHC_PRES_STATE_WPSPL (1 << 19) +#define SDHC_PRES_STATE_BREN (1 << 11) +#define SDHC_PRES_STATE_BWEN (1 << 10) +#define SDHC_PRES_STATE_SDSTB (1 << 3) +#define SDHC_PRES_STATE_DLA (1 << 2) +#define SDHC_PRES_STATE_CDIHB (1 << 1) +#define SDHC_PRES_STATE_CIHB (1 << 0) +#define SDHC_SYS_CTRL_RSTA (1 << 24) +#define SDHC_SYS_CTRL_RSTC (1 << 25) +#define SDHC_SYS_CTRL_RSTD (1 << 26) +#define SDHC_SYS_CTRL_CLOCK_MASK (0xfff << 4) +#define SDHC_SYS_CTRL_CLOCK_DIV_SHIFT 4 +#define SDHC_SYS_CTRL_CLOCK_PRE_SHIFT 8 +#define SDHC_SYS_CTRL_DTOCV_SHIFT 16 +#define SDHC_INT_STATUS_CC (1 << 0) +#define SDHC_INT_STATUS_TC (1 << 1) +#define SDHC_INT_STATUS_BGE (1 << 2) +#define SDHC_INT_STATUS_DINT (1 << 3) +#define SDHC_INT_STATUS_BWR (1 << 4) +#define SDHC_INT_STATUS_BRR (1 << 5) +#define SDHC_INT_STATUS_CINS (1 << 6) +#define SDHC_INT_STATUS_CRM (1 << 7) +#define SDHC_INT_STATUS_CINT (1 << 8) +#define SDHC_INT_STATUS_CTOE (1 << 16) +#define SDHC_INT_STATUS_CCE (1 << 17) +#define SDHC_INT_STATUS_CEBE (1 << 18) +#define SDHC_INT_STATUS_CIC (1 << 19) +#define SDHC_INT_STATUS_DTOE (1 << 20) +#define SDHC_INT_STATUS_DCE (1 << 21) +#define SDHC_INT_STATUS_DEBE (1 << 22) +#define SDHC_INT_STATUS_DMAE (1 << 28) +#define SDHC_INT_STATUS_CMD_ERR (SDHC_INT_STATUS_CIC | SDHC_INT_STATUS_CEBE | SDHC_INT_STATUS_CCE) +#define SDHC_INT_STATUS_ERR (SDHC_INT_STATUS_CTOE | SDHC_INT_STATUS_CCE | SDHC_INT_STATUS_CEBE | \ + SDHC_INT_STATUS_CIC | SDHC_INT_STATUS_DTOE | SDHC_INT_STATUS_DCE | \ + SDHC_INT_STATUS_DEBE | SDHC_INT_STATUS_DMAE) +#define SDHC_MIX_CTRL_DMAEN (1 << 0) +#define SDHC_MIX_CTRL_BCEN (1 << 1) +#define SDHC_MIX_CTRL_AC12EN (1 << 2) +#define SDHC_MIX_CTRL_DTDSEL (1 << 4) +#define SDHC_MIX_CTRL_MSBSEL (1 << 5) +#define SDHC_PROT_CTRL_DMASEL_SDMA_MASK (0x3 << 8) +#define SDHC_HOST_CTRL_CAP_MBL_SHIFT 16 +#define SDHC_HOST_CTRL_CAP_MBL_MASK 0x7 +#define SDHC_HOST_CTRL_CAP_VS33 (1 << 24) +#define SDHC_HOST_CTRL_CAP_VS30 (1 << 25) +#define SDHC_HOST_CTRL_CAP_VS18 (1 << 26) +#define SDHC_VEND_SPEC_FRC_SDCLK_ON (1 << 8) +#define SDHC_WTMK_LVL_RD_WML_SHIFT 0 +#define SDHC_WTMK_LVL_WR_WML_SHIFT 16 + +#define SDHC_COMMAND_TIMEOUT hz +#define SDHC_BUFFER_TIMEOUT hz +#define SDHC_TRANSFER_TIMEOUT hz + +void imxesdhc_attach(struct device *parent, struct device *self, void *args); + +#include <machine/bus.h> + +struct imxesdhc_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + void *sc_ih; /* Interrupt handler */ + u_int sc_flags; + + int unit; /* unit id */ + struct device *sdmmc; /* generic SD/MMC device */ + int clockbit; /* clock control bit */ + u_int clkbase; /* base clock frequency in KHz */ + int maxblklen; /* maximum block length */ + int flags; /* flags for this host */ + uint32_t ocr; /* OCR value from capabilities */ +// u_int8_t regs[14]; /* host controller state */ + uint32_t intr_status; /* soft interrupt status */ + uint32_t intr_error_status; /* */ +}; + + +/* Host controller functions called by the attachment driver. */ +int imxesdhc_host_found(struct imxesdhc_softc *, bus_space_tag_t, + bus_space_handle_t, bus_size_t, int); +void imxesdhc_power(int, void *); +void imxesdhc_shutdown(void *); +int imxesdhc_intr(void *); + +/* RESET MODES */ +#define MMC_RESET_DAT 1 +#define MMC_RESET_CMD 2 +#define MMC_RESET_ALL (MMC_RESET_CMD|MMC_RESET_DAT) + +#define HDEVNAME(sc) ((sc)->sc_dev.dv_xname) + +/* flag values */ +#define SHF_USE_DMA 0x0001 + +/* SDHC should only be accessed with 4 byte reads or writes. */ +#define HREAD4(sc, reg) \ + (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) +#define HWRITE4(sc, reg, val) \ + bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) +#define HSET4(sc, reg, bits) \ + HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) +#define HCLR4(sc, reg, bits) \ + HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) + +int imxesdhc_host_reset(sdmmc_chipset_handle_t); +uint32_t imxesdhc_host_ocr(sdmmc_chipset_handle_t); +int imxesdhc_host_maxblklen(sdmmc_chipset_handle_t); +int imxesdhc_card_detect(sdmmc_chipset_handle_t); +int imxesdhc_bus_power(sdmmc_chipset_handle_t, uint32_t); +int imxesdhc_bus_clock(sdmmc_chipset_handle_t, int); +void imxesdhc_card_intr_mask(sdmmc_chipset_handle_t, int); +void imxesdhc_card_intr_ack(sdmmc_chipset_handle_t); +void imxesdhc_exec_command(sdmmc_chipset_handle_t, struct sdmmc_command *); +int imxesdhc_start_command(struct imxesdhc_softc *, struct sdmmc_command *); +int imxesdhc_wait_state(struct imxesdhc_softc *, uint32_t, uint32_t); +int imxesdhc_soft_reset(struct imxesdhc_softc *, int); +int imxesdhc_wait_intr(struct imxesdhc_softc *, int, int); +void imxesdhc_transfer_data(struct imxesdhc_softc *, struct sdmmc_command *); +void imxesdhc_read_data(struct imxesdhc_softc *, u_char *, int); +void imxesdhc_write_data(struct imxesdhc_softc *, u_char *, int); + +//#define SDHC_DEBUG +#ifdef SDHC_DEBUG +int imxesdhcdebug = 20; +#define DPRINTF(n,s) do { if ((n) <= imxesdhcdebug) printf s; } while (0) +#else +#define DPRINTF(n,s) do {} while(0) +#endif + +struct sdmmc_chip_functions imxesdhc_functions = { + /* host controller reset */ + imxesdhc_host_reset, + /* host controller capabilities */ + imxesdhc_host_ocr, + imxesdhc_host_maxblklen, + /* card detection */ + imxesdhc_card_detect, + /* bus power and clock frequency */ + imxesdhc_bus_power, + imxesdhc_bus_clock, + /* command execution */ + imxesdhc_exec_command, + /* card interrupt */ + imxesdhc_card_intr_mask, + imxesdhc_card_intr_ack +}; + +struct cfdriver imxesdhc_cd = { + NULL, "imxesdhc", DV_DULL +}; + +struct cfattach imxesdhc_ca = { + sizeof(struct imxesdhc_softc), NULL, imxesdhc_attach +}; + +void +imxesdhc_attach(struct device *parent, struct device *self, void *args) +{ + struct imxesdhc_softc *sc = (struct imxesdhc_softc *) self; + struct imx_attach_args *ia = args; + struct sdmmcbus_attach_args saa; + int error = 1; + uint32_t caps; + + sc->unit = ia->ia_dev->unit; + sc->sc_iot = ia->ia_iot; + if (bus_space_map(sc->sc_iot, ia->ia_dev->mem[0].addr, + ia->ia_dev->mem[0].size, 0, &sc->sc_ioh)) + panic("imxesdhc_attach: bus_space_map failed!"); + + printf("\n"); + + /* XXX DMA channels? */ + + sc->sc_ih = arm_intr_establish(ia->ia_dev->irq[0], IPL_SDMMC, + imxesdhc_intr, sc, sc->sc_dev.dv_xname); + + /* + * Reset the host controller and enable interrupts. + */ + if (imxesdhc_host_reset(sc)) + goto err; + + /* Determine host capabilities. */ + caps = HREAD4(sc, SDHC_HOST_CTRL_CAP); + + /* + * Determine the base clock frequency. (2.2.24) + */ + sc->clkbase = imxccm_get_usdhx(ia->ia_dev->unit + 1); + + /* + * Determine SD bus voltage levels supported by the controller. + */ + if (caps & SDHC_HOST_CTRL_CAP_VS18) + SET(sc->ocr, MMC_OCR_1_7V_1_8V | MMC_OCR_1_8V_1_9V); + if (caps & SDHC_HOST_CTRL_CAP_VS30) + SET(sc->ocr, MMC_OCR_2_9V_3_0V | MMC_OCR_3_0V_3_1V); + if (caps & SDHC_HOST_CTRL_CAP_VS33) + SET(sc->ocr, MMC_OCR_3_2V_3_3V | MMC_OCR_3_3V_3_4V); + + /* + * Determine max block size. + */ + switch ((caps >> SDHC_HOST_CTRL_CAP_MBL_SHIFT) + & SDHC_HOST_CTRL_CAP_MBL_MASK) { + case 0: + sc->maxblklen = 512; + break; + case 1: + sc->maxblklen = 1024; + break; + case 2: + sc->maxblklen = 2048; + break; + case 3: + sc->maxblklen = 4096; + break; + default: + sc->maxblklen = 512; + printf("invalid capability blocksize in capa %08x," + " trying 512\n", caps); + } + + /* somewhere this blksize might be used instead of the device's */ + sc->maxblklen = 512; + + /* + * Attach the generic SD/MMC bus driver. (The bus driver must + * not invoke any chipset functions before it is attached.) + */ + + bzero(&saa, sizeof(saa)); + saa.saa_busname = "sdmmc"; + saa.sct = &imxesdhc_functions; + saa.sch = sc; + + sc->sdmmc = config_found(&sc->sc_dev, &saa, NULL); + if (sc->sdmmc == NULL) { + error = 0; + goto err; + } + + return; + +err: + return; +} + + +/* + * Power hook established by or called from attachment driver. + */ +void +imxesdhc_power(int why, void *arg) +{ +} + +/* + * Shutdown hook established by or called from attachment driver. + */ +void +imxesdhc_shutdown(void *arg) +{ + struct imxesdhc_softc *sc = arg; + + /* XXX chip locks up if we don't disable it before reboot. */ + (void)imxesdhc_host_reset(sc); +} + +/* + * Reset the host controller. Called during initialization, when + * cards are removed, upon resume, and during error recovery. + */ +int +imxesdhc_host_reset(sdmmc_chipset_handle_t sch) +{ + struct imxesdhc_softc *sc = sch; + u_int32_t imask; + int error; + int s; + + s = splsdmmc(); + + /* Disable all interrupts. */ + HWRITE4(sc, SDHC_INT_STATUS_EN, 0); + HWRITE4(sc, SDHC_INT_SIGNAL_EN, 0); + + /* + * Reset the entire host controller and wait up to 100ms for + * the controller to clear the reset bit. + */ + if ((error = imxesdhc_soft_reset(sc, SDHC_SYS_CTRL_RSTA)) != 0) { + splx(s); + return (error); + } + + /* Set data timeout counter value to max for now. */ + HSET4(sc, SDHC_SYS_CTRL, 0xe << SDHC_SYS_CTRL_DTOCV_SHIFT); + + /* Enable interrupts. */ + imask = SDHC_INT_STATUS_CC | SDHC_INT_STATUS_TC | + SDHC_INT_STATUS_BGE | +#ifdef SDHC_DMA + SHDC_INT_STATUS_DINT; +#else + SDHC_INT_STATUS_BRR | SDHC_INT_STATUS_BWR; +#endif + + imask |= SDHC_INT_STATUS_CTOE | SDHC_INT_STATUS_CCE | + SDHC_INT_STATUS_CEBE | SDHC_INT_STATUS_CIC | + SDHC_INT_STATUS_DTOE | SDHC_INT_STATUS_DCE | + SDHC_INT_STATUS_DEBE | SDHC_INT_STATUS_DMAE; + + HWRITE4(sc, SDHC_INT_STATUS_EN, imask); + HWRITE4(sc, SDHC_INT_SIGNAL_EN, imask); + + // Use no or simple DMA + HWRITE4(sc, SDHC_PROT_CTRL, + HREAD4(sc, SDHC_PROT_CTRL) & ~SDHC_PROT_CTRL_DMASEL_SDMA_MASK); + + splx(s); + return 0; +} + +uint32_t +imxesdhc_host_ocr(sdmmc_chipset_handle_t sch) +{ + struct imxesdhc_softc *sc = sch; + return sc->ocr; +} + +int +imxesdhc_host_maxblklen(sdmmc_chipset_handle_t sch) +{ + struct imxesdhc_softc *sc = sch; + return sc->maxblklen; +} + +/* + * Return non-zero if the card is currently inserted. + */ +int +imxesdhc_card_detect(sdmmc_chipset_handle_t sch) +{ + struct imxesdhc_softc *sc = sch; + int gpio; + + switch (board_id) + { + case BOARD_ID_IMX6_PHYFLEX: + switch (sc->unit) { + case 1: + gpio = 0*32 + 2; + break; + case 2: + gpio = 4*32 + 22; + break; + default: + return 0; + } + return imxgpio_get_bit(gpio) ? 0 : 1; + case BOARD_ID_IMX6_SABRELITE: + switch (sc->unit) { + case 2: + gpio = 6*32 + 0; + break; + case 3: + gpio = 1*32 + 6; + break; + default: + return 0; + } + return imxgpio_get_bit(gpio) ? 0 : 1; + default: + return 1; + } +} + +/* + * Set or change SD bus voltage and enable or disable SD bus power. + * Return zero on success. + */ +int +imxesdhc_bus_power(sdmmc_chipset_handle_t sch, uint32_t ocr) +{ + return 0; +} + +/* + * Set or change SDCLK frequency or disable the SD clock. + * Return zero on success. + */ +int +imxesdhc_bus_clock(sdmmc_chipset_handle_t sch, int freq) +{ + struct imxesdhc_softc *sc = sch; + int div, pre_div, cur_freq, s; + int error = 0; + + s = splsdmmc(); + + if (sc->clkbase / 16 > freq) { + for (pre_div = 2; pre_div < 256; pre_div *= 2) + if ((sc->clkbase / pre_div) <= (freq * 16)) + break; + } else + pre_div = 2; + + if (sc->clkbase == freq) + pre_div = 1; + + for (div = 1; div <= 16; div++) + if ((sc->clkbase / (div * pre_div)) <= freq) + break; + + div -= 1; + pre_div >>= 1; + + cur_freq = sc->clkbase / (pre_div * 2) / (div + 1); + + /* disable force CLK ouput active */ + HCLR4(sc, SDHC_VEND_SPEC, SDHC_VEND_SPEC_FRC_SDCLK_ON); + + /* wait while clock is unstable */ + if ((error = imxesdhc_wait_state(sc, SDHC_PRES_STATE_SDSTB, SDHC_PRES_STATE_SDSTB)) != 0) + goto ret; + + HCLR4(sc, SDHC_SYS_CTRL, SDHC_SYS_CTRL_CLOCK_MASK); + HSET4(sc, SDHC_SYS_CTRL, (div << SDHC_SYS_CTRL_CLOCK_DIV_SHIFT) | (pre_div << SDHC_SYS_CTRL_CLOCK_PRE_SHIFT)); + + /* wait while clock is unstable */ + if ((error = imxesdhc_wait_state(sc, SDHC_PRES_STATE_SDSTB, SDHC_PRES_STATE_SDSTB)) != 0) + goto ret; + +ret: + splx(s); + return error; +} + +void +imxesdhc_card_intr_mask(sdmmc_chipset_handle_t sch, int enable) +{ + printf("imxesdhc_card_intr_mask\n"); + /* - this is SDIO card interrupt */ + struct imxesdhc_softc *sc = sch; + + if (enable) { + HSET4(sc, SDHC_INT_STATUS_EN, SDHC_INT_STATUS_CINT); + HSET4(sc, SDHC_INT_SIGNAL_EN, SDHC_INT_STATUS_CINT); + } else { + HCLR4(sc, SDHC_INT_STATUS_EN, SDHC_INT_STATUS_CINT); + HCLR4(sc, SDHC_INT_SIGNAL_EN, SDHC_INT_STATUS_CINT); + } +} + +void +imxesdhc_card_intr_ack(sdmmc_chipset_handle_t sch) +{ + printf("imxesdhc_card_intr_ack\n"); + struct imxesdhc_softc *sc = sch; + + HWRITE4(sc, SDHC_INT_STATUS, SDHC_INT_STATUS_CINT); +} + +int +imxesdhc_wait_state(struct imxesdhc_softc *sc, uint32_t mask, uint32_t value) +{ + uint32_t state; + int timeout; + state = HREAD4(sc, SDHC_PRES_STATE); + DPRINTF(3,("%s: wait_state %x %x %x)\n", HDEVNAME(sc), + mask, value, state)); + for (timeout = 1000; timeout > 0; timeout--) { + if (((state = HREAD4(sc, SDHC_PRES_STATE)) & mask) == value) + return 0; + delay(10); + } + DPRINTF(0,("%s: timeout waiting for %x\n", HDEVNAME(sc), + value, state)); + return ETIMEDOUT; +} + +void +imxesdhc_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd) +{ + struct imxesdhc_softc *sc = sch; + int error; + + /* + * Start the command, or mark `cmd' as failed and return. + */ + error = imxesdhc_start_command(sc, cmd); + if (error != 0) { + cmd->c_error = error; + SET(cmd->c_flags, SCF_ITSDONE); + return; + } + + /* + * Wait until the command phase is done, or until the command + * is marked done for any other reason. + */ + if (!imxesdhc_wait_intr(sc, SDHC_INT_STATUS_CC, SDHC_COMMAND_TIMEOUT)) { + cmd->c_error = ETIMEDOUT; + SET(cmd->c_flags, SCF_ITSDONE); + return; + } + + /* + * The host controller removes bits [0:7] from the response + * data (CRC) and we pass the data up unchanged to the bus + * driver (without padding). + */ + if (cmd->c_error == 0 && ISSET(cmd->c_flags, SCF_RSP_PRESENT)) { + if (ISSET(cmd->c_flags, SCF_RSP_136)) { + cmd->c_resp[0] = HREAD4(sc, SDHC_CMD_RSP0); + cmd->c_resp[1] = HREAD4(sc, SDHC_CMD_RSP1); + cmd->c_resp[2] = HREAD4(sc, SDHC_CMD_RSP2); + cmd->c_resp[3] = HREAD4(sc, SDHC_CMD_RSP3); + +#ifdef SDHC_DEBUG + printf("resp[0] 0x%08x\nresp[1] 0x%08x\nresp[2] 0x%08x\nresp[3] 0x%08x\n", cmd->c_resp[0], cmd->c_resp[1], cmd->c_resp[2], cmd->c_resp[3]); +#endif + } else { + cmd->c_resp[0] = HREAD4(sc, SDHC_CMD_RSP0); +#ifdef SDHC_DEBUG + printf("resp[0] 0x%08x\n", cmd->c_resp[0]); +#endif + } + } + + /* + * If the command has data to transfer in any direction, + * execute the transfer now. + */ + if (cmd->c_error == 0 && cmd->c_data) + imxesdhc_transfer_data(sc, cmd); + + DPRINTF(1,("%s: cmd %u done (flags=%#x error=%d)\n", + HDEVNAME(sc), cmd->c_opcode, cmd->c_flags, cmd->c_error)); + SET(cmd->c_flags, SCF_ITSDONE); +} + +int +imxesdhc_start_command(struct imxesdhc_softc *sc, struct sdmmc_command *cmd) +{ + u_int32_t blksize = 0; + u_int32_t blkcount = 0; + u_int32_t command; + int error; + int s; + + DPRINTF(1,("%s: start cmd %u arg=%#x data=%#x dlen=%d flags=%#x " + "proc=\"%s\"\n", HDEVNAME(sc), cmd->c_opcode, cmd->c_arg, + cmd->c_data, cmd->c_datalen, cmd->c_flags, curproc ? + curproc->p_comm : "")); + + /* + * The maximum block length for commands should be the minimum + * of the host buffer size and the card buffer size. (1.7.2) + */ + + /* Fragment the data into proper blocks. */ + if (cmd->c_datalen > 0) { + blksize = MIN(cmd->c_datalen, cmd->c_blklen); + blkcount = cmd->c_datalen / blksize; + if (cmd->c_datalen % blksize > 0) { + /* XXX: Split this command. (1.7.4) */ + printf("%s: data not a multiple of %d bytes\n", + HDEVNAME(sc), blksize); + return EINVAL; + } + } + + /* Check limit imposed by 9-bit block count. (1.7.2) */ + if (blkcount > SDHC_BLK_ATT_BLKCNT_MAX) { + printf("%s: too much data\n", HDEVNAME(sc)); + return EINVAL; + } + + /* setup for PIO, check for write protection */ + if (!ISSET(cmd->c_flags, SCF_CMD_READ)) { + if (!(HREAD4(sc, SDHC_PRES_STATE) & SDHC_PRES_STATE_WPSPL)) { + printf("%s: card is write protected\n", + HDEVNAME(sc)); + return EINVAL; + } + } + +#ifdef SDHC_DMA + /* set watermark level */ + uint32_t wml = blksize / sizeof(uint32_t); + if (ISSET(cmd->c_flags, SCF_CMD_READ)) { + if (wml > 16) + wml = 16; + HWRITE4(sc, SDHC_WTMK_LVL, wml << SDHC_WTMK_LVL_RD_WML_SHIFT); + } else { + if (wml > 128) + wml = 128; + HWRITE4(sc, SDHC_WTMK_LVL, wml << SDHC_WTMK_LVL_WR_WML_SHIFT); + } +#endif + + /* Prepare transfer mode register value. (2.2.5) */ + command = 0; + + if (ISSET(cmd->c_flags, SCF_CMD_READ)) + command |= SDHC_MIX_CTRL_DTDSEL; + if (blkcount > 0) { + command |= SDHC_MIX_CTRL_BCEN; +#ifdef SDHC_DMA + command |= SDHC_MIX_CTRL_DMAEN; +#endif + if (blkcount > 1) { + command |= SDHC_MIX_CTRL_MSBSEL; + command |= SDHC_MIX_CTRL_AC12EN; + } + } + + command |= (cmd->c_opcode << SDHC_CMD_XFR_TYP_CMDINDX_SHIFT) & + SDHC_CMD_XFR_TYP_CMDINDX_SHIFT_MASK; + + if (ISSET(cmd->c_flags, SCF_RSP_CRC)) + command |= SDHC_CMD_XFR_TYP_CCCEN; + if (ISSET(cmd->c_flags, SCF_RSP_IDX)) + command |= SDHC_CMD_XFR_TYP_CICEN; + if (cmd->c_data != NULL) + command |= SDHC_CMD_XFR_TYP_DPSEL; + + if (!ISSET(cmd->c_flags, SCF_RSP_PRESENT)) + command |= SDHC_CMD_XFR_TYP_RSP_NONE; + else if (ISSET(cmd->c_flags, SCF_RSP_136)) + command |= SDHC_CMD_XFR_TYP_RSP136; + else if (ISSET(cmd->c_flags, SCF_RSP_BSY)) + command |= SDHC_CMD_XFR_TYP_RSP48B; + else + command |= SDHC_CMD_XFR_TYP_RSP48; + + /* Wait until command and data inhibit bits are clear. (1.5) */ + if ((error = imxesdhc_wait_state(sc, SDHC_PRES_STATE_CIHB, 0)) != 0) + return error; + + s = splsdmmc(); + + /* + * Start a CPU data transfer. Writing to the high order byte + * of the SDHC_COMMAND register triggers the SD command. (1.5) + */ +#ifdef SDHC_DMA + if (cmd->c_data) + HWRITE4(sc, SDHC_DS_ADDR, (uint32_t)cmd->c_data); +#endif + HWRITE4(sc, SDHC_BLK_ATT, blkcount << SDHC_BLK_ATT_BLKCNT_SHIFT | + blksize << SDHC_BLK_ATT_BLKSIZE_SHIFT); + HWRITE4(sc, SDHC_CMD_ARG, cmd->c_arg); + HWRITE4(sc, SDHC_MIX_CTRL, + (HREAD4(sc, SDHC_MIX_CTRL) & (0xf << 22)) | (command & 0xffff)); + HWRITE4(sc, SDHC_CMD_XFR_TYP, command); + + splx(s); + return 0; +} + +void +imxesdhc_transfer_data(struct imxesdhc_softc *sc, struct sdmmc_command *cmd) +{ +#ifndef SDHC_DMA + u_char *datap = cmd->c_data; + int i; +#endif + int datalen; + int mask; + int error; + + mask = ISSET(cmd->c_flags, SCF_CMD_READ) ? + SDHC_PRES_STATE_BREN : SDHC_PRES_STATE_BWEN; + error = 0; + datalen = cmd->c_datalen; + + DPRINTF(1,("%s: resp=%#x datalen=%d\n", HDEVNAME(sc), + MMC_R1(cmd->c_resp), datalen)); + +#ifndef SDHC_DMA + while (datalen > 0) { + if (!imxesdhc_wait_intr(sc, SDHC_INT_STATUS_BRR | SDHC_INT_STATUS_BWR, + SDHC_BUFFER_TIMEOUT)) { + error = ETIMEDOUT; + break; + } + + if ((error = imxesdhc_wait_state(sc, mask, mask)) != 0) + break; + + /* FIXME: wait a bit, else it fails */ + delay(100); + i = MIN(datalen, cmd->c_blklen); + if (ISSET(cmd->c_flags, SCF_CMD_READ)) + imxesdhc_read_data(sc, datap, i); + else + imxesdhc_write_data(sc, datap, i); + + datap += i; + datalen -= i; + } +#endif + + if (error == 0 && !imxesdhc_wait_intr(sc, SDHC_INT_STATUS_TC, + SDHC_TRANSFER_TIMEOUT)) + error = ETIMEDOUT; + + if (error != 0) + cmd->c_error = error; + SET(cmd->c_flags, SCF_ITSDONE); + + DPRINTF(1,("%s: data transfer done (error=%d)\n", + HDEVNAME(sc), cmd->c_error)); +} + +void +imxesdhc_read_data(struct imxesdhc_softc *sc, u_char *datap, int datalen) +{ + while (datalen > 3) { + *(uint32_t *)datap = HREAD4(sc, SDHC_DATA_BUFF_ACC_PORT); + datap += 4; + datalen -= 4; + } + if (datalen > 0) { + uint32_t rv = HREAD4(sc, SDHC_DATA_BUFF_ACC_PORT); + do { + *datap++ = rv & 0xff; + rv = rv >> 8; + } while (--datalen > 0); + } +} + +void +imxesdhc_write_data(struct imxesdhc_softc *sc, u_char *datap, int datalen) +{ + while (datalen > 3) { + DPRINTF(3,("%08x\n", *(uint32_t *)datap)); + HWRITE4(sc, SDHC_DATA_BUFF_ACC_PORT, *((uint32_t *)datap)); + datap += 4; + datalen -= 4; + } + if (datalen > 0) { + uint32_t rv = *datap++; + if (datalen > 1) + rv |= *datap++ << 8; + if (datalen > 2) + rv |= *datap++ << 16; + DPRINTF(3,("rv %08x\n", rv)); + HWRITE4(sc, SDHC_DATA_BUFF_ACC_PORT, rv); + } +} + +/* Prepare for another command. */ +int +imxesdhc_soft_reset(struct imxesdhc_softc *sc, int mask) +{ + int timo; + + DPRINTF(1,("%s: software reset reg=%#x\n", HDEVNAME(sc), mask)); + + /* disable force CLK ouput active */ + HCLR4(sc, SDHC_VEND_SPEC, SDHC_VEND_SPEC_FRC_SDCLK_ON); + + /* reset */ + HSET4(sc, SDHC_SYS_CTRL, mask); + delay(10); + + for (timo = 1000; timo > 0; timo--) { + if (!ISSET(HREAD4(sc, SDHC_SYS_CTRL), mask)) + break; + delay(10); + } + if (timo == 0) { + DPRINTF(1,("%s: timeout reg=%#x\n", HDEVNAME(sc), + HREAD4(sc, SDHC_SYS_CTRL))); + return ETIMEDOUT; + } + + return 0; +} + +int +imxesdhc_wait_intr(struct imxesdhc_softc *sc, int mask, int timo) +{ + int status; + int s; + + mask |= SDHC_INT_STATUS_ERR; + s = splsdmmc(); + + /* enable interrupts for brr and bwr */ + if (mask & (SDHC_INT_STATUS_BRR | SDHC_INT_STATUS_BWR)) + HSET4(sc, SDHC_INT_SIGNAL_EN, (SDHC_INT_STATUS_BRR | SDHC_INT_STATUS_BWR)); + + status = sc->intr_status & mask; + while (status == 0) { + if (tsleep(&sc->intr_status, PWAIT, "hcintr", timo) + == EWOULDBLOCK) { + status |= SDHC_INT_STATUS_ERR; + break; + } + status = sc->intr_status & mask; + } + sc->intr_status &= ~status; + DPRINTF(2,("%s: intr status %#x error %#x\n", HDEVNAME(sc), status, + sc->intr_error_status)); + + /* Command timeout has higher priority than command complete. */ + if (ISSET(status, SDHC_INT_STATUS_ERR)) { + sc->intr_error_status = 0; + (void)imxesdhc_soft_reset(sc, SDHC_SYS_CTRL_RSTC | SDHC_SYS_CTRL_RSTD); + status = 0; + } + + splx(s); + return status; +} + +/* + * Established by attachment driver at interrupt priority IPL_SDMMC. + */ +int +imxesdhc_intr(void *arg) +{ + struct imxesdhc_softc *sc = arg; + + u_int32_t status; + + /* Find out which interrupts are pending. */ + status = HREAD4(sc, SDHC_INT_STATUS); + +#ifndef SDHC_DMA + /* disable interrupts for brr and bwr, else we get flooded */ + if (status & (SDHC_INT_STATUS_BRR | SDHC_INT_STATUS_BWR)) + HCLR4(sc, SDHC_INT_SIGNAL_EN, (SDHC_INT_STATUS_BRR | SDHC_INT_STATUS_BWR)); +#endif + + /* Acknowledge the interrupts we are about to handle. */ + HWRITE4(sc, SDHC_INT_STATUS, status); + DPRINTF(2,("%s: interrupt status=0x%08x\n", HDEVNAME(sc), + status, status)); + + /* + * Service error interrupts. + */ + if (ISSET(status, SDHC_INT_STATUS_CMD_ERR | + SDHC_INT_STATUS_CTOE | SDHC_INT_STATUS_DTOE)) { + sc->intr_status |= status; + sc->intr_error_status |= status & 0xffff0000; + wakeup(&sc->intr_status); + } + + /* + * Wake up the blocking process to service command + * related interrupt(s). + */ + if (ISSET(status, SDHC_INT_STATUS_BRR | SDHC_INT_STATUS_BWR | + SDHC_INT_STATUS_TC | SDHC_INT_STATUS_CC)) { + sc->intr_status |= status; + wakeup(&sc->intr_status); + } + + /* + * Service SD card interrupts. + */ + if (ISSET(status, SDHC_INT_STATUS_CINT)) { + DPRINTF(0,("%s: card interrupt\n", HDEVNAME(sc))); + HCLR4(sc, SDHC_INT_STATUS, SDHC_INT_STATUS_CINT); + sdmmc_card_intr(sc->sdmmc); + } + return 1; +} diff --git a/sys/arch/armv7/imx/imxgpio.c b/sys/arch/armv7/imx/imxgpio.c new file mode 100644 index 00000000000..fbaf91a8f85 --- /dev/null +++ b/sys/arch/armv7/imx/imxgpio.c @@ -0,0 +1,241 @@ +/* $OpenBSD: imxgpio.c,v 1.1 2013/09/06 20:45:53 patrick Exp $ */ +/* + * Copyright (c) 2007,2009 Dale Rahn <drahn@openbsd.org> + * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/queue.h> +#include <sys/device.h> +#include <sys/malloc.h> +#include <sys/evcount.h> + +#include <arm/cpufunc.h> + +#include <machine/bus.h> +#include <machine/intr.h> + +#include <armv7/imx/imxvar.h> +#include <armv7/imx/imxgpiovar.h> + +/* iMX6 registers */ +#define GPIO_DR 0x00 +#define GPIO_GDIR 0x04 +#define GPIO_PSR 0x08 +#define GPIO_ICR1 0x0C +#define GPIO_ICR2 0x10 +#define GPIO_IMR 0x14 +#define GPIO_ISR 0x18 +#define GPIO_EDGE_SEL 0x1C + +#define GPIO_NUM_PINS 32 + +struct intrhand { + int (*ih_func)(void *); /* handler */ + void *ih_arg; /* arg for handler */ + int ih_ipl; /* IPL_* */ + int ih_irq; /* IRQ number */ + int ih_gpio; /* gpio pin */ + struct evcount ih_count; + char *ih_name; +}; + +struct imxgpio_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + void *sc_ih_h; + void *sc_ih_l; + int sc_max_il; + int sc_min_il; + int sc_irq; + struct intrhand *sc_handlers[GPIO_NUM_PINS]; + unsigned int (*sc_get_bit)(struct imxgpio_softc *sc, + unsigned int gpio); + void (*sc_set_bit)(struct imxgpio_softc *sc, + unsigned int gpio); + void (*sc_clear_bit)(struct imxgpio_softc *sc, + unsigned int gpio); + void (*sc_set_dir)(struct imxgpio_softc *sc, + unsigned int gpio, unsigned int dir); +}; + +#define GPIO_PIN_TO_INST(x) ((x) >> 5) +#define GPIO_PIN_TO_OFFSET(x) ((x) & 0x1f) + +int imxgpio_match(struct device *parent, void *v, void *aux); +void imxgpio_attach(struct device *parent, struct device *self, void *args); +void imxgpio_recalc_interrupts(struct imxgpio_softc *sc); +int imxgpio_irq(void *); +int imxgpio_irq_dummy(void *); + +unsigned int imxgpio_v6_get_bit(struct imxgpio_softc *, unsigned int); +void imxgpio_v6_set_bit(struct imxgpio_softc *, unsigned int); +void imxgpio_v6_clear_bit(struct imxgpio_softc *, unsigned int); +void imxgpio_v6_set_dir(struct imxgpio_softc *, unsigned int, unsigned int); +unsigned int imxgpio_v6_get_dir(struct imxgpio_softc *, unsigned int); + + +struct cfattach imxgpio_ca = { + sizeof (struct imxgpio_softc), imxgpio_match, imxgpio_attach +}; + +struct cfdriver imxgpio_cd = { + NULL, "imxgpio", DV_DULL +}; + +int +imxgpio_match(struct device *parent, void *v, void *aux) +{ + switch (board_id) { + case BOARD_ID_IMX6_PHYFLEX: + case BOARD_ID_IMX6_SABRELITE: + break; /* continue trying */ + default: + return 0; /* unknown */ + } + return (1); +} + +void +imxgpio_attach(struct device *parent, struct device *self, void *args) +{ + struct imx_attach_args *ia = args; + struct imxgpio_softc *sc = (struct imxgpio_softc *) self; + + sc->sc_iot = ia->ia_iot; + if (bus_space_map(sc->sc_iot, ia->ia_dev->mem[0].addr, + ia->ia_dev->mem[0].size, 0, &sc->sc_ioh)) + panic("imxgpio_attach: bus_space_map failed!"); + + + switch (board_id) { + case BOARD_ID_IMX6_PHYFLEX: + case BOARD_ID_IMX6_SABRELITE: + sc->sc_get_bit = imxgpio_v6_get_bit; + sc->sc_set_bit = imxgpio_v6_set_bit; + sc->sc_clear_bit = imxgpio_v6_clear_bit; + sc->sc_set_dir = imxgpio_v6_set_dir; + break; + } + + printf("\n"); + + /* XXX - IRQ */ + /* XXX - SYSCONFIG */ + /* XXX - CTRL */ + /* XXX - DEBOUNCE */ +} + +unsigned int +imxgpio_get_bit(unsigned int gpio) +{ + struct imxgpio_softc *sc = imxgpio_cd.cd_devs[GPIO_PIN_TO_INST(gpio)]; + + return sc->sc_get_bit(sc, gpio); + +} + +void +imxgpio_set_bit(unsigned int gpio) +{ + struct imxgpio_softc *sc = imxgpio_cd.cd_devs[GPIO_PIN_TO_INST(gpio)]; + + sc->sc_set_bit(sc, gpio); +} + +void +imxgpio_clear_bit(unsigned int gpio) +{ + struct imxgpio_softc *sc = imxgpio_cd.cd_devs[GPIO_PIN_TO_INST(gpio)]; + + sc->sc_clear_bit(sc, gpio); +} +void +imxgpio_set_dir(unsigned int gpio, unsigned int dir) +{ + struct imxgpio_softc *sc = imxgpio_cd.cd_devs[GPIO_PIN_TO_INST(gpio)]; + + sc->sc_set_dir(sc, gpio, dir); +} + +unsigned int +imxgpio_v6_get_bit(struct imxgpio_softc *sc, unsigned int gpio) +{ + u_int32_t val; + + val = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GPIO_DR); + + return (val >> GPIO_PIN_TO_OFFSET(gpio)) & 0x1; +} + +void +imxgpio_v6_set_bit(struct imxgpio_softc *sc, unsigned int gpio) +{ + u_int32_t val; + + val = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GPIO_DR); + + bus_space_write_4(sc->sc_iot, sc->sc_ioh, GPIO_DR, + val | (1 << GPIO_PIN_TO_OFFSET(gpio))); +} + +void +imxgpio_v6_clear_bit(struct imxgpio_softc *sc, unsigned int gpio) +{ + u_int32_t val; + + val = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GPIO_DR); + + bus_space_write_4(sc->sc_iot, sc->sc_ioh, GPIO_DR, + val & ~(1 << GPIO_PIN_TO_OFFSET(gpio))); +} + +void +imxgpio_v6_set_dir(struct imxgpio_softc *sc, unsigned int gpio, unsigned int dir) +{ + int s; + u_int32_t val; + + s = splhigh(); + + val = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GPIO_GDIR); + if (dir == IMXGPIO_DIR_OUT) + val |= 1 << GPIO_PIN_TO_OFFSET(gpio); + else + val &= ~(1 << GPIO_PIN_TO_OFFSET(gpio)); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, GPIO_GDIR, val); + + splx(s); +} + +unsigned int +imxgpio_v6_get_dir(struct imxgpio_softc *sc, unsigned int gpio) +{ + int s; + u_int32_t val; + + s = splhigh(); + + val = bus_space_read_4(sc->sc_iot, sc->sc_ioh, GPIO_GDIR); + if (val & (1 << GPIO_PIN_TO_OFFSET(gpio))) + val = IMXGPIO_DIR_OUT; + else + val = IMXGPIO_DIR_IN; + + splx(s); + return val; +} diff --git a/sys/arch/armv7/imx/imxgpiovar.h b/sys/arch/armv7/imx/imxgpiovar.h new file mode 100644 index 00000000000..1451624c58c --- /dev/null +++ b/sys/arch/armv7/imx/imxgpiovar.h @@ -0,0 +1,41 @@ +/* $OpenBSD: imxgpiovar.h,v 1.1 2013/09/06 20:45:53 patrick Exp $ */ +/* + * Copyright (c) 2007,2009 Dale Rahn <drahn@openbsd.org> + * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IMXGPIOVAR_H +#define IMXGPIOVAR_H + +#define IMXGPIO_DIR_IN 0 +#define IMXGPIO_DIR_OUT 1 + +unsigned int imxgpio_get_function(unsigned int gpio, unsigned int fn); +void imxgpio_set_function(unsigned int gpio, unsigned int fn); +unsigned int imxgpio_get_bit(unsigned int gpio); +void imxgpio_set_bit(unsigned int gpio); +void imxgpio_clear_bit(unsigned int gpio); +void imxgpio_set_dir(unsigned int gpio, unsigned int dir); + +/* interrupts */ +void imxgpio_clear_intr(unsigned int gpio); +void imxgpio_intr_mask(unsigned int gpio); +void imxgpio_intr_unmask(unsigned int gpio); +void imxgpio_intr_level(unsigned int gpio, unsigned int level); +void *imxgpio_intr_establish(unsigned int gpio, int level, int spl, + int (*func)(void *), void *arg, char *name); +void imxgpio_intr_disestablish(void *cookie); + +#endif /* IMXGPIOVAR_H */ diff --git a/sys/arch/armv7/imx/imxiic.c b/sys/arch/armv7/imx/imxiic.c new file mode 100644 index 00000000000..5997967bc0d --- /dev/null +++ b/sys/arch/armv7/imx/imxiic.c @@ -0,0 +1,372 @@ +/* $OpenBSD: imxiic.c,v 1.1 2013/09/06 20:45:53 patrick Exp $ */ +/* + * Copyright (c) 2013 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/device.h> +#include <sys/kernel.h> +#include <sys/kthread.h> +#include <sys/malloc.h> +#include <sys/systm.h> +#include <machine/bus.h> + +#include <armv7/imx/imxvar.h> +#include <armv7/imx/imxiomuxcvar.h> +#include <armv7/imx/imxccmvar.h> +#include <armv7/imx/imxiicvar.h> + +/* registers */ +#define I2C_IADR 0x00 +#define I2C_IFDR 0x04 +#define I2C_I2CR 0x08 +#define I2C_I2SR 0x0C +#define I2C_I2DR 0x10 + +#define I2C_I2CR_RSTA (1 << 2) +#define I2C_I2CR_TXAK (1 << 3) +#define I2C_I2CR_MTX (1 << 4) +#define I2C_I2CR_MSTA (1 << 5) +#define I2C_I2CR_IIEN (1 << 6) +#define I2C_I2CR_IEN (1 << 7) +#define I2C_I2SR_RXAK (1 << 0) +#define I2C_I2SR_IIF (1 << 1) +#define I2C_I2SR_IBB (1 << 5) + +struct imxiic_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + bus_size_t sc_ios; + void *sc_ih; + int unit; + + struct rwlock sc_buslock; + struct i2c_controller i2c_tag; + + uint16_t frequency; + uint16_t intr_status; + uint16_t stopped; +}; + +void imxiic_attach(struct device *, struct device *, void *); +int imxiic_detach(struct device *, int); +void imxiic_setspeed(struct imxiic_softc *, u_int); +int imxiic_intr(void *); +int imxiic_wait_intr(struct imxiic_softc *, int, int); +int imxiic_wait_state(struct imxiic_softc *, uint32_t, uint32_t); +int imxiic_start(struct imxiic_softc *, int, int, void *, int); +int imxiic_read(struct imxiic_softc *, int, int, void *, int); +int imxiic_write(struct imxiic_softc *, int, int, const void *, int); + +int imxiic_i2c_acquire_bus(void *, int); +void imxiic_i2c_release_bus(void *, int); +int imxiic_i2c_exec(void *, i2c_op_t, i2c_addr_t, const void *, size_t, + void *, size_t, int); + +#define HREAD2(sc, reg) \ + (bus_space_read_2((sc)->sc_iot, (sc)->sc_ioh, (reg))) +#define HWRITE2(sc, reg, val) \ + bus_space_write_2((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) +#define HSET2(sc, reg, bits) \ + HWRITE2((sc), (reg), HREAD2((sc), (reg)) | (bits)) +#define HCLR2(sc, reg, bits) \ + HWRITE2((sc), (reg), HREAD2((sc), (reg)) & ~(bits)) + + +struct cfattach imxiic_ca = { + sizeof(struct imxiic_softc), NULL, imxiic_attach, imxiic_detach +}; + +struct cfdriver imxiic_cd = { + NULL, "imxiic", DV_DULL +}; + +void +imxiic_attach(struct device *parent, struct device *self, void *args) +{ + struct imxiic_softc *sc = (struct imxiic_softc *)self; + struct imx_attach_args *ia = args; + + sc->sc_iot = ia->ia_iot; + sc->sc_ios = ia->ia_dev->mem[0].size; + sc->unit = ia->ia_dev->unit; + if (bus_space_map(sc->sc_iot, ia->ia_dev->mem[0].addr, + ia->ia_dev->mem[0].size, 0, &sc->sc_ioh)) + panic("imxiic_attach: bus_space_map failed!"); + +#if 0 + sc->sc_ih = arm_intr_establish(ia->ia_dev->irq[0], IPL_BIO, + imxiic_intr, sc, sc->sc_dev.dv_xname); +#endif + + printf("\n"); + + /* set iomux pins */ + imxiomuxc_enable_i2c(sc->unit); + + /* set speed to 100kHz */ + imxiic_setspeed(sc, 100); + + /* reset */ + HWRITE2(sc, I2C_I2CR, 0); + HWRITE2(sc, I2C_I2SR, 0); + + sc->stopped = 1; + rw_init(&sc->sc_buslock, sc->sc_dev.dv_xname); + + struct i2cbus_attach_args iba; + + sc->i2c_tag.ic_cookie = sc; + sc->i2c_tag.ic_acquire_bus = imxiic_i2c_acquire_bus; + sc->i2c_tag.ic_release_bus = imxiic_i2c_release_bus; + sc->i2c_tag.ic_exec = imxiic_i2c_exec; + + bzero(&iba, sizeof iba); + iba.iba_name = "iic"; + iba.iba_tag = &sc->i2c_tag; + config_found(&sc->sc_dev, &iba, NULL); +} + +void +imxiic_setspeed(struct imxiic_softc *sc, u_int speed) +{ + if (!sc->frequency) { + uint32_t i2c_clk_rate; + uint32_t div; + int i; + + i2c_clk_rate = imxccm_get_ipg_perclk(); + div = (i2c_clk_rate + speed - 1) / speed; + if (div < imxiic_clk_div[0][0]) + i = 0; + else if (div > imxiic_clk_div[49][0]) + i = 49; + else + for (i = 0; imxiic_clk_div[i][0] < div; i++); + + sc->frequency = imxiic_clk_div[i][1]; + } + + HWRITE2(sc, I2C_IFDR, sc->frequency); +} + +#if 0 +int +imxiic_intr(void *arg) +{ + struct imxiic_softc *sc = arg; + u_int16_t status; + + status = HREAD2(sc, I2C_I2SR); + + if (ISSET(status, I2C_I2SR_IIF)) { + /* acknowledge the interrupts */ + HWRITE2(sc, I2C_I2SR, + HREAD2(sc, I2C_I2SR) & ~I2C_I2SR_IIF); + + sc->intr_status |= status; + wakeup(&sc->intr_status); + } + + return (0); +} + +int +imxiic_wait_intr(struct imxiic_softc *sc, int mask, int timo) +{ + int status; + int s; + + s = splbio(); + + status = sc->intr_status & mask; + while (status == 0) { + if (tsleep(&sc->intr_status, PWAIT, "hcintr", timo) + == EWOULDBLOCK) { + break; + } + status = sc->intr_status & mask; + } + status = sc->intr_status & mask; + sc->intr_status &= ~status; + + splx(s); + return status; +} +#endif + +int +imxiic_wait_state(struct imxiic_softc *sc, uint32_t mask, uint32_t value) +{ + uint32_t state; + int timeout; + state = HREAD2(sc, I2C_I2SR); + for (timeout = 1000; timeout > 0; timeout--) { + if (((state = HREAD2(sc, I2C_I2SR)) & mask) == value) + return 0; + delay(10); + } + return ETIMEDOUT; +} + +int +imxiic_read(struct imxiic_softc *sc, int addr, int subaddr, void *data, int len) +{ + int i; + + HWRITE2(sc, I2C_I2DR, addr | 1); + + if (imxiic_wait_state(sc, I2C_I2SR_IIF, I2C_I2SR_IIF)) + return (EIO); + while(!(HREAD2(sc, I2C_I2SR) & I2C_I2SR_IIF)); + if (HREAD2(sc, I2C_I2SR) & I2C_I2SR_RXAK) + return (EIO); + + HCLR2(sc, I2C_I2CR, I2C_I2CR_MTX); + if (len) + HCLR2(sc, I2C_I2CR, I2C_I2CR_TXAK); + + /* dummy read */ + HREAD2(sc, I2C_I2DR); + + for (i = 0; i < len; i++) { + if (imxiic_wait_state(sc, I2C_I2SR_IIF, I2C_I2SR_IIF)) + return (EIO); + if (i == (len - 1)) { + HCLR2(sc, I2C_I2CR, I2C_I2CR_MSTA | I2C_I2CR_MTX); + imxiic_wait_state(sc, I2C_I2SR_IBB, 0); + sc->stopped = 1; + } else if (i == (len - 2)) { + HSET2(sc, I2C_I2CR, I2C_I2CR_TXAK); + } + ((uint8_t*)data)[i] = HREAD2(sc, I2C_I2DR); + } + + return 0; +} + +int +imxiic_write(struct imxiic_softc *sc, int addr, int subaddr, const void *data, int len) +{ + int i; + + HWRITE2(sc, I2C_I2DR, addr); + + if (imxiic_wait_state(sc, I2C_I2SR_IIF, I2C_I2SR_IIF)) + return (EIO); + if (HREAD2(sc, I2C_I2SR) & I2C_I2SR_RXAK) + return (EIO); + + for (i = 0; i < len; i++) { + HWRITE2(sc, I2C_I2DR, ((uint8_t*)data)[i]); + if (imxiic_wait_state(sc, I2C_I2SR_IIF, I2C_I2SR_IIF)) + return (EIO); + if (HREAD2(sc, I2C_I2SR) & I2C_I2SR_RXAK) + return (EIO); + } + return 0; +} + +int +imxiic_i2c_acquire_bus(void *cookie, int flags) +{ + struct imxiic_softc *sc = cookie; + + return (rw_enter(&sc->sc_buslock, RW_WRITE)); +} + +void +imxiic_i2c_release_bus(void *cookie, int flags) +{ + struct imxiic_softc *sc = cookie; + + (void) rw_exit(&sc->sc_buslock); +} + +int +imxiic_i2c_exec(void *cookie, i2c_op_t op, i2c_addr_t addr, + const void *cmdbuf, size_t cmdlen, void *buf, size_t len, int flags) +{ + struct imxiic_softc *sc = cookie; + uint32_t ret = 0; + u_int8_t cmd = 0; + + if (!I2C_OP_STOP_P(op) || cmdlen > 1) + return (EINVAL); + + if (cmdlen > 0) + cmd = *(u_int8_t *)cmdbuf; + + addr &= 0x7f; + + /* clock gating */ + imxccm_enable_i2c(sc->unit); + + /* set speed to 100kHz */ + imxiic_setspeed(sc, 100); + + /* enable the controller */ + HWRITE2(sc, I2C_I2SR, 0); + HWRITE2(sc, I2C_I2CR, I2C_I2CR_IEN); + + /* wait for it to be stable */ + delay(50); + + /* start transaction */ + HSET2(sc, I2C_I2CR, I2C_I2CR_MSTA); + + if (imxiic_wait_state(sc, I2C_I2SR_IBB, I2C_I2SR_IBB)) { + ret = (EIO); + goto fail; + } + + sc->stopped = 0; + + HSET2(sc, I2C_I2CR, I2C_I2CR_IIEN | I2C_I2CR_MTX | I2C_I2CR_TXAK); + + if (I2C_OP_READ_P(op)) { + if (imxiic_read(sc, (addr << 1), cmd, buf, len) != 0) + ret = (EIO); + } else { + if (imxiic_write(sc, (addr << 1), cmd, buf, len) != 0) + ret = (EIO); + } + +fail: + if (!sc->stopped) { + HCLR2(sc, I2C_I2CR, I2C_I2CR_MSTA | I2C_I2CR_MTX); + imxiic_wait_state(sc, I2C_I2SR_IBB, 0); + sc->stopped = 1; + } + + HWRITE2(sc, I2C_I2CR, 0); + + return ret; +} + +int +imxiic_detach(struct device *self, int flags) +{ + struct imxiic_softc *sc = (struct imxiic_softc *)self; + + HWRITE2(sc, I2C_IADR, 0); + HWRITE2(sc, I2C_IFDR, 0); + HWRITE2(sc, I2C_I2CR, 0); + HWRITE2(sc, I2C_I2SR, 0); + + bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios); + return 0; +} diff --git a/sys/arch/armv7/imx/imxiicvar.h b/sys/arch/armv7/imx/imxiicvar.h new file mode 100644 index 00000000000..cd703366bfa --- /dev/null +++ b/sys/arch/armv7/imx/imxiicvar.h @@ -0,0 +1,44 @@ +/* $OpenBSD: imxiicvar.h,v 1.1 2013/09/06 20:45:54 patrick Exp $ */ +/* + * Copyright (c) 2013 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IMXIICVAR_H +#define IMXIICVAR_H + +#include <sys/param.h> +#include <sys/device.h> +#include <sys/systm.h> +#include <sys/rwlock.h> + +#include <dev/i2c/i2cvar.h> + +static uint16_t imxiic_clk_div[50][2] = { + { 22, 0x20 }, { 24, 0x21 }, { 26, 0x22 }, { 28, 0x23 }, + { 30, 0x00 }, { 32, 0x24 }, { 36, 0x25 }, { 40, 0x26 }, + { 42, 0x03 }, { 44, 0x27 }, { 48, 0x28 }, { 52, 0x05 }, + { 56, 0x29 }, { 60, 0x06 }, { 64, 0x2A }, { 72, 0x2B }, + { 80, 0x2C }, { 88, 0x09 }, { 96, 0x2D }, { 104, 0x0A }, + { 112, 0x2E }, { 128, 0x2F }, { 144, 0x0C }, { 160, 0x30 }, + { 192, 0x31 }, { 224, 0x32 }, { 240, 0x0F }, { 256, 0x33 }, + { 288, 0x10 }, { 320, 0x34 }, { 384, 0x35 }, { 448, 0x36 }, + { 480, 0x13 }, { 512, 0x37 }, { 576, 0x14 }, { 640, 0x38 }, + { 768, 0x39 }, { 896, 0x3A }, { 960, 0x17 }, { 1024, 0x3B }, + { 1152, 0x18 }, { 1280, 0x3C }, { 1536, 0x3D }, { 1792, 0x3E }, + { 1920, 0x1B }, { 2048, 0x3F }, { 2304, 0x1C }, { 2560, 0x1D }, + { 3072, 0x1E }, { 3840, 0x1F } +}; + +#endif diff --git a/sys/arch/armv7/imx/imxiomuxc.c b/sys/arch/armv7/imx/imxiomuxc.c new file mode 100644 index 00000000000..f1ec2a1b8a9 --- /dev/null +++ b/sys/arch/armv7/imx/imxiomuxc.c @@ -0,0 +1,262 @@ +/* $OpenBSD: imxiomuxc.c,v 1.1 2013/09/06 20:45:54 patrick Exp $ */ +/* + * Copyright (c) 2013 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/queue.h> +#include <sys/malloc.h> +#include <sys/device.h> +#include <sys/evcount.h> +#include <sys/socket.h> +#include <sys/timeout.h> +#include <machine/intr.h> +#include <machine/bus.h> +#include <armv7/imx/imxvar.h> + +/* registers */ +#define IOMUXC_GPR1 0x004 +#define IOMUXC_GPR8 0x020 +#define IOMUXC_GPR12 0x030 +#define IOMUXC_GPR13 0x034 + +#define IOMUXC_MUX_CTL_PAD_EIM_EB2 0x08C +#define IOMUXC_MUX_CTL_PAD_EIM_DATA16 0x090 +#define IOMUXC_MUX_CTL_PAD_EIM_DATA17 0x094 +#define IOMUXC_MUX_CTL_PAD_EIM_DATA18 0x098 +#define IOMUXC_MUX_CTL_PAD_EIM_DATA21 0x0A4 +#define IOMUXC_MUX_CTL_PAD_EIM_DATA28 0x0C4 + +#define IOMUXC_PAD_CTL_PAD_EIM_EB2 0x3A0 +#define IOMUXC_PAD_CTL_PAD_EIM_DATA16 0x3A4 +#define IOMUXC_PAD_CTL_PAD_EIM_DATA17 0x3A8 +#define IOMUXC_PAD_CTL_PAD_EIM_DATA18 0x3AC +#define IOMUXC_PAD_CTL_PAD_EIM_DATA21 0x3B8 +#define IOMUXC_PAD_CTL_PAD_EIM_DATA28 0x3D8 + +#define IOMUXC_I2C1_SCL_IN_SELECT_INPUT 0x898 +#define IOMUXC_I2C1_SDA_IN_SELECT_INPUT 0x89C +#define IOMUXC_I2C2_SCL_IN_SELECT_INPUT 0x8A0 +#define IOMUXC_I2C2_SDA_IN_SELECT_INPUT 0x8A4 +#define IOMUXC_I2C3_SCL_IN_SELECT_INPUT 0x8A8 +#define IOMUXC_I2C3_SDA_IN_SELECT_INPUT 0x8AC + +/* bits and bytes */ +#define IOMUXC_GPR1_REF_SSP_EN (1 << 16) +#define IOMUXC_GPR1_TEST_POWERDOWN (1 << 18) + +#define IOMUXC_GPR8_PCS_TX_DEEMPH_GEN1 (0 << 0) +#define IOMUXC_GPR8_PCS_TX_DEEMPH_GEN2_3P5DB (0 << 6) +#define IOMUXC_GPR8_PCS_TX_DEEMPH_GEN2_6DB (20 << 12) +#define IOMUXC_GPR8_PCS_TX_SWING_FULL (127 << 18) +#define IOMUXC_GPR8_PCS_TX_SWING_LOW (127 << 25) + +#define IOMUXC_GPR12_LOS_LEVEL_MASK (0x1f << 4) +#define IOMUXC_GPR12_LOS_LEVEL_9 (9 << 4) +#define IOMUXC_GPR12_APPS_PM_XMT_PME (1 << 9) +#define IOMUXC_GPR12_APPS_LTSSM_ENABLE (1 << 10) +#define IOMUXC_GPR12_APPS_INIT_RST (1 << 11) +#define IOMUXC_GPR12_DEVICE_TYPE_RC (2 << 12) +#define IOMUXC_GPR12_DEVICE_TYPE_MASK (3 << 12) +#define IOMUXC_GPR12_APPS_PM_XMT_TURNOFF (1 << 16) + +#define IOMUXC_GPR13_SATA_PHY_1_FAST_EDGE_RATE (0x00 << 0) +#define IOMUXC_GPR13_SATA_PHY_1_SLOW_EDGE_RATE (0x02 << 0) +#define IOMUXC_GPR13_SATA_PHY_1_EDGE_RATE_MASK 0x3 +#define IOMUXC_GPR13_SATA_PHY_2_1104V (0x11 << 2) +#define IOMUXC_GPR13_SATA_PHY_3_333DB (0x00 << 7) +#define IOMUXC_GPR13_SATA_PHY_4_9_16 (0x04 << 11) +#define IOMUXC_GPR13_SATA_PHY_5_SS (0x01 << 14) +#define IOMUXC_GPR13_SATA_SPEED_3G (0x01 << 15) +#define IOMUXC_GPR13_SATA_PHY_6 (0x03 << 16) +#define IOMUXC_GPR13_SATA_PHY_7_SATA2M (0x12 << 19) +#define IOMUXC_GPR13_SATA_PHY_8_30DB (0x05 << 24) +#define IOMUXC_GPR13_SATA_MASK 0x07FFFFFD + +#define IOMUXC_PAD_CTL_SRE_SLOW (1 << 0) +#define IOMUXC_PAD_CTL_SRE_FAST (1 << 0) +#define IOMUXC_PAD_CTL_DSE_HIZ (0 << 3) +#define IOMUXC_PAD_CTL_DSE_240OHM (1 << 3) +#define IOMUXC_PAD_CTL_DSE_120OHM (2 << 3) +#define IOMUXC_PAD_CTL_DSE_80OHM (3 << 3) +#define IOMUXC_PAD_CTL_DSE_60OHM (4 << 3) +#define IOMUXC_PAD_CTL_DSE_48OHM (5 << 3) +#define IOMUXC_PAD_CTL_DSE_40OHM (6 << 3) +#define IOMUXC_PAD_CTL_DSE_34OHM (7 << 3) +#define IOMUXC_PAD_CTL_SPEED_TBD (0 << 6) +#define IOMUXC_PAD_CTL_SPEED_LOW (1 << 6) +#define IOMUXC_PAD_CTL_SPEED_MED (2 << 6) +#define IOMUXC_PAD_CTL_SPEED_MAX (3 << 6) +#define IOMUXC_PAD_CTL_ODE_DISABLED (0 << 11) +#define IOMUXC_PAD_CTL_ODE_ENABLED (1 << 11) +#define IOMUXC_PAD_CTL_PKE_DISABLED (0 << 12) +#define IOMUXC_PAD_CTL_PKE_ENABLED (1 << 12) +#define IOMUXC_PAD_CTL_PUE_KEEP (0 << 13) +#define IOMUXC_PAD_CTL_PUE_PULL (1 << 13) +#define IOMUXC_PAD_CTL_PUS_100K_OHM_PD (0 << 14) +#define IOMUXC_PAD_CTL_PUS_47K_OHM_PU (1 << 14) +#define IOMUXC_PAD_CTL_PUS_100K_OHM_PU (2 << 14) +#define IOMUXC_PAD_CTL_PUS_22K_OHM_PU (3 << 14) +#define IOMUXC_PAD_CTL_HYS_DISABLED (0 << 16) +#define IOMUXC_PAD_CTL_HYS_ENABLED (1 << 16) + +#define IOMUXC_IMX6Q_I2C_PAD_CTRL (IOMUXC_PAD_CTL_SRE_FAST | IOMUXC_PAD_CTL_ODE_ENABLED | \ + IOMUXC_PAD_CTL_PKE_ENABLED | IOMUXC_PAD_CTL_PUE_PULL | IOMUXC_PAD_CTL_DSE_40OHM | \ + IOMUXC_PAD_CTL_PUS_100K_OHM_PU | IOMUXC_PAD_CTL_HYS_ENABLED | IOMUXC_PAD_CTL_SPEED_MED) + +#define IOMUX_CONFIG_SION (1 << 4) + +struct imxiomuxc_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; +}; + +struct imxiomuxc_softc *imxiomuxc_sc; + +void imxiomuxc_attach(struct device *parent, struct device *self, void *args); +void imxiomuxc_enable_sata(void); +void imxiomuxc_enable_i2c(int); +void imxiomuxc_enable_pcie(void); +void imxiomuxc_pcie_refclk(int); +void imxiomuxc_pcie_test_powerdown(int); + +struct cfattach imxiomuxc_ca = { + sizeof (struct imxiomuxc_softc), NULL, imxiomuxc_attach +}; + +struct cfdriver imxiomuxc_cd = { + NULL, "imxiomuxc", DV_DULL +}; + +void +imxiomuxc_attach(struct device *parent, struct device *self, void *args) +{ + struct imx_attach_args *ia = args; + struct imxiomuxc_softc *sc = (struct imxiomuxc_softc *) self; + + sc->sc_iot = ia->ia_iot; + if (bus_space_map(sc->sc_iot, ia->ia_dev->mem[0].addr, + ia->ia_dev->mem[0].size, 0, &sc->sc_ioh)) + panic("imxiomuxc_attach: bus_space_map failed!"); + + printf("\n"); + imxiomuxc_sc = sc; +} + +void +imxiomuxc_enable_sata(void) +{ + struct imxiomuxc_softc *sc = imxiomuxc_sc; + + bus_space_write_4(sc->sc_iot, sc->sc_ioh, IOMUXC_GPR13, + (bus_space_read_4(sc->sc_iot, sc->sc_ioh, IOMUXC_GPR13) & ~IOMUXC_GPR13_SATA_MASK) | + IOMUXC_GPR13_SATA_PHY_1_FAST_EDGE_RATE | IOMUXC_GPR13_SATA_PHY_2_1104V | + IOMUXC_GPR13_SATA_PHY_3_333DB | IOMUXC_GPR13_SATA_PHY_4_9_16 | + IOMUXC_GPR13_SATA_SPEED_3G | IOMUXC_GPR13_SATA_PHY_6 | + IOMUXC_GPR13_SATA_PHY_7_SATA2M | IOMUXC_GPR13_SATA_PHY_8_30DB); + + bus_space_write_4(sc->sc_iot, sc->sc_ioh, IOMUXC_GPR13, + (bus_space_read_4(sc->sc_iot, sc->sc_ioh, IOMUXC_GPR13) & ~IOMUXC_GPR13_SATA_PHY_1_SLOW_EDGE_RATE) | + IOMUXC_GPR13_SATA_PHY_1_SLOW_EDGE_RATE); +} + +void +imxiomuxc_enable_pcie(void) +{ + struct imxiomuxc_softc *sc = imxiomuxc_sc; + + bus_space_write_4(sc->sc_iot, sc->sc_ioh, IOMUXC_GPR12, + bus_space_read_4(sc->sc_iot, sc->sc_ioh, IOMUXC_GPR12) & ~IOMUXC_GPR12_APPS_LTSSM_ENABLE); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, IOMUXC_GPR12, + (bus_space_read_4(sc->sc_iot, sc->sc_ioh, IOMUXC_GPR12) & ~IOMUXC_GPR12_DEVICE_TYPE_MASK) | + IOMUXC_GPR12_DEVICE_TYPE_RC); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, IOMUXC_GPR12, + (bus_space_read_4(sc->sc_iot, sc->sc_ioh, IOMUXC_GPR12) & ~IOMUXC_GPR12_LOS_LEVEL_MASK) | + IOMUXC_GPR12_LOS_LEVEL_9); + + bus_space_write_4(sc->sc_iot, sc->sc_ioh, IOMUXC_GPR8, + bus_space_read_4(sc->sc_iot, sc->sc_ioh, IOMUXC_GPR8) | + IOMUXC_GPR8_PCS_TX_DEEMPH_GEN1 | IOMUXC_GPR8_PCS_TX_DEEMPH_GEN2_3P5DB | + IOMUXC_GPR8_PCS_TX_DEEMPH_GEN2_6DB | IOMUXC_GPR8_PCS_TX_SWING_FULL | + IOMUXC_GPR8_PCS_TX_SWING_LOW); +} + +void +imxiomuxc_pcie_refclk(int enable) +{ + struct imxiomuxc_softc *sc = imxiomuxc_sc; + + bus_space_write_4(sc->sc_iot, sc->sc_ioh, IOMUXC_GPR1, + bus_space_read_4(sc->sc_iot, sc->sc_ioh, IOMUXC_GPR1) & ~IOMUXC_GPR1_REF_SSP_EN); + + if (enable) + bus_space_write_4(sc->sc_iot, sc->sc_ioh, IOMUXC_GPR1, + bus_space_read_4(sc->sc_iot, sc->sc_ioh, IOMUXC_GPR1) | IOMUXC_GPR1_REF_SSP_EN); +} + +void +imxiomuxc_pcie_test_powerdown(int enable) +{ + struct imxiomuxc_softc *sc = imxiomuxc_sc; + + bus_space_write_4(sc->sc_iot, sc->sc_ioh, IOMUXC_GPR1, + bus_space_read_4(sc->sc_iot, sc->sc_ioh, IOMUXC_GPR1) & ~IOMUXC_GPR1_TEST_POWERDOWN); + + if (enable) + bus_space_write_4(sc->sc_iot, sc->sc_ioh, IOMUXC_GPR1, + bus_space_read_4(sc->sc_iot, sc->sc_ioh, IOMUXC_GPR1) | IOMUXC_GPR1_TEST_POWERDOWN); +} + +void +imxiomuxc_enable_i2c(int x) +{ + struct imxiomuxc_softc *sc = imxiomuxc_sc; + + /* let's just use EIM for those */ + switch (x) { + case 0: + /* scl in select */ + bus_space_write_4(sc->sc_iot, sc->sc_ioh, IOMUXC_MUX_CTL_PAD_EIM_DATA21, IOMUX_CONFIG_SION | 6); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, IOMUXC_I2C1_SCL_IN_SELECT_INPUT, 0); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, IOMUXC_PAD_CTL_PAD_EIM_DATA21, IOMUXC_IMX6Q_I2C_PAD_CTRL); + /* sda in select */ + bus_space_write_4(sc->sc_iot, sc->sc_ioh, IOMUXC_MUX_CTL_PAD_EIM_DATA28, IOMUX_CONFIG_SION | 1); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, IOMUXC_I2C1_SDA_IN_SELECT_INPUT, 0); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, IOMUXC_PAD_CTL_PAD_EIM_DATA28, IOMUXC_IMX6Q_I2C_PAD_CTRL); + break; + case 1: + /* scl in select */ + bus_space_write_4(sc->sc_iot, sc->sc_ioh, IOMUXC_MUX_CTL_PAD_EIM_EB2, IOMUX_CONFIG_SION | 6); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, IOMUXC_I2C2_SCL_IN_SELECT_INPUT, 0); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, IOMUXC_PAD_CTL_PAD_EIM_EB2, IOMUXC_IMX6Q_I2C_PAD_CTRL); + /* sda in select */ + bus_space_write_4(sc->sc_iot, sc->sc_ioh, IOMUXC_MUX_CTL_PAD_EIM_DATA16, IOMUX_CONFIG_SION | 6); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, IOMUXC_I2C2_SDA_IN_SELECT_INPUT, 0); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, IOMUXC_PAD_CTL_PAD_EIM_DATA16, IOMUXC_IMX6Q_I2C_PAD_CTRL); + break; + case 2: + /* scl in select */ + bus_space_write_4(sc->sc_iot, sc->sc_ioh, IOMUXC_MUX_CTL_PAD_EIM_DATA17, IOMUX_CONFIG_SION | 6); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, IOMUXC_I2C3_SCL_IN_SELECT_INPUT, 0); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, IOMUXC_PAD_CTL_PAD_EIM_DATA17, IOMUXC_IMX6Q_I2C_PAD_CTRL); + /* sda in select */ + bus_space_write_4(sc->sc_iot, sc->sc_ioh, IOMUXC_MUX_CTL_PAD_EIM_DATA18, IOMUX_CONFIG_SION | 6); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, IOMUXC_I2C3_SDA_IN_SELECT_INPUT, 0); + bus_space_write_4(sc->sc_iot, sc->sc_ioh, IOMUXC_PAD_CTL_PAD_EIM_DATA18, IOMUXC_IMX6Q_I2C_PAD_CTRL); + break; + } +} diff --git a/sys/arch/armv7/imx/imxiomuxcvar.h b/sys/arch/armv7/imx/imxiomuxcvar.h new file mode 100644 index 00000000000..682711eb12d --- /dev/null +++ b/sys/arch/armv7/imx/imxiomuxcvar.h @@ -0,0 +1,27 @@ +/* $OpenBSD: imxiomuxcvar.h,v 1.1 2013/09/06 20:45:54 patrick Exp $ */ +/* + * Copyright (c) 2013 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef IMXIOMUXCVAR_H +#define IMXIOMUXCVAR_H + +void imxiomuxc_enable_sata(void); +void imxiomuxc_enable_i2c(int); +void imxiomuxc_enable_pcie(void); +void imxiomuxc_pcie_refclk(int); +void imxiomuxc_pcie_test_powerdown(int); + +#endif /* IMXIOMUXCVAR_H */ diff --git a/sys/arch/armv7/imx/imxocotp.c b/sys/arch/armv7/imx/imxocotp.c new file mode 100644 index 00000000000..0e4ce9d7305 --- /dev/null +++ b/sys/arch/armv7/imx/imxocotp.c @@ -0,0 +1,82 @@ +/* $OpenBSD: imxocotp.c,v 1.1 2013/09/06 20:45:54 patrick Exp $ */ +/* + * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/queue.h> +#include <sys/malloc.h> +#include <sys/device.h> +#include <sys/evcount.h> +#include <sys/socket.h> +#include <sys/timeout.h> +#include <machine/intr.h> +#include <machine/bus.h> + +#include <armv7/imx/imxvar.h> +#include <armv7/imx/imxocotpvar.h> + +/* registers */ +#define OCOTP_MAC0 0x620 +#define OCOTP_MAC1 0x630 + +struct imxocotp_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; +}; + +struct imxocotp_softc *imxocotp_sc; + +void imxocotp_attach(struct device *parent, struct device *self, void *args); + +struct cfattach imxocotp_ca = { + sizeof (struct imxocotp_softc), NULL, imxocotp_attach +}; + +struct cfdriver imxocotp_cd = { + NULL, "imxocotp", DV_DULL +}; + +void +imxocotp_attach(struct device *parent, struct device *self, void *args) +{ + struct imx_attach_args *ia = args; + struct imxocotp_softc *sc = (struct imxocotp_softc *) self; + + sc->sc_iot = ia->ia_iot; + if (bus_space_map(sc->sc_iot, ia->ia_dev->mem[0].addr, + ia->ia_dev->mem[0].size, 0, &sc->sc_ioh)) + panic("imxocotp_attach: bus_space_map failed!"); + + printf("\n"); + imxocotp_sc = sc; +} + +void +imxocotp_get_ethernet_address(u_int8_t* mac) +{ + uint32_t value; + + value = bus_space_read_4(imxocotp_sc->sc_iot, imxocotp_sc->sc_ioh, OCOTP_MAC0); + mac[5] = value & 0xff; + mac[4] = (value >> 8) & 0xff; + mac[3] = (value >> 16) & 0xff; + mac[2] = (value >> 24) & 0xff; + value = bus_space_read_4(imxocotp_sc->sc_iot, imxocotp_sc->sc_ioh, OCOTP_MAC1); + mac[1] = value & 0xff; + mac[0] = (value >> 8) & 0xff; +} diff --git a/sys/arch/armv7/imx/imxocotpvar.h b/sys/arch/armv7/imx/imxocotpvar.h new file mode 100644 index 00000000000..b546638de5c --- /dev/null +++ b/sys/arch/armv7/imx/imxocotpvar.h @@ -0,0 +1,18 @@ +/* $OpenBSD: imxocotpvar.h,v 1.1 2013/09/06 20:45:54 patrick Exp $ */ +/* + * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +void imxocotp_get_ethernet_address(u_int8_t* mac); diff --git a/sys/arch/armv7/imx/imxuart.c b/sys/arch/armv7/imx/imxuart.c new file mode 100644 index 00000000000..4e895b465fe --- /dev/null +++ b/sys/arch/armv7/imx/imxuart.c @@ -0,0 +1,830 @@ +/* $OpenBSD: imxuart.c,v 1.1 2013/09/06 20:45:54 patrick Exp $ */ +/* + * Copyright (c) 2005 Dale Rahn <drahn@motorola.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/proc.h> +#include <sys/tty.h> +#include <sys/uio.h> +#include <sys/systm.h> +#include <sys/time.h> +#include <sys/device.h> +#include <sys/syslog.h> +#include <sys/conf.h> +#include <sys/fcntl.h> +#include <sys/select.h> +#include <sys/kernel.h> + +#include <dev/cons.h> + + +#ifdef DDB +#include <ddb/db_var.h> +#endif + +#include <machine/bus.h> +#include <armv7/imx/imxuartreg.h> +#include <armv7/imx/imxuartvar.h> +#include <armv7/imx/imxvar.h> +#include <armv7/imx/imxccmvar.h> + +#define DEVUNIT(x) (minor(x) & 0x7f) +#define DEVCUA(x) (minor(x) & 0x80) + +struct imxuart_softc { + struct device sc_dev; + bus_space_tag_t sc_iot; + bus_space_handle_t sc_ioh; + struct soft_intrhand *sc_si; + void *sc_irq; + struct tty *sc_tty; + struct timeout sc_diag_tmo; + struct timeout sc_dtr_tmo; + int sc_overflows; + int sc_floods; + int sc_errors; + int sc_halt; + u_int16_t sc_ucr1; + u_int16_t sc_ucr2; + u_int16_t sc_ucr3; + u_int16_t sc_ucr4; + u_int8_t sc_hwflags; +#define COM_HW_NOIEN 0x01 +#define COM_HW_FIFO 0x02 +#define COM_HW_SIR 0x20 +#define COM_HW_CONSOLE 0x40 +#define COM_HW_KGDB 0x80 + u_int8_t sc_swflags; +#define COM_SW_SOFTCAR 0x01 +#define COM_SW_CLOCAL 0x02 +#define COM_SW_CRTSCTS 0x04 +#define COM_SW_MDMBUF 0x08 +#define COM_SW_PPS 0x10 + + u_int8_t sc_initialize; + u_int8_t sc_cua; + u_int16_t *sc_ibuf, *sc_ibufp, *sc_ibufhigh, *sc_ibufend; +#define IMXUART_IBUFSIZE 128 +#define IMXUART_IHIGHWATER 100 + u_int16_t sc_ibufs[2][IMXUART_IBUFSIZE]; +}; + + +int imxuartprobe(struct device *parent, void *self, void *aux); +void imxuartattach(struct device *parent, struct device *self, void *aux); + +void imxuartcnprobe(struct consdev *cp); +void imxuartcnprobe(struct consdev *cp); +void imxuartcninit(struct consdev *cp); +int imxuartcnattach(bus_space_tag_t iot, bus_addr_t iobase, int rate, + tcflag_t cflag); +int imxuartcngetc(dev_t dev); +void imxuartcnputc(dev_t dev, int c); +void imxuartcnpollc(dev_t dev, int on); +int imxuart_param(struct tty *tp, struct termios *t); +void imxuart_start(struct tty *); +void imxuart_pwroff(struct imxuart_softc *sc); +void imxuart_diag(void *arg); +void imxuart_raisedtr(void *arg); +void imxuart_softint(void *arg); +struct imxuart_softc *imxuart_sc(dev_t dev); + +int imxuart_intr(void *); + + +/* XXX - we imitate 'com' serial ports and take over their entry points */ +/* XXX: These belong elsewhere */ +cdev_decl(imxuart); + +struct cfdriver imxuart_cd = { + NULL, "imxuart", DV_TTY +}; + +struct cfattach imxuart_ca = { + sizeof(struct imxuart_softc), imxuartprobe, imxuartattach +}; + +bus_space_tag_t imxuartconsiot; +bus_space_handle_t imxuartconsioh; +bus_addr_t imxuartconsaddr; +tcflag_t imxuartconscflag = TTYDEF_CFLAG; +int imxuartdefaultrate = B115200; + +int +imxuartprobe(struct device *parent, void *self, void *aux) +{ + return 1; +} + +struct cdevsw imxuartdev = + cdev_tty_init(3/*XXX NIMXUART */ ,imxuart); /* 12: serial port */ + +void +imxuartattach(struct device *parent, struct device *self, void *args) +{ + struct imx_attach_args *ia = args; + struct imxuart_softc *sc = (struct imxuart_softc *) self; + + sc->sc_irq = arm_intr_establish(ia->ia_dev->irq[0], IPL_TTY, + imxuart_intr, sc, sc->sc_dev.dv_xname); + + sc->sc_iot = ia->ia_iot; + if (bus_space_map(sc->sc_iot, ia->ia_dev->mem[0].addr, + ia->ia_dev->mem[0].size, 0, &sc->sc_ioh)) + panic("imxuartattach: bus_space_map failed!"); + + if (ia->ia_dev->mem[0].addr == imxuartconsaddr) + printf(" console"); + + timeout_set(&sc->sc_diag_tmo, imxuart_diag, sc); + timeout_set(&sc->sc_dtr_tmo, imxuart_raisedtr, sc); + sc->sc_si = softintr_establish(IPL_TTY, imxuart_softint, sc); + + if(sc->sc_si == NULL) + panic("%s: can't establish soft interrupt.", + sc->sc_dev.dv_xname); + + printf("\n"); +} + +int +imxuart_intr(void *arg) +{ + struct imxuart_softc *sc = arg; + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + struct tty *tp = sc->sc_tty; + u_int16_t sr1; + u_int16_t *p; + u_int16_t c; + + sr1 = bus_space_read_2(iot, ioh, IMXUART_USR1); + if (ISSET(sr1, IMXUART_SR1_TRDY) && ISSET(tp->t_state, TS_BUSY)) { + CLR(tp->t_state, TS_BUSY | TS_FLUSH); + if (sc->sc_halt > 0) + wakeup(&tp->t_outq); + (*linesw[tp->t_line].l_start)(tp); + } + + if (sc->sc_tty == NULL) + return(0); + + if(!ISSET(bus_space_read_2(iot, ioh, IMXUART_USR2), IMXUART_SR2_RDR)) + return 0; + + p = sc->sc_ibufp; + + while(ISSET(bus_space_read_2(iot, ioh, IMXUART_USR2), IMXUART_SR2_RDR)) { + c = bus_space_read_1(iot, ioh, IMXUART_URXD); + if (p >= sc->sc_ibufend) { + sc->sc_floods++; + if (sc->sc_errors++ == 0) + timeout_add(&sc->sc_diag_tmo, 60 * hz); + } else { + *p++ = c; + if (p == sc->sc_ibufhigh && ISSET(tp->t_cflag, CRTSCTS)) + /* XXX */ + CLR(sc->sc_ucr3, IMXUART_CR3_DSR); + bus_space_write_2(iot, ioh, IMXUART_UCR3, + sc->sc_ucr3); + + } + /* XXX - msr stuff ? */ + } + sc->sc_ibufp = p; + + softintr_schedule(sc->sc_si); + + return 1; +} + +int +imxuart_param(struct tty *tp, struct termios *t) +{ + struct imxuart_softc *sc = imxuart_cd.cd_devs[DEVUNIT(tp->t_dev)]; + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + int ospeed = t->c_ospeed; + int error; + tcflag_t oldcflag; + + + if (t->c_ospeed < 0 || (t->c_ispeed && t->c_ispeed != t->c_ospeed)) + return EINVAL; + + switch (ISSET(t->c_cflag, CSIZE)) { + case CS5: + return EINVAL; + case CS6: + return EINVAL; + case CS7: + CLR(sc->sc_ucr2, IMXUART_CR2_WS); + break; + case CS8: + SET(sc->sc_ucr2, IMXUART_CR2_WS); + break; + } +// bus_space_write_2(iot, ioh, IMXUART_UCR2, sc->sc_ucr2); + + if (ISSET(t->c_cflag, PARENB)) { + SET(sc->sc_ucr2, IMXUART_CR2_PREN); + bus_space_write_2(iot, ioh, IMXUART_UCR2, sc->sc_ucr2); + } + /* STOPB - XXX */ + if (ospeed == 0) { + /* lower dtr */ + } + + if (ospeed != 0) { + while (ISSET(tp->t_state, TS_BUSY)) { + ++sc->sc_halt; + error = ttysleep(tp, &tp->t_outq, + TTOPRI | PCATCH, "imxuartprm", 0); + --sc->sc_halt; + if (error) { + imxuart_start(tp); + return (error); + } + } + /* set speed */ + } + + /* setup fifo */ + + /* When not using CRTSCTS, RTS follows DTR. */ + /* sc->sc_dtr = MCR_DTR; */ + + + /* and copy to tty */ + tp->t_ispeed = t->c_ispeed; + tp->t_ospeed = t->c_ospeed; + oldcflag = tp->t_cflag; + tp->t_cflag = t->c_cflag; + + /* + * If DCD is off and MDMBUF is changed, ask the tty layer if we should + * stop the device. + */ + /* XXX */ + + imxuart_start(tp); + + return 0; +} + +void +imxuart_start(struct tty *tp) +{ + struct imxuart_softc *sc = imxuart_cd.cd_devs[DEVUNIT(tp->t_dev)]; + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + + int s; + s = spltty(); + if (ISSET(tp->t_state, TS_BUSY)) { + splx(s); + return; + } + if (ISSET(tp->t_state, TS_TIMEOUT | TS_TTSTOP)) + goto stopped; +#ifdef DAMNFUCKSHIT + /* clear to send (IE the RTS pin on this shit) is not directly \ + * readable - skip check for now + */ + if (ISSET(tp->t_cflag, CRTSCTS) && !ISSET(sc->sc_msr, IMXUART_CTS)) + goto stopped; +#endif + if (tp->t_outq.c_cc <= tp->t_lowat) { + if (ISSET(tp->t_state, TS_ASLEEP)) { + CLR(tp->t_state, TS_ASLEEP); + wakeup(&tp->t_outq); + } + if (tp->t_outq.c_cc == 0) + goto stopped; + selwakeup(&tp->t_wsel); + } + SET(tp->t_state, TS_BUSY); + + if (!ISSET(sc->sc_ucr1, IMXUART_CR1_TXMPTYEN)) { + SET(sc->sc_ucr1, IMXUART_CR1_TXMPTYEN); + bus_space_write_2(iot, ioh, IMXUART_UCR1, sc->sc_ucr1); + } + + { + u_char buf[32]; + int n = q_to_b(&tp->t_outq, buf, 32/*XXX*/); + int i; + for (i = 0; i < n; i++) + bus_space_write_1(iot, ioh, IMXUART_UTXD, buf[i]); + } + splx(s); + return; +stopped: + if (ISSET(sc->sc_ucr1, IMXUART_CR1_TXMPTYEN)) { + CLR(sc->sc_ucr1, IMXUART_CR1_TXMPTYEN); + bus_space_write_2(iot, ioh, IMXUART_UCR1, sc->sc_ucr1); + } + splx(s); +} + +void +imxuart_pwroff(struct imxuart_softc *sc) +{ +} + +void +imxuart_diag(void *arg) +{ + struct imxuart_softc *sc = arg; + int overflows, floods; + int s; + + s = spltty(); + sc->sc_errors = 0; + overflows = sc->sc_overflows; + sc->sc_overflows = 0; + floods = sc->sc_floods; + sc->sc_floods = 0; + splx(s); + log(LOG_WARNING, "%s: %d silo overflow%s, %d ibuf overflow%s\n", + sc->sc_dev.dv_xname, + overflows, overflows == 1 ? "" : "s", + floods, floods == 1 ? "" : "s"); +} + +void +imxuart_raisedtr(void *arg) +{ + struct imxuart_softc *sc = arg; + + SET(sc->sc_ucr3, IMXUART_CR3_DSR); /* XXX */ + bus_space_write_2(sc->sc_iot, sc->sc_ioh, IMXUART_UCR3, sc->sc_ucr3); +} + +void +imxuart_softint(void *arg) +{ + struct imxuart_softc *sc = arg; + struct tty *tp; + u_int16_t *ibufp; + u_int16_t *ibufend; + int c; + int err; + int s; + + if (sc == NULL || sc->sc_ibufp == sc->sc_ibuf) + return; + + tp = sc->sc_tty; + s = spltty(); + + ibufp = sc->sc_ibuf; + ibufend = sc->sc_ibufp; + + if (ibufp == ibufend || tp == NULL || !ISSET(tp->t_state, TS_ISOPEN)) { + splx(s); + return; + } + + sc->sc_ibufp = sc->sc_ibuf = (ibufp == sc->sc_ibufs[0]) ? + sc->sc_ibufs[1] : sc->sc_ibufs[0]; + sc->sc_ibufhigh = sc->sc_ibuf + IMXUART_IHIGHWATER; + sc->sc_ibufend = sc->sc_ibuf + IMXUART_IBUFSIZE; + + if (ISSET(tp->t_cflag, CRTSCTS) && + !ISSET(sc->sc_ucr3, IMXUART_CR3_DSR)) { + /* XXX */ + SET(sc->sc_ucr3, IMXUART_CR3_DSR); + bus_space_write_2(sc->sc_iot, sc->sc_ioh, IMXUART_UCR3, + sc->sc_ucr3); + } + + splx(s); + + while (ibufp < ibufend) { + c = *ibufp++; + if (ISSET(c, IMXUART_RX_OVERRUN)) { + sc->sc_overflows++; + if (sc->sc_errors++ == 0) + timeout_add(&sc->sc_diag_tmo, 60 * hz); + } + /* This is ugly, but fast. */ + + err = 0; + if (ISSET(c, IMXUART_RX_PRERR)) + err |= TTY_PE; + if (ISSET(c, IMXUART_RX_FRMERR)) + err |= TTY_FE; + c = (c & 0xff) | err; + (*linesw[tp->t_line].l_rint)(c, tp); + } +} + +int +imxuartopen(dev_t dev, int flag, int mode, struct proc *p) +{ + int unit = DEVUNIT(dev); + struct imxuart_softc *sc; + bus_space_tag_t iot; + bus_space_handle_t ioh; + struct tty *tp; + int s; + int error = 0; + + if (unit >= imxuart_cd.cd_ndevs) + return ENXIO; + sc = imxuart_cd.cd_devs[unit]; + if (sc == NULL) + return ENXIO; + + s = spltty(); + if (sc->sc_tty == NULL) + tp = sc->sc_tty = ttymalloc(0); + else + tp = sc->sc_tty; + + splx(s); + + tp->t_oproc = imxuart_start; + tp->t_param = imxuart_param; + tp->t_dev = dev; + + if (!ISSET(tp->t_state, TS_ISOPEN)) { + SET(tp->t_state, TS_WOPEN); + ttychars(tp); + tp->t_iflag = TTYDEF_IFLAG; + tp->t_oflag = TTYDEF_OFLAG; + + if (ISSET(sc->sc_hwflags, COM_HW_CONSOLE)) + tp->t_cflag = imxuartconscflag; + else + tp->t_cflag = TTYDEF_CFLAG; + if (ISSET(sc->sc_swflags, COM_SW_CLOCAL)) + SET(tp->t_cflag, CLOCAL); + if (ISSET(sc->sc_swflags, COM_SW_CRTSCTS)) + SET(tp->t_cflag, CRTSCTS); + if (ISSET(sc->sc_swflags, COM_SW_MDMBUF)) + SET(tp->t_cflag, MDMBUF); + tp->t_lflag = TTYDEF_LFLAG; + tp->t_ispeed = tp->t_ospeed = imxuartdefaultrate; + + s = spltty(); + + sc->sc_initialize = 1; + imxuart_param(tp, &tp->t_termios); + ttsetwater(tp); + sc->sc_ibufp = sc->sc_ibuf = sc->sc_ibufs[0]; + sc->sc_ibufhigh = sc->sc_ibuf + IMXUART_IHIGHWATER; + sc->sc_ibufend = sc->sc_ibuf + IMXUART_IBUFSIZE; + + iot = sc->sc_iot; + ioh = sc->sc_ioh; + + sc->sc_ucr1 = bus_space_read_2(iot, ioh, IMXUART_UCR1); + sc->sc_ucr2 = bus_space_read_2(iot, ioh, IMXUART_UCR2); + sc->sc_ucr3 = bus_space_read_2(iot, ioh, IMXUART_UCR3); + sc->sc_ucr4 = bus_space_read_2(iot, ioh, IMXUART_UCR4); + + /* interrupt after one char on tx/rx */ + /* reference frequency divider: 1 */ + bus_space_write_2(iot, ioh, IMXUART_UFCR, + 1 << IMXUART_FCR_TXTL_SH | + 5 << IMXUART_FCR_RFDIV_SH | + 1 << IMXUART_FCR_RXTL_SH); + + bus_space_write_2(iot, ioh, IMXUART_UBIR, + (imxuartdefaultrate / 100) - 1); + + /* formula: clk / (rfdiv * 1600) */ + bus_space_write_2(iot, ioh, IMXUART_UBMR, + (imxccm_get_uartclk() * 1000) / 1600); + + SET(sc->sc_ucr1, IMXUART_CR1_EN|IMXUART_CR1_RRDYEN); + SET(sc->sc_ucr2, IMXUART_CR2_TXEN|IMXUART_CR2_RXEN); + bus_space_write_2(iot, ioh, IMXUART_UCR1, sc->sc_ucr1); + bus_space_write_2(iot, ioh, IMXUART_UCR2, sc->sc_ucr2); + + /* sc->sc_mcr = MCR_DTR | MCR_RTS; XXX */ + SET(sc->sc_ucr3, IMXUART_CR3_DSR); /* XXX */ + bus_space_write_2(iot, ioh, IMXUART_UCR3, sc->sc_ucr3); + + SET(tp->t_state, TS_CARR_ON); /* XXX */ + + + } else if (ISSET(tp->t_state, TS_XCLUDE) && p->p_ucred->cr_uid != 0) + return EBUSY; + else + s = spltty(); + + if (DEVCUA(dev)) { + if (ISSET(tp->t_state, TS_ISOPEN)) { + splx(s); + return EBUSY; + } + sc->sc_cua = 1; + } else { + /* tty (not cua) device; wait for carrier if necessary */ + if (ISSET(flag, O_NONBLOCK)) { + if (sc->sc_cua) { + /* Opening TTY non-blocking... but the CUA is busy */ + splx(s); + return EBUSY; + } + } else { + while (sc->sc_cua || + (!ISSET(tp->t_cflag, CLOCAL) && + !ISSET(tp->t_state, TS_CARR_ON))) { + SET(tp->t_state, TS_WOPEN); + error = ttysleep(tp, &tp->t_rawq, + TTIPRI | PCATCH, ttopen, 0); + /* + * If TS_WOPEN has been reset, that means the + * cua device has been closed. We don't want + * to fail in that case, + * so just go around again. + */ + if (error && ISSET(tp->t_state, TS_WOPEN)) { + CLR(tp->t_state, TS_WOPEN); + if (!sc->sc_cua && !ISSET(tp->t_state, + TS_ISOPEN)) + imxuart_pwroff(sc); + splx(s); + return error; + } + } + } + } + splx(s); + return (*linesw[tp->t_line].l_open)(dev,tp,p); +} + +int +imxuartclose(dev_t dev, int flag, int mode, struct proc *p) +{ + int unit = DEVUNIT(dev); + struct imxuart_softc *sc = imxuart_cd.cd_devs[unit]; + bus_space_tag_t iot = sc->sc_iot; + bus_space_handle_t ioh = sc->sc_ioh; + struct tty *tp = sc->sc_tty; + int s; + + /* XXX This is for cons.c. */ + if (!ISSET(tp->t_state, TS_ISOPEN)) + return 0; + + (*linesw[tp->t_line].l_close)(tp, flag, p); + s = spltty(); + if (ISSET(tp->t_state, TS_WOPEN)) { + /* tty device is waiting for carrier; drop dtr then re-raise */ + CLR(sc->sc_ucr3, IMXUART_CR3_DSR); + bus_space_write_2(iot, ioh, IMXUART_UCR3, sc->sc_ucr3); + timeout_add(&sc->sc_dtr_tmo, hz * 2); + } else { + /* no one else waiting; turn off the uart */ + imxuart_pwroff(sc); + } + CLR(tp->t_state, TS_BUSY | TS_FLUSH); + + sc->sc_cua = 0; + splx(s); + ttyclose(tp); + + return 0; +} + +int +imxuartread(dev_t dev, struct uio *uio, int flag) +{ + struct tty *tty; + + tty = imxuarttty(dev); + if (tty == NULL) + return ENODEV; + + return((*linesw[tty->t_line].l_read)(tty, uio, flag)); +} + +int +imxuartwrite(dev_t dev, struct uio *uio, int flag) +{ + struct tty *tty; + + tty = imxuarttty(dev); + if (tty == NULL) + return ENODEV; + + return((*linesw[tty->t_line].l_write)(tty, uio, flag)); +} + +int +imxuartioctl( dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p) +{ + struct imxuart_softc *sc; + struct tty *tp; + int error; + + sc = imxuart_sc(dev); + if (sc == NULL) + return (ENODEV); + + tp = sc->sc_tty; + if (tp == NULL) + return (ENXIO); + + error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); + if (error >= 0) + return (error); + + error = ttioctl(tp, cmd, data, flag, p); + if (error >= 0) + return (error); + + switch(cmd) { + case TIOCSBRK: + /* */ + break; + + case TIOCCBRK: + /* */ + break; + + case TIOCSDTR: +#if 0 + (void) clmctl(dev, TIOCM_DTR | TIOCM_RTS, DMBIS); +#endif + break; + + case TIOCCDTR: +#if 0 + (void) clmctl(dev, TIOCM_DTR | TIOCM_RTS, DMBIC); +#endif + break; + + case TIOCMSET: +#if 0 + (void) clmctl(dev, *(int *) data, DMSET); +#endif + break; + + case TIOCMBIS: +#if 0 + (void) clmctl(dev, *(int *) data, DMBIS); +#endif + break; + + case TIOCMBIC: +#if 0 + (void) clmctl(dev, *(int *) data, DMBIC); +#endif + break; + + case TIOCMGET: +#if 0 + *(int *)data = clmctl(dev, 0, DMGET); +#endif + break; + + case TIOCGFLAGS: +#if 0 + *(int *)data = cl->cl_swflags; +#endif + break; + + case TIOCSFLAGS: + error = suser(p, 0); + if (error != 0) + return(EPERM); + +#if 0 + cl->cl_swflags = *(int *)data; + cl->cl_swflags &= /* only allow valid flags */ + (TIOCFLAG_SOFTCAR | TIOCFLAG_CLOCAL | TIOCFLAG_CRTSCTS); +#endif + break; + default: + return (ENOTTY); + } + + return 0; +} + +int +imxuartstop(struct tty *tp, int flag) +{ + return 0; +} + +struct tty * +imxuarttty(dev_t dev) +{ + int unit; + struct imxuart_softc *sc; + unit = DEVUNIT(dev); + if (unit >= imxuart_cd.cd_ndevs) + return NULL; + sc = (struct imxuart_softc *)imxuart_cd.cd_devs[unit]; + if (sc == NULL) + return NULL; + return sc->sc_tty; +} + +struct imxuart_softc * +imxuart_sc(dev_t dev) +{ + int unit; + struct imxuart_softc *sc; + unit = DEVUNIT(dev); + if (unit >= imxuart_cd.cd_ndevs) + return NULL; + sc = (struct imxuart_softc *)imxuart_cd.cd_devs[unit]; + return sc; +} + + +/* serial console */ +void +imxuartcnprobe(struct consdev *cp) +{ + cp->cn_dev = makedev(12 /* XXX */, 0); + cp->cn_pri = CN_MIDPRI; +} + +void +imxuartcninit(struct consdev *cp) +{ +} + +int +imxuartcnattach(bus_space_tag_t iot, bus_addr_t iobase, int rate, tcflag_t cflag) +{ + static struct consdev imxuartcons = { + NULL, NULL, imxuartcngetc, imxuartcnputc, imxuartcnpollc, NULL, + NODEV, CN_MIDPRI + }; + + if (bus_space_map(iot, iobase, IMXUART_SPACE, 0, &imxuartconsioh)) + return ENOMEM; + + cn_tab = &imxuartcons; + cn_tab->cn_dev = makedev(12 /* XXX */, 0); + cdevsw[12] = imxuartdev; /* KLUDGE */ + + imxuartconsiot = iot; + imxuartconsaddr = iobase; + imxuartconscflag = cflag; + + // XXXX: Overwrites some sensitive bits, recheck later. + /* + bus_space_write_2(imxuartconsiot, imxuartconsioh, IMXUART_UCR1, + IMXUART_CR1_EN); + bus_space_write_2(imxuartconsiot, imxuartconsioh, IMXUART_UCR2, + IMXUART_CR2_TXEN|IMXUART_CR2_RXEN); + */ + + return 0; +} + +int +imxuartcngetc(dev_t dev) +{ + int c; + int s; + s = splhigh(); + while((bus_space_read_2(imxuartconsiot, imxuartconsioh, IMXUART_USR2) & + IMXUART_SR2_RDR) == 0) + ; + c = bus_space_read_1(imxuartconsiot, imxuartconsioh, IMXUART_URXD); + splx(s); + return c; +} + +void +imxuartcnputc(dev_t dev, int c) +{ + int s; + s = splhigh(); + bus_space_write_1(imxuartconsiot, imxuartconsioh, IMXUART_UTXD, (uint8_t)c); + while((bus_space_read_2(imxuartconsiot, imxuartconsioh, IMXUART_USR2) & + IMXUART_SR2_TXDC) == 0) + ; + splx(s); +} + +void +imxuartcnpollc(dev_t dev, int on) +{ +} diff --git a/sys/arch/armv7/imx/imxuartreg.h b/sys/arch/armv7/imx/imxuartreg.h new file mode 100644 index 00000000000..4518afd547c --- /dev/null +++ b/sys/arch/armv7/imx/imxuartreg.h @@ -0,0 +1,130 @@ +/* $OpenBSD: imxuartreg.h,v 1.1 2013/09/06 20:45:54 patrick Exp $ */ +/* + * Copyright (c) 2005 Dale Rahn <drahn@motorola.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#define IMXUART_URXD 0x000 +#define IMXUART_RX_ERR 0x4000 +#define IMXUART_RX_OVERRUN 0x2000 +#define IMXUART_RX_FRMERR 0x1000 +#define IMXUART_RX_BRK 0x0800 +#define IMXUART_RX_PRERR 0x0400 +#define IMXUART_RX_PRERR_SH 10 +#define IMXUART_UTXD 0x040 +#define IMXUART_UCR1 0x080 +#define IMXUART_CR1_ADEN 0x8000 +#define IMXUART_CR1_ADBR 0x4000 +#define IMXUART_CR1_TRDYEN 0x2000 +#define IMXUART_CR1_IDEN 0x1000 +#define IMXUART_CR1_ICD 0xc000 +#define IMXUART_CR1_RRDYEN 0x0200 +#define IMXUART_CR1_RXDMAEN 0x0100 +#define IMXUART_CR1_IREN 0x0080 +#define IMXUART_CR1_TXMPTYEN 0x0040 +#define IMXUART_CR1_RTSDEN 0x0020 +#define IMXUART_CR1_SNDBRK 0x0010 +#define IMXUART_CR1_TXDMAEN 0x0008 +#define IMXUART_CR1_ATDMAEN 0x0004 +#define IMXUART_CR1_DOZE 0x0002 +#define IMXUART_CR1_EN 0x0001 +#define IMXUART_UCR2 0x084 +#define IMXUART_CR2_ESCI 0x8000 +#define IMXUART_CR2_IRTS 0x4000 +#define IMXUART_CR2_CTSC 0x2000 +#define IMXUART_CR2_CTS 0x1000 +#define IMXUART_CR2_ESCEN 0x0800 +#define IMXUART_CR2_RTEC 0x0600 +#define IMXUART_CR2_PREN 0x0100 +#define IMXUART_CR2_PROE 0x0080 +#define IMXUART_CR2_STPB 0x0040 +#define IMXUART_CR2_WS 0x0020 +#define IMXUART_CR2_RTSEN 0x0010 +#define IMXUART_CR2_ATEN 0x0008 +#define IMXUART_CR2_TXEN 0x0004 +#define IMXUART_CR2_RXEN 0x0002 +#define IMXUART_CR2_SRTS 0x0001 +#define IMXUART_UCR3 0x088 +#define IMXUART_CR3_DPEC 0xc000 +#define IMXUART_CR3_DTREN 0x2000 +#define IMXUART_CR3_PARERREN 0x1000 +#define IMXUART_CR3_FRAERREN 0x0800 +#define IMXUART_CR3_DSR 0x0400 +#define IMXUART_CR3_DCD 0x0200 +#define IMXUART_CR3_RI 0x0100 +#define IMXUART_CR3_ADNIMP 0x0080 +#define IMXUART_CR3_RXDSEN 0x0040 +#define IMXUART_CR3_AIRINTEN 0x0020 +#define IMXUART_CR3_AWAKEN 0x0010 +#define IMXUART_CR3_DTRDEN 0x0008 +#define IMXUART_CR3_RXDMUXSEL 0x0004 +#define IMXUART_CR3_INVT 0x0002 +#define IMXUART_CR3_ACIEN 0x0001 +#define IMXUART_UCR4 0x08c +#define IMXUART_CR4_CSTL 0xfc00 +#define IMXUART_CR4_INVR 0x0200 +#define IMXUART_CR4_ENIRI 0x0100 +#define IMXUART_CR4_WKEN 0x0080 +#define IMXUART_CR4_IDDMAEN 0x0040 +#define IMXUART_CR4_IRSC 0x0020 +#define IMXUART_CR4_LPBYP 0x0010 +#define IMXUART_CR4_TCEN 0x0008 +#define IMXUART_CR4_BKEN 0x0004 +#define IMXUART_CR4_OREN 0x0002 +#define IMXUART_CR4_DREN 0x0001 +#define IMXUART_UFCR 0x090 +#define IMXUART_FCR_TXTL_SH 10 +#define IMXUART_FCR_TXTL_M 0x3f +#define IMXUART_FCR_RFDIV_SH 7 +#define IMXUART_FCR_RFDIV_M 0x07 +#define IMXUART_FCR_RXTL_SH 0 +#define IMXUART_FCR_RXTL_M 0x3f +#define IMXUART_USR1 0x094 +#define IMXUART_SR1_PARITYERR 0x8000 +#define IMXUART_SR1_RTSS 0x4000 +#define IMXUART_SR1_TRDY 0x2000 +#define IMXUART_SR1_RTSD 0x1000 +#define IMXUART_SR1_ESCF 0x0800 +#define IMXUART_SR1_FRAMERR 0x0400 +#define IMXUART_SR1_RRDY 0x0200 +#define IMXUART_SR1_AGTIM 0x0100 +#define IMXUART_SR1_DTRD 0x0080 +#define IMXUART_SR1_RXDS 0x0040 +#define IMXUART_SR1_AIRINT 0x0020 +#define IMXUART_SR1_AWAKE 0x0010 +#define IMXUART_USR2 0x098 +#define IMXUART_SR2_ADET 0x8000 +#define IMXUART_SR2_TXFE 0x4000 +#define IMXUART_SR2_DTRF 0x2000 +#define IMXUART_SR2_IDLE 0x1000 +#define IMXUART_SR2_ACST 0x0800 +#define IMXUART_SR2_RIDELT 0x0400 +#define IMXUART_SR2_RIIN 0x0200 +#define IMXUART_SR2_IRINT 0x0100 +#define IMXUART_SR2_WAKE 0x0080 +#define IMXUART_SR2_DCDELT 0x0040 +#define IMXUART_SR2_DCDIN 0x0020 +#define IMXUART_SR2_RTSF 0x0010 +#define IMXUART_SR2_TXDC 0x0008 +#define IMXUART_SR2_BRCD 0x0004 +#define IMXUART_SR2_ORE 0x0002 +#define IMXUART_SR2_RDR 0x0001 +#define IMXUART_UESC 0x09c +#define IMXUART_UTIM 0x0a0 +#define IMXUART_UBIR 0x0a4 +#define IMXUART_UBMR 0x0a8 +#define IMXUART_UBRC 0x0ac +#define IMXUART_ONEMS 0x0b0 +#define IMXUART_UTS 0x0b4 +#define IMXUART_SPACE 0x0c0 diff --git a/sys/arch/armv7/imx/imxuartvar.h b/sys/arch/armv7/imx/imxuartvar.h new file mode 100644 index 00000000000..66b9a4d2123 --- /dev/null +++ b/sys/arch/armv7/imx/imxuartvar.h @@ -0,0 +1,19 @@ +/* $OpenBSD: imxuartvar.h,v 1.1 2013/09/06 20:45:54 patrick Exp $ */ +/* + * Copyright (c) 2005 Dale Rahn <drahn@motorola.com> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +int imxuartcnattach(bus_space_tag_t iot, bus_addr_t iobase, int rate, + tcflag_t cflag); diff --git a/sys/arch/armv7/imx/imxvar.h b/sys/arch/armv7/imx/imxvar.h new file mode 100644 index 00000000000..1a00bc99298 --- /dev/null +++ b/sys/arch/armv7/imx/imxvar.h @@ -0,0 +1,55 @@ +/* $OpenBSD: imxvar.h,v 1.1 2013/09/06 20:45:54 patrick Exp $ */ +/* + * Copyright (c) 2005,2008 Dale Rahn <drahn@drahn.com> + * Copyright (c) 2012-2013 Patrick Wildt <patrick@blueri.se> + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +/* Physical memory range for on-chip devices. */ +struct imx_mem { + u_int32_t addr; /* physical start address */ + u_int32_t size; /* size of range in bytes */ +}; + +#define IMX_DEV_NMEM 6 /* number of memory ranges */ +#define IMX_DEV_NIRQ 4 /* number of IRQs per device */ + +/* Descriptor for all on-chip devices. */ +struct imx_dev { + char *name; /* driver name or made up name */ + int unit; /* driver instance number or -1 */ + struct imx_mem mem[IMX_DEV_NMEM]; /* memory ranges */ + int irq[IMX_DEV_NIRQ]; /* IRQ number(s) */ +}; + +/* Passed as third arg to attach functions. */ +struct imx_attach_args { + struct imx_dev *ia_dev; + bus_space_tag_t ia_iot; + bus_dma_tag_t ia_dmat; +}; + +void imx_set_devs(struct imx_dev *); +struct imx_dev *imx_find_dev(const char *, int); + +void imx6_init(void); + +/* XXX */ +void *avic_intr_establish(int irqno, int level, int (*func)(void *), + void *arg, char *name); + +/* board identification - from uboot */ +#define BOARD_ID_IMX6_PHYFLEX 3529 +#define BOARD_ID_IMX6_SABRELITE 3769 +extern uint32_t board_id; |