diff options
38 files changed, 4347 insertions, 357 deletions
diff --git a/arch/arm/configs/colibri_vf_defconfig b/arch/arm/configs/colibri_vf_defconfig new file mode 100644 index 000000000000..135b3ee87f41 --- /dev/null +++ b/arch/arm/configs/colibri_vf_defconfig @@ -0,0 +1,318 @@ +CONFIG_EXPERIMENTAL=y +CONFIG_SYSVIPC=y +CONFIG_TASKSTATS=y +CONFIG_TASK_DELAY_ACCT=y +CONFIG_TASK_XACCT=y +CONFIG_TASK_IO_ACCOUNTING=y +CONFIG_TINY_RCU=y +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_CGROUPS=y +CONFIG_BLK_CGROUP=y +CONFIG_NAMESPACES=y +CONFIG_UTS_NS=y +CONFIG_IPC_NS=y +CONFIG_USER_NS=y +CONFIG_PID_NS=y +CONFIG_NET_NS=y +CONFIG_BLK_DEV_INITRD=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +# CONFIG_SYSCTL_SYSCALL is not set +CONFIG_KALLSYMS=y +# CONFIG_ELF_CORE is not set +CONFIG_EMBEDDED=y +# CONFIG_PERF_EVENTS is not set +CONFIG_SLUB=y +CONFIG_PROFILING=y +CONFIG_OPROFILE=y +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +# CONFIG_BLK_DEV_BSG is not set +# CONFIG_IOSCHED_DEADLINE is not set +# CONFIG_IOSCHED_CFQ is not set +CONFIG_ARCH_MXC=y +CONFIG_ARCH_MVF=y +CONFIG_MACH_COLIBRI_VF50=y +CONFIG_MACH_COLIBRI_VF61=y +CONFIG_MXC_PWM=y +CONFIG_MXC_USE_PIT=y +CONFIG_CLK_DEBUG=y +CONFIG_DMA_ZONE_SIZE=16 +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_PREEMPT=y +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +CONFIG_HIGHMEM=y +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CPU_IDLE=y +CONFIG_VFP=y +CONFIG_NEON=y +CONFIG_PM_RUNTIME=y +CONFIG_PM_DEBUG=y +CONFIG_NET=y +CONFIG_PACKET=y +CONFIG_UNIX=y +CONFIG_NET_KEY=y +CONFIG_INET=y +CONFIG_IP_MULTICAST=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_INET_AH=m +CONFIG_INET_ESP=y +CONFIG_INET_IPCOMP=m +CONFIG_INET_XFRM_MODE_TRANSPORT=m +CONFIG_INET_XFRM_MODE_TUNNEL=m +CONFIG_INET_XFRM_MODE_BEET=m +# CONFIG_INET_LRO is not set +# CONFIG_INET_DIAG is not set +CONFIG_IPV6=y +CONFIG_IPV6_PRIVACY=y +CONFIG_IPV6_ROUTER_PREF=y +CONFIG_IPV6_OPTIMISTIC_DAD=y +CONFIG_INET6_AH=y +CONFIG_INET6_ESP=y +CONFIG_INET6_IPCOMP=y +CONFIG_IPV6_MIP6=y +CONFIG_IPV6_TUNNEL=y +CONFIG_IPV6_MULTIPLE_TABLES=y +CONFIG_NETFILTER=y +CONFIG_NETFILTER_NETLINK_LOG=y +CONFIG_NF_CONNTRACK=y +CONFIG_NF_CONNTRACK_EVENTS=y +CONFIG_NF_CT_PROTO_DCCP=y +CONFIG_NF_CT_PROTO_SCTP=y +CONFIG_NF_CT_PROTO_UDPLITE=y +CONFIG_NF_CONNTRACK_AMANDA=y +CONFIG_NF_CONNTRACK_FTP=y +CONFIG_NF_CONNTRACK_IRC=y +CONFIG_NF_CONNTRACK_NETBIOS_NS=y +CONFIG_NF_CONNTRACK_PPTP=y +CONFIG_NF_CONNTRACK_SANE=y +CONFIG_NF_CONNTRACK_SIP=y +CONFIG_NF_CONNTRACK_TFTP=y +CONFIG_NETFILTER_TPROXY=y +CONFIG_NF_CONNTRACK_IPV4=y +CONFIG_IP_NF_IPTABLES=y +CONFIG_IP_NF_MATCH_AH=y +CONFIG_IP_NF_MATCH_ECN=y +CONFIG_IP_NF_MATCH_TTL=y +CONFIG_IP_NF_FILTER=y +CONFIG_IP_NF_TARGET_REJECT=y +CONFIG_IP_NF_TARGET_LOG=y +CONFIG_NF_NAT=y +CONFIG_IP_NF_TARGET_MASQUERADE=y +CONFIG_IP_NF_TARGET_NETMAP=y +CONFIG_IP_NF_TARGET_REDIRECT=y +CONFIG_IP_NF_MANGLE=y +CONFIG_IP_NF_ARPTABLES=y +CONFIG_IP_NF_ARPFILTER=y +CONFIG_IP_NF_ARP_MANGLE=y +CONFIG_IP6_NF_IPTABLES=y +CONFIG_NET_SCHED=y +CONFIG_NET_SCH_HTB=y +CONFIG_NET_SCH_INGRESS=y +CONFIG_NET_CLS_U32=y +CONFIG_NET_EMATCH=y +CONFIG_NET_EMATCH_U32=y +CONFIG_NET_CLS_ACT=y +CONFIG_NET_ACT_POLICE=y +CONFIG_NET_ACT_GACT=y +CONFIG_NET_ACT_MIRRED=y +CONFIG_BT=y +CONFIG_BT_L2CAP=y +CONFIG_BT_SCO=y +CONFIG_BT_RFCOMM=y +CONFIG_BT_RFCOMM_TTY=y +CONFIG_BT_BNEP=y +CONFIG_BT_HIDP=y +CONFIG_BT_HCIUART=y +CONFIG_BT_HCIUART_H4=y +CONFIG_BT_HCIUART_LL=y +CONFIG_CFG80211=y +CONFIG_MAC80211=y +CONFIG_DEVTMPFS=y +# May crash systemd +# CONFIG_DEVTMPFS_MOUNT=y +# CONFIG_FIRMWARE_IN_KERNEL is not set +CONFIG_MTD=y +CONFIG_MTD_CMDLINE_PARTS=y +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y +CONFIG_MTD_NAND=y +CONFIG_MTD_NAND_FSL_NFC=y +CONFIG_MTD_UBI=y +CONFIG_BLK_DEV_LOOP=y +CONFIG_MISC_DEVICES=y +CONFIG_MVF_ADC=y +CONFIG_SCSI=y +CONFIG_BLK_DEV_SD=y +CONFIG_BLK_DEV_SR=y +CONFIG_BLK_DEV_SR_VENDOR=y +CONFIG_CHR_DEV_SG=y +CONFIG_SCSI_MULTI_LUN=y +CONFIG_NETDEVICES=y +CONFIG_DUMMY=y +CONFIG_TUN=y +CONFIG_PHYLIB=y +CONFIG_MICREL_PHY=y +CONFIG_NET_ETHERNET=y +CONFIG_FEC=y +CONFIG_FEC1=y +# CONFIG_NETDEV_1000 is not set +# CONFIG_NETDEV_10000 is not set +# Hack to force WIRELESS_EXT required to build Redpine Signals LiteFi driver +CONFIG_USB_ZD1201=m +CONFIG_RT2X00=y +CONFIG_RT2800USB=y +CONFIG_USB_USBNET=y +# CONFIG_USB_NET_CDC_NCM is not set +# CONFIG_USB_NET_NET1080 is not set +# CONFIG_USB_BELKIN is not set +# CONFIG_USB_NET_ZAURUS is not set +CONFIG_PPP=y +CONFIG_PPP_ASYNC=y +CONFIG_PPP_DEFLATE=y +CONFIG_PPP_BSDCOMP=y +CONFIG_PPP_MPPE=y +# CONFIG_INPUT_MOUSEDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_KEYBOARD_ATKBD is not set +CONFIG_KEYBOARD_GPIO=y +# CONFIG_INPUT_MOUSE is not set +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_WM97XX=y +# CONFIG_TOUCHSCREEN_WM9705 is not set +# CONFIG_TOUCHSCREEN_WM9713 is not set +CONFIG_TOUCHSCREEN_COLIBRI_VF50=y +CONFIG_TOUCHSCREEN_FUSION_F0710A=m +CONFIG_INPUT_MISC=y +CONFIG_INPUT_UINPUT=y +# CONFIG_SERIO is not set +# CONFIG_LEGACY_PTYS is not set +# CONFIG_DEVKMEM is not set +CONFIG_SERIAL_IMX=y +CONFIG_SERIAL_IMX_CONSOLE=y +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +# CONFIG_HW_RANDOM is not set +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y +CONFIG_I2C_IMX=y +CONFIG_SPI=y +CONFIG_SPI_MVF=y +# CONFIG_SPI_MVF_DSPI_EDMA is not set +CONFIG_SPI_SPIDEV=y +CONFIG_GPIO_SYSFS=y +CONFIG_POWER_SUPPLY=y +CONFIG_BATTERY_WM97XX=y +# CONFIG_MXC_MMA8450 is not set +# CONFIG_MXC_MMA8451 is not set +CONFIG_WATCHDOG=y +CONFIG_IMX2_WDT=y +# CONFIG_MFD_MXC_HDMI is not set +CONFIG_MEDIA_SUPPORT=y +CONFIG_VIDEO_DEV=y +CONFIG_USB_VIDEO_CLASS=y +# CONFIG_RADIO_ADAPTERS is not set +CONFIG_VIDEO_OUTPUT_CONTROL=y +CONFIG_FB=y +CONFIG_FB_MVF_DCU=y +CONFIG_BACKLIGHT_LCD_SUPPORT=y +# CONFIG_LCD_CLASS_DEVICE is not set +CONFIG_BACKLIGHT_CLASS_DEVICE=y +# CONFIG_BACKLIGHT_GENERIC is not set +CONFIG_BACKLIGHT_PWM=y +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_LOGO=y +CONFIG_SOUND=y +CONFIG_SND=y +CONFIG_SND_USB=y +CONFIG_SND_USB_AUDIO=y +CONFIG_SND_SOC=y +CONFIG_SND_MVF_SOC=m +# CONFIG_SND_SOC_MVF_SGTL5000 is not set +CONFIG_SND_SOC_MVF_COLIBRI_VF61=m +CONFIG_HIDRAW=y +CONFIG_USB_HIDDEV=y +CONFIG_USB=y +CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +CONFIG_USB_DEVICEFS=y +CONFIG_USB_SUSPEND=y +# CONFIG_USB_OTG_WHITELIST is not set +CONFIG_USB_EHCI_HCD=y +CONFIG_USB_EHCI_ARC=y +CONFIG_USB_EHCI_ARC_OTG=y +CONFIG_USB_EHCI_ROOT_HUB_TT=y +CONFIG_USB_ACM=y +CONFIG_USB_WDM=y +CONFIG_USB_STORAGE=y +CONFIG_USB_LIBUSUAL=y +CONFIG_USB_SERIAL=y +CONFIG_USB_SERIAL_GENERIC=y +CONFIG_USB_SERIAL_PL2303=y +CONFIG_USB_GADGET=y +CONFIG_USB_ETH=y +CONFIG_MMC=y +CONFIG_MMC_UNSAFE_RESUME=y +CONFIG_MMC_SDHCI=y +CONFIG_MMC_SDHCI_PLTFM=y +CONFIG_MMC_SDHCI_ESDHC_IMX=y +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y +CONFIG_LEDS_PWM=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_DRV_DS1307=y +CONFIG_RTC_DRV_SNVS=y +CONFIG_DMADEVICES=y +CONFIG_MVF_EDMA=y +CONFIG_STAGING=y +CONFIG_IIO=y +# CONFIG_MXC_HMP4E is not set +# CONFIG_MXC_HWEVENT is not set +CONFIG_MXC_ASRC=y +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +CONFIG_EXT2_FS_POSIX_ACL=y +CONFIG_EXT2_FS_SECURITY=y +CONFIG_EXT3_FS=y +# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set +CONFIG_EXT3_FS_POSIX_ACL=y +CONFIG_EXT3_FS_SECURITY=y +CONFIG_EXT4_FS=y +CONFIG_EXT4_FS_POSIX_ACL=y +CONFIG_EXT4_FS_SECURITY=y +# CONFIG_DNOTIFY is not set +CONFIG_AUTOFS4_FS=y +CONFIG_FUSE_FS=y +CONFIG_CUSE=y +CONFIG_ISO9660_FS=m +CONFIG_JOLIET=y +CONFIG_ZISOFS=y +CONFIG_VFAT_FS=y +CONFIG_NTFS_FS=m +CONFIG_NTFS_RW=y +CONFIG_TMPFS=y +CONFIG_TMPFS_POSIX_ACL=y +CONFIG_UBIFS_FS=y +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +CONFIG_NFS_V3_ACL=y +CONFIG_NFS_V4=y +CONFIG_NFS_V4_1=y +CONFIG_ROOT_NFS=y +CONFIG_CIFS=y +CONFIG_PARTITION_ADVANCED=y +CONFIG_EFI_PARTITION=y +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_ASCII=m +CONFIG_NLS_ISO8859_1=y +CONFIG_NLS_UTF8=m +CONFIG_PRINTK_TIME=y +CONFIG_DEBUG_FS=y +CONFIG_FTRACE=y +CONFIG_CRYPTO_SHA256=y +CONFIG_CRYPTO_TWOFISH=y +# CONFIG_CRYPTO_ANSI_CPRNG is not set diff --git a/arch/arm/mach-mvf/Kconfig b/arch/arm/mach-mvf/Kconfig index 931ef6af6dd4..cece02089f68 100644 --- a/arch/arm/mach-mvf/Kconfig +++ b/arch/arm/mach-mvf/Kconfig @@ -25,6 +25,63 @@ config FORCE_MAX_ZONEORDER config SOC_MVFA5 bool +config MACH_COLIBRI_VF50 + bool "Support Toradex Colibri VF50 module" + select COLIBRI_VF + help + Include support for Toradex Colibri VF50 module on Iris carrier board. This + includes specific configurations for the board and its peripherals. + +config MACH_COLIBRI_VF61 + bool "Support Toradex Colibri VF61 module" + select COLIBRI_VF + help + Include support for Toradex Colibri VF61 module on Iris carrier board. This + includes specific configurations for the board and its peripherals. + +config UART3_SUPPORT + bool "Support alternative pinmux for UART3" + depends on MACH_COLIBRI_VF50 || MACH_COLIBRI_VF61 + help + Include support for UART3 on SODIMM 23/31. + +config UART4_SUPPORT + bool "Support alternative pinmux for UART4" + depends on MACH_COLIBRI_VF50 || MACH_COLIBRI_VF61 + help + Include support for UART4 on SODIMM 51/53. + +config COLIBRI_VF + bool + select ARCH_MVFA5 + select SOC_MVFA5 + select IMX_HAVE_PLATFORM_IMX_UART + select IMX_HAVE_PLATFORM_DMA + select IMX_HAVE_PLATFORM_FEC + select IMX_HAVE_PLATFORM_GPMI_NFC + select IMX_HAVE_PLATFORM_SDHCI_ESDHC_IMX + select IMX_HAVE_PLATFORM_IMX_I2C + select IMX_HAVE_PLATFORM_VIV_GPU + select IMX_HAVE_PLATFORM_IMX_VPU + select IMX_HAVE_PLATFORM_IMX_SSI + select IMX_HAVE_PLATFORM_FSL_USB2_UDC + select IMX_HAVE_PLATFORM_MXC_EHCI + select IMX_HAVE_PLATFORM_FSL_OTG + select IMX_HAVE_PLATFORM_FSL_USB_WAKEUP + select IMX_HAVE_PLATFORM_AHCI + select IMX_HAVE_PLATFORM_IMX_OCOTP + select IMX_HAVE_PLATFORM_IMX2_WDT + select IMX_HAVE_PLATFORM_IMX_SNVS_RTC + select IMX_HAVE_PLATFORM_IMX_PM + select IMX_HAVE_PLATFORM_MXC_HDMI + select IMX_HAVE_PLATFORM_IMX_ASRC + select IMX_HAVE_PLATFORM_FLEXCAN + select IMX_HAVE_PLATFORM_MVF_SPI + select IMX_HAVE_PLATFORM_MVF_DCU + select IMX_HAVE_PLATFORM_MVF_SAI + select IMX_HAVE_PLATFORM_MXC_NAND + select IMX_HAVE_PLATFORM_MVF_CAAM + config MACH_MVFA5_TWR_VF700 bool "Support MVF CORTEX-A5 TWR-VF700 platform" select ARCH_MVFA5 diff --git a/arch/arm/mach-mvf/Makefile b/arch/arm/mach-mvf/Makefile index fdbd22479609..373166a97adc 100644 --- a/arch/arm/mach-mvf/Makefile +++ b/arch/arm/mach-mvf/Makefile @@ -7,4 +7,5 @@ obj-y := cpu.o mm.o system.o devices.o dummy_gpio.o irq.o bus_freq.o mvf_fec.o obj-y += l2switch.o obj-$(CONFIG_ARCH_MVFA5) += clock.o mvf_suspend.o +obj-$(CONFIG_COLIBRI_VF) += board-colibri_vf.o obj-$(CONFIG_MACH_MVFA5_TWR_VF700) += board-twr-vf700.o diff --git a/arch/arm/mach-mvf/board-colibri_vf.c b/arch/arm/mach-mvf/board-colibri_vf.c new file mode 100644 index 000000000000..83d0fe269513 --- /dev/null +++ b/arch/arm/mach-mvf/board-colibri_vf.c @@ -0,0 +1,820 @@ +/* + * Copyright 2013 Toradex, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include <linux/types.h> +#include <linux/sched.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/nodemask.h> +#include <linux/clk.h> +#include <linux/platform_device.h> +#include <linux/fsl_devices.h> +#include <linux/spi/spi.h> +#include <linux/i2c.h> +#include <linux/pmic_external.h> +#include <linux/pmic_status.h> +#include <linux/pwm_backlight.h> +#include <linux/leds_pwm.h> +#include <linux/fec.h> +#include <linux/memblock.h> +#include <linux/gpio.h> +#include <linux/etherdevice.h> +#include <linux/regulator/anatop-regulator.h> +#include <linux/regulator/consumer.h> +#include <linux/regulator/machine.h> +#include <linux/regulator/fixed.h> +#include <linux/input/fusion_F0710A.h> +#include <linux/can/platform/mcp251x.h> +#include <sound/pcm.h> + +#include <mach/common.h> +#include <mach/hardware.h> +#include <mach/memory.h> +#include <mach/iomux-mvf.h> +#include <mach/spi-mvf.h> +#include <mach/mxc_asrc.h> +#include <mach/mxc.h> +#include <mach/colibri-ts.h> +#include <asm/irq.h> +#include <asm/setup.h> +#include <asm/mach-types.h> +#include <asm/mach/arch.h> +#include <asm/mach/time.h> + +#include "devices-mvf.h" +#include "regs-pm.h" +#include "usb.h" +#include "crm_regs.h" + +#define MVF600_SD1_CD 42 + +#define colibri_vf50_bl_enb 45 /* BL_ON */ + +static iomux_v3_cfg_t mvf600_pads[] = { + /* SDHC1: MMC/SD */ + MVF600_PAD14_PTA24__SDHC1_CLK, + MVF600_PAD15_PTA25__SDHC1_CMD, + MVF600_PAD16_PTA26__SDHC1_DAT0, + MVF600_PAD17_PTA27__SDHC1_DAT1, +#ifndef CONFIG_UART4_SUPPORT + MVF600_PAD18_PTA28__SDHC1_DAT2, + MVF600_PAD19_PTA29__SDHC1_DAT3, +#endif + /* set PTB20 as GPIO for sdhc card detecting */ + MVF600_PAD42_PTB20__SDHC1_SW_CD, + + /* I2C0: I2C_SDA/SCL on SODIMM pin 194/196 (e.g. RTC on carrier board) + */ + MVF600_PAD36_PTB14__I2C0_SCL, + MVF600_PAD37_PTB15__I2C0_SDA, + +#if 0 /* optional secondary pinmux */ + /* CAN0 */ + MVF600_PAD36_PTB14__CAN0_RX, /* conflicts with + MVF600_PAD36_PTB14__I2C0_SCL */ + MVF600_PAD37_PTB15__CAN0_TX, /* conflicts with + MVF600_PAD37_PTB15__I2C0_SDA */ + + /*CAN1*/ + MVF600_PAD38_PTB16__CAN1_RX, /* conflicts with + MVF600_PAD38_PTB16_GPIO */ + MVF600_PAD39_PTB17__CAN1_TX, /* conflicts with + MVF600_PAD39_PTB17_GPIO */ +#endif + + /* DSPI1: SSP on SODIMM pin 86, 88, 90 and 92 */ + MVF600_PAD84_PTD5__DSPI1_PCS0, + MVF600_PAD85_PTD6__DSPI1_SIN, + MVF600_PAD86_PTD7__DSPI1_SOUT, + MVF600_PAD87_PTD8__DSPI1_SCK, + + /* GPIO for CAN Interrupt */ + MVF600_PAD43_PTB21__CAN_INT, + + /* FEC0: Ethernet */ +#ifdef CONFIG_FEC0 + MVF600_PAD2_PTA9__RMII_CLKOUT, + MVF600_PAD45_PTC0__RMII0_MDC, + MVF600_PAD46_PTC1__RMII0_MDIO, + MVF600_PAD47_PTC2__RMII0_CRS_DV, + MVF600_PAD48_PTC3__RMII0_RXD1, + MVF600_PAD49_PTC4__RMII0_RXD0, + MVF600_PAD50_PTC5__RMII0_RXER, + MVF600_PAD51_PTC6__RMII0_TXD1, + MVF600_PAD52_PTC7__RMII0_TXD0, + MVF600_PAD53_PTC8__RMII0_TXEN, +#endif + + /* FEC1: Ethernet */ + MVF600_PAD0_PTA6__RMII_CLKOUT, + MVF600_PAD54_PTC9__RMII1_MDC, + MVF600_PAD55_PTC10__RMII1_MDIO, + MVF600_PAD56_PTC11__RMII1_CRS_DV, + MVF600_PAD57_PTC12__RMII1_RXD1, + MVF600_PAD58_PTC13__RMII1_RXD0, + MVF600_PAD59_PTC14__RMII1_RXER, + MVF600_PAD60_PTC15__RMII1_TXD1, + MVF600_PAD61_PTC16__RMII1_TXD0, + MVF600_PAD62_PTC17__RMII1_TXEN, + + /* ADC */ +//ADC0_SE8 +//ADC0_SE9 +//ADC1_SE8 +//ADC1_SE9 + + /* DCU0: Display */ + MVF600_PAD105_PTE0_DCU0_HSYNC, + MVF600_PAD106_PTE1_DCU0_VSYNC, + MVF600_PAD107_PTE2_DCU0_PCLK, + MVF600_PAD109_PTE4_DCU0_DE, /* L_BIAS */ + MVF600_PAD110_PTE5_DCU0_R0, + MVF600_PAD111_PTE6_DCU0_R1, + MVF600_PAD112_PTE7_DCU0_R2, + MVF600_PAD113_PTE8_DCU0_R3, + MVF600_PAD114_PTE9_DCU0_R4, + MVF600_PAD115_PTE10_DCU0_R5, + MVF600_PAD116_PTE11_DCU0_R6, + MVF600_PAD117_PTE12_DCU0_R7, + MVF600_PAD118_PTE13_DCU0_G0, + MVF600_PAD119_PTE14_DCU0_G1, + MVF600_PAD120_PTE15_DCU0_G2, + MVF600_PAD121_PTE16_DCU0_G3, + MVF600_PAD122_PTE17_DCU0_G4, + MVF600_PAD123_PTE18_DCU0_G5, + MVF600_PAD124_PTE19_DCU0_G6, + MVF600_PAD125_PTE20_DCU0_G7, + MVF600_PAD126_PTE21_DCU0_B0, + MVF600_PAD127_PTE22_DCU0_B1, + MVF600_PAD128_PTE23_DCU0_B2, + MVF600_PAD129_PTE24_DCU0_B3, + MVF600_PAD130_PTE25_DCU0_B4, + MVF600_PAD131_PTE26_DCU0_B5, + MVF600_PAD132_PTE27_DCU0_B6, + MVF600_PAD133_PTE28_DCU0_B7, +#ifndef CONFIG_FEC0 + MVF600_PAD45_PTC0_BL_ON, +#endif + + /* UART1: UART_C */ + MVF600_PAD26_PTB4_UART1_TX, + MVF600_PAD27_PTB5_UART1_RX, + + /* UART0: UART_A */ +//MVF600_PAD10_PTA20_UART0_DTR, +//MVF600_PAD11_PTA21_UART0_DCD, +//MVF600_PAD20_PTA30_UART0_RI, +//MVF600_PAD21_PTA31_UART0_DSR, + MVF600_PAD32_PTB10_UART0_TX, + MVF600_PAD33_PTB11_UART0_RX, + MVF600_PAD34_PTB12_UART0_RTS, + MVF600_PAD35_PTB13_UART0_CTS, + + /* UART2: UART_B */ + MVF600_PAD79_PTD0_UART2_TX, + MVF600_PAD80_PTD1_UART2_RX, + MVF600_PAD81_PTD2_UART2_RTS, + MVF600_PAD82_PTD3_UART2_CTS, + + /* UART3/4 */ +#ifdef CONFIG_UART3_SUPPORT + MVF600_PAD10_UART3_TX, /* SODIMM 23 */ + MVF600_PAD11_UART3_RX, /* SODIMM 31 */ +#endif +#ifdef CONFIG_UART4_SUPPORT + MVF600_PAD18_UART4_TX, /* SODIMM 51 */ + MVF600_PAD19_UART4_RX, /* SODIMM 53 */ +#endif + + /* USB */ + MVF600_PAD83_PTD4__USBH_PEN, + MVF600_PAD102_PTC29__USBC_DET, /* multiplexed USB0_VBUS_DET */ + MVF600_PAD108_PTE3__USB_OC, + + /* PWM */ +#ifndef CONFIG_FEC0 + MVF600_PAD22_PTB0_FTM0CH0, //PWM<A> multiplexed MVF600_PAD52_PTC7_VID7 +#else + MVF600_PAD22_PTB0_GPIO, +#endif + MVF600_PAD23_PTB1_FTM0CH1, //PWM<c> + MVF600_PAD30_PTB8_FTM1CH0, //PWM<B> +#ifndef CONFIG_FEC0 + MVF600_PAD31_PTB9_FTM1CH1, //PWM<D> multiplexed MVF600_PAD51_PTC6_VID6 +#else + MVF600_PAD31_PTB9_GPIO, +#endif + + /* Wake-Up GPIO */ + MVF600_PAD41_PTB19__GPIO, +#if 0 + /* NAND */ + MVF600_PAD71_PTD23_NF_IO7, + MVF600_PAD72_PTD22_NF_IO6, + MVF600_PAD73_PTD21_NF_IO5, + MVF600_PAD74_PTD20_NF_IO4, + MVF600_PAD75_PTD19_NF_IO3, + MVF600_PAD76_PTD18_NF_IO2, + MVF600_PAD77_PTD17_NF_IO1, + MVF600_PAD78_PTD16_NF_IO0, + MVF600_PAD94_PTB24_NF_WE, + MVF600_PAD95_PTB25_NF_CE0, + MVF600_PAD97_PTB27_NF_RE, + MVF600_PAD99_PTC26_NF_RB, + MVF600_PAD100_PTC27_NF_ALE, + MVF600_PAD101_PTC28_NF_CLE, +#endif + +//MVF600_PAD2_PTA9_GPIO, /* carefull also used for JTAG JTDI, may be used +// for RMII_CLKOUT */ +//MVF600_PAD7_PTA17_GPIO, +//MVF600_PAD38_PTB16_GPIO, /* carefull also used as SW1_WAKEUP_PIN */ +//MVF600_PAD39_PTB17_GPIO, +//MVF600_PAD40_PTB18_GPIO, /* IOMUXC_CCM_AUD_EXT_CLK_SELECT_INPUT 2 +// Selecting Pad: PTB18 for Mode: ALT2. */ +//MVF600_PAD43_PTB21_GPIO, /* CAN_INT */ +//MVF600_PAD44_PTB22_GPIO, +//MVF600_PAD63_PTD31_GPIO, +//MVF600_PAD65_PTD29_GPIO, +//MVF600_PAD66_PTD28_GPIO, +//MVF600_PAD67_PTD27_GPIO, +//MVF600_PAD68_PTD26_GPIO, +//MVF600_PAD69_PTD25_GPIO, +//MVF600_PAD70_PTD24_GPIO, +//MVF600_PAD88_PTD9_GPIO, +//MVF600_PAD89_PTD10_GPIO, +//MVF600_PAD90_PTD11_GPIO, +//MVF600_PAD92_PTD13_GPIO, +//MVF600_PAD93_PTB23_GPIO, +//MVF600_PAD96_PTB26_GPIO, +//MVF600_PAD98_PTB28_GPIO, +//MVF600_PAD103_PTC30_GPIO, + +//optional secondary pinmux +//MVF600_PAD28_PTB6_VIDHSYNC, +//MVF600_PAD29_PTB7_VIDVSYNC, +//MVF600_PAD46_PTC1_VID1, +//MVF600_PAD47_PTC2_VID2, +//MVF600_PAD48_PTC3_VID3, +//MVF600_PAD49_PTC4_VID4, +//MVF600_PAD50_PTC5_VID5, +//MVF600_PAD51_PTC6_VID6, /* multiplexed MVF600_PAD31_PTB9_FTM1CH1 */ +//MVF600_PAD52_PTC7_VID7, /* multiplexed MVF600_PAD22_PTB0_FTM0CH0 */ +//MVF600_PAD53_PTC8_VID8, +//MVF600_PAD64_PTD30_VID10, +//MVF600_PAD91_PTD12_VID, /* VIDMCLK? */ +//MVF600_PAD134_PTA7_VIDPCLK, /* IOMUXC_VIDEO_IN0_IPP_IND_PIX_CLK_SELECT_INPUT +// 1 Selecting Pad: PTA7 for Mode: ALT1. */ + +//MVF600_PAD104_PTC31_ADC1_SE5, /* nVDD_FAULT/SENSE */ +//MVF600_PAD25_PTB3_ADC1_SE3, /* nBATT_FAULT/SENSE */ + +//VADCSE0 +//VADCSE1 +//VADCSE2 +//VADCSE3 + +//EXT_TAMPER0 +//EXT_TAMPER1 +//EXT_TAMPER2/EXT_WM0_TAMPER_IN +//EXT_TAMPER3/EXT_WM0_TAMPER_OUT +//EXT_TAMPER4/EXT_WM1_TAMPER_IN +//EXT_TAMPER5/EXT_WM1_TAMPER_OUT + +//IOMUXC_VIDEO_IN0_IPP_IND_DE_SELECT_INPUT: PTB5, PTB8 or PTB10 as ALT5 +}; + +static iomux_v3_cfg_t colibri_vf50_v10_pads[] = { + /* Touchscreen */ + MVF600_PAD4_PTA11, + MVF600_PAD5_PTA12, + MVF600_PAD6_PTA16_ADC1_SE0, + MVF600_PAD8_PTA18_ADC0_SE0, + MVF600_PAD9_PTA19_ADC0_SE1, + MVF600_PAD12_PTA22, + MVF600_PAD13_PTA23, + MVF600_PAD24_PTB2_ADC1_SE2, +}; + +static iomux_v3_cfg_t colibri_vf50_v11_pads[] = { + /* Touchscreen */ + MVF600_PAD4_PTA11, + MVF600_PAD93_PTB23, + MVF600_PAD6_PTA16_ADC1_SE0, + MVF600_PAD8_PTA18_ADC0_SE0, + MVF600_PAD9_PTA19_ADC0_SE1, + MVF600_PAD12_PTA22, + MVF600_PAD13_PTA23, + MVF600_PAD24_PTB2_ADC1_SE2, +}; + +static iomux_v3_cfg_t colibri_vf61_pads[] = { + /* SAI2: AC97/Touchscreen */ + MVF600_PAD4_PTA11_WM9715L_PENDOWN, /* carefull also used for JTAG + JTMS/SWDIO */ + MVF600_PAD6_PTA16_SAI2_TX_BCLK, /* AC97_BIT_CLK */ + MVF600_PAD8_PTA18_WM9715L_SDATAOUT, /* AC97_SDATA_OUT, initially + driven low to avoid wolfson entering test mode */ + MVF600_PAD9_PTA19_WM9715L_SYNC, /* AC97_SYNC, initially used to + do wolfson warm reset by toggling it as a GPIO */ + MVF600_PAD12_PTA22_SAI2_RX_DATA, /* AC97_SDATA_IN */ + MVF600_PAD13_PTA23_WM9715L_RESET, + MVF600_PAD24_PTB2_WM9715L_GENIRQ, + MVF600_PAD93_PTB23_SAI0_TX_BCLK, /* AC97_MCLK */ +}; + +static struct imxuart_platform_data mvf_uart0_pdata = { +//IMXUART_USE_DCEDTE not supported on Vybrid (i.MX only) +//IMXUART_EDMA +//IMXUART_FIFO +//49.4.8 ISO-7816/smartcard support +//49.4.9 Infrared interface (aka IrDA up to 115.2 kbits/s) +//49.8.7.2 Transceiver driver enable using RTS (e.g. for RS485 driver) + .flags = IMXUART_FIFO | IMXUART_EDMA | IMXUART_HAVE_RTSCTS, + .dma_req_rx = DMA_MUX03_UART0_RX, + .dma_req_tx = DMA_MUX03_UART0_TX, +}; + +static struct imxuart_platform_data mvf_uart1_pdata = { + .flags = IMXUART_FIFO | IMXUART_EDMA, + .dma_req_rx = DMA_MUX03_UART1_RX, + .dma_req_tx = DMA_MUX03_UART1_TX, +}; + +static struct imxuart_platform_data mvf_uart2_pdata = { + .flags = IMXUART_FIFO | IMXUART_EDMA | IMXUART_HAVE_RTSCTS, + .dma_req_rx = DMA_MUX03_UART2_RX, + .dma_req_tx = DMA_MUX03_UART2_TX, +}; + +#ifdef CONFIG_UART3_SUPPORT +static struct imxuart_platform_data mvf_uart3_pdata = { + .flags = IMXUART_FIFO | IMXUART_EDMA, + .dma_req_rx = DMA_MUX03_UART3_RX, + .dma_req_tx = DMA_MUX03_UART3_TX, +}; +#endif + +#ifdef CONFIG_UART4_SUPPORT +static struct imxuart_platform_data mvf_uart4_pdata = { + .flags = IMXUART_FIFO | IMXUART_EDMA, + .dma_req_rx = DMA_MUX12_UART4_RX + 64, + .dma_req_tx = DMA_MUX12_UART4_TX + 64, +}; +#endif + +static inline void mvf_vf700_init_uart(void) +{ + mvf_add_imx_uart(0, &mvf_uart0_pdata); + mvf_add_imx_uart(1, &mvf_uart1_pdata); + mvf_add_imx_uart(2, &mvf_uart2_pdata); +#ifdef CONFIG_UART3_SUPPORT + mvf_add_imx_uart(3, &mvf_uart3_pdata); +#endif +#ifdef CONFIG_UART4_SUPPORT + mvf_add_imx_uart(4, &mvf_uart4_pdata); +#endif +} + +static int colibri_ts_mux_pen_interrupt(struct platform_device *pdev) +{ + mxc_iomux_v3_setup_pad(MVF600_PAD8_PTA18); + mxc_iomux_v3_setup_pad(MVF600_PAD9_PTA19); + + dev_dbg(&pdev->dev, "Muxed XP/XM as GPIO\n"); + + return 0; +} + +static int colibri_ts_mux_adc(struct platform_device *pdev) +{ + mxc_iomux_v3_setup_pad(MVF600_PAD8_PTA18_ADC0_SE0); + mxc_iomux_v3_setup_pad(MVF600_PAD9_PTA19_ADC0_SE1); + + dev_dbg(&pdev->dev, "Muxed XP/XM for ADC mode\n"); + + return 0; +} + +static struct colibri_ts_platform_data colibri_ts_pdata = { + .mux_pen_interrupt = &colibri_ts_mux_pen_interrupt, + .mux_adc = &colibri_ts_mux_adc, + .gpio_xp = 13, + .gpio_xm = 93, + .gpio_yp = 12, + .gpio_ym = 4, + .gpio_pen_detect = 8, /* PAD8 */ + .gpio_pen_detect_pullup = 9, +}; + +struct platform_device *__init colibri_add_touchdev( + const struct colibri_ts_platform_data *pdata) +{ + return imx_add_platform_device("colibri-vf50-ts", 0, NULL, 0, + pdata, sizeof(*pdata)); +} + +/* + * Fusion touch screen GPIOs (using Toradex display/touch adapater) + * Iris X16-38, SODIMM pin 28 (PWM B), pen down interrupt + * Iris X16-39, SODIMM pin 30 (PWM C), reset + */ +static int colibri_mux_fusion(void) +{ + mxc_iomux_v3_setup_pad(MVF600_PAD30_PTB8_INT); + mxc_iomux_v3_setup_pad(MVF600_PAD23_PTB1_RESET); + + return 0; +} + +static struct fusion_f0710a_init_data colibri_fusion_pdata = { + .pinmux_fusion_pins = &colibri_mux_fusion, + .gpio_int = 30, /* SO-DIMM 28: Pen down interrupt */ + .gpio_reset = 23, /* SO-DIMM 30: Reset interrupt */ +}; + +static struct fec_platform_data fec_data __initdata = { + .phy = PHY_INTERFACE_MODE_RMII, +}; + +static int mvf_vf600_spi_cs[] = { + 84, +}; + +static const struct spi_mvf_master mvf_vf600_spi_data __initconst = { + .bus_num = 1, + .chipselect = mvf_vf600_spi_cs, + .num_chipselect = ARRAY_SIZE(mvf_vf600_spi_cs), + .cs_control = NULL, +}; + +static struct spi_mvf_chip spidev_chip_info = { + .mode = SPI_MODE_0, + .bits_per_word = 8, + .void_write_data = 0, + .dbr = 0, + .pbr = 0, + .br = 0, + .pcssck = 0, + .pasc = 0, + .pdt = 0, + .cssck = 0, + .asc = 0, + .dt = 0, +}; + +#if defined(CONFIG_CAN_MCP251X) +#define CAN_INTERRUPT_GPIO 43 /* active low interrupt (MCP2515 nINT) */ + +static struct mcp251x_platform_data mcp251x_pdata = { + .board_specific_setup = NULL, + .oscillator_frequency = 16000000, + .power_enable = NULL, + .transceiver_enable = NULL +}; +#endif + +static struct spi_board_info mvf_spi_board_info[] __initdata = { +#if defined(CONFIG_CAN_MCP251X) + { + .bus_num = 1, + .chip_select = 0, + .max_speed_hz = 10000000, + .modalias = "mcp2515", + .platform_data = &mcp251x_pdata, + .controller_data = &spidev_chip_info, + }, +#elif defined(CONFIG_SPI_SPIDEV) + { + .bus_num = 1, /* DSPI1: Colibri SSP */ + .chip_select = 0, + .irq = 0, + .max_speed_hz = 50000000, + .modalias = "spidev", + .mode = SPI_MODE_0, + .platform_data = NULL, + .controller_data = &spidev_chip_info, + }, +#endif +}; + +static void spi_device_init(void) +{ +#if defined(CONFIG_CAN_MCP251X) + mvf_spi_board_info[0].irq = gpio_to_irq(CAN_INTERRUPT_GPIO); +#endif + spi_register_board_info(mvf_spi_board_info, + ARRAY_SIZE(mvf_spi_board_info)); +} + +static void vf600_suspend_enter(void) +{ + /* suspend preparation */ +} + +static void vf600_suspend_exit(void) +{ + /* resmue resore */ +} + +static const struct pm_platform_data mvf_vf600_pm_data __initconst = { + .name = "mvf_pm", + .suspend_enter = vf600_suspend_enter, + .suspend_exit = vf600_suspend_exit, +}; + +static int colibri_vf50_backlight_init(struct device *dev) { + int ret; + + ret = gpio_request(colibri_vf50_bl_enb, "BL_ON"); + if (ret < 0) + return ret; + + ret = gpio_direction_output(colibri_vf50_bl_enb, 1); + if (ret < 0) + gpio_free(colibri_vf50_bl_enb); + + return ret; +}; + +static void colibri_vf50_backlight_exit(struct device *dev) { + gpio_set_value(colibri_vf50_bl_enb, 0); + gpio_free(colibri_vf50_bl_enb); +} + +static int colibri_vf50_backlight_notify(struct device *dev, int brightness) +{ + struct platform_pwm_backlight_data *pdata = dev->platform_data; + + gpio_set_value(colibri_vf50_bl_enb, !!brightness); + + /* Unified TFT interface displays (e.g. EDT ET070080DH6) LEDCTRL pin + with inverted behaviour (e.g. 0V brightest vs. 3.3V darkest) + Note: brightness polarity display model specific */ + if (brightness) return pdata->max_brightness - brightness; + else return brightness; +} + +static struct platform_pwm_backlight_data colibri_vf50_backlight_data = { + .pwm_id = 1, /* PWM<A> (FTM0CH0) */ + .max_brightness = 255, + .dft_brightness = 127, + .pwm_period_ns = 1000000, /* 1 kHz */ + .init = colibri_vf50_backlight_init, + .exit = colibri_vf50_backlight_exit, + .notify = colibri_vf50_backlight_notify, +}; + +static struct mvf_dcu_platform_data mvf_dcu_pdata = { + .mode_str = "640x480", + .default_bpp = 16, +}; + +static void __init fixup_mxc_board(struct machine_desc *desc, struct tag *tags, + char **cmdline, struct meminfo *mi) +{ + if (!mi->nr_banks) + arm_add_memory(PHYS_OFFSET, SZ_128M); +} + +/* + * Not defined the cd/wp so far, set it always present for debug */ +static const struct esdhc_platform_data mvfa5_sd1_data __initconst = { + .cd_gpio = MVF600_SD1_CD, + .wp_gpio = -1, +}; + +static struct imxi2c_platform_data mvf600_i2c_data = { + .bitrate = 100000, +}; + +static struct i2c_board_info mxc_i2c0_board_info[] __initdata = { + { + /* M41T0M6 real time clock on Iris carrier board */ + I2C_BOARD_INFO("rtc-ds1307", 0x68), + .type = "m41t00", + }, + { + /* TouchRevolution Fusion 7 and 10 multi-touch controller */ + I2C_BOARD_INFO("fusion_F0710A", 0x10), + .platform_data = &colibri_fusion_pdata, + }, +}; + +static struct mxc_nand_platform_data mvf_data __initdata = { + .width = 1, +}; + +/* PWM LEDs */ +static struct led_pwm tegra_leds_pwm[] = { +#if 0 + { + .name = "PWM<A>", + .pwm_id = 1, + .max_brightness = 255, + .pwm_period_ns = 19600, + }, +#endif + { + .name = "PWM<B>", + .pwm_id = 2, + .max_brightness = 255, + .pwm_period_ns = 19600, + }, + { + .name = "PWM<C>", + .pwm_id = 3, + .max_brightness = 255, + .pwm_period_ns = 19600, + }, + { + .name = "PWM<D>", + .pwm_id = 4, + .max_brightness = 255, + .pwm_period_ns = 19600, + }, +}; + +static struct led_pwm_platform_data tegra_leds_pwm_data = { + .num_leds = ARRAY_SIZE(tegra_leds_pwm), + .leds = tegra_leds_pwm, +}; + +static struct imx_asrc_platform_data imx_asrc_data = { + .channel_bits = 4, + .clk_map_ver = 3, +}; + +static void __init mvf_twr_init_usb(void) +{ + imx_otg_base = MVF_IO_ADDRESS(MVF_USBC0_BASE_ADDR); + /*mvf_set_otghost_vbus_func(mvf_twr_usbotg_vbus);*/ + gpio_request(83, "USBH_PEN"); + gpio_direction_output(83, 0); +#ifdef CONFIG_USB_EHCI_ARC + mvf_usb_dr2_init(); +#endif +#ifdef CONFIG_USB_GADGET_ARC + mvf_usb_dr_init(); +#endif +} + +static void __init mvf_init_adc(void) +{ + mvf_add_adc(0); + mvf_add_adc(1); +} + +static void mvf_power_off(void) +{ + void __iomem *gpc_base = MVF_GPC_BASE; + u32 gpc_pgcr; + + /* + * Power gate Power Domain 1 + */ + gpc_pgcr = __raw_readl(gpc_base + GPC_PGCR_OFFSET); + gpc_pgcr |= GPC_PGCR_PG_PD1; + __raw_writel(gpc_pgcr, gpc_base + GPC_PGCR_OFFSET); + + /* Set low power mode */ + mvf_cpu_lp_set(STOP_MODE); +} + +/*! + * Board specific initialization. + */ +static void __init mvf_board_init(void) +{ + mxc_iomux_v3_setup_multiple_pads(mvf600_pads, + ARRAY_SIZE(mvf600_pads)); + mvf_vf700_init_uart(); + +#ifdef CONFIG_FEC + mvf_init_fec(fec_data); +#endif + + mvf_add_snvs_rtc(); + + mvf_init_adc(); + + mvf_add_pm_imx(0, &mvf_vf600_pm_data); + + mvf700_add_caam(); + +#ifndef CONFIG_UART4_SUPPORT + mvf_add_sdhci_esdhc_imx(1, &mvfa5_sd1_data); +#endif + + mvf_add_imx_i2c(0, &mvf600_i2c_data); + + i2c_register_board_info(0, mxc_i2c0_board_info, + ARRAY_SIZE(mxc_i2c0_board_info)); + + mvf_add_dspi(1, &mvf_vf600_spi_data); + spi_device_init(); + + mvfa5_add_dcu(0, &mvf_dcu_pdata); + mvf_add_mxc_pwm(0); + mvf_add_mxc_pwm_backlight(0, &colibri_vf50_backlight_data); + + mvf_add_wdt(0); + + mvf_twr_init_usb(); + + mvf_add_nand(&mvf_data); + + mvf_add_mxc_pwm(1); + mvf_add_mxc_pwm(2); + mvf_add_mxc_pwm(3); + mvf_add_pwm_leds(&tegra_leds_pwm_data); + + imx_asrc_data.asrc_core_clk = clk_get(NULL, "asrc_clk"); + imx_asrc_data.asrc_audio_clk = clk_get(NULL, "asrc_serial_clk"); + mvf_add_asrc(&imx_asrc_data); + + pm_power_off = mvf_power_off; +} + +static void __init colibri_vf50_init(void) +{ + printk("System REV: %x\n", system_rev); + if (system_rev < 0x011a) { + /* Use GPIO 5 for XM... */ + colibri_ts_pdata.gpio_xm = 5; + mxc_iomux_v3_setup_multiple_pads(colibri_vf50_v10_pads, + ARRAY_SIZE(colibri_vf50_v10_pads)); + + } else { + /* Leave default GPIO 93 for XM... */ + mxc_iomux_v3_setup_multiple_pads(colibri_vf50_v11_pads, + ARRAY_SIZE(colibri_vf50_v11_pads)); + } + + mvf_board_init(); + + colibri_add_touchdev(&colibri_ts_pdata); +} + +static void __init colibri_vf61_init(void) +{ + mxc_iomux_v3_setup_multiple_pads(colibri_vf61_pads, + ARRAY_SIZE(colibri_vf61_pads)); + + mvf_board_init(); + + mvfa5_add_sai(2, NULL); +} + +static void __init mvf_timer_init(void) +{ + struct clk *uart_clk; + uart_clk = clk_get_sys("mvf-uart.0", NULL); + early_console_setup(MVF_UART0_BASE_ADDR, uart_clk); + + mvf_clocks_init(32768, 24000000, 0, 0); +} + +static struct sys_timer mxc_timer = { + .init = mvf_timer_init, +}; + +/* + * initialize __mach_desc_ data structure. + */ +MACHINE_START(COLIBRI_VF50, "Toradex Colibri VF50 Module") + .boot_params = MVF_PHYS_OFFSET + 0x100, + .fixup = fixup_mxc_board, + .init_irq = mvf_init_irq, + .init_machine = colibri_vf50_init, + .map_io = mvf_map_io, + .timer = &mxc_timer, +MACHINE_END + +MACHINE_START(COLIBRI_VF61, "Toradex Colibri VF61 Module") + .boot_params = MVF_PHYS_OFFSET + 0x100, + .fixup = fixup_mxc_board, + .init_irq = mvf_init_irq, + .init_machine = colibri_vf61_init, + .map_io = mvf_map_io, + .timer = &mxc_timer, +MACHINE_END diff --git a/arch/arm/mach-mvf/clock.c b/arch/arm/mach-mvf/clock.c index b37992318170..37247607311d 100644 --- a/arch/arm/mach-mvf/clock.c +++ b/arch/arm/mach-mvf/clock.c @@ -1,4 +1,3 @@ - /* * Copyright 2012 Freescale Semiconductor, Inc. * @@ -38,7 +37,6 @@ #define __INIT_CLK_DEBUG(n) #endif - void __iomem *apll_base; static struct clk pll1_sys_main_clk; static struct clk pll2_528_bus_main_clk; @@ -50,11 +48,6 @@ static struct clk pll5_enet_main_clk; static struct clk pll1_pfd3_396M; static struct clk pll1_pfd4_528M; -unsigned long arm_core_clk = 396000000; /* cpu core clk, up to 452MHZ */ -unsigned long arm_sys_clk = 396000000; /* ARM_CLK_DIV, system bus clock */ -unsigned long platform_bus_clk = 132000000; /* BUS_CLK_DIV, up to 166MHZ */ -unsigned long ipg_bus_clk = 66000000; /* IPS clk */ - #define SPIN_DELAY 3000000 /* in nanoseconds */ #define AUDIO_VIDEO_MIN_CLK_FREQ 650000000 @@ -88,7 +81,6 @@ static unsigned long external_high_reference, external_low_reference; static unsigned long oscillator_reference, ckih2_reference; static unsigned long anaclk_1_reference, anaclk_2_reference; - static int _clk_enable(struct clk *clk) { u32 reg; @@ -106,7 +98,6 @@ static void _clk_disable(struct clk *clk) reg = __raw_readl(clk->enable_reg); reg &= ~(MXC_CCM_CCGRx_CG_MASK << clk->enable_shift); __raw_writel(reg, clk->enable_reg); - } /* Clock off in wait mode */ @@ -165,7 +156,6 @@ static inline void __iomem *_get_pll_base(struct clk *pll) return NULL; } - /* * For the 6-to-1 muxed input clock */ @@ -288,7 +278,6 @@ static unsigned long pfd_get_rate(struct clk *clk) u64 tmp; tmp = (u64)clk_get_rate(clk->parent) * 18; - frac = (__raw_readl(clk->enable_reg) >> clk->enable_shift) & ANADIG_PFD_FRAC_MASK; @@ -303,7 +292,6 @@ static int pfd_set_rate(struct clk *clk, unsigned long rate) u64 tmp; tmp = (u64)clk_get_rate(clk->parent) * 18; - /* Round up the divider so that we don't set a rate * higher than what is requested. */ tmp += rate/2; @@ -341,7 +329,6 @@ static void _clk_pfd_disable(struct clk *clk) /* set clk gate bit */ __raw_writel(reg | (1 << (clk->enable_shift + 7)), clk->enable_reg); - } static int _clk_pll_enable(struct clk *clk) @@ -403,7 +390,6 @@ static void _clk_pll_disable(struct clk *clk) reg &= ~ANADIG_PLL_ENABLE; __raw_writel(reg, pllbase); - } /* PLL sys: 528 or 480 MHz*/ @@ -523,11 +509,9 @@ static int _clk_pll1_sw_set_parent(struct clk *clk, struct clk *parent) if (parent == &pll1_sys_main_clk) { reg &= ~MXC_CCM_CCSR_PLL1_PFD_CLK_SEL_MASK; - } else if (parent == &pll1_pfd2_452M) { reg &= ~MXC_CCM_CCSR_PLL1_PFD_CLK_SEL_MASK; reg |= (0x2 << MXC_CCM_CCSR_PLL1_PFD_CLK_SEL_OFFSET); - } else if (parent == &pll1_pfd3_396M) { reg &= ~MXC_CCM_CCSR_PLL1_PFD_CLK_SEL_MASK; reg |= (0x3 << MXC_CCM_CCSR_PLL1_PFD_CLK_SEL_OFFSET); @@ -554,7 +538,6 @@ static unsigned long _clk_pll1_sw_get_rate(struct clk *clk) return 396000000; else return 528000000; - } static struct clk pll1_sw_clk = { @@ -678,7 +661,6 @@ static int _clk_pll3_usb_otg_set_rate(struct clk *clk, unsigned long rate) return 0; } - /* same as pll3_main_clk. These two clocks should always be the same */ static struct clk pll3_usb_otg_main_clk = { __INIT_CLK_DEBUG(pll3_usb_otg_main_clk) @@ -715,7 +697,6 @@ static struct clk usb_phy1_clk = { .get_rate = _clk_pll3_usb_otg_get_rate, }; - static struct clk pll3_pfd2_396M = { __INIT_CLK_DEBUG(pll3_pfd2_396M) .parent = &pll3_usb_otg_main_clk, @@ -769,26 +750,26 @@ static struct clk pll3_sw_clk = { static unsigned long _clk_audio_video_get_rate(struct clk *clk) { unsigned int div, mfn, mfd; - unsigned long rate; unsigned int parent_rate = clk_get_rate(clk->parent); + unsigned long long ll; void __iomem *pllbase; - unsigned int test_div_sel, control3, post_div = 1; - if (clk == &pll4_audio_main_clk) pllbase = PLL4_AUDIO_BASE_ADDR; else pllbase = PLL6_VIDEO_BASE_ADDR; - + /* Multiplication Factor Integer (MFI) */ div = __raw_readl(pllbase) & ANADIG_PLL_SYS_DIV_SELECT_MASK; + /* Multiplication Factor Numerator (MFN) */ mfn = __raw_readl(pllbase + PLL_NUM_DIV_OFFSET); + /* Multiplication Factor Denominator (MFD) */ mfd = __raw_readl(pllbase + PLL_DENOM_DIV_OFFSET); - rate = (parent_rate * div) + ((parent_rate / mfd) * mfn); - rate = rate / post_div; + ll = (unsigned long long)parent_rate * mfn; + do_div(ll, mfd); - return rate; + return (parent_rate * div) + ll; } static int _clk_audio_video_set_rate(struct clk *clk, unsigned long rate) @@ -803,7 +784,6 @@ static int _clk_audio_video_set_rate(struct clk *clk, unsigned long rate) u32 test_div_sel = 2; u32 control3 = 0; - if (clk == &pll4_audio_main_clk) min_clk_rate = AUDIO_VIDEO_MIN_CLK_FREQ / 4; else @@ -929,6 +909,54 @@ static struct clk pll6_video_main_clk = { .set_parent = _clk_audio_video_set_parent, }; +static unsigned long _clk_pll4_audio_div_get_rate(struct clk *clk) +{ + u32 reg, div; + unsigned int parent_rate = clk_get_rate(clk->parent); + + reg = __raw_readl(MXC_CCM_CACRR); + div = (((reg & MXC_CCM_CACRR_PLL4_CLK_DIV_MASK) >> + MXC_CCM_CACRR_PLL4_CLK_DIV_OFFSET) + 1) * 2; + if (2 == div) + div = 1; + + return parent_rate / div; +} + +static int _clk_pll4_audio_div_set_rate(struct clk *clk, unsigned long rate) +{ + u32 reg, div; + unsigned int parent_rate = clk_get_rate(clk->parent); + + div = parent_rate / rate; + + /* Make sure rate is not greater than the maximum value for the clock. + * Also prevent a div of 0. + */ + if (0 == div) + div++; + + if (16 < div) + div = 16; + + div /= 2; + if (1 <= div) + div -= 1; + + reg = __raw_readl(MXC_CCM_CACRR); + reg &= ~MXC_CCM_CACRR_PLL4_CLK_DIV_MASK; + reg |= (div << MXC_CCM_CACRR_PLL4_CLK_DIV_OFFSET); + __raw_writel(reg, MXC_CCM_CACRR); + + return 0; +} + +static struct clk pll4_audio_div_clk = { + __INIT_CLK_DEBUG(pll4_audio_div_clk) + .parent = &pll4_audio_main_clk, + .set_rate = _clk_pll4_audio_div_set_rate, + .get_rate = _clk_pll4_audio_div_get_rate, +}; static struct clk pll5_enet_main_clk = { __INIT_CLK_DEBUG(pll5_enet_main_clk) @@ -942,9 +970,9 @@ static unsigned long _clk_arm_get_rate(struct clk *clk) u32 cacrr, div; cacrr = __raw_readl(MXC_CCM_CACRR); - div = (cacrr & MXC_CCM_CACRR_ARM_CLK_DIV_MASK) + 1; - return arm_core_clk; - /*return clk_get_rate(clk->parent) / div;*/ + div = ((cacrr & MXC_CCM_CACRR_ARM_CLK_DIV_MASK) >> + MXC_CCM_CACRR_ARM_CLK_DIV_OFFSET) + 1; + return clk_get_rate(clk->parent) / div; } static int _clk_arm_set_rate(struct clk *clk, unsigned long rate) @@ -956,10 +984,10 @@ static int _clk_arm_set_rate(struct clk *clk, unsigned long rate) return 0; } - static struct clk cpu_clk = { __INIT_CLK_DEBUG(cpu_clk) - .parent = &pll1_sw_clk, /* A5 clock from PLL1 pfd3 out 396MHZ */ + .parent = &pll1_sw_clk, /* A5 clock from PLL1 pfd1 out 500 MHz resp. + pfd3 out 396MHZ */ .set_rate = _clk_arm_set_rate, .get_rate = _clk_arm_get_rate, }; @@ -983,8 +1011,8 @@ static int _clk_periph_set_parent(struct clk *clk, struct clk *parent) __raw_writel(reg, MXC_CCM_CCSR); /* - * Set the BUS_CLK_DIV to 3, 396/3=132 - * Set IPG_CLK_DIV to 2, 132/2=66 + * Set the BUS_CLK_DIV to 3, 396/3=132 resp. 500/3=166 + * Set IPG_CLK_DIV to 2, 132/2=66 resp. 166/2=83 */ reg = __raw_readl(MXC_CCM_CACRR); reg &= ~MXC_CCM_CACRR_BUS_CLK_DIV_MASK; @@ -998,24 +1026,30 @@ static int _clk_periph_set_parent(struct clk *clk, struct clk *parent) static unsigned long _clk_periph_get_rate(struct clk *clk) { - unsigned long val = 132000000; - return val; + u32 cacrr, div; + + cacrr = __raw_readl(MXC_CCM_CACRR); + div = ((cacrr & MXC_CCM_CACRR_BUS_CLK_DIV_MASK) >> + MXC_CCM_CACRR_BUS_CLK_DIV_OFFSET) + 1; + return clk_get_rate(clk->parent) / div; } static struct clk periph_clk = { __INIT_CLK_DEBUG(periph_clk) - .parent = &pll2_pfd2_396M, + .parent = &pll1_sw_clk, .set_parent = _clk_periph_set_parent, .get_rate = _clk_periph_get_rate, }; - - static unsigned long _clk_ipg_get_rate(struct clk *clk) { - return 66000000; -} + u32 cacrr, div; + cacrr = __raw_readl(MXC_CCM_CACRR); + div = ((cacrr & MXC_CCM_CACRR_IPG_CLK_DIV_MASK) >> + MXC_CCM_CACRR_IPG_CLK_DIV_OFFSET) + 1; + return clk_get_rate(clk->parent) / div; +} static struct clk ipg_clk = { __INIT_CLK_DEBUG(ipg_clk) @@ -1023,13 +1057,11 @@ static struct clk ipg_clk = { .get_rate = _clk_ipg_get_rate, }; - static struct clk scu_clk = { __INIT_CLK_DEBUG(scu_clk) .parent = &periph_clk, }; - static int _clk_enet_set_parent(struct clk *clk, struct clk *parent) { int mux; @@ -1147,8 +1179,6 @@ static struct clk enet_clk[] = { }, }; - - static unsigned long _clk_uart_round_rate(struct clk *clk, unsigned long rate) { @@ -1168,6 +1198,7 @@ static unsigned long _clk_uart_round_rate(struct clk *clk, return parent_rate / div; } + /* */ static unsigned long _clk_uart_get_rate(struct clk *clk) @@ -1209,6 +1240,15 @@ static struct clk dspi_clk[] = { .enable = _clk_enable, .disable = _clk_disable, }, + { + __INIT_CLK_DEBUG(dspi1_clk) + .id = 1, + .parent = &ipg_clk, + .enable_reg = MXC_CCM_CCGR0, + .enable_shift = MXC_CCM_CCGRx_CG13_OFFSET, + .enable = _clk_enable, + .disable = _clk_disable, + }, }; static int _clk_esdhc1_set_parent(struct clk *clk, struct clk *parent) @@ -1345,7 +1385,7 @@ static int _clk_dcu0_set_rate(struct clk *clk, unsigned long rate) u32 reg, div; u32 parent_rate = clk_get_rate(clk->parent); - div = (parent_rate + rate - 1) / rate; + div = parent_rate / rate; if (div == 0) div++; if (((parent_rate / div) != rate) || (div > 8)) @@ -1401,34 +1441,34 @@ static struct clk audio_external_clk = { .get_rate = get_audio_external_clock_rate, }; -static int _clk_sai2_set_parent(struct clk *clk, struct clk *parent) +static int _clk_sai0_set_parent(struct clk *clk, struct clk *parent) { int mux; u32 reg = __raw_readl(MXC_CCM_CSCMR1) - & ~MXC_CCM_CSCMR1_SAI2_CLK_SEL_MASK; + & ~MXC_CCM_CSCMR1_SAI0_CLK_SEL_MASK; mux = _get_mux6(parent, &audio_external_clk, NULL, - NULL, &pll4_audio_main_clk, NULL, NULL); + NULL /* spdif */, &pll4_audio_div_clk, NULL, NULL); - reg |= (mux << MXC_CCM_CSCMR1_SAI2_CLK_SEL_OFFSET); + reg |= (mux << MXC_CCM_CSCMR1_SAI0_CLK_SEL_OFFSET); __raw_writel(reg, MXC_CCM_CSCMR1); return 0; } -static unsigned long _clk_sai2_get_rate(struct clk *clk) +static unsigned long _clk_sai0_get_rate(struct clk *clk) { u32 reg, div; reg = __raw_readl(MXC_CCM_CSCDR1); - div = ((reg & MXC_CCM_CSCDR1_SAI2_DIV_MASK) >> - MXC_CCM_CSCDR1_SAI2_DIV_OFFSET) + 1; + div = ((reg & MXC_CCM_CSCDR1_SAI0_DIV_MASK) >> + MXC_CCM_CSCDR1_SAI0_DIV_OFFSET) + 1; return clk_get_rate(clk->parent) / div; } -static int _clk_sai2_set_rate(struct clk *clk, unsigned long rate) +static int _clk_sai0_set_rate(struct clk *clk, unsigned long rate) { u32 reg, div; u32 parent_rate = clk_get_rate(clk->parent); @@ -1440,31 +1480,30 @@ static int _clk_sai2_set_rate(struct clk *clk, unsigned long rate) return -EINVAL; reg = __raw_readl(MXC_CCM_CSCDR1); - reg &= ~MXC_CCM_CSCDR1_SAI2_DIV_MASK; - reg |= (div - 1) << MXC_CCM_CSCDR1_SAI2_DIV_OFFSET; - reg |= MXC_CCM_CSCDR1_SAI2_EN; + reg &= ~MXC_CCM_CSCDR1_SAI0_DIV_MASK; + reg |= (div - 1) << MXC_CCM_CSCDR1_SAI0_DIV_OFFSET; __raw_writel(reg, MXC_CCM_CSCDR1); return 0; } -static int _clk_sai2_enable(struct clk *clk) +static int _clk_sai0_enable(struct clk *clk) { u32 reg; reg = __raw_readl(MXC_CCM_CSCDR1); - reg |= MXC_CCM_CSCDR1_SAI2_EN; + reg |= MXC_CCM_CSCDR1_SAI0_EN; __raw_writel(reg, MXC_CCM_CSCDR1); return 0; } -static void _clk_sai2_disable(struct clk *clk) +static void _clk_sai0_disable(struct clk *clk) { u32 reg; reg = __raw_readl(MXC_CCM_CSCDR1); - reg &= ~MXC_CCM_CSCDR1_SAI2_EN; + reg &= ~MXC_CCM_CSCDR1_SAI0_EN; __raw_writel(reg, MXC_CCM_CSCDR1); return 0; @@ -1490,6 +1529,87 @@ static unsigned long _clk_sai_round_rate(struct clk *clk, return parent_rate / div; } +static struct clk sai0_clk = { + __INIT_CLK_DEBUG(sai0_clk) + .parent = &audio_external_clk, + .enable_reg = MXC_CCM_CCGR0, + .enable_shift = MXC_CCM_CCGRx_CG15_OFFSET, + .enable = _clk_sai0_enable, + .disable = _clk_sai0_disable, + .set_parent = _clk_sai0_set_parent, + .round_rate = _clk_sai_round_rate, + .set_rate = _clk_sai0_set_rate, + .get_rate = _clk_sai0_get_rate, +}; + +static int _clk_sai2_set_parent(struct clk *clk, struct clk *parent) +{ + int mux; + u32 reg = __raw_readl(MXC_CCM_CSCMR1) + & ~MXC_CCM_CSCMR1_SAI2_CLK_SEL_MASK; + + mux = _get_mux6(parent, &audio_external_clk, NULL, + NULL /* spdif */, &pll4_audio_div_clk, NULL, NULL); + + reg |= (mux << MXC_CCM_CSCMR1_SAI2_CLK_SEL_OFFSET); + + __raw_writel(reg, MXC_CCM_CSCMR1); + + return 0; +} + +static unsigned long _clk_sai2_get_rate(struct clk *clk) +{ + u32 reg, div; + + reg = __raw_readl(MXC_CCM_CSCDR1); + div = ((reg & MXC_CCM_CSCDR1_SAI2_DIV_MASK) >> + MXC_CCM_CSCDR1_SAI2_DIV_OFFSET) + 1; + + return clk_get_rate(clk->parent) / div; +} + +static int _clk_sai2_set_rate(struct clk *clk, unsigned long rate) +{ + u32 reg, div; + u32 parent_rate = clk_get_rate(clk->parent); + + div = parent_rate / rate; + if (div == 0) + div++; + if (((parent_rate / div) != rate) || (div > 16)) + return -EINVAL; + + reg = __raw_readl(MXC_CCM_CSCDR1); + reg &= ~MXC_CCM_CSCDR1_SAI2_DIV_MASK; + reg |= (div - 1) << MXC_CCM_CSCDR1_SAI2_DIV_OFFSET; + __raw_writel(reg, MXC_CCM_CSCDR1); + + return 0; +} + +static int _clk_sai2_enable(struct clk *clk) +{ + u32 reg; + + reg = __raw_readl(MXC_CCM_CSCDR1); + reg |= MXC_CCM_CSCDR1_SAI2_EN; + __raw_writel(reg, MXC_CCM_CSCDR1); + + return 0; +} + +static void _clk_sai2_disable(struct clk *clk) +{ + u32 reg; + + reg = __raw_readl(MXC_CCM_CSCDR1); + reg &= ~MXC_CCM_CSCDR1_SAI2_EN; + __raw_writel(reg, MXC_CCM_CSCDR1); + + return 0; +} + static struct clk sai2_clk = { __INIT_CLK_DEBUG(sai2_clk) .parent = &audio_external_clk, @@ -1534,15 +1654,32 @@ static int _clk_clko_set_parent(struct clk *clk, struct clk *parent) else if (parent == &pll6_video_main_clk) sel = 3; else if (parent == &pll4_audio_main_clk) + sel = 6; + else if (parent == &pll4_audio_div_clk) + sel = 7; + else if (parent == &pll4_audio_main_clk) sel = 15; else return -EINVAL; + + reg = __raw_readl(MXC_CCM_CCOSR) + & ~MXC_CCM_CCOSR_CKO1_SEL_MASK; + reg |= (sel << MXC_CCM_CCOSR_CKO1_SEL_OFFSET); + __raw_writel(reg, MXC_CCM_CCOSR); + return 0; } static unsigned long _clk_clko_get_rate(struct clk *clk) { - return 0; + u32 reg, div; + unsigned int parent_rate = clk_get_rate(clk->parent); + + reg = __raw_readl(MXC_CCM_CCOSR); + div = ((reg & MXC_CCM_CCOSR_CKO1_DIV_MASK) >> + MXC_CCM_CCOSR_CKO1_DIV_OFFSET) + 1; + + return parent_rate / div; } static int _clk_clko_set_rate(struct clk *clk, unsigned long rate) @@ -1553,9 +1690,14 @@ static int _clk_clko_set_rate(struct clk *clk, unsigned long rate) if (div == 0) div++; - if (((parent_rate / div) != rate) || (div > 8)) + if (((parent_rate / div) != rate) || (div > 16)) return -EINVAL; + reg = __raw_readl(MXC_CCM_CCOSR) + & ~MXC_CCM_CCOSR_CKO1_DIV_MASK; + reg |= ((div -1) << MXC_CCM_CCOSR_CKO1_DIV_OFFSET); + __raw_writel(reg, MXC_CCM_CCOSR); + return 0; } @@ -1601,7 +1743,9 @@ static int _clk_clko2_set_rate(struct clk *clk, unsigned long rate) static struct clk clko_clk = { __INIT_CLK_DEBUG(clko_clk) - .parent = &pll2_528_bus_main_clk, + .parent = &pll4_audio_div_clk, + .enable_reg = MXC_CCM_CCOSR, + .enable_shift = MXC_CCM_CCOSR_CKO1_EN_OFFSET, .enable = _clk_enable1, .disable = _clk_disable1, .set_parent = _clk_clko_set_parent, @@ -1650,6 +1794,15 @@ static struct clk adc_clk[] = { .enable = _clk_enable, .disable = _clk_disable, }, + { + __INIT_CLK_DEBUG(adc_clk) + .id = 1, + .parent = &ipg_clk, + .enable_reg = MXC_CCM_CCGR7, + .enable_shift = MXC_CCM_CCGRx_CG11_OFFSET, + .enable = _clk_enable, + .disable = _clk_disable, + }, }; static struct clk i2c_clk[] = { @@ -1692,7 +1845,6 @@ static void ftm_pwm_clk_disable(struct clk *pwm_clk) reg = __raw_readl(MXC_CCM_CSCDR1); reg &= ~(0x0F << 25); __raw_writel(reg, MXC_CCM_CSCDR1); - } static struct clk ftm_pwm_clk = { @@ -1700,7 +1852,6 @@ static struct clk ftm_pwm_clk = { .parent = &ipg_clk, .enable = ftm_pwm_clk_enable, .disable = ftm_pwm_clk_disable, - }; static int _clk_qspi0_set_parent(struct clk *clk, struct clk *parent) @@ -1878,7 +2029,6 @@ static struct clk dummy_clk = { .clk = &c, \ } - static struct clk_lookup lookups[] = { _REGISTER_CLOCK(NULL, "osc", osc_clk), _REGISTER_CLOCK(NULL, "ckih", ckih_clk), @@ -1898,6 +2048,7 @@ static struct clk_lookup lookups[] = { _REGISTER_CLOCK(NULL, "pll3_pfd3_308M", pll3_pfd3_308M), _REGISTER_CLOCK(NULL, "pll3_pfd4_320M", pll3_pfd4_320M), _REGISTER_CLOCK(NULL, "pll4", pll4_audio_main_clk), + _REGISTER_CLOCK(NULL, "pll4_div", pll4_audio_div_clk), _REGISTER_CLOCK(NULL, "pll5", pll6_video_main_clk), _REGISTER_CLOCK(NULL, "pll6", pll5_enet_main_clk), _REGISTER_CLOCK(NULL, "cpu_clk", cpu_clk), /* arm core clk */ @@ -1909,11 +2060,14 @@ static struct clk_lookup lookups[] = { _REGISTER_CLOCK(NULL, "mvf-uart.1", uart_clk[0]), _REGISTER_CLOCK(NULL, "mvf-uart.2", uart_clk[0]), _REGISTER_CLOCK(NULL, "mvf-uart.3", uart_clk[0]), + _REGISTER_CLOCK(NULL, "mvf-uart.4", uart_clk[0]), _REGISTER_CLOCK("mvf-dspi.0", NULL, dspi_clk[0]), + _REGISTER_CLOCK("mvf-dspi.1", NULL, dspi_clk[1]), _REGISTER_CLOCK("pit", NULL, pit_clk), _REGISTER_CLOCK("fec.0", NULL, enet_clk[0]), _REGISTER_CLOCK("fec.1", NULL, enet_clk[1]), _REGISTER_CLOCK("mvf-adc.0", NULL, adc_clk[0]), + _REGISTER_CLOCK("mvf-adc.1", NULL, adc_clk[1]), _REGISTER_CLOCK("switch.0", NULL, enet_clk[0]), _REGISTER_CLOCK("imx2-wdt.0", NULL, dummy_clk), _REGISTER_CLOCK("sdhci-esdhc-imx.1", NULL, esdhc1_clk), @@ -1949,7 +2103,6 @@ static void clk_tree_init(void) __raw_writel(reg, MXC_CCM_CCGR11); } - int __init mvf_clocks_init(unsigned long ckil, unsigned long osc, unsigned long ckih1, unsigned long ckih2) { @@ -1963,8 +2116,10 @@ int __init mvf_clocks_init(unsigned long ckil, unsigned long osc, apll_base = MVF_IO_ADDRESS(MVF_ANATOP_BASE_ADDR); - for (i = 0; i < ARRAY_SIZE(lookups); i++) + for (i = 0; i < ARRAY_SIZE(lookups); i++) { clkdev_add(&lookups[i]); + clk_debug_register(lookups[i].clk); + } clk_tree_init(); @@ -1976,11 +2131,7 @@ int __init mvf_clocks_init(unsigned long ckil, unsigned long osc, pll2_528_bus_main_clk.usecount += 5; periph_clk.usecount++; ipg_clk.usecount++; -#if 0 - clk_set_parent(&periph_clk, &pll2_pfd2_396M); - clk_enable(&periph_clk); /* platform bus clk */ - clk_enable(&ipg_clk); /* ips bus clk */ -#endif + clk_enable(&pll3_usb_otg_main_clk); #ifdef CONFIG_MXC_USE_PIT @@ -1997,14 +2148,28 @@ int __init mvf_clocks_init(unsigned long ckil, unsigned long osc, clk_set_parent(&esdhc1_clk, &pll1_pfd3_396M); clk_set_rate(&esdhc1_clk, 200000000); +//only for 640x480 and 1024x768 clk_set_parent(&dcu0_clk, &pll1_pfd2_452M); +//480 MHz +// clk_set_parent(&dcu0_clk, &pll3_usb_otg_main_clk); +#if !defined(CONFIG_COLIBRI_VF) clk_set_rate(&dcu0_clk, 113000000); - clk_set_parent(&sai2_clk, &audio_external_clk); +#else + clk_set_rate(&dcu0_clk, 452000000); +// clk_set_rate(&dcu0_clk, 480000000); + clk_set_rate(&pll4_audio_div_clk, 147456000); + clk_set_parent(&sai0_clk, &pll4_audio_div_clk); + clk_set_parent(&sai2_clk, &pll4_audio_div_clk); + clk_set_rate(&sai0_clk, 147456000); + clk_enable(&sai0_clk); +#endif clk_set_rate(&sai2_clk, 24576000); +#if !defined(CONFIG_COLIBRI_VF) clk_set_parent(&qspi0_clk, &pll1_pfd4_528M); clk_set_rate(&qspi0_clk, 66000000); - return 0; +#endif + return 0; } diff --git a/arch/arm/mach-mvf/crm_regs.h b/arch/arm/mach-mvf/crm_regs.h index 57fac5bbe973..51a898700baa 100644 --- a/arch/arm/mach-mvf/crm_regs.h +++ b/arch/arm/mach-mvf/crm_regs.h @@ -199,6 +199,7 @@ #define MXC_CCM_CLPCR (MXC_CCM_BASE + 0x2c) #define MXC_CCM_CISR (MXC_CCM_BASE + 0x30) #define MXC_CCM_CIMR (MXC_CCM_BASE + 0x34) +#define MXC_CCM_CCOSR (MXC_CCM_BASE + 0x38) #define MXC_CCM_CGPR (MXC_CCM_BASE + 0x3c) #define MXC_CCM_CCGR0 (MXC_CCM_BASE + 0x40) #define MXC_CCM_CCGR1 (MXC_CCM_BASE + 0x44) @@ -435,6 +436,14 @@ #define MXC_CCM_CIMR_LRF_PLL2 (1 << 1) #define MXC_CCM_CIMR_LRF_PLL1 (1) +/* CCOSR */ +#define MXC_CCM_CCOSR_CKO1_EN_OFFSET (10) +#define MXC_CCM_CCOSR_CKO1_DIV_MASK (0xF << 6) +#define MXC_CCM_CCOSR_CKO1_DIV_OFFSET (6) + +#define MXC_CCM_CCOSR_CKO1_SEL_MASK (0x3F << 0) +#define MXC_CCM_CCOSR_CKO1_SEL_OFFSET (0) + /* Define the bits in registers CGPR */ #define MXC_CCM_CGPR_EFUSE_PROG (1 << 4) #define MXC_CCM_CGPR_QSPI1_ACCZ (1 << 1) diff --git a/arch/arm/mach-mvf/devices-mvf.h b/arch/arm/mach-mvf/devices-mvf.h index c434834ba0b2..c5c3db9d13a1 100644 --- a/arch/arm/mach-mvf/devices-mvf.h +++ b/arch/arm/mach-mvf/devices-mvf.h @@ -28,8 +28,8 @@ extern const struct imx_snvs_rtc_data mvf_snvs_rtc_data __initconst; imx_add_snvs_rtc(&mvf_snvs_rtc_data) extern const struct imx_fec_data mvf_fec_data[] __initconst; -#define mvf_add_fec(id, pdata) \ - imx_add_fec(id, &mvf_fec_data[id], pdata) +#define mvf_add_fec(id, fec_data, pdata) \ + imx_add_fec(id, fec_data, pdata) extern const struct imx_sdhci_esdhc_imx_data mvf_sdhci_esdhc_imx_data[] __initconst; diff --git a/arch/arm/mach-mvf/mvf_fec.c b/arch/arm/mach-mvf/mvf_fec.c index 1ab339b9252e..0ded64ebed13 100644 --- a/arch/arm/mach-mvf/mvf_fec.c +++ b/arch/arm/mach-mvf/mvf_fec.c @@ -52,8 +52,20 @@ void __init mvf_init_fec(struct fec_platform_data fec_data) if (!is_valid_ether_addr(fec_data.mac)) memcpy(fec_data.mac, default_mac, ETH_ALEN); - mvf_add_fec(0, &fec_data); +#if !defined(CONFIG_COLIBRI_VF) +#ifdef CONFIG_FEC0 + mvf_add_fec(0, &mvf_fec_data[0], &fec_data); +#endif +#ifdef CONFIG_FEC1 + mvf_add_fec(1, &mvf_fec_data[1], &fec_data); +#endif +#else + /* Inverse device ID */ #ifdef CONFIG_FEC1 - mvf_add_fec(1, &fec_data); + mvf_add_fec(0, &mvf_fec_data[1], &fec_data); +#endif +#ifdef CONFIG_FEC0 + mvf_add_fec(1, &mvf_fec_data[0], &fec_data); +#endif #endif } diff --git a/arch/arm/mach-mvf/system.c b/arch/arm/mach-mvf/system.c index 989c75a02ed1..6eaefb0dbbb6 100644 --- a/arch/arm/mach-mvf/system.c +++ b/arch/arm/mach-mvf/system.c @@ -32,16 +32,24 @@ #include "regs-anadig.h" #include "regs-pm.h" -#define SW1_WAKEUP_PIN 38 -#define SW1_PORT1_PCR6_ADDR 0x4004a018 +/* PTB19, GPIO 41, SO-DIMM 45 */ +#define SW1_WAKEUP_GPIO 41 +#define SW1_WAKEUP_PIN 12 +#define SW1_PORT1_PCR9_ADDR MVF_IO_ADDRESS(0x4004a024) + +/* PTB20, GPIO 42, SO-DIMM 43 */ +#define SW2_WAKEUP_GPIO 42 +#define SW2_WAKEUP_PIN 13 +#define SW2_PORT1_PCR10_ADDR MVF_IO_ADDRESS(0x4004a028) static void __iomem *gpc_base = MVF_GPC_BASE; void gpc_set_wakeup(void) { __raw_writel(0xffffffff, gpc_base + GPC_IMR1_OFFSET); - __raw_writel(0xffffffff, gpc_base + GPC_IMR2_OFFSET); - /* unmask WKPU0 interrupt */ + /* unmask UART0 */ + __raw_writel(0xdfffffff, gpc_base + GPC_IMR2_OFFSET); + /* unmask WKPU12/13 interrupt */ __raw_writel(0xefffffff, gpc_base + GPC_IMR3_OFFSET); /* unmask GPIO4 interrupt */ __raw_writel(0xffff7fff, gpc_base + GPC_IMR4_OFFSET); @@ -51,13 +59,22 @@ void gpc_set_wakeup(void) void enable_wkpu(u32 source, u32 rise_fall) { - __raw_writel(1 << source, MVF_WKPU_BASE + WKPU_IRER_OFFSET); - __raw_writel(1 << source, MVF_WKPU_BASE + WKPU_WRER_OFFSET); - - if (rise_fall == RISING_EDGE_ENABLED) - __raw_writel(1 << source, MVF_WKPU_BASE + WKPU_WIREER_OFFSET); - else - __raw_writel(1 << source, MVF_WKPU_BASE + WKPU_WIFEER_OFFSET); + u32 tmp; + tmp = __raw_readl(MVF_WKPU_BASE + WKPU_IRER_OFFSET); + __raw_writel(tmp | 1 << source, MVF_WKPU_BASE + WKPU_IRER_OFFSET); + + tmp = __raw_readl(MVF_WKPU_BASE + WKPU_WRER_OFFSET); + __raw_writel(tmp | 1 << source, MVF_WKPU_BASE + WKPU_WRER_OFFSET); + + if (rise_fall == RISING_EDGE_ENABLED) { + tmp = __raw_readl(MVF_WKPU_BASE + WKPU_WIREER_OFFSET); + tmp |= 1 << source; + __raw_writel(tmp, MVF_WKPU_BASE + WKPU_WIREER_OFFSET); + } else { + tmp = __raw_readl(MVF_WKPU_BASE + WKPU_WIFEER_OFFSET); + tmp |= 1 << source; + __raw_writel(tmp, MVF_WKPU_BASE + WKPU_WIFEER_OFFSET); + } } static irqreturn_t wkpu_irq(int irq, void *dev_id) @@ -71,29 +88,41 @@ static irqreturn_t wkpu_irq(int irq, void *dev_id) return IRQ_NONE; } +static void mvf_configure_wakeup_pin(int gpio, int wkup_pin, + const volatile void __iomem *pinctl_addr) +{ + u32 tmp; + + /* config SW1 for waking up system */ + gpio_request_one(gpio, GPIOF_IN, "SW wakeup"); + gpio_set_value(gpio, 0); + + /* Disable IRQC interrupt/dma request in pin contorl register */ + tmp = __raw_readl(pinctl_addr); + tmp &= ~0x000f0000; + __raw_writel(tmp, pinctl_addr); + + /* enable WKPU interrupt for this wakeup pin */ + enable_wkpu(wkup_pin, FALLING_EDGE_ENABLED); +} + /* set cpu multiple modes before WFI instruction */ void mvf_cpu_lp_set(enum mvf_cpu_pwr_mode mode) { u32 ccm_ccsr, ccm_clpcr, ccm_ccr; - u32 tmp; int ret; if ((mode == LOW_POWER_STOP) || (mode == STOP_MODE)) { - /* config SW1 for waking up system */ - gpio_request_one(SW1_WAKEUP_PIN, GPIOF_IN, "SW1 wakeup"); - gpio_set_value(SW1_WAKEUP_PIN, 0); - /* PORT1_PCR6 IRQC interrupt/dma request disabled */ - tmp = __raw_readl(MVF_IO_ADDRESS(SW1_PORT1_PCR6_ADDR)); - tmp &= ~0x000f0000; - __raw_writel(tmp, MVF_IO_ADDRESS(SW1_PORT1_PCR6_ADDR)); - ret = request_irq(MVF_INT_WKPU0, wkpu_irq, IRQF_DISABLED, "wkpu irq", NULL); + if (ret) printk(KERN_ERR "Request wkpu IRQ failed\n"); - /* enable WKPU interrupt */ - enable_wkpu(11, FALLING_EDGE_ENABLED); + mvf_configure_wakeup_pin(SW1_WAKEUP_GPIO, SW1_WAKEUP_PIN, + SW1_PORT1_PCR9_ADDR); + mvf_configure_wakeup_pin(SW2_WAKEUP_GPIO, SW2_WAKEUP_PIN, + SW2_PORT1_PCR10_ADDR); } ccm_ccr = __raw_readl(MXC_CCM_CCR); diff --git a/arch/arm/plat-mxc/devices/platform-imx-uart.c b/arch/arm/plat-mxc/devices/platform-imx-uart.c index 3c9010784cda..fc356e031f30 100644 --- a/arch/arm/plat-mxc/devices/platform-imx-uart.c +++ b/arch/arm/plat-mxc/devices/platform-imx-uart.c @@ -144,6 +144,8 @@ const struct imx_imx_uart_1irq_data mvf_imx_uart_data[] __initconst = { mvf_imx_uart_data_entry(1, 1), mvf_imx_uart_data_entry(2, 2), mvf_imx_uart_data_entry(3, 3), + mvf_imx_uart_data_entry(4, 4), + mvf_imx_uart_data_entry(5, 5), }; #endif /* ifdef CONFIG_SOC_MVFA5 */ diff --git a/arch/arm/plat-mxc/devices/platform-mvf-adc.c b/arch/arm/plat-mxc/devices/platform-mvf-adc.c index 38add8b5fd4f..085bc4508a31 100644 --- a/arch/arm/plat-mxc/devices/platform-mvf-adc.c +++ b/arch/arm/plat-mxc/devices/platform-mvf-adc.c @@ -25,7 +25,8 @@ #ifdef CONFIG_SOC_MVFA5 const struct mvf_adc_data mvfa5_adc_data[] __initconst = { - mvf_adc_data_entry(MVF, 0, SZ_4K), + mvf_adc_data_entry(MVF, 0, SZ_4K), + mvf_adc_data_entry(MVF, 1, SZ_4K), }; #endif @@ -48,3 +49,4 @@ struct platform_device *__init mvf_add_adcdev( return imx_add_platform_device("mvf-adc", data->id, res, ARRAY_SIZE(res), NULL, 0); } + diff --git a/arch/arm/plat-mxc/devices/platform-mvf-dcu.c b/arch/arm/plat-mxc/devices/platform-mvf-dcu.c index 0fe5099eca34..cba3d2552132 100644 --- a/arch/arm/plat-mxc/devices/platform-mvf-dcu.c +++ b/arch/arm/plat-mxc/devices/platform-mvf-dcu.c @@ -26,12 +26,14 @@ int __init mvf_dcu_init(int id) { int ret = 0; +#if !defined(CONFIG_COLIBRI_VF) ret = gpio_request_one(DCU_LCD_ENABLE_PIN, GPIOF_OUT_INIT_LOW, "DCU"); if (ret) printk(KERN_ERR "DCU: failed to request GPIO 30\n"); msleep(2); gpio_set_value(DCU_LCD_ENABLE_PIN, 1); +#endif writel(0x20000000, MVF_IO_ADDRESS(MVF_TCON0_BASE_ADDR)); return ret; diff --git a/arch/arm/plat-mxc/global_timer.c b/arch/arm/plat-mxc/global_timer.c index abbccb955af3..c58349b26555 100644 --- a/arch/arm/plat-mxc/global_timer.c +++ b/arch/arm/plat-mxc/global_timer.c @@ -203,7 +203,7 @@ void __init global_timer_init(struct clk *clk, void __iomem *base, int irq) __raw_writel(0, timer_base + GT_AUTO_INC); timer_rate = clk_get_rate(clk); - init_sched_clock(&cd, gtimer_update_sched_clock, 64, timer_rate); + init_sched_clock(&cd, gtimer_update_sched_clock, 32, timer_rate); clocksource_register_hz(&clocksource_gtimer, timer_rate); clockevent_gtimer.irq = irq; clockevent_gtimer.cpumask = cpumask_of(0); diff --git a/arch/arm/plat-mxc/gpio-mvf.c b/arch/arm/plat-mxc/gpio-mvf.c index ee1344f35573..58bdb781cf24 100644 --- a/arch/arm/plat-mxc/gpio-mvf.c +++ b/arch/arm/plat-mxc/gpio-mvf.c @@ -183,16 +183,18 @@ static void _set_gpio_direction(struct gpio_chip *chip, unsigned offset, pad_addr = MVF_IO_ADDRESS( MVF_IOMUXC_BASE_ADDR + 4 * (chip->base + offset)); + /* Get current flags, clear direction */ + l = __raw_readl(pad_addr) & ~(PAD_CTL_OBE_ENABLE | PAD_CTL_IBE_ENABLE); + if (dir) - l = MVF600_GPIO_GENERAL_CTRL | PAD_CTL_OBE_ENABLE; + l |= PAD_CTL_OBE_ENABLE; else { - l = MVF600_GPIO_GENERAL_CTRL | PAD_CTL_IBE_ENABLE; + l |= PAD_CTL_IBE_ENABLE; __raw_writel((1 << offset), port->base_int + GPIO_DFER); __raw_writel(1, port->base_int + GPIO_DFCR); __raw_writel(0xFF, port->base_int + GPIO_DFWR); } - /*Note: This will destroy the original IOMUX settings.*/ __raw_writel(l, pad_addr); spin_unlock_irqrestore(&port->lock, flags); diff --git a/arch/arm/plat-mxc/include/mach/colibri-ts.h b/arch/arm/plat-mxc/include/mach/colibri-ts.h new file mode 100644 index 000000000000..937502badaf2 --- /dev/null +++ b/arch/arm/plat-mxc/include/mach/colibri-ts.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2013 by Stefan Agner <stefan.agner@toradex.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#ifndef ASMARM_ARCH_COLIBRI_TS_H +#define ASMARM_ARCH_COLIBRI_TS_H + +struct colibri_ts_platform_data { + int (*init)(struct platform_device *pdev); + void (*exit)(struct platform_device *pdev); + int (*mux_pen_interrupt)(struct platform_device *pdev); + int (*mux_adc)(struct platform_device *pdev); + unsigned int gpio_xp; + unsigned int gpio_xm; + unsigned int gpio_yp; + unsigned int gpio_ym; + unsigned int gpio_pen_detect; + unsigned int gpio_pen_detect_pullup; +}; + +#endif diff --git a/arch/arm/plat-mxc/include/mach/iomux-mvf.h b/arch/arm/plat-mxc/include/mach/iomux-mvf.h index 449a43914507..500f78b0d0be 100644 --- a/arch/arm/plat-mxc/include/mach/iomux-mvf.h +++ b/arch/arm/plat-mxc/include/mach/iomux-mvf.h @@ -47,11 +47,14 @@ typedef enum iomux_config { #define MVF600_ENET_PAD_CTRL (PAD_CTL_PUS_47K_UP | PAD_CTL_SPEED_HIGH | \ PAD_CTL_DSE_50ohm) -#define MVF600_I2C_PAD_CTRL (PAD_CTL_DSE_50ohm | PAD_CTL_PUS_47K_UP | PAD_CTL_SPEED_HIGH) +#define MVF600_I2C_PAD_CTRL (PAD_CTL_DSE_37ohm | PAD_CTL_ODE | \ + PAD_CTL_SPEED_HIGH) #define MVF600_SAI_PAD_CTRL (PAD_CTL_DSE_50ohm | PAD_CTL_HYS | \ PAD_CTL_PKE | PAD_CTL_PUE | PAD_CTL_PUS_100K_UP) +#define MVF600_TS_PAD_CTRL (PAD_CTL_DSE_150ohm) + #define MVF600_ESAI_PAD_CTRL (PAD_CTL_DSE_50ohm | PAD_CTL_HYS | \ PAD_CTL_PKE | PAD_CTL_PUE | PAD_CTL_PUS_100K_UP) @@ -60,18 +63,25 @@ typedef enum iomux_config { #define MVF600_DSPI_PAD_CTRL (PAD_CTL_SPEED_LOW | PAD_CTL_DSE_25ohm) +//lowest drive! #define MVF600_HIGH_DRV PAD_CTL_DSE_150ohm +#if defined(CONFIG_COLIBRI_VF) +#define MVF600_DCU_PAD_CTRL (PAD_CTL_DSE_37ohm | PAD_CTL_SRE_FAST | \ + PAD_CTL_SPEED_HIGH | PAD_CTL_OBE_ENABLE) +#else #define MVF600_DCU_PAD_CTRL (MVF600_HIGH_DRV | PAD_CTL_OBE_ENABLE) +#endif #define MVF600_UART_PAD_CTRL (PAD_CTL_PUS_100K_UP | PAD_CTL_SPEED_MED | \ PAD_CTL_DSE_25ohm) +//why PKE? #define MVF600_GPIO_GENERAL_CTRL (PAD_CTL_PKE | PAD_CTL_PUE | PAD_CTL_SPEED_MED | PAD_CTL_PUS_47K_UP | \ PAD_CTL_DSE_25ohm) #define MVF600_FTM0_CH_CTRL (PAD_CTL_SPEED_LOW | PAD_CTL_OBE_ENABLE | \ - PAD_CTL_ODE | PAD_CTL_DSE_25ohm) + PAD_CTL_DSE_25ohm) #define MVF600_FTM1_CH_CTRL (PAD_CTL_SPEED_LOW | PAD_CTL_OBE_ENABLE | \ PAD_CTL_DSE_25ohm) /*SDHC1*/ @@ -91,6 +101,14 @@ typedef enum iomux_config { #define MVF600_PAD134_PTA7__SDHC1_SW_CD \ IOMUX_PAD(0x0218, 0x0218, 0, 0x0000, 0, \ MVF600_GPIO_GENERAL_CTRL | PAD_CTL_IBE_ENABLE) +#define MVF600_PAD42_PTB20__SDHC1_SW_CD \ + IOMUX_PAD(0x00a8, 0x00a8, 0, 0x0000, 0, \ + MVF600_GPIO_GENERAL_CTRL | PAD_CTL_IBE_ENABLE) + +/*GPIO Wake-Up*/ +#define MVF600_PAD41_PTB19__GPIO \ + IOMUX_PAD(0x00a4, 0x00a4, 0, 0x0000, 0, \ + MVF600_GPIO_GENERAL_CTRL | PAD_CTL_IBE_ENABLE) /*I2C0*/ #define MVF600_PAD36_PTB14__I2C0_SCL \ @@ -125,10 +143,37 @@ typedef enum iomux_config { IOMUX_PAD(0x00B0, 0x00B0, 1, 0x0000, 0, \ MVF600_DSPI_PAD_CTRL | PAD_CTL_OBE_ENABLE) +/* DSPI1 */ +#define MVF600_PAD84_PTD5__DSPI1_PCS0 \ + IOMUX_PAD(0x0150, 0x0150, 3, 0x0300, 1, \ + MVF600_DSPI_PAD_CTRL | PAD_CTL_OBE_ENABLE) +#define MVF600_PAD85_PTD6__DSPI1_SIN \ + IOMUX_PAD(0x0154, 0x0154, 3, 0x02FC, 1, \ + MVF600_DSPI_PAD_CTRL | PAD_CTL_IBE_ENABLE) +#define MVF600_PAD86_PTD7__DSPI1_SOUT \ + IOMUX_PAD(0x0158, 0x0158, 3, 0x0000, 0, \ + MVF600_DSPI_PAD_CTRL | PAD_CTL_OBE_ENABLE) +#define MVF600_PAD87_PTD8__DSPI1_SCK \ + IOMUX_PAD(0x015C, 0x015C, 3, 0x02F8, 1, \ + MVF600_DSPI_PAD_CTRL | PAD_CTL_OBE_ENABLE) + +/* CAN_INT GPIO */ +#define MVF600_PAD43_PTB21__CAN_INT \ + IOMUX_PAD(0x00AC, 0x00AC, 0, 0x0000, 0, \ + MVF600_GPIO_GENERAL_CTRL | PAD_CTL_IBE_ENABLE) + + /*FEC0*/ #define MVF600_PAD0_PTA6__RMII_CLKIN \ IOMUX_PAD(0x0000, 0x0000, 2, 0x02F0, 0, \ MVF600_ENET_PAD_CTRL | PAD_CTL_IBE_ENABLE) +#define MVF600_PAD0_PTA6__RMII_CLKOUT \ + IOMUX_PAD(0x0000, 0x0000, 1, 0x0000, 0, \ + MVF600_ENET_PAD_CTRL | PAD_CTL_IBE_ENABLE) +#define MVF600_PAD2_PTA9__RMII_CLKOUT \ + IOMUX_PAD(0x0008, 0x0008, 2, 0x0000, 0, \ + MVF600_ENET_PAD_CTRL | PAD_CTL_IBE_ENABLE) + #define MVF600_PAD45_PTC0__RMII0_MDC \ IOMUX_PAD(0x00B4, 0x00B4, 1, 0x0000, 0, \ MVF600_ENET_PAD_CTRL | PAD_CTL_OBE_ENABLE) @@ -188,12 +233,21 @@ typedef enum iomux_config { MVF600_ENET_PAD_CTRL | PAD_CTL_OBE_ENABLE) /*USB0/1 VBUS, using the GPIO*/ +#define MVF600_PAD83_PTD4__USBH_PEN \ + IOMUX_PAD(0x014C, 0x014C, 0, 0x0000, 0, \ + MVF600_GPIO_GENERAL_CTRL) #define MVF600_PAD85_PTD6__USB0_VBUS_EN \ IOMUX_PAD(0x0154, 0x0154, 0, 0x0000, 0, \ MVF600_GPIO_GENERAL_CTRL) #define MVF600_PAD92_PTD13__USB1_VBUS_EN \ IOMUX_PAD(0x0170, 0x0170, 0, 0x0000, 0, \ MVF600_GPIO_GENERAL_CTRL) +#define MVF600_PAD102_PTC29__USBC_DET \ + IOMUX_PAD(0x0198, 0x0198, 0, 0x0000, 0, \ + MVF600_GPIO_GENERAL_CTRL) +#define MVF600_PAD108_PTE3__USB_OC \ + IOMUX_PAD(0x01B0, 0x01B0, 0, 0x0000, 0, \ + MVF600_GPIO_GENERAL_CTRL) /*ESAI0(share with FEC1)*/ #define MVF600_PAD54_PTC9__ESAI_SCKT \ @@ -222,16 +276,29 @@ typedef enum iomux_config { #define MVF600_PAD78_PTD16__ESAI_HCKT \ IOMUX_PAD(0x0138, 0x0138, 3, 0x0324, 1, MVF600_ESAI_PAD_CTRL) +#define MVF600_PAD93_PTB23_SAI0_TX_BCLK \ + IOMUX_PAD(0x0174, 0x0174, 1, 0x0000, 0, \ + MVF600_SAI_PAD_CTRL | PAD_CTL_OBE_ENABLE) + /*SAI2*/ +#define MVF600_PAD5_PTA12_EXT_AUDIO_MCLK \ + IOMUX_PAD(0x0014, 0x0014, 2, 0x02ec, 1, \ + MVF600_SAI_PAD_CTRL | PAD_CTL_IBE_ENABLE) #define MVF600_PAD6_PTA16_SAI2_TX_BCLK \ IOMUX_PAD(0x0018, 0x0018, 5, 0x0370, 0, \ MVF600_SAI_PAD_CTRL | PAD_CTL_IBE_ENABLE) #define MVF600_PAD8_PTA18_SAI2_TX_DATA \ IOMUX_PAD(0x0020, 0x0020, 5, 0x0000, 0, \ MVF600_SAI_PAD_CTRL | PAD_CTL_OBE_ENABLE) +#if !defined(CONFIG_COLIBRI_VF) #define MVF600_PAD9_PTA19_SAI2_TX_SYNC \ IOMUX_PAD(0x0024, 0x0024, 5, 0x0374, 0, \ MVF600_SAI_PAD_CTRL | PAD_CTL_IBE_ENABLE) +#else +#define MVF600_PAD9_PTA19_SAI2_TX_SYNC \ + IOMUX_PAD(0x0024, 0x0024, 5, 0x0374, 0, \ + MVF600_SAI_PAD_CTRL | PAD_CTL_OBE_ENABLE) +#endif #define MVF600_PAD11_PTA21_SAI2_RX_BCLK \ IOMUX_PAD(0x002C, 0x002C, 5, 0x0364, 0, \ MVF600_SAI_PAD_CTRL | PAD_CTL_IBE_ENABLE) @@ -241,13 +308,21 @@ typedef enum iomux_config { #define MVF600_PAD13_PTA23_SAI2_RX_SYNC \ IOMUX_PAD(0x0034, 0x0034, 5, 0x036c, 0, \ MVF600_SAI_PAD_CTRL | PAD_CTL_IBE_ENABLE) +#define MVF600_PAD22_PTB0_SAI2_RX_BCLK \ + IOMUX_PAD(0x0058, 0x0058, 5, 0x0364, 1, \ + MVF600_SAI_PAD_CTRL | PAD_CTL_IBE_ENABLE) #define MVF600_PAD40_PTB18_EXT_AUDIO_MCLK \ IOMUX_PAD(0x00A0, 0x00A0, 2, 0x02ec, 2, \ MVF600_SAI_PAD_CTRL | PAD_CTL_IBE_ENABLE) +#define MVF600_PAD40_PTB18_CKO1 \ + IOMUX_PAD(0x00A0, 0x00A0, 4, 0x0000, 0, \ + PAD_CTL_DSE_75ohm | PAD_CTL_OBE_ENABLE) /*DCU0*/ #define MVF600_PAD30_PTB8_LCD_ENABLE \ IOMUX_PAD(0x78, 0x78, 0, 0x0000, 0, MVF600_DCU_PAD_CTRL) +#define MVF600_PAD45_PTC0_BL_ON \ + IOMUX_PAD(0x00B4, 0x00B4, 0, 0x0000, 0, MVF600_GPIO_GENERAL_CTRL) #define MVF600_PAD105_PTE0_DCU0_HSYNC \ IOMUX_PAD(0x01A4, 0x01A4, 1, 0x0000, 0, MVF600_DCU_PAD_CTRL) #define MVF600_PAD106_PTE1_DCU0_VSYNC \ @@ -313,12 +388,55 @@ typedef enum iomux_config { IOMUX_PAD(0x006C, 0x006C, 2, 0x037C, 0, \ MVF600_UART_PAD_CTRL | PAD_CTL_IBE_ENABLE) +/* UART0 */ #define MVF600_PAD32_PTB10_UART0_TX \ IOMUX_PAD(0x0080, 0x0080, 1, 0x0000, 0, \ MVF600_UART_PAD_CTRL | PAD_CTL_OBE_ENABLE) #define MVF600_PAD33_PTB11_UART0_RX \ IOMUX_PAD(0x0084, 0x0084, 1, 0x0000, 0, \ MVF600_UART_PAD_CTRL | PAD_CTL_IBE_ENABLE) +#define MVF600_PAD34_PTB12_UART0_RTS \ + IOMUX_PAD(0x0088, 0x0088, 1, 0x0000, 0, \ + MVF600_UART_PAD_CTRL | PAD_CTL_OBE_ENABLE) +#define MVF600_PAD35_PTB13_UART0_CTS \ + IOMUX_PAD(0x008C, 0x008C, 1, 0x0000, 0, \ + MVF600_UART_PAD_CTRL | PAD_CTL_IBE_ENABLE) + +/* UART2 */ +#define MVF600_PAD79_PTD0_UART2_TX \ + IOMUX_PAD(0x013C, 0x013C, 2, 0x038C, 2, \ + MVF600_UART_PAD_CTRL | PAD_CTL_OBE_ENABLE) +#define MVF600_PAD80_PTD1_UART2_RX \ + IOMUX_PAD(0x0140, 0x0140, 2, 0x0388, 2, \ + MVF600_UART_PAD_CTRL | PAD_CTL_IBE_ENABLE) +#define MVF600_PAD81_PTD2_UART2_RTS \ + IOMUX_PAD(0x0144, 0x0144, 2, 0x0000, 0, \ + MVF600_UART_PAD_CTRL | PAD_CTL_OBE_ENABLE) +#define MVF600_PAD82_PTD3_UART2_CTS \ + IOMUX_PAD(0x0148, 0x0148, 2, 0x0384, 1, \ + MVF600_UART_PAD_CTRL | PAD_CTL_IBE_ENABLE) + +/* UART3 */ +#define MVF600_PAD10_UART3_TX \ + IOMUX_PAD(0x0028, 0x0028, 6, 0x0394, 0, \ + MVF600_UART_PAD_CTRL | PAD_CTL_OBE_ENABLE) +#define MVF600_PAD11_UART3_RX \ + IOMUX_PAD(0x002C, 0x002C, 6, 0x0390, 0, \ + MVF600_UART_PAD_CTRL | PAD_CTL_IBE_ENABLE) + +/* UART4 */ +#define MVF600_PAD18_UART4_TX \ + IOMUX_PAD(0x0048, 0x0048, 4, 0x0000, 0, \ + MVF600_UART_PAD_CTRL | PAD_CTL_OBE_ENABLE) +#define MVF600_PAD19_UART4_RX \ + IOMUX_PAD(0x004C, 0x004C, 4, 0x0000, 0, \ + MVF600_UART_PAD_CTRL | PAD_CTL_IBE_ENABLE) + +/* SO-DIMM 28/30 used for touch interrupt/reset (also PWM-B/PWM-C) */ +#define MVF600_PAD23_PTB1_RESET \ + IOMUX_PAD(0x005c, 0x005c, 0, 0x0000, 0, MVF600_FTM0_CH_CTRL) +#define MVF600_PAD30_PTB8_INT \ + IOMUX_PAD(0x0078, 0x0078, 0, 0x032C, 0, MVF600_FTM1_CH_CTRL) #define MVF600_PAD28_PTB6_UART2_TX \ IOMUX_PAD(0x0070, 0x0070, 7, 0x038C, 0, \ @@ -334,6 +452,15 @@ typedef enum iomux_config { IOMUX_PAD(0x002C, 0x002C, 6, 0x0390, 0, \ MVF600_UART_PAD_CTRL | PAD_CTL_IBE_ENABLE) +/* GPIO Input (disabled in case of 2nd Ethernet) */ +#define MVF600_PAD22_PTB0_GPIO \ + IOMUX_PAD(0x0058, 0x0058, 0, 0x0000, 0, \ + MVF600_GPIO_GENERAL_CTRL | PAD_CTL_IBE_ENABLE) +#define MVF600_PAD31_PTB9_GPIO \ + IOMUX_PAD(0x007C, 0x007C, 0, 0x0000, 0, \ + MVF600_GPIO_GENERAL_CTRL | PAD_CTL_IBE_ENABLE) + + /* FlexTimer channel pin */ #define MVF600_PAD22_PTB0_FTM0CH0 \ IOMUX_PAD(0x0058, 0x0058, 1, 0x0000, 0, MVF600_FTM0_CH_CTRL) @@ -354,8 +481,62 @@ typedef enum iomux_config { IOMUX_PAD(0x007C, 0x007C, 1, 0x0330, 0, MVF600_FTM1_CH_CTRL) /* Touch Screen */ +#define MVF600_PAD4_PTA11 \ + IOMUX_PAD(0x0010, 0x0010, 0, 0x0, 0, \ + MVF600_TS_PAD_CTRL | PAD_CTL_OBE_ENABLE) + +/* Colirbi VF50 V1.0 */ +#define MVF600_PAD5_PTA12 \ + IOMUX_PAD(0x0014, 0x0014, 0, 0x0, 0, \ + MVF600_TS_PAD_CTRL | PAD_CTL_OBE_ENABLE) +/* Colibri VF50 V1.1 */ +#define MVF600_PAD93_PTB23 \ + IOMUX_PAD(0x0174, 0x0174, 0, 0x0, 0, \ + MVF600_TS_PAD_CTRL | PAD_CTL_OBE_ENABLE) + +#define MVF600_PAD6_PTA16_ADC1_SE0 \ + IOMUX_PAD(0x0018, 0x0018, 3, 0x0, 0, \ + MVF600_TS_PAD_CTRL | PAD_CTL_IBE_ENABLE) +#define MVF600_PAD8_PTA18_ADC0_SE0 \ + IOMUX_PAD(0x0020, 0x0020, 2, 0x0, 0, \ + MVF600_TS_PAD_CTRL | PAD_CTL_IBE_ENABLE) +#define MVF600_PAD9_PTA19_ADC0_SE1 \ + IOMUX_PAD(0x0024, 0x0024, 2, 0x0, 0, \ + MVF600_TS_PAD_CTRL | PAD_CTL_IBE_ENABLE) +#define MVF600_PAD12_PTA22 \ + IOMUX_PAD(0x0030, 0x0030, 0, 0x0, 0, \ + MVF600_TS_PAD_CTRL | PAD_CTL_OBE_ENABLE) +#define MVF600_PAD13_PTA23 \ + IOMUX_PAD(0x0034, 0x0034, 0, 0x0, 0, \ + MVF600_TS_PAD_CTRL | PAD_CTL_OBE_ENABLE) +#define MVF600_PAD24_PTB2_ADC1_SE2 \ + IOMUX_PAD(0x0060, 0x0060, 2, 0x0, 0, \ + MVF600_TS_PAD_CTRL | PAD_CTL_IBE_ENABLE) + +/*Touchscreen touch detection*/ +#define MVF600_PAD4_PTA11_WM9715L_PENDOWN \ + IOMUX_PAD(0x0010, 0x0010, 0, 0x0000, 0, \ + MVF600_GPIO_GENERAL_CTRL | PAD_CTL_IBE_ENABLE) +#define MVF600_PAD8_PTA18 \ + IOMUX_PAD(0x0020, 0x0020, 0, 0x0, 0, \ + MVF600_TS_PAD_CTRL | PAD_CTL_IBE_ENABLE) +#define MVF600_PAD8_PTA18_WM9715L_SDATAOUT \ + IOMUX_PAD(0x0020, 0x0020, 0, 0x0, 0, \ + MVF600_TS_PAD_CTRL | PAD_CTL_OBE_ENABLE) +#define MVF600_PAD9_PTA19 \ + IOMUX_PAD(0x0024, 0x0024, 0, 0x0, 0, \ + MVF600_GPIO_GENERAL_CTRL | PAD_CTL_IBE_ENABLE) +#define MVF600_PAD9_PTA19_WM9715L_SYNC \ + IOMUX_PAD(0x0024, 0x0024, 0, 0x0, 0, \ + MVF600_TS_PAD_CTRL | PAD_CTL_OBE_ENABLE) +#define MVF600_PAD13_PTA23_WM9715L_RESET \ + IOMUX_PAD(0x0034, 0x0034, 0, 0x0, 0, \ + MVF600_TS_PAD_CTRL | PAD_CTL_OBE_ENABLE) #define MVF600_PAD21_PTA31_TS_IRQ \ - IOMUX_PAD(0x0054, 0x0054, 0, 0x0000, 0, \ + IOMUX_PAD(0x0054, 0x0054, 0, 0x0000, 0, \ + MVF600_GPIO_GENERAL_CTRL | PAD_CTL_IBE_ENABLE) +#define MVF600_PAD24_PTB2_WM9715L_GENIRQ \ + IOMUX_PAD(0x0060, 0x0060, 0, 0x0000, 0, \ MVF600_GPIO_GENERAL_CTRL | PAD_CTL_IBE_ENABLE) /*QSPI*/ diff --git a/arch/arm/tools/mach-types b/arch/arm/tools/mach-types index b805e9660348..287cd75bc69c 100644 --- a/arch/arm/tools/mach-types +++ b/arch/arm/tools/mach-types @@ -1121,3 +1121,5 @@ mvf_twr_vf400 MACH_MVFA5_TWR_VF400 MVFA5_TWR_VF400 4148 mvf_twr_vf500 MACH_MVFA5_TWR_VF500 MVFA5_TWR_VF500 4147 mvf_twr_vf600 MACH_MVFA5_TWR_VF600 MVFA5_TWR_VF600 4146 mvf_twr_vf700 MACH_MVFA5_TWR_VF700 MVFA5_TWR_VF700 2125 +colibri_vf50 MACH_COLIBRI_VF50 COLIBRI_VF50 4749 +colibri_vf61 MACH_COLIBRI_VF61 COLIBRI_VF61 4750 diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index dcd283c11eaa..482013b2225f 100755 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig @@ -777,4 +777,24 @@ config TOUCHSCREEN_CRTOUCH To compile this driver as a module, choose M here: the module will be called crtouch_ts. + +config TOUCHSCREEN_COLIBRI_VF50 + tristate "Toradex Colibri on board touchscreen driver" + depends on ARCH_MVF && MVF_ADC + help + Say Y here if you have a Colibri VF50 and plan to use + the on-board provided 4-wire touchscreen driver. + + If unsure, say N. + + To compile this driver as a module, choose M here: the + module will be called colibri_vf50_ts. + +config TOUCHSCREEN_FUSION_F0710A + tristate "TouchRevolution Fusion F0710A Touchscreens" + depends on I2C + help + Say Y here if you want to support the multi-touch input driver for + the TouchRevolution Fusion 7 and 10 panels. + endif diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile index efed4c9c3adc..66b42836bc7e 100755 --- a/drivers/input/touchscreen/Makefile +++ b/drivers/input/touchscreen/Makefile @@ -66,3 +66,5 @@ obj-$(CONFIG_TOUCHSCREEN_TPS6507X) += tps6507x-ts.o obj-$(CONFIG_TOUCHSCREEN_MAX11801) += max11801_ts.o obj-$(CONFIG_TOUCHSCREEN_EGALAX) += egalax_ts.o obj-$(CONFIG_TOUCHSCREEN_CRTOUCH) += crtouch_ts.o +obj-$(CONFIG_TOUCHSCREEN_COLIBRI_VF50) += colibri-vf50-ts.o +obj-$(CONFIG_TOUCHSCREEN_FUSION_F0710A) += fusion_F0710A.o diff --git a/drivers/input/touchscreen/colibri-vf50-ts.c b/drivers/input/touchscreen/colibri-vf50-ts.c new file mode 100644 index 000000000000..bc42253f778b --- /dev/null +++ b/drivers/input/touchscreen/colibri-vf50-ts.c @@ -0,0 +1,413 @@ +/* Copyright 2013 Toradex AG + * + * Toradex Colibri VF50 Touchscreen driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License. +*/ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/sched.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <linux/uaccess.h> +#include <linux/miscdevice.h> +#include <linux/mvf_adc.h> +#include <linux/device.h> +#include <linux/cdev.h> +#include <linux/input.h> +#include <linux/delay.h> +#include <linux/gpio.h> +#include <mach/colibri-ts.h> + +#define DRIVER_NAME "colibri-vf50-ts" +#define DRV_VERSION "1.0" + +#define MVF_ADC_MAX ((1 << 12) - 1) + +#define COLI_TOUCH_MIN_DELAY_US 1000 +#define COLI_TOUCH_MAX_DELAY_US 2000 + +struct adc_touch_device { + struct platform_device *pdev; + + bool stop_touchscreen; + + int pen_irq; + struct input_dev *ts_input; + struct workqueue_struct *ts_workqueue; + struct work_struct ts_work; +}; + +struct adc_touch_device *touch; + +/* + * Enables given plates and measures touch parameters using ADC + */ +static int adc_ts_measure(int plate_p, int plate_m, int adc, int adc_channel) +{ + int i, value = 0; + gpio_set_value(plate_p, 0); /* Low active */ + gpio_set_value(plate_m, 1); /* High active */ + + /* Use hrtimer sleep since msleep sleeps 10ms+ */ + usleep_range(COLI_TOUCH_MIN_DELAY_US, COLI_TOUCH_MAX_DELAY_US); + + for (i = 0; i < 5; i++) { + int ret = mvf_adc_register_and_convert(adc, adc_channel); + if (ret < 0) + return -EINVAL; + + value += ret; + } + + value /= 5; + + gpio_set_value(plate_p, 1); + gpio_set_value(plate_m, 0); + + return value; +} + +/* + * Enable touch detection using falling edge detection on XM + */ +static void adc_ts_enable_touch_detection(struct adc_touch_device *adc_ts) +{ + struct colibri_ts_platform_data *pdata = adc_ts->pdev->dev.platform_data; + + /* Enable plate YM (needs to be strong GND, high active) */ + gpio_set_value(pdata->gpio_ym, 1); + + /* Let the platform mux to GPIO in order to enable Pull-Up on GPIO */ + if (pdata->mux_pen_interrupt) + pdata->mux_pen_interrupt(adc_ts->pdev); +} + +/* + * ADC touch screen sampling worker function + */ +static void adc_ts_work(struct work_struct *ts_work) +{ + struct adc_touch_device *adc_ts = container_of(ts_work, + struct adc_touch_device, ts_work); + struct device *dev = &adc_ts->pdev->dev; + struct colibri_ts_platform_data *pdata = adc_ts->pdev->dev.platform_data; + int val_x, val_y, val_z1, val_z2, val_p = 0; + + struct adc_feature feature = { + .channel = ADC0, + .clk_sel = ADCIOC_BUSCLK_SET, + .clk_div_num = 1, + .res_mode = 12, + .sam_time = 6, + .lp_con = ADCIOC_LPOFF_SET, + .hs_oper = ADCIOC_HSOFF_SET, + .vol_ref = ADCIOC_VR_VREF_SET, + .tri_sel = ADCIOC_SOFTTS_SET, + .ha_sel = ADCIOC_HA_SET, + .ha_sam = 8, + .do_ena = ADCIOC_DOEOFF_SET, + .ac_ena = ADCIOC_ADACKENOFF_SET, + .dma_ena = ADCIDC_DMAOFF_SET, + .cc_ena = ADCIOC_CCEOFF_SET, + .compare_func_ena = ADCIOC_ACFEOFF_SET, + .range_ena = ADCIOC_ACRENOFF_SET, + .greater_ena = ADCIOC_ACFGTOFF_SET, + .result0 = 0, + .result1 = 0, + }; + + mvf_adc_initiate(0); + mvf_adc_set(0, &feature); + + mvf_adc_initiate(1); + mvf_adc_set(1, &feature); + + while (!adc_ts->stop_touchscreen) + { + /* X-Direction */ + val_x = adc_ts_measure(pdata->gpio_xp, pdata->gpio_xm, 1, 0); + if (val_x < 0) + continue; + + /* Y-Direction */ + val_y = adc_ts_measure(pdata->gpio_yp, pdata->gpio_ym, 0, 0); + if (val_y < 0) + continue; + + /* Touch pressure + * Measure on XP/YM + */ + val_z1 = adc_ts_measure(pdata->gpio_yp, pdata->gpio_xm, 0, 1); + if (val_z1 < 0) + continue; + val_z2 = adc_ts_measure(pdata->gpio_yp, pdata->gpio_xm, 1, 2); + if (val_z2 < 0) + continue; + + /* According to datasheet of our touchscreen, + * resistance on X axis is 400~1200.. + */ + /* Validate signal (avoid calculation using noise) */ + if (val_z1 > 64 && val_x > 64) { + /* Calculate resistance between the plates + * lower resistance means higher pressure */ + int r_x = (1000 * val_x) / MVF_ADC_MAX; + val_p = (r_x * val_z2) / val_z1 - r_x; + } else { + val_p = 2000; + } + + dev_dbg(dev, "Measured values: x: %d, y: %d, z1: %d, z2: %d, " + "p: %d\n", val_x, val_y, val_z1, val_z2, val_p); + + /* + * If touch pressure is too low, stop measuring and reenable + * touch detection + */ + if (val_p > 1800) + break; + + /* Report touch position and sleep for next measurement */ + input_report_abs(adc_ts->ts_input, ABS_X, MVF_ADC_MAX - val_x); + input_report_abs(adc_ts->ts_input, ABS_Y, MVF_ADC_MAX - val_y); + input_report_abs(adc_ts->ts_input, ABS_PRESSURE, 2000 - val_p); + input_report_key(adc_ts->ts_input, BTN_TOUCH, 1); + input_sync(adc_ts->ts_input); + + msleep(10); + } + + /* Report no more touch, reenable touch detection */ + input_report_abs(adc_ts->ts_input, ABS_PRESSURE, 0); + input_report_key(adc_ts->ts_input, BTN_TOUCH, 0); + input_sync(adc_ts->ts_input); + + /* Wait the pull-up to be stable on high */ + adc_ts_enable_touch_detection(adc_ts); + msleep(10); + + /* Reenable IRQ to detect touch */ + enable_irq(adc_ts->pen_irq); + + dev_dbg(dev, "Reenabled touch detection interrupt\n"); +} + +static irqreturn_t adc_tc_touched(int irq, void *dev_id) +{ + struct adc_touch_device *adc_ts = (struct adc_touch_device *)dev_id; + struct device *dev = &adc_ts->pdev->dev; + struct colibri_ts_platform_data *pdata = adc_ts->pdev->dev.platform_data; + + dev_dbg(dev, "Touch detected, start worker thread\n"); + + /* Stop IRQ */ + disable_irq_nosync(irq); + + /* Disable the touch detection plates */ + gpio_set_value(pdata->gpio_ym, 0); + + /* Let the platform mux to GPIO in order to enable Pull-Up on GPIO */ + if (pdata->mux_adc) + pdata->mux_adc(adc_ts->pdev); + + /* Start worker thread */ + queue_work(adc_ts->ts_workqueue, &adc_ts->ts_work); + + return IRQ_HANDLED; +} + +static int adc_ts_open(struct input_dev *dev_input) +{ + int ret; + struct adc_touch_device *adc_ts = input_get_drvdata(dev_input); + struct device *dev = &adc_ts->pdev->dev; + struct colibri_ts_platform_data *pdata = adc_ts->pdev->dev.platform_data; + + dev_dbg(dev, "Input device %s opened, starting touch detection\n", + dev_input->name); + + adc_ts->stop_touchscreen = false; + + /* Initialize GPIOs, leave FETs closed by default */ + gpio_direction_output(pdata->gpio_xp, 1); /* Low active */ + gpio_direction_output(pdata->gpio_xm, 0); /* High active */ + gpio_direction_output(pdata->gpio_yp, 1); /* Low active */ + gpio_direction_output(pdata->gpio_ym, 0); /* High active */ + + /* Mux detection before request IRQ, wait for pull-up to settle */ + adc_ts_enable_touch_detection(adc_ts); + msleep(10); + + adc_ts->pen_irq = gpio_to_irq(pdata->gpio_pen_detect); + if (adc_ts->pen_irq < 0) { + dev_err(dev, "Unable to get IRQ for GPIO %d\n", + pdata->gpio_pen_detect); + return adc_ts->pen_irq; + } + + ret = request_irq(adc_ts->pen_irq, adc_tc_touched, IRQF_TRIGGER_FALLING, + "touch detected", adc_ts); + if (ret < 0) { + dev_err(dev, "Unable to request IRQ %d\n", adc_ts->pen_irq); + return ret; + } + + return 0; +} + +static void adc_ts_close(struct input_dev *dev_input) +{ + struct adc_touch_device *adc_ts = input_get_drvdata(dev_input); + struct device *dev = &adc_ts->pdev->dev; + + free_irq(adc_ts->pen_irq, adc_ts); + + adc_ts->stop_touchscreen = true; + + /* Wait until touchscreen thread finishes any possible remnants. */ + cancel_work_sync(&adc_ts->ts_work); + + dev_dbg(dev, "Input device %s closed, disable touch detection\n", + dev_input->name); +} + + +static int __devinit adc_ts_probe(struct platform_device *pdev) +{ + int ret = 0; + struct device *dev = &pdev->dev; + struct input_dev *input; + struct adc_touch_device *adc_ts; + struct colibri_ts_platform_data *pdata = pdev->dev.platform_data; + + adc_ts = kzalloc(sizeof(struct adc_touch_device), GFP_KERNEL); + if (!adc_ts) { + dev_err(dev, "Failed to allocate TS device!\n"); + return -ENOMEM; + } + + adc_ts->pdev = pdev; + + input = input_allocate_device(); + if (!input) { + dev_err(dev, "Failed to allocate TS input device!\n"); + ret = -ENOMEM; + goto err_input_allocate; + } + + input->name = DRIVER_NAME; + input->id.bustype = BUS_HOST; + input->dev.parent = dev; + input->open = adc_ts_open; + input->close = adc_ts_close; + + __set_bit(EV_ABS, input->evbit); + __set_bit(EV_KEY, input->evbit); + __set_bit(BTN_TOUCH, input->keybit); + input_set_abs_params(input, ABS_X, 0, MVF_ADC_MAX, 0, 0); + input_set_abs_params(input, ABS_Y, 0, MVF_ADC_MAX, 0, 0); + input_set_abs_params(input, ABS_PRESSURE, 0, MVF_ADC_MAX, 0, 0); + + adc_ts->ts_input = input; + input_set_drvdata(input, adc_ts); + ret = input_register_device(input); + if (ret) { + dev_err(dev, "failed to register input device\n"); + goto err; + } + + /* Create workqueue for ADC sampling and calculation */ + INIT_WORK(&adc_ts->ts_work, adc_ts_work); + adc_ts->ts_workqueue = create_singlethread_workqueue("mvf-adc-touch"); + + if (!adc_ts->ts_workqueue) { + dev_err(dev, "failed to create workqueue"); + goto err; + } + + /* Request GPIOs for FETs and touch detection */ + ret = gpio_request(pdata->gpio_xp, "Touchscreen XP"); + if (ret) + goto err; + ret = gpio_request(pdata->gpio_xm, "Touchscreen XM"); + if (ret) + goto err; + ret = gpio_request(pdata->gpio_yp, "Touchscreen YP"); + if (ret) + goto err; + ret = gpio_request(pdata->gpio_ym, "Touchscreen YM"); + if (ret) + goto err; + ret = gpio_request(pdata->gpio_pen_detect, "Pen/Touch detect"); + if (ret) + goto err; + ret = gpio_request(pdata->gpio_pen_detect_pullup, + "Pen/Touch detect pull-up"); + if (ret) + goto err; + + dev_info(dev, "attached driver successfully\n"); + + return 0; +err: + input_free_device(touch->ts_input); + +err_input_allocate: + kfree(adc_ts); + + return ret; +} + +static int __devexit adc_ts_remove(struct platform_device *pdev) +{ + struct adc_touch_device *adc_ts = platform_get_drvdata(pdev); + + input_unregister_device(adc_ts->ts_input); + + destroy_workqueue(adc_ts->ts_workqueue); + kfree(adc_ts->ts_input); + kfree(adc_ts); + + return 0; +} + +static struct platform_driver adc_ts_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = adc_ts_probe, + .remove = __devexit_p(adc_ts_remove), +}; + +static int __init adc_ts_init(void) +{ + int ret; + + ret = platform_driver_register(&adc_ts_driver); + if (ret) + printk(KERN_ERR "%s: failed to add adc touchscreen driver\n", + __func__); + + return ret; +} + +static void __exit adc_ts_exit(void) +{ + platform_driver_unregister(&adc_ts_driver); +} +module_init(adc_ts_init); +module_exit(adc_ts_exit); + +MODULE_AUTHOR("Stefan Agner"); +MODULE_DESCRIPTION("Colibri VF50 Touchscreen driver"); +MODULE_LICENSE("GPL v2"); +MODULE_VERSION(DRV_VERSION); diff --git a/drivers/input/touchscreen/fusion_F0710A.c b/drivers/input/touchscreen/fusion_F0710A.c new file mode 100644 index 000000000000..05bbdf5e8c5b --- /dev/null +++ b/drivers/input/touchscreen/fusion_F0710A.c @@ -0,0 +1,502 @@ +/* + * "fusion_F0710A" touchscreen driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/input.h> +#include <linux/interrupt.h> +#include <linux/platform_device.h> +#include <linux/workqueue.h> +#include <linux/i2c.h> +#include <linux/delay.h> +#include <asm/irq.h> +#include <linux/gpio.h> +#include <linux/input/fusion_F0710A.h> + +#include "fusion_F0710A.h" + +#define DRV_NAME "fusion_F0710A" + + +static struct fusion_F0710A_data fusion_F0710A; + +static unsigned short normal_i2c[] = { fusion_F0710A_I2C_SLAVE_ADDR, I2C_CLIENT_END }; + +//I2C_CLIENT_INSMOD; + +static int fusion_F0710A_write_u8(u8 addr, u8 data) +{ + return i2c_smbus_write_byte_data(fusion_F0710A.client, addr, data); +} + +static int fusion_F0710A_read_u8(u8 addr) +{ + return i2c_smbus_read_byte_data(fusion_F0710A.client, addr); +} + +static int fusion_F0710A_read_block(u8 addr, u8 len, u8 *data) +{ +#if 0 + /* When i2c_smbus_read_i2c_block_data() takes a block length parameter, we can do + * this. lm-sensors lists hints this has been fixed, but I can't tell whether it + * was or will be merged upstream. */ + + return i2c_smbus_read_i2c_block_data(&fusion_F0710A.client, addr, data); +#else + u8 msgbuf0[1] = { addr }; + u16 slave = fusion_F0710A.client->addr; + u16 flags = fusion_F0710A.client->flags; + struct i2c_msg msg[2] = { { slave, flags, 1, msgbuf0 }, + { slave, flags | I2C_M_RD, len, data } + }; + + return i2c_transfer(fusion_F0710A.client->adapter, msg, ARRAY_SIZE(msg)); +#endif +} + + +static int fusion_F0710A_register_input(void) +{ + int ret; + struct input_dev *dev; + + dev = fusion_F0710A.input = input_allocate_device(); + if (dev == NULL) + return -ENOMEM; + + dev->name = "fusion_F0710A"; + + set_bit(EV_KEY, dev->evbit); + set_bit(EV_ABS, dev->evbit); + + input_set_abs_params(dev, ABS_MT_POSITION_X, 0, fusion_F0710A.info.xres-1, 0, 0); + input_set_abs_params(dev, ABS_MT_POSITION_Y, 0, fusion_F0710A.info.yres-1, 0, 0); + input_set_abs_params(dev, ABS_MT_TOUCH_MAJOR, 0, 255, 0, 0); + input_set_abs_params(dev, ABS_MT_WIDTH_MAJOR, 0, 15, 0, 0); + + input_set_abs_params(dev, ABS_X, 0, fusion_F0710A.info.xres-1, 0, 0); + input_set_abs_params(dev, ABS_Y, 0, fusion_F0710A.info.yres-1, 0, 0); + input_set_abs_params(dev, ABS_PRESSURE, 0, 255, 0, 0); + + ret = input_register_device(dev); + if (ret < 0) + goto bail1; + + return 0; + +bail1: + input_free_device(dev); + return ret; +} + +#define WC_RETRY_COUNT 3 +static int fusion_F0710A_write_complete(void) +{ + int ret, i; + + for(i=0; i<WC_RETRY_COUNT; i++) + { + ret = fusion_F0710A_write_u8(fusion_F0710A_SCAN_COMPLETE, 0); + if(ret == 0) + break; + else + dev_err(&fusion_F0710A.client->dev, "Write complete failed(%d): %d\n", i, ret); + } + + return ret; +} + +#define DATA_START fusion_F0710A_DATA_INFO +#define DATA_END fusion_F0710A_SEC_TIDTS +#define DATA_LEN (DATA_END - DATA_START + 1) +#define DATA_OFF(x) ((x) - DATA_START) + +static int fusion_F0710A_read_sensor(void) +{ + int ret; + u8 data[DATA_LEN]; + +#define DATA(x) (data[DATA_OFF(x)]) + /* To ensure data coherency, read the sensor with a single transaction. */ + ret = fusion_F0710A_read_block(DATA_START, DATA_LEN, data); + if (ret < 0) { + dev_err(&fusion_F0710A.client->dev, + "Read block failed: %d\n", ret); + + return ret; + } + + fusion_F0710A.f_num = DATA(fusion_F0710A_DATA_INFO)&0x03; + + fusion_F0710A.y1 = DATA(fusion_F0710A_POS_X1_HI) << 8; + fusion_F0710A.y1 |= DATA(fusion_F0710A_POS_X1_LO); + fusion_F0710A.x1 = DATA(fusion_F0710A_POS_Y1_HI) << 8; + fusion_F0710A.x1 |= DATA(fusion_F0710A_POS_Y1_LO); + fusion_F0710A.z1 = DATA(fusion_F0710A_FIR_PRESS); + fusion_F0710A.tip1 = DATA(fusion_F0710A_FIR_TIDTS)&0x0f; + fusion_F0710A.tid1 = (DATA(fusion_F0710A_FIR_TIDTS)&0xf0)>>4; + + + fusion_F0710A.y2 = DATA(fusion_F0710A_POS_X2_HI) << 8; + fusion_F0710A.y2 |= DATA(fusion_F0710A_POS_X2_LO); + fusion_F0710A.x2 = DATA(fusion_F0710A_POS_Y2_HI) << 8; + fusion_F0710A.x2 |= DATA(fusion_F0710A_POS_Y2_LO); + fusion_F0710A.z2 = DATA(fusion_F0710A_SEC_PRESS); + fusion_F0710A.tip2 = DATA(fusion_F0710A_SEC_TIDTS)&0x0f; + fusion_F0710A.tid2 =(DATA(fusion_F0710A_SEC_TIDTS)&0xf0)>>4; +#undef DATA + + return 0; +} + +#define val_cut_max(x, max, reverse) \ +do \ +{ \ + if(x > max) \ + x = max; \ + if(reverse) \ + x = (max) - (x); \ +} \ +while(0) + +static void fusion_F0710A_wq(struct work_struct *work) +{ + struct input_dev *dev = fusion_F0710A.input; + int save_points = 0; + int x1 = 0, y1 = 0, z1 = 0, x2 = 0, y2 = 0, z2 = 0; + + if (fusion_F0710A_read_sensor() < 0) + goto restore_irq; + +#ifdef DEBUG + printk(KERN_DEBUG "tip1, tid1, x1, y1, z1 (%x,%x,%d,%d,%d); tip2, tid2, x2, y2, z2 (%x,%x,%d,%d,%d)\n", + fusion_F0710A.tip1, fusion_F0710A.tid1, fusion_F0710A.x1, fusion_F0710A.y1, fusion_F0710A.z1, + fusion_F0710A.tip2, fusion_F0710A.tid2, fusion_F0710A.x2, fusion_F0710A.y2, fusion_F0710A.z2); +#endif /* DEBUG */ + + val_cut_max(fusion_F0710A.x1, fusion_F0710A.info.xres-1, fusion_F0710A.info.xy_reverse); + val_cut_max(fusion_F0710A.y1, fusion_F0710A.info.yres-1, fusion_F0710A.info.xy_reverse); + val_cut_max(fusion_F0710A.x2, fusion_F0710A.info.xres-1, fusion_F0710A.info.xy_reverse); + val_cut_max(fusion_F0710A.y2, fusion_F0710A.info.yres-1, fusion_F0710A.info.xy_reverse); + + if(fusion_F0710A.tip1 == 1) + { + if(fusion_F0710A.tid1 == 1) + { + /* first point */ + x1 = fusion_F0710A.x1; + y1 = fusion_F0710A.y1; + z1 = fusion_F0710A.z1; + save_points |= fusion_F0710A_SAVE_PT1; + } + else if(fusion_F0710A.tid1 == 2) + { + /* second point ABS_DISTANCE second point pressure, BTN_2 second point touch */ + x2 = fusion_F0710A.x1; + y2 = fusion_F0710A.y1; + z2 = fusion_F0710A.z1; + save_points |= fusion_F0710A_SAVE_PT2; + } + } + + if(fusion_F0710A.tip2 == 1) + { + if(fusion_F0710A.tid2 == 2) + { + /* second point ABS_DISTANCE second point pressure, BTN_2 second point touch */ + x2 = fusion_F0710A.x2; + y2 = fusion_F0710A.y2; + z2 = fusion_F0710A.z2; + save_points |= fusion_F0710A_SAVE_PT2; + } + else if(fusion_F0710A.tid2 == 1)/* maybe this will never happen */ + { + /* first point */ + x1 = fusion_F0710A.x2; + y1 = fusion_F0710A.y2; + z1 = fusion_F0710A.z2; + save_points |= fusion_F0710A_SAVE_PT1; + } + } + + input_report_abs(dev, ABS_MT_TOUCH_MAJOR, z1); + input_report_abs(dev, ABS_MT_WIDTH_MAJOR, 1); + input_report_abs(dev, ABS_MT_POSITION_X, x1); + input_report_abs(dev, ABS_MT_POSITION_Y, y1); + input_mt_sync(dev); + input_report_abs(dev, ABS_MT_TOUCH_MAJOR, z2); + input_report_abs(dev, ABS_MT_WIDTH_MAJOR, 2); + input_report_abs(dev, ABS_MT_POSITION_X, x2); + input_report_abs(dev, ABS_MT_POSITION_Y, y2); + input_mt_sync(dev); + + input_report_abs(dev, ABS_X, x1); + input_report_abs(dev, ABS_Y, y1); + input_report_abs(dev, ABS_PRESSURE, z1); + input_report_key(dev, BTN_TOUCH, fusion_F0710A.tip1); + + input_sync(dev); + +restore_irq: + enable_irq(fusion_F0710A.client->irq); + + /* Clear fusion_F0710A interrupt */ + fusion_F0710A_write_complete(); +} +static DECLARE_WORK(fusion_F0710A_work, fusion_F0710A_wq); + +static irqreturn_t fusion_F0710A_interrupt(int irq, void *dev_id) +{ + disable_irq_nosync(fusion_F0710A.client->irq); + + queue_work(fusion_F0710A.workq, &fusion_F0710A_work); + + return IRQ_HANDLED; +} + +const static u8* g_ver_product[4] = { + "10Z8", "70Z7", "43Z6", "" +}; + +static int fusion_F0710A_probe(struct i2c_client *i2c, const struct i2c_device_id *id) +{ + struct fusion_f0710a_init_data *pdata = i2c->dev.platform_data; + int ret; + u8 ver_product, ver_id; + u32 version; + + if (pdata == NULL) + { + dev_err(&i2c->dev, "No platform data for Fusion driver\n"); + return -ENODEV; + } + + /* Request pinmuxing, if necessary */ + if (pdata->pinmux_fusion_pins != NULL) + { + ret = pdata->pinmux_fusion_pins(); + if (ret < 0) { + dev_err(&i2c->dev, "muxing GPIOs failed\n"); + return -ENODEV; + } + } + + if ((gpio_request(pdata->gpio_int, "SO-DIMM 28 (Iris X16-38 Pen)") == 0) && + (gpio_direction_input(pdata->gpio_int) == 0)) { + gpio_export(pdata->gpio_int, 0); + } else { + dev_warn(&i2c->dev, "Could not obtain GPIO for Fusion pen down\n"); + return -ENODEV; + } + + if ((gpio_request(pdata->gpio_reset, "SO-DIMM 30 (Iris X16-39 RST)") == 0) && + (gpio_direction_output(pdata->gpio_reset, 1) == 0)) { + + /* Generate a 0 => 1 edge explicitly, and wait for startup... */ + gpio_set_value(pdata->gpio_reset, 0); + msleep(10); + gpio_set_value(pdata->gpio_reset, 1); + /* Wait for startup (up to 125ms according to datasheet) */ + msleep(125); + + gpio_export(pdata->gpio_reset, 0); + } else { + dev_warn(&i2c->dev, "Could not obtain GPIO for Fusion reset\n"); + ret = -ENODEV; + goto bail0; + } + + /* Use Pen Down GPIO as sampling interrupt */ + i2c->irq = gpio_to_irq(pdata->gpio_int); + + if(!i2c->irq) + { + dev_err(&i2c->dev, "fusion_F0710A irq < 0 \n"); + ret = -ENOMEM; + goto bail1; + } + + /* Attach the I2C client */ + fusion_F0710A.client = i2c; + i2c_set_clientdata(i2c, &fusion_F0710A); + + dev_info(&i2c->dev, "Touchscreen registered with bus id (%d) with slave address 0x%x\n", + i2c_adapter_id(fusion_F0710A.client->adapter), fusion_F0710A.client->addr); + + /* Read out a lot of registers */ + ret = fusion_F0710A_read_u8(fusion_F0710A_VIESION_INFO_LO); + if (ret < 0) { + dev_err(&i2c->dev, "query failed: %d\n", ret); + goto bail1; + } + ver_product = (((u8)ret) & 0xc0) >> 6; + version = (10 + ((((u32)ret)&0x30) >> 4)) * 100000; + version += (((u32)ret)&0xf) * 1000; + /* Read out a lot of registers */ + ret = fusion_F0710A_read_u8(fusion_F0710A_VIESION_INFO); + if (ret < 0) { + dev_err(&i2c->dev, "query failed: %d\n", ret); + goto bail1; + } + ver_id = ((u8)(ret) & 0x6) >> 1; + version += ((((u32)ret) & 0xf8) >> 3) * 10; + version += (((u32)ret) & 0x1) + 1; /* 0 is build 1, 1 is build 2 */ + dev_info(&i2c->dev, "version product %s(%d)\n", g_ver_product[ver_product] ,ver_product); + dev_info(&i2c->dev, "version id %s(%d)\n", ver_id ? "1.4" : "1.0", ver_id); + dev_info(&i2c->dev, "version series (%d)\n", version); + + switch(ver_product) + { + case fusion_F0710A_VIESION_07: /* 7 inch */ + fusion_F0710A.info.xres = fusion_F0710A07_XMAX; + fusion_F0710A.info.yres = fusion_F0710A07_YMAX; + fusion_F0710A.info.xy_reverse = fusion_F0710A07_REV; + break; + case fusion_F0710A_VIESION_43: /* 4.3 inch */ + fusion_F0710A.info.xres = fusion_F0710A43_XMAX; + fusion_F0710A.info.yres = fusion_F0710A43_YMAX; + fusion_F0710A.info.xy_reverse = fusion_F0710A43_REV; + break; + default: /* fusion_F0710A_VIESION_10 10 inch */ + fusion_F0710A.info.xres = fusion_F0710A10_XMAX; + fusion_F0710A.info.yres = fusion_F0710A10_YMAX; + fusion_F0710A.info.xy_reverse = fusion_F0710A10_REV; + break; + } + + /* Register the input device. */ + ret = fusion_F0710A_register_input(); + if (ret < 0) { + dev_err(&i2c->dev, "can't register input: %d\n", ret); + goto bail1; + } + + /* Create a worker thread */ + fusion_F0710A.workq = create_singlethread_workqueue(DRV_NAME); + if (fusion_F0710A.workq == NULL) { + dev_err(&i2c->dev, "can't create work queue\n"); + ret = -ENOMEM; + goto bail2; + } + + + /* Register for the interrupt and enable it. Our handler will + * start getting invoked after this call. */ + ret = request_irq(i2c->irq, fusion_F0710A_interrupt, IRQF_TRIGGER_RISING, + i2c->name, &fusion_F0710A); + if (ret < 0) { + dev_err(&i2c->dev, "can't get irq %d: %d\n", i2c->irq, ret); + goto bail3; + } + /* clear the irq first */ + ret = fusion_F0710A_write_u8(fusion_F0710A_SCAN_COMPLETE, 0); + if (ret < 0) { + dev_err(&i2c->dev, "Clear irq failed: %d\n", ret); + goto bail4; + } + + return 0; + +bail4: + free_irq(i2c->irq, &fusion_F0710A); + +bail3: + destroy_workqueue(fusion_F0710A.workq); + fusion_F0710A.workq = NULL; + +bail2: + input_unregister_device(fusion_F0710A.input); +bail1: + gpio_free(pdata->gpio_reset); +bail0: + gpio_free(pdata->gpio_int); + + return ret; +} + +#ifdef CONFIG_PM_SLEEP +static int fusion_F0710A_suspend(struct device *dev) +{ + struct i2c_client *i2c = to_i2c_client(dev); + disable_irq(i2c->irq); + flush_workqueue(fusion_F0710A.workq); + + return 0; +} + +static int fusion_F0710A_resume(struct device *dev) +{ + struct i2c_client *i2c = to_i2c_client(dev); + enable_irq(i2c->irq); + + return 0; +} +#endif + +static int fusion_F0710A_remove(struct i2c_client *i2c) +{ + struct fusion_f0710a_init_data *pdata = i2c->dev.platform_data; + + gpio_free(pdata->gpio_int); + gpio_free(pdata->gpio_reset); + destroy_workqueue(fusion_F0710A.workq); + free_irq(i2c->irq, &fusion_F0710A); + input_unregister_device(fusion_F0710A.input); + i2c_set_clientdata(i2c, NULL); + + dev_info(&i2c->dev, "driver removed\n"); + + return 0; +} + +static struct i2c_device_id fusion_F0710A_id[] = { + {"fusion_F0710A", 0}, + {}, +}; + +static const struct dev_pm_ops fusion_F0710A_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(fusion_F0710A_suspend, fusion_F0710A_resume) +}; + +static struct i2c_driver fusion_F0710A_i2c_drv = { + .driver = { + .owner = THIS_MODULE, + .name = DRV_NAME, + .pm = &fusion_F0710A_pm_ops, + }, + .probe = fusion_F0710A_probe, + .remove = fusion_F0710A_remove, + .id_table = fusion_F0710A_id, + .address_list = normal_i2c, +}; + +static int __init fusion_F0710A_init( void ) +{ + int ret; + + memset(&fusion_F0710A, 0, sizeof(fusion_F0710A)); + + /* Probe for fusion_F0710A on I2C. */ + ret = i2c_add_driver(&fusion_F0710A_i2c_drv); + if (ret < 0) { + printk(KERN_WARNING DRV_NAME " can't add i2c driver: %d\n", ret); + } + + return ret; +} + +static void __exit fusion_F0710A_exit( void ) +{ + i2c_del_driver(&fusion_F0710A_i2c_drv); +} +module_init(fusion_F0710A_init); +module_exit(fusion_F0710A_exit); + +MODULE_DESCRIPTION("fusion_F0710A Touchscreen Driver"); +MODULE_LICENSE("GPL"); + diff --git a/drivers/input/touchscreen/fusion_F0710A.h b/drivers/input/touchscreen/fusion_F0710A.h new file mode 100644 index 000000000000..85f8210345a9 --- /dev/null +++ b/drivers/input/touchscreen/fusion_F0710A.h @@ -0,0 +1,87 @@ +/* + * "fusion_F0710A" touchscreen driver + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +/* I2C slave address */ +#define fusion_F0710A_I2C_SLAVE_ADDR 0x10 + +/* I2C registers */ +#define fusion_F0710A_DATA_INFO 0x00 + +/* First Point*/ +#define fusion_F0710A_POS_X1_HI 0x01 /* 16-bit register, MSB */ +#define fusion_F0710A_POS_X1_LO 0x02 /* 16-bit register, LSB */ +#define fusion_F0710A_POS_Y1_HI 0x03 /* 16-bit register, MSB */ +#define fusion_F0710A_POS_Y1_LO 0x04 /* 16-bit register, LSB */ +#define fusion_F0710A_FIR_PRESS 0X05 +#define fusion_F0710A_FIR_TIDTS 0X06 + +/* Second Point */ +#define fusion_F0710A_POS_X2_HI 0x07 /* 16-bit register, MSB */ +#define fusion_F0710A_POS_X2_LO 0x08 /* 16-bit register, LSB */ +#define fusion_F0710A_POS_Y2_HI 0x09 /* 16-bit register, MSB */ +#define fusion_F0710A_POS_Y2_LO 0x0A /* 16-bit register, LSB */ +#define fusion_F0710A_SEC_PRESS 0x0B +#define fusion_F0710A_SEC_TIDTS 0x0C + +#define fusion_F0710A_VIESION_INFO_LO 0X0E +#define fusion_F0710A_VIESION_INFO 0X0F + +#define fusion_F0710A_RESET 0x10 +#define fusion_F0710A_SCAN_COMPLETE 0x11 + + +#define fusion_F0710A_VIESION_10 0 +#define fusion_F0710A_VIESION_07 1 +#define fusion_F0710A_VIESION_43 2 + +/* fusion_F0710A 10 inch panel */ +#define fusion_F0710A10_XMAX 2275 +#define fusion_F0710A10_YMAX 1275 +#define fusion_F0710A10_REV 1 + +/* fusion_F0710A 7 inch panel */ +#define fusion_F0710A07_XMAX 1500 +#define fusion_F0710A07_YMAX 900 +#define fusion_F0710A07_REV 0 + +/* fusion_F0710A 4.3 inch panel */ +#define fusion_F0710A43_XMAX 900 +#define fusion_F0710A43_YMAX 500 +#define fusion_F0710A43_REV 0 + +#define fusion_F0710A_SAVE_PT1 0x1 +#define fusion_F0710A_SAVE_PT2 0x2 + + + +/* fusion_F0710A touch screen information */ +struct fusion_F0710A_info { + int xres; /* x resolution */ + int yres; /* y resolution */ + int xy_reverse; /* if need reverse in the x,y value x=xres-1-x, y=yres-1-y*/ +}; + +struct fusion_F0710A_data { + struct fusion_F0710A_info info; + struct i2c_client *client; + struct workqueue_struct *workq; + struct input_dev *input; + u16 x1; + u16 y1; + u8 z1; + u8 tip1; + u8 tid1; + u16 x2; + u16 y2; + u8 z2; + u8 tip2; + u8 tid2; + u8 f_num; + u8 save_points; +}; + diff --git a/drivers/misc/mvf_adc.c b/drivers/misc/mvf_adc.c index 63e0f95804f9..662d76667bdc 100644 --- a/drivers/misc/mvf_adc.c +++ b/drivers/misc/mvf_adc.c @@ -1,4 +1,5 @@ /* Copyright 2012 Freescale Semiconductor, Inc. + * Copyright 2013 Toradex AG * * Freescale Faraday Quad ADC driver * @@ -24,13 +25,20 @@ #include <linux/mvf_adc.h> #include <linux/device.h> #include <linux/cdev.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> #define DRIVER_NAME "mvf-adc" -#define DRV_VERSION "1.0" +#define DRV_VERSION "1.2" + +#define MVF_ADC_MAX_DEVICES 4 +#define MVF_ADC_MAX ((1 << 12) - 1) /* * wait_event_interruptible(wait_queue_head_t suspendq, int suspend_flag) */ +static struct class *adc_class; +static int mvf_adc_major; struct adc_client { struct platform_device *pdev; @@ -46,27 +54,29 @@ static DECLARE_COMPLETION(adc_tsi); struct adc_device { struct platform_device *pdev; - struct platform_device *owner; + struct device *hwmon_dev; struct clk *clk; struct adc_client *cur; void __iomem *regs; spinlock_t lock; + struct device *dev; + struct cdev cdev; + int irq; }; +static struct adc_device *adc_devices[MVF_ADC_MAX_DEVICES]; + struct data { unsigned int res_value; bool flag; }; -struct data data_array[7]; - -static struct adc_device *adc_dev; +struct data data_array[32]; #define adc_dbg(_adc, msg...) dev_dbg(&(_adc)->pdev->dev, msg) -static int res_proc(void); struct adc_client *adc_register(struct platform_device *pdev, unsigned char channel); @@ -95,32 +105,7 @@ static void adc_try(struct adc_device *adc) } } -/* channel and sample */ -int adc_start(struct adc_client *client, - unsigned int channel, unsigned int nr_samples) -{ - struct adc_device *adc = adc_dev; - unsigned long flags; - - if (!adc) { - printk(KERN_ERR "%s: failed to find adc\n", __func__); - return -EINVAL; - } - - - spin_lock_irqsave(&adc->lock, flags); - - client->channel = channel; - - if (!adc->cur) - adc_try(adc_dev); - - spin_unlock_irqrestore(&adc->lock, flags); - - return 0; -} - -int adc_initiate(struct adc_device *adc_dev) +static int adc_initiate(struct adc_device *adc_dev) { unsigned long reg, tmp, pin; struct adc_device *adc = adc_dev; @@ -176,7 +161,6 @@ static int adc_set(struct adc_device *adc_dev, struct adc_feature *adc_fea) default: return -EINVAL; } - writel(con, adc->regs+ADC_CFG); break; @@ -200,7 +184,6 @@ static int adc_set(struct adc_device *adc_dev, struct adc_feature *adc_fea) default: return -EINVAL; } - writel(con, adc->regs+ADC_CFG); break; @@ -224,7 +207,6 @@ static int adc_set(struct adc_device *adc_dev, struct adc_feature *adc_fea) default: return -EINVAL; } - writel(con, adc->regs+ADC_CFG); break; default: @@ -249,7 +231,6 @@ static int adc_set(struct adc_device *adc_dev, struct adc_feature *adc_fea) adc_fea->res_mode); return -EINVAL; } - writel(con, adc->regs+ADC_CFG); /* Defines the sample time duration */ /* clear 4, 9-8 */ @@ -284,20 +265,17 @@ static int adc_set(struct adc_device *adc_dev, struct adc_feature *adc_fea) adc_fea->sam_time); return -EINVAL; } - writel(con, adc->regs+ADC_CFG); /* low power configuration */ /* */ switch (adc_fea->lp_con) { case ADCIOC_LPOFF_SET: con &= ~CLEAR_ADLPC_BIT; - writel(con, adc->regs+ADC_CFG); break; case ADCIOC_LPON_SET: con &= ~CLEAR_ADLPC_BIT; con |= ADLPC_EN; - writel(con, adc->regs+ADC_CFG); break; default: return -EINVAL; @@ -308,34 +286,28 @@ static int adc_set(struct adc_device *adc_dev, struct adc_feature *adc_fea) case ADCIOC_HSON_SET: con &= ~CLEAR_ADHSC_BIT; con |= ADHSC_EN; - writel(con, adc->regs+ADC_CFG); break; case ADCIOC_HSOFF_SET: con &= ~CLEAR_ADHSC_BIT; - writel(con, adc->regs+ADC_CFG); break; default: return -EINVAL; } - /* voltage reference*/ switch (adc_fea->vol_ref) { case ADCIOC_VR_VREF_SET: con &= ~CLEAR_REFSEL_BIT; - writel(con, adc->regs+ADC_CFG); break; case ADCIOC_VR_VALT_SET: con &= ~CLEAR_REFSEL_BIT; con |= REFSEL_VALT; - writel(con, adc->regs+ADC_CFG); break; case ADCIOC_VR_VBG_SET: con &= ~CLEAR_REFSEL_BIT; con |= REFSEL_VBG; - writel(con, adc->regs+ADC_CFG); break; default: return -EINVAL; @@ -345,13 +317,11 @@ static int adc_set(struct adc_device *adc_dev, struct adc_feature *adc_fea) switch (adc_fea->tri_sel) { case ADCIOC_SOFTTS_SET: con &= ~CLEAR_ADTRG_BIT; - writel(con, adc->regs+ADC_CFG); break; case ADCIOC_HARDTS_SET: con &= ~CLEAR_ADTRG_BIT; con |= ADTRG_HARD; - writel(con, adc->regs+ADC_CFG); break; default: return -EINVAL; @@ -360,8 +330,8 @@ static int adc_set(struct adc_device *adc_dev, struct adc_feature *adc_fea) /* hardware average select */ switch (adc_fea->ha_sel) { case ADCIOC_HA_DIS: + con &= ~CLEAR_AVGS_BIT; res &= ~CLEAR_AVGE_BIT; - writel(con, adc->regs+ADC_GC); break; case ADCIOC_HA_SET: @@ -383,8 +353,6 @@ static int adc_set(struct adc_device *adc_dev, struct adc_feature *adc_fea) } res &= ~CLEAR_AVGE_BIT; res |= AVGEN; - writel(con, adc->regs+ADC_CFG); - writel(res, adc->regs+ADC_GC); break; default: @@ -394,13 +362,11 @@ static int adc_set(struct adc_device *adc_dev, struct adc_feature *adc_fea) /* data overwrite enable */ switch (adc_fea->do_ena) { case ADCIOC_DOEON_SET: - con &= ~CLEAR_OVWREN_BIT; - writel(con, adc->regs+ADC_CFG); + con |= OVWREN; break; case ADCIOC_DOEOFF_SET: - con |= OVWREN; - writel(con, adc->regs+ADC_CFG); + con &= ~CLEAR_OVWREN_BIT; break; default: return -EINVAL; @@ -410,13 +376,11 @@ static int adc_set(struct adc_device *adc_dev, struct adc_feature *adc_fea) switch (adc_fea->ac_ena) { case ADCIOC_ADACKENON_SET: res &= ~CLEAR_ADACKEN_BIT; - writel(res, adc->regs+ADC_GC); + res |= ADACKEN; break; case ADCIOC_ADACKENOFF_SET: res &= ~CLEAR_ADACKEN_BIT; - res |= ADACKEN; - writel(res, adc->regs+ADC_GC); break; default: return -EINVAL; @@ -426,13 +390,11 @@ static int adc_set(struct adc_device *adc_dev, struct adc_feature *adc_fea) switch (adc_fea->dma_ena) { case ADCIDC_DMAON_SET: res &= ~CLEAR_DMAEN_BIT; - writel(res, adc->regs+ADC_GC); + res |= DMAEN; break; case ADCIDC_DMAOFF_SET: res &= ~CLEAR_DMAEN_BIT; - res |= DMAEN; - writel(res, adc->regs+ADC_GC); break; default: return -EINVAL; @@ -442,13 +404,11 @@ static int adc_set(struct adc_device *adc_dev, struct adc_feature *adc_fea) switch (adc_fea->cc_ena) { case ADCIOC_CCEOFF_SET: res &= ~CLEAR_ADCO_BIT; - writel(res, adc->regs+ADC_GC); break; case ADCIOC_CCEON_SET: res &= ~CLEAR_ADCO_BIT; res |= ADCON; - writel(res, adc->regs+ADC_GC); break; default: return -EINVAL; @@ -459,12 +419,10 @@ static int adc_set(struct adc_device *adc_dev, struct adc_feature *adc_fea) case ADCIOC_ACFEON_SET: res &= ~CLEAR_ACFE_BIT; res |= ACFE; - writel(res, adc->regs+ADC_GC); break; case ADCIOC_ACFEOFF_SET: res &= ~CLEAR_ACFE_BIT; - writel(res, adc->regs+ADC_GC); break; default: return -EINVAL; @@ -475,12 +433,10 @@ static int adc_set(struct adc_device *adc_dev, struct adc_feature *adc_fea) case ADCIOC_ACFGTON_SET: res &= ~CLEAR_ACFGT_BIT; res |= ACFGT; - writel(res, adc->regs+ADC_GC); break; case ADCIOC_ACFGTOFF_SET: res &= ~CLEAR_ACFGT_BIT; - writel(res, adc->regs+ADC_GC); break; default: return -EINVAL; @@ -491,22 +447,175 @@ static int adc_set(struct adc_device *adc_dev, struct adc_feature *adc_fea) case ADCIOC_ACRENON_SET: res &= ~CLEAR_ACREN_BIT; res |= ACREN; - writel(res, adc->regs+ADC_GC); break; case ADCIOC_ACRENOFF_SET: res &= ~CLEAR_ACREN_BIT; - writel(res, adc->regs+ADC_GC); break; default: return -ENOTTY; } + + /* write register once */ + writel(con, adc->regs+ADC_CFG); + writel(res, adc->regs+ADC_GC); + + return 0; +} + +static int adc_convert_wait(struct adc_device *adc_dev, unsigned char channel) +{ + INIT_COMPLETION(adc_tsi); + adc_try(adc_dev); + wait_for_completion(&adc_tsi); + + if (!data_array[channel].flag) + return -EINVAL; + + data_array[channel].flag = 0; + return data_array[channel].res_value; +} + +/** + * mvf_adc_initiate - Initiate a given ADC converter + * + * @adc: ADC block to initiate + */ +int mvf_adc_initiate(unsigned int adc) +{ + return adc_initiate(adc_devices[adc]); +} +EXPORT_SYMBOL(mvf_adc_initiate); + +/** + * mvf_adc_set - Configure a given ADC converter + * + * @adc: ADC block to configure + * @adc_fea: Features to enable + * + * Returns zero on success, error number otherwise + */ +int mvf_adc_set(unsigned int adc, struct adc_feature *adc_fea) +{ + return adc_set(adc_devices[adc], adc_fea); +} +EXPORT_SYMBOL(mvf_adc_set); + +/** + * mvf_adc_register_and_convert - Register a client and start a convertion + * + * @adc: ADC block + * @channel: Channel to convert + * + * Returns converted value or error code + */ +int mvf_adc_register_and_convert(unsigned int adc, unsigned char channel) +{ + struct adc_client *client; + int result; + + /* Register client... */ + client = adc_register(adc_devices[adc]->pdev, channel); + if (!client) + return -ENOMEM; + + /* Start convertion */ + result = adc_convert_wait(adc_devices[adc], channel); + + /* Free client */ + kfree(client); + + return result; +} +EXPORT_SYMBOL(mvf_adc_register_and_convert); + +/* Temperature sensor (hwmon) */ + +static ssize_t adc_show_temp(struct device *dev, + struct device_attribute *dev_attr, char *buf) +{ + struct adc_device *adc_dev = dev_get_drvdata(dev); + struct adc_client *client; + unsigned char channel = 26; + int temperature; + int ret; + + struct adc_feature feature = { + .channel = ADC26, + .clk_sel = ADCIOC_BUSCLK_SET, + .clk_div_num = 1, + .res_mode = 12, + .sam_time = 6, + .lp_con = ADCIOC_LPOFF_SET, + .hs_oper = ADCIOC_HSOFF_SET, + .vol_ref = ADCIOC_VR_VREF_SET, + .tri_sel = ADCIOC_SOFTTS_SET, + .ha_sel = ADCIOC_HA_SET, + .ha_sam = 8, + .do_ena = ADCIOC_DOEOFF_SET, + .ac_ena = ADCIOC_ADACKENOFF_SET, + .dma_ena = ADCIDC_DMAOFF_SET, + .cc_ena = ADCIOC_CCEOFF_SET, + .compare_func_ena = ADCIOC_ACFEOFF_SET, + .range_ena = ADCIOC_ACRENOFF_SET, + .greater_ena = ADCIOC_ACFGTOFF_SET, + .result0 = 0, + .result1 = 0, + }; + + /* Initialize device */ + adc_initiate(adc_dev); + ret = adc_set(adc_dev, &feature); + if (ret) + return ret; + + /* Register client... */ + client = adc_register(adc_dev->pdev, channel); + if (!client) + return -ENOMEM; + + /* Do the ADC convertion of the temperature channel */ + temperature = adc_convert_wait(adc_dev, channel); + + /* + * Calculate in degree celsius times 1000) + * Using sensor slope of 1.84 mV/°C and + * V at 25°C of 696mv + */ + temperature = 25000 - (temperature - 864) * 1000000 / 1840; + + /* Free client */ + kfree(client); + + return sprintf(buf, "%d\n", temperature); +} + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, adc_show_temp, NULL, 0); + +static struct attribute *mvf_adc_attributes[] = { + &sensor_dev_attr_temp1_input.dev_attr.attr, + NULL +}; + +static const struct attribute_group mvf_adc_group = { + .attrs = mvf_adc_attributes, +}; + + +static int adc_open(struct inode *inode, struct file *file) +{ + struct adc_device *dev = container_of(inode->i_cdev, + struct adc_device, cdev); + + file->private_data = dev; + return 0; } static long adc_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { + struct adc_device *adc_dev = file->private_data; void __user *argp = (void __user *)arg; struct adc_feature feature; int channel; @@ -515,18 +624,19 @@ static long adc_ioctl(struct file *file, unsigned int cmd, return -ENOTTY; if (copy_from_user(&feature, (struct adc_feature *)argp, - sizeof(feature))) { + sizeof(feature))) return -EFAULT; - } + + if (feature.channel > 31) + return -EINVAL; switch (cmd) { case ADC_INIT: - adc_initiate(adc_dev); + return adc_initiate(adc_dev); break; case ADC_CONFIGURATION: - - adc_set(adc_dev, &feature); + return adc_set(adc_dev, &feature); break; case ADC_REG_CLIENT: @@ -535,13 +645,8 @@ static long adc_ioctl(struct file *file, unsigned int cmd, break; case ADC_CONVERT: - INIT_COMPLETION(adc_tsi); - adc_try(adc_dev); - wait_for_completion_interruptible(&adc_tsi); - if (data_array[feature.channel].flag) { - feature.result0 = data_array[feature.channel].res_value; - data_array[feature.channel].flag = 0; - } + feature.result0 = adc_convert_wait(adc_dev, feature.channel); + if (copy_to_user((struct adc_feature *)argp, &feature, sizeof(feature))) return -EFAULT; @@ -581,26 +686,6 @@ struct adc_client *adc_register(struct platform_device *pdev, return client; } - -/*result process */ -static int res_proc(void) -{ - int con, res; - struct adc_device *adc = adc_dev; - con = readl(adc->regs + ADC_CFG); - - if ((con & (1 << 2)) == 0) { - if ((con & (1 << 3)) == 1) - res = (0xFFF & readl(adc->regs + ADC_R0)); - else - res = (0xFF & readl(adc->regs + ADC_R0)); - } else - res = (0x3FF & readl(adc->regs + ADC_R0)); - - return readl(adc->regs + ADC_R0); - return res; -} - static irqreturn_t adc_irq(int irq, void *pw) { int coco; @@ -614,7 +699,8 @@ static irqreturn_t adc_irq(int irq, void *pw) coco = readl(adc->regs + ADC_HS); if (coco & 1) { - data_array[client->channel].res_value = res_proc(); + data_array[client->channel].res_value = + readl(adc->regs + ADC_R0); data_array[client->channel].flag = 1; complete(&adc_tsi); } @@ -625,8 +711,8 @@ exit: static const struct file_operations adc_fops = { .owner = THIS_MODULE, + .open = adc_open, .unlocked_ioctl = adc_ioctl, - .open = NULL, .read = NULL, }; @@ -636,11 +722,8 @@ static int __devinit adc_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct adc_device *adc; struct resource *regs; - struct cdev *adc_cdev; - static struct class *adc_class; + dev_t devt; int ret; - dev_t id; - adc = kzalloc(sizeof(struct adc_device), GFP_KERNEL); if (adc == NULL) { @@ -686,34 +769,52 @@ static int __devinit adc_probe(struct platform_device *pdev) goto err_clk; } - /* Obtain device numbers and register char device */ - ret = alloc_chrdev_region(&id, 0, 1, "mvf-adc"); + /* clk enable */ + clk_enable(adc->clk); + + /* Save device structure by Platform device ID for touch */ + adc_devices[pdev->id] = adc; + + /* Register temperature sensor */ + ret = sysfs_create_group(&pdev->dev.kobj, &mvf_adc_group); if (ret < 0) - return ret; + goto err_clk; + + adc->hwmon_dev = hwmon_device_register(&pdev->dev); + if (IS_ERR(adc->hwmon_dev)) { + ret = PTR_ERR(adc->hwmon_dev); + dev_err(dev, "class registration failed (%d)\n", ret); + goto err_sysfs; + } - adc_cdev = cdev_alloc(); - adc_cdev->ops = &adc_fops; - adc_cdev->owner = THIS_MODULE; - ret = cdev_add(adc_cdev, id, 1); + /* Create character device for ADC */ + cdev_init(&adc->cdev, &adc_fops); + adc->cdev.owner = THIS_MODULE; + devt = MKDEV(mvf_adc_major, pdev->id); + ret = cdev_add(&adc->cdev, devt, 1); if (ret < 0) - return ret; + goto err_sysfs; - adc_class = class_create(THIS_MODULE, "mvf-adc.0"); - if (IS_ERR(adc_class)) - return -1; + adc->dev = device_create(adc_class, &pdev->dev, devt, + NULL, "mvf-adc.%d", pdev->id); + if (IS_ERR(adc->dev)) { + dev_err(dev, "failed to create device\n"); + goto err_cdev; + } - device_create(adc_class, NULL, id, NULL, "mvf-adc.0"); - /* clk enable */ - clk_enable(adc->clk); /* Associated structures */ platform_set_drvdata(pdev, adc); - adc_dev = adc; - dev_info(dev, "attached adc driver\n"); return 0; +err_cdev: + cdev_del(&adc->cdev); + +err_sysfs: + sysfs_remove_group(&pdev->dev.kobj, &mvf_adc_group); + err_clk: clk_put(adc->clk); @@ -728,12 +829,22 @@ err_alloc: static int __devexit adc_remove(struct platform_device *pdev) { + struct device *dev = &pdev->dev; struct adc_device *adc = platform_get_drvdata(pdev); + dev_info(dev, "remove adc driver\n"); + + hwmon_device_unregister(adc->hwmon_dev); + sysfs_remove_group(&pdev->dev.kobj, &mvf_adc_group); + + device_destroy(adc_class, adc->dev->devt); + cdev_del(&adc->cdev); + iounmap(adc->regs); free_irq(adc->irq, adc); clk_disable(adc->clk); clk_put(adc->clk); + adc_devices[pdev->id] = NULL; kfree(adc); return 0; @@ -751,21 +862,45 @@ static struct platform_driver adc_driver = { static int __init adc_init(void) { int ret; + dev_t dev; + + adc_class = class_create(THIS_MODULE, "mvf-adc"); + if (IS_ERR(adc_class)) { + ret = PTR_ERR(adc_class); + printk(KERN_ERR "%s: can't register mvf-adc class\n",__func__); + goto err; + } + + /* Obtain device numbers and register char device */ + ret = alloc_chrdev_region(&dev, 0, MVF_ADC_MAX_DEVICES, "mvf-adc"); + if (ret) + { + printk(KERN_ERR "%s: can't register character device\n", + __func__); + goto err_class; + } + mvf_adc_major = MAJOR(dev); + ret = platform_driver_register(&adc_driver); if (ret) printk(KERN_ERR "%s: failed to add adc driver\n", __func__); + return 0; +err_class: + class_destroy(adc_class); +err: return ret; } static void __exit adc_exit(void) { platform_driver_unregister(&adc_driver); + class_destroy(adc_class); } module_init(adc_init); module_exit(adc_exit); -MODULE_AUTHOR("Xiaojun Wang"); +MODULE_AUTHOR("Xiaojun Wang, Stefan Agner"); MODULE_DESCRIPTION("Vybrid ADC driver"); MODULE_LICENSE("GPL v2"); MODULE_VERSION(DRV_VERSION); diff --git a/drivers/mtd/nand/fsl_nfc.c b/drivers/mtd/nand/fsl_nfc.c index f84c785e4383..27562653956a 100644 --- a/drivers/mtd/nand/fsl_nfc.c +++ b/drivers/mtd/nand/fsl_nfc.c @@ -27,6 +27,7 @@ #include <linux/slab.h> #include <asm/fsl_nfc.h> #include <mach/hardware.h> +#include <mach/mxc_nand.h> #ifdef CONFIG_COLDFIRE #include <asm/mcfsim.h> @@ -351,39 +352,20 @@ fsl_nfc_addr_cycle(struct mtd_info *mtd, int column, int page) CONFIG_PAGE_CNT_SHIFT, 0x1); } -/* Control chips select signal on m54418twr board */ static void nfc_select_chip(struct mtd_info *mtd, int chip) { -#ifdef CONFIG_COLDFIRE - if (chip < 0) { - MCF_GPIO_PAR_FBCTL &= (MCF_GPIO_PAR_FBCTL_ALE_MASK & - MCF_GPIO_PAR_FBCTL_TA_MASK); - MCF_GPIO_PAR_FBCTL |= MCF_GPIO_PAR_FBCTL_ALE_FB_TS | - MCF_GPIO_PAR_FBCTL_TA_TA; - - MCF_GPIO_PAR_BE = - MCF_GPIO_PAR_BE_BE3_BE3 | MCF_GPIO_PAR_BE_BE2_BE2 | - MCF_GPIO_PAR_BE_BE1_BE1 | MCF_GPIO_PAR_BE_BE0_BE0; - - MCF_GPIO_PAR_CS &= ~MCF_GPIO_PAR_CS_CS1_NFC_CE; - MCF_GPIO_PAR_CS |= MCF_GPIO_PAR_CS_CS0_CS0; - return; - } + nfc_set_field(mtd, NFC_ROW_ADDR, ROW_ADDR_CHIP_SEL_RB_MASK, + ROW_ADDR_CHIP_SEL_RB_SHIFT, 1); - MCF_GPIO_PAR_FBCTL &= (MCF_GPIO_PAR_FBCTL_ALE_MASK & - MCF_GPIO_PAR_FBCTL_TA_MASK); - MCF_GPIO_PAR_FBCTL |= MCF_GPIO_PAR_FBCTL_ALE_FB_ALE | - MCF_GPIO_PAR_FBCTL_TA_NFC_RB; - MCF_GPIO_PAR_BE = MCF_GPIO_PAR_BE_BE3_FB_A1 | - MCF_GPIO_PAR_BE_BE2_FB_A0 | - MCF_GPIO_PAR_BE_BE1_BE1 | MCF_GPIO_PAR_BE_BE0_BE0; - - MCF_GPIO_PAR_CS &= (MCF_GPIO_PAR_BE_BE3_MASK & - MCF_GPIO_PAR_BE_BE2_MASK); - MCF_GPIO_PAR_CS |= MCF_GPIO_PAR_CS_CS1_NFC_CE; - return; -#endif + if (chip == 0) + nfc_set_field(mtd, NFC_ROW_ADDR, ROW_ADDR_CHIP_SEL_MASK, + ROW_ADDR_CHIP_SEL_SHIFT, 1); + else if (chip == 1) + nfc_set_field(mtd, NFC_ROW_ADDR, ROW_ADDR_CHIP_SEL_MASK, + ROW_ADDR_CHIP_SEL_SHIFT, 2); + else + nfc_clear(mtd, NFC_ROW_ADDR, ROW_ADDR_CHIP_SEL_MASK); } /* Read NAND Ready/Busy signal */ @@ -423,19 +405,8 @@ fsl_nfc_command(struct mtd_info *mtd, unsigned command, CONFIG_ECC_MODE_MASK, CONFIG_ECC_MODE_SHIFT, ECC_BYPASS); - if (!(page%0x40)) { - nfc_set_field(mtd, NFC_FLASH_CONFIG, - CONFIG_ECC_MODE_MASK, - CONFIG_ECC_MODE_SHIFT, ECC_BYPASS); - } - switch (command) { case NAND_CMD_PAGEPROG: - if (!(prv->page%0x40)) - nfc_set_field(mtd, NFC_FLASH_CONFIG, - CONFIG_ECC_MODE_MASK, - CONFIG_ECC_MODE_SHIFT, ECC_BYPASS); - fsl_nfc_send_cmd(mtd, PROGRAM_PAGE_CMD_BYTE1, PROGRAM_PAGE_CMD_BYTE2, @@ -663,25 +634,55 @@ fsl_nfc_read_word(struct mtd_info *mtd) return tmp; } -#if 0 -static void fsl_nfc_check_ecc_status(struct mtd_info *mtd) +static int nfc_calculate_ecc(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) +{ + return 0; +} + +/* Count the number of 0's in buff upto max_bits */ +static int count_written_bits(uint8_t *buff, int size, int max_bits) +{ + int k, written_bits = 0; + + for (k = 0; k < size; k++) { + written_bits += hweight8(~buff[k]); + if (written_bits > max_bits) + break; + } + + return written_bits; +} + +static int fsl_nfc_check_ecc_status(struct mtd_info *mtd, u_char *dat) { struct nand_chip *chip = mtd->priv; struct fsl_nfc_prv *prv = chip->priv; - u8 ecc_status, ecc_count; + u32 ecc_status; + u8 ecc_count; + int flip; - ecc_status = *(u8 *)(prv->regs + ECC_SRAM_ADDR * 8 + 7); + /* + * ECC status is stored at NFC_CFG[ECCADD] +4 for + * little-endian and +7 for big-endian SOC. Access as 32 bits + * and use low byte. + */ + ecc_status = __raw_readl(prv->regs + ECC_SRAM_ADDR * 8 + 4); ecc_count = ecc_status & ECC_ERR_COUNT; - if (ecc_status & ECC_STATUS_MASK) { - /*mtd->ecc_stats.failed++;*/ - printk("ECC failed to correct all errors!\n"); - } else if (ecc_count) { - /*mtd->ecc_stats.corrected += ecc_count;*/ - printk(KERN_INFO"ECC corrected %d errors\n", ecc_count); + if (!(ecc_status & ECC_STATUS_MASK)) + return ecc_count; + + /* If 'ecc_count' zero or less then buffer is all 0xff or erased. */ + flip = count_written_bits(dat, chip->ecc.size, ecc_count); + + + if (flip > ecc_count) { + printk("ECC failed to correct all errors (%08x)\n", ecc_status); + return -1; } + return 0; } -#endif static void copy_from_to_spare(struct mtd_info *mtd, void *pbuf, int len, int wr) @@ -748,12 +749,19 @@ static int fsl_nfc_write_oob(struct mtd_info *mtd, struct nand_chip *chip, static int fsl_nfc_read_page(struct mtd_info *mtd, struct nand_chip *chip, uint8_t *buf, int page) { + int stat; struct fsl_nfc_prv *prv = chip->priv; - /*fsl_nfc_check_ecc_status(mtd);*/ memcpy_fromio((void *)buf, prv->regs + NFC_MAIN_AREA(0), mtd->writesize); copy_from_to_spare(mtd, chip->oob_poi, mtd->oobsize, 0); + + stat = fsl_nfc_check_ecc_status(mtd, buf); + if (stat < 0) + mtd->ecc_stats.failed++; + else + mtd->ecc_stats.corrected += stat; + return 0; } @@ -787,6 +795,7 @@ fsl_nfc_probe(struct platform_device *pdev) struct resource *res; struct mtd_info *mtd; struct mtd_partition *parts; + struct mxc_nand_platform_data *pdata = pdev->dev.platform_data; struct nand_chip *chip; unsigned long regs_paddr, regs_size; int retval = 0; @@ -830,7 +839,7 @@ fsl_nfc_probe(struct platform_device *pdev) return -ENOMEM; } - mtd->name = "NAND"; + mtd->name = "fsl_nfc"; mtd->writesize = 2048; mtd->oobsize = 64; @@ -842,7 +851,11 @@ fsl_nfc_probe(struct platform_device *pdev) chip->write_buf = fsl_nfc_write_buf; chip->verify_buf = fsl_nfc_verify_buf; chip->options = NAND_NO_AUTOINCR | NAND_USE_FLASH_BBT | - NAND_BUSWIDTH_16 | NAND_CACHEPRG; + NAND_CACHEPRG; + + /* NAND bus width determines access funtions used by upper layer */ + if (pdata->width == 2) + chip->options |= NAND_BUSWIDTH_16; chip->select_chip = nfc_select_chip; @@ -917,9 +930,11 @@ fsl_nfc_probe(struct platform_device *pdev) CONFIG_FAST_FLASH_SHIFT, 1); #endif - nfc_set_field(mtd, NFC_FLASH_CONFIG, - CONFIG_16BIT_MASK, - CONFIG_16BIT_SHIFT, 1); + /* Flash mode width (BITWIDTH) required for 16-bit access */ + if (pdata->width == 2) + nfc_set_field(mtd, NFC_FLASH_CONFIG, + CONFIG_16BIT_MASK, + CONFIG_16BIT_SHIFT, 1); /* Detect NAND chips */ if (nand_scan(mtd, 1)) { diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index 251cb84b4d5f..a08fd7d07523 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -178,6 +178,7 @@ struct nand_manufacturers nand_manuf_ids[] = { {NAND_MFR_AMD, "AMD"}, {NAND_MFR_SANDISK, "SanDisk"}, {NAND_MFR_INTEL, "Intel"}, + {NAND_MFR_EON, "Eon"}, {0x0, "Unknown"} }; diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index de2905a67e76..31da73894a70 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -1951,6 +1951,13 @@ config FEC Say Y here if you want to use the built-in 10/100 Fast ethernet controller on some Motorola ColdFire and Freescale i.MX processors. +config FEC0 + bool "First FEC ethernet controller (of Vybrid and ColdFire)" + depends on FEC && (ARCH_MVF || M54455 || M5441X) + help + Say Y here if you want to use the first built-in 10/100 Fast + ethernet controller on Vybrid and some ColdFire processors. + config FEC1 bool "Second FEC ethernet controller (of Vybrid and ColdFire)" depends on FEC && (ARCH_MVF || M54455 || M5441X) diff --git a/drivers/net/fec.c b/drivers/net/fec.c index 73f3f2b49a61..bcd02e5cfaf0 100755 --- a/drivers/net/fec.c +++ b/drivers/net/fec.c @@ -692,8 +692,14 @@ static void __inline__ fec_get_mac(struct net_device *ndev) memcpy(ndev->dev_addr, iap, ETH_ALEN); /* Adjust MAC if using macaddr */ +#if CONFIG_COLIBRI_VF + /* Add 0x100000 to the first MAC address to get the second */ + if (iap == macaddr) + ndev->dev_addr[3] = macaddr[3] + (fep->pdev->id * 0x10); +#else if (iap == macaddr) ndev->dev_addr[ETH_ALEN-1] = macaddr[ETH_ALEN-1] + fep->pdev->id; +#endif /* !CONFIG_COLIBRI_VF */ } /* ------------------------------------------------------------------------- */ @@ -876,6 +882,7 @@ static int fec_enet_mii_init(struct platform_device *pdev) platform_get_device_id(fep->pdev); int err = -ENXIO, i; +#if !defined(CONFIG_COLIBRI_VF) /* * The dual fec interfaces are not equivalent with enet-mac. * Here are the differences: @@ -897,6 +904,7 @@ static int fec_enet_mii_init(struct platform_device *pdev) fep->mii_bus = fec0_mii_bus; return 0; } +#endif /* !CONFIG_COLIBRI_VF */ fep->mii_timeout = 0; @@ -940,9 +948,11 @@ static int fec_enet_mii_init(struct platform_device *pdev) if (mdiobus_register(fep->mii_bus)) goto err_out_free_mdio_irq; +#if !defined(CONFIG_COLIBRI_VF) /* save fec0 mii_bus */ if (id_entry->driver_data & FEC_QUIRK_ENET_MAC) fec0_mii_bus = fep->mii_bus; +#endif /* !CONFIG_COLIBRI_VF */ return 0; diff --git a/drivers/net/phy/micrel.c b/drivers/net/phy/micrel.c index 1d18fdbebf6c..5b1f5ea977b7 100644 --- a/drivers/net/phy/micrel.c +++ b/drivers/net/phy/micrel.c @@ -125,6 +125,8 @@ static struct phy_driver ks8737_driver = { .read_status = genphy_read_status, .ack_interrupt = kszphy_ack_interrupt, .config_intr = ks8737_config_intr, + .suspend = genphy_suspend, + .resume = genphy_resume, .driver = { .owner = THIS_MODULE,}, }; @@ -140,6 +142,8 @@ static struct phy_driver ks8041_driver = { .read_status = genphy_read_status, .ack_interrupt = kszphy_ack_interrupt, .config_intr = kszphy_config_intr, + .suspend = genphy_suspend, + .resume = genphy_resume, .driver = { .owner = THIS_MODULE,}, }; @@ -155,6 +159,8 @@ static struct phy_driver ks8051_driver = { .read_status = genphy_read_status, .ack_interrupt = kszphy_ack_interrupt, .config_intr = kszphy_config_intr, + .suspend = genphy_suspend, + .resume = genphy_resume, .driver = { .owner = THIS_MODULE,}, }; @@ -169,6 +175,8 @@ static struct phy_driver ks8001_driver = { .read_status = genphy_read_status, .ack_interrupt = kszphy_ack_interrupt, .config_intr = kszphy_config_intr, + .suspend = genphy_suspend, + .resume = genphy_resume, .driver = { .owner = THIS_MODULE,}, }; @@ -184,6 +192,8 @@ static struct phy_driver ksz9021_driver = { .read_status = genphy_read_status, .ack_interrupt = kszphy_ack_interrupt, .config_intr = ksz9021_config_intr, + .suspend = genphy_suspend, + .resume = genphy_resume, .driver = { .owner = THIS_MODULE, }, }; diff --git a/drivers/spi/spi_mvf_dspi.c b/drivers/spi/spi_mvf_dspi.c index a394659a3b02..203999285e57 100644 --- a/drivers/spi/spi_mvf_dspi.c +++ b/drivers/spi/spi_mvf_dspi.c @@ -54,8 +54,6 @@ #if defined(CONFIG_SPI_MVF_DSPI_EDMA) #define SPI_DSPI_EDMA #define EDMA_BUFSIZE_KMALLOC (DSPI_FIFO_SIZE * 4) -#define DSPI_DMA_RX_TCD DMA_MUX_DSPI0_RX -#define DSPI_DMA_TX_TCD DMA_MUX_DSPI0_TX #endif struct DSPI_MCR { @@ -207,7 +205,8 @@ static inline void set_16bit_transfer_mode(struct spi_mvf_data *spi_mvf) writel(temp, spi_mvf->base + SPI_CTAR(spi_mvf->cs)); } -static unsigned char hz_to_spi_baud(int pbr, int dbr, int speed_hz) +static unsigned char hz_to_spi_baud(struct spi_mvf_data *spi_mvf, + int pbr, int dbr, int speed_hz) { /* Valid baud rate pre-scaler values */ int pbr_tbl[4] = {2, 3, 5, 7}; @@ -215,14 +214,14 @@ static unsigned char hz_to_spi_baud(int pbr, int dbr, int speed_hz) 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768 }; - int temp, index = 0; + int temp, pclk, index = 0; /* table indexes out of range, go slow */ if ((pbr < 0) || (pbr > 3) || (dbr < 0) || (dbr > 1)) return 15; - /* cpu core clk need to check */ - temp = ((((66000000 / 2) / pbr_tbl[pbr]) * (1 + dbr)) / speed_hz); + pclk = clk_get_rate(clk_get_parent(spi_mvf->clk)); + temp = (((pclk / pbr_tbl[pbr]) * (1 + dbr)) / speed_hz); while (temp > brs[index]) if (index++ >= 15) @@ -309,7 +308,7 @@ static int write(struct spi_mvf_data *spi_mvf) if (tx_count > 0) { mcf_edma_set_tcd_params(spi_mvf->tx_chan, spi_mvf->edma_tx_buf_pa, - 0x4002c034, + (u32)(spi_mvf->base + SPI_PUSHR), MCF_EDMA_TCD_ATTR_SSIZE_32BIT | MCF_EDMA_TCD_ATTR_DSIZE_32BIT, 4, /* soff */ @@ -324,7 +323,7 @@ static int write(struct spi_mvf_data *spi_mvf) 0); /* enable sg */ mcf_edma_set_tcd_params(spi_mvf->rx_chan, - 0x4002c038, + (u32)(spi_mvf->base + SPI_POPR), spi_mvf->edma_rx_buf_pa, MCF_EDMA_TCD_ATTR_SSIZE_32BIT | MCF_EDMA_TCD_ATTR_DSIZE_32BIT, @@ -572,7 +571,7 @@ static void pump_transfers(unsigned long data) if (transfer->speed_hz) writel((chip->ctar_val & ~0xf) | - hz_to_spi_baud(chip->ctar.pbr, chip->ctar.dbr, + hz_to_spi_baud(spi_mvf, chip->ctar.pbr, chip->ctar.dbr, transfer->speed_hz), spi_mvf->base + SPI_CTAR(spi_mvf->cs)); @@ -659,6 +658,7 @@ static int transfer(struct spi_device *spi, struct spi_message *msg) static int setup(struct spi_device *spi) { + struct spi_mvf_data *spi_mvf = spi_master_get_devdata(spi->master); struct chip_data *chip; struct spi_mvf_chip *chip_info = (struct spi_mvf_chip *)spi->controller_data; @@ -702,8 +702,8 @@ static int setup(struct spi_device *spi) chip->void_write_data = chip_info->void_write_data; if (spi->max_speed_hz != 0) - chip_info->br = hz_to_spi_baud(chip_info->pbr, chip_info->dbr, - spi->max_speed_hz); + chip_info->br = hz_to_spi_baud(spi_mvf, chip_info->pbr, + chip_info->dbr, spi->max_speed_hz); chip->ctar.cpha = (spi->mode & SPI_CPHA) ? 1 : 0; chip->ctar.cpol = (spi->mode & SPI_CPOL) ? 1 : 0; @@ -825,6 +825,9 @@ static int spi_mvf_probe(struct platform_device *pdev) struct spi_mvf_data *spi_mvf; struct resource *res; int ret = 0; +#if defined(SPI_DSPI_EDMA) + int rx_channel, tx_channel; +#endif int i; platform_info = dev_get_platdata(&pdev->dev); @@ -843,6 +846,7 @@ static int spi_mvf_probe(struct platform_device *pdev) INIT_LIST_HEAD(&spi_mvf->queue); spin_lock_init(&spi_mvf->lock); + master->mode_bits = SPI_CPHA | SPI_CPOL | SPI_LSB_FIRST; master->bus_num = platform_info->bus_num; master->num_chipselect = platform_info->num_chipselect; master->cleanup = cleanup; @@ -869,7 +873,7 @@ static int spi_mvf_probe(struct platform_device *pdev) spi_mvf->base = ioremap(res->start, resource_size(res)); if (!spi_mvf->base) { - ret = EINVAL; + ret = -EINVAL; goto out_error_release_mem; } @@ -904,6 +908,7 @@ static int spi_mvf_probe(struct platform_device *pdev) spi_mvf->clk = clk_get(&pdev->dev, "dspi_clk"); if (IS_ERR(spi_mvf->clk)) { dev_err(&pdev->dev, "unable to get clock\n"); + ret = -EINVAL; goto out_error_irq_alloc; } clk_enable(spi_mvf->clk); @@ -929,7 +934,23 @@ static int spi_mvf_probe(struct platform_device *pdev) spi_mvf->edma_tx_buf, spi_mvf->edma_tx_buf_pa, spi_mvf->edma_rx_buf, spi_mvf->edma_rx_buf_pa); - spi_mvf->tx_chan = mcf_edma_request_channel(DSPI_DMA_TX_TCD, + /* TODO: move this to platform data */ + switch (pdev->id) { + case 0: + rx_channel = DMA_MUX_DSPI0_RX; + tx_channel = DMA_MUX_DSPI0_TX; + break; + case 1: + rx_channel = DMA_MUX_DSPI1_RX; + tx_channel = DMA_MUX_DSPI1_TX; + break; + default: + dev_err(&pdev->dev, "unknown device id, no eDMA channels\n"); + ret = -EINVAL; + goto out_error_queue_alloc; + } + + spi_mvf->tx_chan = mcf_edma_request_channel(tx_channel, edma_tx_handler, NULL, 0x00, pdev, NULL, DRIVER_NAME); if (spi_mvf->tx_chan < 0) { dev_err(&pdev->dev, "eDMA transmit channel request\n"); @@ -942,7 +963,7 @@ static int spi_mvf_probe(struct platform_device *pdev) * by SPI communicate machnisim, i.e, is half duplex mode, that is * whether read or write, we need write data out to get we wanted. */ - spi_mvf->rx_chan = mcf_edma_request_channel(DSPI_DMA_RX_TCD, + spi_mvf->rx_chan = mcf_edma_request_channel(rx_channel, edma_rx_handler, NULL, 0x06, pdev, NULL, DRIVER_NAME); if (spi_mvf->rx_chan < 0) { dev_err(&pdev->dev, "eDAM receive channel request\n"); diff --git a/drivers/tty/serial/mvf.c b/drivers/tty/serial/mvf.c index 0ea0181cd455..a174b63e2225 100644 --- a/drivers/tty/serial/mvf.c +++ b/drivers/tty/serial/mvf.c @@ -97,7 +97,7 @@ struct imx_port { void *rx_buf; unsigned char *tx_buf; unsigned int rx_bytes, tx_bytes; - struct work_struct tsk_rx, tsk_dma_tx; + struct work_struct tsk_dma_tx; unsigned int dma_tx_nents; bool dma_is_rxing, dma_is_txing; wait_queue_head_t dma_wait; @@ -368,17 +368,6 @@ out: return IRQ_HANDLED; } -static void rx_work(struct work_struct *w) -{ - struct imx_port *sport = container_of(w, struct imx_port, tsk_rx); - struct tty_struct *tty = sport->port.state->port.tty; - - if (sport->rx_bytes) { - tty_flip_buffer_push(tty); - sport->rx_bytes = 0; - } -} - static irqreturn_t imx_rxint(int irq, void *dev_id) { struct imx_port *sport = dev_id; @@ -450,6 +439,7 @@ out: tty_flip_buffer_push(tty); sport->rx_bytes = 0; } + return IRQ_HANDLED; } @@ -553,6 +543,10 @@ static int imx_setup_watermark(struct imx_port *sport, unsigned int mode) MXC_UARTCR2_RIE | MXC_UARTCR2_RE); writeb(cr2, sport->port.membase + MXC_UARTCR2); + /* Clear pending receive interrupt if needed */ + while (readb(sport->port.membase + MXC_UARTSR1) & MXC_UARTSR1_RDRF) + val = readb(sport->port.membase + MXC_UARTDR); + val = TXTL; writeb(val, sport->port.membase + MXC_UARTTWFIFO); val = RXTL; @@ -564,6 +558,7 @@ static int imx_setup_watermark(struct imx_port *sport, unsigned int mode) MXC_UARTPFIFO_TXFIFOSIZE_MASK) + 1); sport->rx_fifo_size = 0x1 << (((val >> MXC_UARTPFIFO_RXFIFOSIZE_OFF) & MXC_UARTPFIFO_RXFIFOSIZE_MASK) + 1); + writeb(val | MXC_UARTPFIFO_TXFE | MXC_UARTPFIFO_RXFE, sport->port.membase + MXC_UARTPFIFO); @@ -588,10 +583,8 @@ static int imx_startup(struct uart_port *port) struct tty_struct *tty; struct imxuart_platform_data *pdata = port->dev->platform_data; -#ifndef CONFIG_SERIAL_CORE_CONSOLE if (sport->fifo_en) imx_setup_watermark(sport, 0); -#endif /* * Allocate the IRQ(s) @@ -616,9 +609,7 @@ static int imx_startup(struct uart_port *port) temp |= MXC_UARTCR5_TDMAS; writeb(temp, sport->port.membase + MXC_UARTCR5); - sport->port.flags |= UPF_LOW_LATENCY; INIT_WORK(&sport->tsk_dma_tx, dma_tx_work); - INIT_WORK(&sport->tsk_rx, rx_work); init_waitqueue_head(&sport->dma_wait); } diff --git a/drivers/video/mvf_dcu.c b/drivers/video/mvf_dcu.c index 036097b77fcb..9eed2135dd31 100644 --- a/drivers/video/mvf_dcu.c +++ b/drivers/video/mvf_dcu.c @@ -38,6 +38,7 @@ #define DRIVER_NAME "mvf-dcu" static struct fb_videomode __devinitdata mvf_dcu_default_mode = { +#if !defined(CONFIG_COLIBRI_VF) .xres = 480, .yres = 272, .left_margin = 2, @@ -46,8 +47,23 @@ static struct fb_videomode __devinitdata mvf_dcu_default_mode = { .lower_margin = 1, .hsync_len = 41, .vsync_len = 2, - .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, .vmode = FB_VMODE_NONINTERLACED, +#else /* !CONFIG_COLIBRI_VF */ + .refresh = 60, + .xres = 640, + .yres = 480, + /* pixel clock period in picoseconds (25.18 MHz) */ + .pixclock = 38000, + .left_margin = 48, + .right_margin = 16, + .upper_margin = 31, + .lower_margin = 11, + .hsync_len = 96, + .vsync_len = 2, + .sync = 0, + .vmode = FB_VMODE_NONINTERLACED, +#endif /* !CONFIG_COLIBRI_VF */ }; static struct fb_videomode __devinitdata mvf_dcu_mode_db[] = { @@ -61,7 +77,93 @@ static struct fb_videomode __devinitdata mvf_dcu_mode_db[] = { .lower_margin = 1, .hsync_len = 41, .vsync_len = 2, - .sync = FB_SYNC_COMP_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + .vmode = FB_VMODE_NONINTERLACED, + }, + { + /* 640x480p 60hz: EIA/CEA-861-B Format 1 */ + .name = "640x480", + .refresh = 60, + .xres = 640, + .yres = 480, + /* pixel clock period in picoseconds (25.18 MHz) */ + .pixclock = 38000, + .left_margin = 48, + .right_margin = 16, + .upper_margin = 31, + .lower_margin = 11, + .hsync_len = 96, + .vsync_len = 2, + .sync = 0, + .vmode = FB_VMODE_NONINTERLACED, + }, + { + /* 800x480@60 (e.g. EDT ET070080DH6) */ + .name = "800x480", + .refresh = 60, + .xres = 800, + .yres = 480, + /* pixel clock period in picoseconds (33.26 MHz) */ + .pixclock = 30066, + .left_margin = 216, + .right_margin = 40, + .upper_margin = 35, + .lower_margin = 10, + .hsync_len = 128, + .vsync_len = 2, + .sync = 0, + .vmode = FB_VMODE_NONINTERLACED, + }, + { + /* 800x600@60 */ + .name = "800x600", + .refresh = 60, + .xres = 800, + .yres = 600, + /* pixel clock period in picoseconds (40 MHz) */ + .pixclock = 25000, + .left_margin = 88, + .right_margin = 40, + .upper_margin = 23, + .lower_margin = 1, + .hsync_len = 128, + .vsync_len = 4, + .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + .vmode = FB_VMODE_NONINTERLACED, + }, + { + /* TouchRevolution Fusion 10 aka Chunghwa Picture Tubes + CLAA101NC05 10.1 inch 1024x600 single channel LVDS panel */ + .name = "1024x600", + .refresh = 60, + .xres = 1024, + .yres = 600, + /* pixel clock period in picoseconds (48 MHz) */ + .pixclock = 20833, + .left_margin = 104, + .right_margin = 43, + .upper_margin = 24, + .lower_margin = 20, + .hsync_len = 5, + .vsync_len = 5, + .sync = 0, + .vmode = FB_VMODE_NONINTERLACED, + }, + { + /* 1024x768@60 */ + .name = "1024x768", + .refresh = 60, + .xres = 1024, + .yres = 768, + /* pixel clock period in picoseconds (65 MHz) */ + .pixclock = 15385, + .left_margin = 160, + .right_margin = 24, + .upper_margin = 29, + .lower_margin = 3, + .hsync_len = 136, + .vsync_len = 6, + .sync = 0, .vmode = FB_VMODE_NONINTERLACED, }, }; @@ -76,6 +178,8 @@ struct mvf_dcu_fb_data { unsigned int irq; struct clk *clk; int fb_enabled; + int clock_pol; + int default_bpp; }; struct mfb_info { @@ -84,12 +188,11 @@ struct mfb_info { char *id; int registered; int blank; - char *mode_str; - int default_bpp; unsigned long pseudo_palette[16]; struct dcu_layer_desc *layer_desc; int cursor_reset; unsigned char g_alpha; + unsigned char blend; unsigned int count; int x_layer_d; /* layer display x offset to physical screen */ int y_layer_d; /* layer display y offset to physical screen */ @@ -103,6 +206,7 @@ static struct mfb_info mfb_template[] = { .id = "Layer0", .registered = 0, .g_alpha = 0xff, + .blend = 0, .count = 0, .x_layer_d = 0, .y_layer_d = 0, @@ -113,6 +217,7 @@ static struct mfb_info mfb_template[] = { .id = "Layer1", .registered = 0, .g_alpha = 0xff, + .blend = 0, .count = 0, .x_layer_d = 50, .y_layer_d = 50, @@ -123,6 +228,7 @@ static struct mfb_info mfb_template[] = { .id = "Layer2", .registered = 0, .g_alpha = 0xff, + .blend = 0, .count = 0, .x_layer_d = 100, .y_layer_d = 100, @@ -133,6 +239,7 @@ static struct mfb_info mfb_template[] = { .id = "Layer3", .registered = 0, .g_alpha = 0xff, + .blend = 0, .count = 0, .x_layer_d = 150, .y_layer_d = 150, @@ -270,8 +377,6 @@ static void adjust_layer_size_position(struct fb_var_screeninfo *var, static int mvf_dcu_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { - struct mfb_info *mfbi = info->par; - if (var->xres_virtual < var->xres) var->xres_virtual = var->xres; if (var->yres_virtual < var->yres) @@ -291,7 +396,7 @@ static int mvf_dcu_check_var(struct fb_var_screeninfo *var, if ((var->bits_per_pixel != 32) && (var->bits_per_pixel != 24) && (var->bits_per_pixel != 16)) - var->bits_per_pixel = mfbi->default_bpp; + var->bits_per_pixel = 16; switch (var->bits_per_pixel) { case 16: @@ -375,11 +480,36 @@ static void set_fix(struct fb_info *info) fix->ypanstep = 1; } +static int calc_div_ratio(struct fb_info *info) +{ + struct mfb_info *mfbi = info->par; + struct mvf_dcu_fb_data *dcufb = mfbi->parent; + unsigned long dcu_clk; + unsigned long long tmp; + + /* + * Calculation could be done more precisly when we take parent clock + * into account too. We can change between 452MHz and 480MHz (see + * arch/arm/mach-mvf/clock.c + */ + dcu_clk = clk_get_rate(dcufb->clk); + tmp = info->var.pixclock * (unsigned long long)dcu_clk; + + do_div(tmp, 1000000); + + if (do_div(tmp, 1000000) > 500000) + tmp++; + + tmp = tmp - 1; + return tmp; +} + static void update_lcdc(struct fb_info *info) { struct fb_var_screeninfo *var = &info->var; struct mfb_info *mfbi = info->par; struct mvf_dcu_fb_data *dcu = mfbi->parent; + unsigned int ratio; if (mfbi->type == DCU_TYPE_OFF) { mvf_dcu_disable_panel(info); @@ -417,11 +547,12 @@ static void update_lcdc(struct fb_info *info) writel(DCU_MODE_BLEND_ITER(3) | DCU_MODE_RASTER_EN(1), dcu->base + DCU_DCU_MODE); - writel(9, dcu->base + DCU_DIV_RATIO); + ratio = calc_div_ratio(info); + writel(ratio, dcu->base + DCU_DIV_RATIO); + + /* Set various clock polarity (DCUx_SYNPOL) */ + writel(dcu->clock_pol, dcu->base + DCU_SYN_POL); - writel(DCU_SYN_POL_INV_PXCK(0) | DCU_SYN_POL_NEG(0) | - DCU_SYN_POL_INV_VS(1) | DCU_SYN_POL_INV_HS(1), - dcu->base + DCU_SYN_POL); writel(DCU_THRESHOLD_LS_BF_VS(0x3) | DCU_THRESHOLD_OUT_BUF_HIGH(0x78) | DCU_THRESHOLD_OUT_BUF_LOW(0), dcu->base + DCU_THRESHOLD); @@ -519,11 +650,24 @@ static int mvf_dcu_set_par(struct fb_info *info) layer_desc->posx = mfbi->x_layer_d; layer_desc->posy = mfbi->y_layer_d; - layer_desc->blend = 0x01; + switch (var->bits_per_pixel) { + case 16: + layer_desc->bpp = BPP_16_RGB565; + break; + case 24: + layer_desc->bpp = BPP_24; + break; + case 32: + layer_desc->bpp = BPP_32_ARGB8888; + break; + default: + printk(KERN_ERR "Unable to support other bpp now\n"); + } + + layer_desc->blend = mfbi->blend; layer_desc->chroma_key_en = 0; layer_desc->lut_offset = 0; layer_desc->rle_en = 0; - layer_desc->bpp = BPP_24; layer_desc->trans = mfbi->g_alpha; layer_desc->safety_en = 0; layer_desc->data_sel_clut = 0; @@ -624,9 +768,8 @@ static int mvf_dcu_pan_display(struct fb_var_screeninfo *var, static int mvf_dcu_blank(int blank_mode, struct fb_info *info) { - struct mfb_info *mfbi = info->par; - #ifdef CONFIG_MVF_DCU_BLANKING_TEST + struct mfb_info *mfbi = info->par; mfbi->blank = blank_mode; switch (blank_mode) { @@ -686,6 +829,7 @@ static int mvf_dcu_ioctl(struct fb_info *info, unsigned int cmd, case MFB_SET_ALPHA: if (copy_from_user(&global_alpha, buf, sizeof(global_alpha))) return -EFAULT; + mfbi->blend = 1; mfbi->g_alpha = global_alpha; mvf_dcu_check_var(&info->var, info); mvf_dcu_set_par(info); @@ -815,15 +959,6 @@ static int init_fbinfo(struct fb_info *info) static int __devinit install_fb(struct fb_info *info) { struct mfb_info *mfbi = info->par; - struct fb_videomode *db = mvf_dcu_mode_db; - unsigned int dbsize = ARRAY_SIZE(mvf_dcu_mode_db); - int rc; - - if (init_fbinfo(info)) - return -EINVAL; - - rc = fb_find_mode(&info->var, info, mfbi->mode_str, db, dbsize, - &mvf_dcu_default_mode, mfbi->default_bpp); if (mvf_dcu_check_var(&info->var, info)) { printk(KERN_ERR "fb_check_var failed"); @@ -942,6 +1077,68 @@ static int mvf_dcu_resume(struct platform_device *pdev) #define mvf_dcu_resume NULL #endif +static int parse_opt(struct mvf_dcu_fb_data *dcu, char *this_opt) +{ + if (!strncmp(this_opt, "hsync:", 6)) { + /* Inverted logic + * hsync:0 => active low => INV_HS(1) + * hsync:1 => active high => INV_HS(0) + */ + if (simple_strtoul(this_opt+6, NULL, 0) == 0) + dcu->clock_pol |= DCU_SYN_POL_INV_HS(1); + else + dcu->clock_pol &= ~DCU_SYN_POL_INV_HS(1); + return 0; + } else if (!strncmp(this_opt, "vsync:", 6)) { + /* Inverted logic + * vsync:0 => active low => INV_VS(1) + * vsync:1 => active high => INV_VS(0) + */ + if (simple_strtoul(this_opt+6, NULL, 0) == 0) + dcu->clock_pol |= DCU_SYN_POL_INV_VS(1); + else + dcu->clock_pol &= ~DCU_SYN_POL_INV_VS(1); + return 0; + } else if (!strncmp(this_opt, "pixclockpol:", 12)) { + /* Inverted logic too, altough, datasheet seems to + * be wrong here! (1 => Display samples data on + * _falling_ edge) + * pixclockpol:0 => falling edge => INV_PXCK(1) + * pixclockpol:1 => rising edge => INV_PXCK(0) + */ + if (simple_strtoul(this_opt+12, NULL, 0) == 0) + dcu->clock_pol |= DCU_SYN_POL_INV_PXCK(1); + else + dcu->clock_pol &= ~DCU_SYN_POL_INV_PXCK(1); + return 0; + } + + return -1; +} + +static int mvf_dcu_parse_options(struct mvf_dcu_fb_data *dcu, + struct fb_info *info, char *option) +{ + char *this_opt; + struct fb_videomode *db = mvf_dcu_mode_db; + unsigned int dbsize = ARRAY_SIZE(mvf_dcu_mode_db); + int ret = 0; + + while ((this_opt = strsep(&option, ",")) != NULL) { + /* Parse driver specific arguments */ + if (parse_opt(dcu, this_opt) == 0) + continue; + + /* No valid driver specific argument, has to be mode */ + ret = fb_find_mode(&info->var, info, this_opt, db, dbsize, + &mvf_dcu_default_mode, dcu->default_bpp); + if (ret < 0) + return ret; + } + return 0; +} + + static int __devinit mvf_dcu_probe(struct platform_device *pdev) { struct mvf_dcu_platform_data *plat_data = pdev->dev.platform_data; @@ -950,11 +1147,24 @@ static int __devinit mvf_dcu_probe(struct platform_device *pdev) struct resource *res; int ret = 0; int i; + char *option = NULL; dcu = kmalloc(sizeof(struct mvf_dcu_fb_data), GFP_KERNEL); if (!dcu) return -ENOMEM; + fb_get_options("dcufb", &option); + + if (option != NULL) { + printk(KERN_INFO "dcufb: parse cmd options: %s\n", option); + } else { + option = plat_data->mode_str; + printk(KERN_INFO "dcufb: use default mode: %s\n", option); + } + + if (!strcmp(option, "off")) + return -ENODEV; + for (i = 0; i < ARRAY_SIZE(dcu->mvf_dcu_info); i++) { dcu->mvf_dcu_info[i] = framebuffer_alloc(sizeof(struct mfb_info), @@ -967,8 +1177,21 @@ static int __devinit mvf_dcu_probe(struct platform_device *pdev) mfbi = dcu->mvf_dcu_info[i]->par; memcpy(mfbi, &mfb_template[i], sizeof(struct mfb_info)); mfbi->parent = dcu; - mfbi->mode_str = plat_data->mode_str; - mfbi->default_bpp = plat_data->default_bpp; + if (init_fbinfo(dcu->mvf_dcu_info[i])) { + ret = -EINVAL; + goto failed_alloc_framebuffer; + } + } + + dcu->default_bpp = plat_data->default_bpp; + dcu->clock_pol = DCU_SYN_POL_INV_HS(1) | DCU_SYN_POL_INV_VS(1) | + DCU_SYN_POL_INV_PXCK(1); + + /* Use framebuffer of first layer to store display mode */ + ret = mvf_dcu_parse_options(dcu, dcu->mvf_dcu_info[0], option); + if (ret < 0) { + ret = -EINVAL; + goto failed_alloc_framebuffer; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -1003,9 +1226,11 @@ static int __devinit mvf_dcu_probe(struct platform_device *pdev) goto failed_get_resource; } +#if !defined(CONFIG_COLIBRI_VF) gpio_request_one(DCU_LCD_ENABLE_PIN, GPIOF_OUT_INIT_LOW, "DCU"); msleep(2); gpio_set_value(DCU_LCD_ENABLE_PIN, 1); +#endif writel(0x20000000, MVF_IO_ADDRESS(MVF_TCON0_BASE_ADDR)); diff --git a/include/linux/input/fusion_F0710A.h b/include/linux/input/fusion_F0710A.h new file mode 100644 index 000000000000..7d152cbdd06e --- /dev/null +++ b/include/linux/input/fusion_F0710A.h @@ -0,0 +1,20 @@ +/* linux/input/fusion_F0710A.h + * + * Platform data for Fusion F0710A driver + * + * Copyright (c) 2013 Toradex AG (stefan.agner@toradex.ch) + * + * For licencing details see kernel-base/COPYING + */ + +#ifndef __LINUX_I2C_FUSION_F0710A_H +#define __LINUX_I2C_FUSION_F0710A_H + +/* Board specific touch screen initial values */ +struct fusion_f0710a_init_data { + int (*pinmux_fusion_pins)(void); + int gpio_int; + int gpio_reset; +}; + +#endif /* __LINUX_I2C_FUSION_F0710A_H */ diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 1cd6d901dcd4..c24d913a1209 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -563,6 +563,7 @@ struct nand_chip { #define NAND_MFR_AMD 0x01 #define NAND_MFR_SANDISK 0x45 #define NAND_MFR_INTEL 0x89 +#define NAND_MFR_EON 0x92 /** * struct nand_flash_dev - NAND Flash Device ID Structure diff --git a/include/linux/mvf_adc.h b/include/linux/mvf_adc.h index 26562531afe0..93f464ff9343 100644 --- a/include/linux/mvf_adc.h +++ b/include/linux/mvf_adc.h @@ -24,8 +24,8 @@ /* Conversion RES Mode Selection 3-2 */ #define CLEAR_MODE_BIT 0xC #define BIT8 0x00 -#define BIT10 0x01 -#define BIT12 0x10 +#define BIT10 0x04 +#define BIT12 0x08 /* Low-Power Configuration 7 */ #define CLEAR_ADLPC_BIT 0x80 #define ADLPC_EN 0x80 @@ -83,7 +83,7 @@ #define ADC_HC1 0x04/* Control register for hardware triggers 1 */ #define IRQ_EN 0x80 -#define ADCHC0(x) ((x)&0xF) +#define ADCHC0(x) ((x)&0x1F) #define AIEN1 0x00000080 #define COCOA 0x00000000 @@ -177,6 +177,22 @@ enum adc_channel { ADC13, ADC14, ADC15, + ADC16, + ADC17, + ADC18, + ADC19, + ADC20, + ADC21, + ADC22, + ADC23, + ADC24, + ADC25, + ADC26, + ADC27, + ADC28, + ADC29, + ADC30, + ADC31, }; struct adc_feature { @@ -201,4 +217,12 @@ struct adc_feature { unsigned int result0, result1; }; +#ifdef __KERNEL__ +extern int mvf_adc_initiate(unsigned int adc); + +extern int mvf_adc_set(unsigned int adc, struct adc_feature *adc_fea); + +extern int mvf_adc_register_and_convert(unsigned int adc, unsigned char channel); +#endif + #endif diff --git a/sound/soc/mvf/Kconfig b/sound/soc/mvf/Kconfig index 6577df843926..7c707032fa0b 100644 --- a/sound/soc/mvf/Kconfig +++ b/sound/soc/mvf/Kconfig @@ -10,6 +10,12 @@ menuconfig SND_MVF_SOC if SND_MVF_SOC +config SND_MVF_AC97 + tristate "Vybrid SAI Software AC97" + select AC97_BUS + select SND_AC97_CODEC + select SND_SOC_AC97_BUS + config SND_MVF_SOC_TWR tristate @@ -23,4 +29,13 @@ config SND_SOC_MVF_SGTL5000 Say Y if you want to add support for SoC audio on an Farday board with a sgtl5000 codec. +config SND_SOC_MVF_COLIBRI_VF61 + tristate "SoC AC97 Audio support for Colibri VF61" + depends on MACH_COLIBRI_VF61 + select SND_MVF_AC97 + select SND_SOC_WM9712 + help + Say Y if you want to add support for SoC audio on Toradex + Colibri VF61 module. + endif diff --git a/sound/soc/mvf/Makefile b/sound/soc/mvf/Makefile index c9e179ad0a8e..8c5a6d7e03f9 100644 --- a/sound/soc/mvf/Makefile +++ b/sound/soc/mvf/Makefile @@ -2,6 +2,8 @@ snd-soc-mvf-objs := mvf-sai.o snd-soc-mvf-twr-objs := mvf-pcm-dma-twr.o +obj-$(CONFIG_SND_MVF_AC97) += mvf-sai-ac97.o + obj-$(CONFIG_SND_MVF_SOC) += snd-soc-mvf.o obj-$(CONFIG_SND_MVF_SOC_TWR) += snd-soc-mvf-twr.o diff --git a/sound/soc/mvf/mvf-sai-ac97.c b/sound/soc/mvf/mvf-sai-ac97.c new file mode 100644 index 000000000000..df387bc5a69b --- /dev/null +++ b/sound/soc/mvf/mvf-sai-ac97.c @@ -0,0 +1,838 @@ +/* + * sound/soc/mvf/mvf-ac97.c + * + * Copyright (C) 2013 Toradex, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + */ + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/mutex.h> +#include <linux/platform_device.h> +#include <linux/slab.h> + +#include <mach/gpio.h> +#include <mach/iomux-mvf.h> +#include <mach/mcf_edma.h> + +#include <sound/ac97_codec.h> +#include <sound/initval.h> +#include <sound/core.h> +#include <sound/soc.h> + +#include "mvf-sai.h" + +#define DRIVER_NAME "mvf-sai-ac97" +#define EDMA_PRIO_HIGH 6 +#define MVF_SAI_AC97_DMABUF_SIZE (13 * 4) + +/* read states */ +enum read_state { + RD_IDLE = 0, + RD_INIT, /* command initiated via mvf_sai_ac97_read() call, + rd_address valid */ + RD_WAIT, /* command sent, waiting for reply, + rd_address still valid */ + RD_REP_RCV, /* reply received */ +}; + +/* write states */ +enum write_state { + WR_IDLE = 0, + WR_INIT, /* command initiated via mvf_sai_ac97_write() call, + rd_address and rd_data valid */ + WR_CMD_SENT, /* command sent, + slot subsequently to be cleared again */ + WR_CMD_CLR, /* slot cleared */ +}; + +/* AC97 controller */ +struct mvf_sai_ac97_info { + struct platform_device *pdev; + struct tegra_audio_platform_data *pdata; + phys_addr_t ac97_phys; + void __iomem *ac97_base; + void __iomem *sai0_base; + struct clk *clk; + struct snd_card *card; + struct imx_pcm_dma_params dma_params_rx; + struct imx_pcm_dma_params dma_params_tx; + struct snd_dma_buffer rx_buf; + struct snd_dma_buffer tx_buf; + int rx_tcd_chan; + int tx_tcd_chan; + struct mutex lock; + enum read_state rd_state; + unsigned int rd_address; + unsigned int rd_data; + enum write_state wr_state; + unsigned int wr_address; + unsigned int wr_data; +}; + +static u64 mvf_pcm_dmamask = DMA_BIT_MASK(32); + +/* TODO: not just global */ +static struct mvf_sai_ac97_info *info; + +/* software AC97 register read access */ +static unsigned short mvf_sai_ac97_read(struct snd_ac97 *ac97, + unsigned short reg) +{ + u32 val; + int timeout = 100; + + mutex_lock(&info->lock); + + if (info->rd_state != RD_IDLE) + dev_warn(&info->pdev->dev, "read state machine was %d instead " + "of RD_IDLE\n", info->rd_state); + if (info->wr_state != WR_IDLE) + dev_warn(&info->pdev->dev, "write state machine was %d instead " + "of WR_IDLE\n", info->wr_state); + + /* Slot 1: Command Address Port + Bit(19) Read/Write command (1=read, 0=write) + Bit(18:12) Control Register Index (64 16-bit locations, + addressed on even byte boundaries) + Bit(11:0) Reserved (Stuffed with 0’s) */ + info->rd_address = (1 << 19) | /* read */ + (reg << 12); + info->wr_address = info->rd_address; + + info->rd_state = RD_INIT; + info->wr_state = WR_INIT; + + /* Slot 2: Status Data Port + The status data port delivers 16-bit control register read data. + Bit(19:4) Control Register Read Data (Completed with 0’s if tagged + “invalid” by AC‘97) */ + do { + mdelay(1); + val = (info->rd_data & 0xfffff) >> 4; + } while ((info->rd_state != RD_REP_RCV) && --timeout); + + info->rd_state = RD_IDLE; + + if (!timeout) { + dev_warn(&info->pdev->dev, + "timeout reading register %x\n", reg); + mutex_unlock(&info->lock); + return -ETIMEDOUT; + } + + mutex_unlock(&info->lock); + + pr_debug("%s: 0x%02x 0x%04x\n", __func__, reg, val); + + return val; +} + +/* software AC97 register write access */ +static void mvf_sai_ac97_write(struct snd_ac97 *ac97, unsigned short reg, + unsigned short val) +{ + int timeout = 100; + + pr_debug("%s: 0x%02x 0x%04x\n", __func__, reg, val); + + if (info->wr_state != WR_IDLE) + dev_warn(&info->pdev->dev, "write state machine was %d instead " + "of WR_IDLE\n", info->wr_state); + + mutex_lock(&info->lock); + + /* Slot 1: Command Address Port + Bit(19) Read/Write command (1=read, 0=write) + Bit(18:12) Control Register Index (64 16-bit locations, + addressed on even byte boundaries) + Bit(11:0) Reserved (Stuffed with 0’s) */ + info->wr_address = reg << 12; + + /* Slot 2: Command Data Port + The command data port is used to deliver 16-bit control register write + data in the event that the current command + port operation is a write cycle. (as indicated by Slot 1, bit 19) + Bit(19:4) Control Register Write Data (Completed with 0’s if current + operation is a read) + Bit(3:0) Reserved (Completed with 0’s) */ + info->wr_data = (val << 4); + + info->wr_state = WR_INIT; + + while ((info->wr_state != WR_CMD_CLR) && --timeout) + mdelay(1); + + info->wr_state = WR_IDLE; + + if (!timeout) + dev_warn(&info->pdev->dev, "timeout writing register %x\n", reg); + + mutex_unlock(&info->lock); +} + +struct snd_ac97_bus_ops soc_ac97_ops = { + .read = mvf_sai_ac97_read, + .write = mvf_sai_ac97_write, +}; +EXPORT_SYMBOL_GPL(soc_ac97_ops); + +static struct snd_ac97_bus_ops mvf_sai_ac97_ops = { + .read = mvf_sai_ac97_read, + .write = mvf_sai_ac97_write, +}; + +static struct snd_ac97 *mvf_sai_ac97_ac97; + +static __initdata iomux_v3_cfg_t ac97_pinmux[] = { + /* SAI2: AC97/Touchscreen */ + MVF600_PAD4_PTA11_WM9715L_PENDOWN, /* carefull also used for JTAG + JTMS/SWDIO */ + MVF600_PAD6_PTA16_SAI2_TX_BCLK, /* AC97_BIT_CLK */ + MVF600_PAD8_PTA18_SAI2_TX_DATA, /* AC97_SDATA_OUT */ + MVF600_PAD9_PTA19_SAI2_TX_SYNC, /* AC97_SYNC */ + MVF600_PAD12_PTA22_SAI2_RX_DATA, /* AC97_SDATA_IN */ + MVF600_PAD13_PTA23_WM9715L_RESET, + MVF600_PAD24_PTB2_WM9715L_GENIRQ, + MVF600_PAD40_PTB18_CKO1, /* AC97_MCLK fed back in from + camera clock pin */ + MVF600_PAD93_PTB23_SAI0_TX_BCLK, /* AC97_MCLK */ +}; + +static irqreturn_t mvf_sai_ac97_dma_irq(int channel, void *data) +{ + if (channel == info->rx_tcd_chan) { + if ((info->rd_state == RD_WAIT) && + ((*((unsigned int *)(info->rx_buf.area + 4)) & (3 << (13 + + 4))) == (3 << (13 + 4))) && /* valid slot 1 & 2 */ + ((*((unsigned int *)(info->rx_buf.area + 8)) | (1 << 19)) == + info->rd_address)) { + info->rd_data = *((unsigned int *)(info->rx_buf.area + + 12)); + info->rd_state = RD_REP_RCV; + } + + mcf_edma_confirm_interrupt_handled(DMA_MUX_SAI2_RX); + } else if (channel == info->tx_tcd_chan) { + if (info->wr_state == WR_INIT) { + /* Slot 0: TAG + Bit 15 Codec Ready + Bit 14:3 Slot Valid (Which of slot 1 to slot 12 contain + valid data) + Bit 2:0 Zero */ + *((unsigned int *)(info->tx_buf.area + 0)) = + (1 << (15 + 4)) | /* valid frame */ + (1 << (14 + 4)) | /* slot 1 valid */ + (((info->wr_address & (1 << 19))?0:1) << + (13 + 4)); /* slot 2 valid */ + + /* Slot 1: Command Address Port + Bit(19) Read/Write command (1=read, 0=write) + Bit(18:12) Control Register Index (64 16-bit locations, + addressed on even byte boundaries) + Bit(11:0) Reserved (Stuffed with 0’s) */ + *((unsigned int *)(info->tx_buf.area + 4)) = + info->wr_address; + + if (!(info->wr_address & (1 << 19))) { + /* Slot 2: Command Data Port + The command data port is used to deliver 16-bit + control register write data in the event that + the current command port operation is a write + cycle. (as indicated by Slot 1, bit 19) + Bit(19:4) Control Register Write Data (Completed + with 0’s if current operation is + a read) + Bit(3:0) Reserved (Completed with 0’s) */ + *((unsigned int *)(info->tx_buf.area + 8)) = + info->wr_data; + info->wr_state = WR_CMD_CLR; + } else { + info->rd_state = RD_WAIT; + info->wr_state = WR_IDLE; + } + } + + mcf_edma_confirm_interrupt_handled(DMA_MUX_SAI2_TX); + } + + return IRQ_HANDLED; +} + +static int __devinit mvf_sai_ac97_dev_probe(struct platform_device *pdev) +{ + struct snd_ac97_bus *ac97_bus; + struct snd_ac97_template ac97_template; + int err = 0; + int gpio_status; + int i; + unsigned int rcsr; + unsigned int reg; + struct resource *res; + unsigned int tcsr; + + info = kzalloc(sizeof(*info), GFP_KERNEL); + if (!info) { + err = -ENOMEM; + goto failed_alloc; + } + dev_set_drvdata(&pdev->dev, info); + info->pdev = pdev; + + mutex_init(&info->lock); + +/* + res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res) { + ret = -ENODEV; + goto failed_irq; + } + info->irq = res->start; +*/ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(&pdev->dev, "no mem resource!\n"); + err = -ENODEV; + goto failed_mem; + } + + if (!request_mem_region(res->start, resource_size(res), DRV_NAME)) { + dev_err(&pdev->dev, "request_mem_region failed\n"); + err = -EBUSY; + goto failed_region; + } + + info->ac97_phys = res->start; + info->ac97_base = ioremap(res->start, resource_size(res)); + if (!info->ac97_base) { + dev_err(&pdev->dev, "cannot remap iomem!\n"); + err = -ENOMEM; + goto failed_map; + } + + /* SAI0_TX_BCLK used as AC97 master clock */ + info->sai0_base = ioremap(MVF_SAI0_BASE_ADDR, resource_size(res)); + if (!info->sai0_base) { + dev_err(&pdev->dev, "cannot remap iomem!\n"); + err = -ENOMEM; + goto failed_map2; + } + + /* configure AC97 master clock */ + reg = readl(info->sai0_base + SAI_TCR2); + reg &= ~SAI_TCR2_SYNC_MASK; /* asynchronous aka independent + operation */ + reg &= ~SAI_TCR2_BCS; /* bit clock not swapped */ + reg &= ~SAI_TCR2_MSEL_MASK; + reg |= SAI_TCR2_MSEL_MCLK1; /* Clock selected by + CCM_CSCMR1[SAIn_CLK_SEL] */ + reg |= SAI_TCR2_BCD_MSTR; /* Bitclock is generated internally + (master mode) */ + reg &= ~SAI_TCR2_DIV_MASK; /* Divides down the audio master */ + reg |= SAI_TCR2_DIV(2); /* clock by 6 to generate the bitclock + when configured for an internal + bitclock (master). */ + writel(reg, info->sai0_base + SAI_TCR2); + + /* enable AC97 master clock */ + reg = readl(info->sai0_base + SAI_TCSR); + writel(reg | SAI_TCSR_BCE, info->sai0_base + SAI_TCSR); + + info->clk = clk_get(&pdev->dev, "sai_clk"); + if (IS_ERR(info->clk)) { + err = PTR_ERR(info->clk); + dev_err(&pdev->dev, "Cannot get the clock: %d\n", + err); + goto failed_clk; + } + clk_enable(info->clk); + + err = snd_card_create(SNDRV_DEFAULT_IDX1, SNDRV_DEFAULT_STR1, + THIS_MODULE, 0, &info->card); + if (err < 0) + goto failed_create; + + info->card->dev = &pdev->dev; + strncpy(info->card->driver, pdev->dev.driver->name, sizeof(info->card->driver)); + + /* AC'97 controller required to drive and keep SYNC and SDATA_OUT low + to avoid wolfson entering test mode */ +#define GPIO_AC97_SDATAOUT 8 + gpio_status = gpio_request(GPIO_AC97_SDATAOUT, "WOLFSON_SDATAOUT"); + if (gpio_status < 0) { + pr_info("WOLFSON_SDATAOUT request GPIO FAILED\n"); + WARN_ON(1); + } + gpio_status = gpio_direction_output(GPIO_AC97_SDATAOUT, 0); + if (gpio_status < 0) { + pr_info("WOLFSON_SDATAOU request GPIO DIRECTION FAILED\n"); + WARN_ON(1); + } + udelay(2); + + /* do wolfson hard reset */ +#define GPIO_AC97_nRESET 13 + gpio_status = gpio_request(GPIO_AC97_nRESET, "WOLFSON_RESET"); + if (gpio_status < 0) { + pr_info("WOLFSON_RESET request GPIO FAILED\n"); + WARN_ON(1); + } + + /* Wolfson initially powered up disabled due to ADCIRQ aka PWRUP + strapping pin being held high. + WM9715L awakes from sleep mode on warm reset of AC-Link + (according to the AC’97 specification). */ + + /* do wolfson warm reset by toggling SYNC */ +#define GPIO_AC97_SYNC 9 + gpio_status = gpio_request(GPIO_AC97_SYNC, "WOLFSON_SYNC"); + if (gpio_status < 0) { + pr_info("WOLFSON_SYNC request GPIO FAILED\n"); + WARN_ON(1); + } + gpio_status = gpio_direction_output(GPIO_AC97_SYNC, 0); + if (gpio_status < 0) { + pr_info("WOLFSON_SYNC request GPIO DIRECTION FAILED\n"); + WARN_ON(1); + } + udelay(2); + gpio_status = gpio_direction_output(GPIO_AC97_nRESET, 0); + if (gpio_status < 0) { + pr_info("WOLFSON_RESET request GPIO DIRECTION FAILED\n"); + WARN_ON(1); + } + udelay(2); + gpio_set_value(GPIO_AC97_nRESET, 1); + udelay(2); + gpio_set_value(GPIO_AC97_SYNC, 1); + udelay(2); + gpio_set_value(GPIO_AC97_SYNC, 0); + udelay(2); + gpio_free(GPIO_AC97_SYNC); + + mxc_iomux_v3_setup_multiple_pads(ac97_pinmux, ARRAY_SIZE(ac97_pinmux)); + + /* clear transmit/receive configuration/status registers */ + writel(0x0, info->ac97_base + SAI_TCSR); + writel(0x0, info->ac97_base + SAI_RCSR); + + info->dma_params_tx.dma_addr = res->start + SAI_TDR; + info->dma_params_rx.dma_addr = res->start + SAI_RDR; + + /* 32 deep FIFOs */ + info->dma_params_tx.burstsize = 16; + info->dma_params_rx.burstsize = 16; + + res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx0"); + if (res) + info->dma_params_tx.dma = res->start; + + res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx0"); + if (res) + info->dma_params_rx.dma = res->start; + + platform_set_drvdata(pdev, info); + + if (!pdev->dev.dma_mask) + pdev->dev.dma_mask = &mvf_pcm_dmamask; + if (!pdev->dev.coherent_dma_mask) + pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32); + + /* pre allocate DMA buffers */ + size_t size = MVF_SAI_AC97_DMABUF_SIZE; + + info->tx_buf.dev.type = SNDRV_DMA_TYPE_DEV; + info->tx_buf.dev.dev = &pdev->dev; + info->tx_buf.private_data = NULL; + info->tx_buf.area = dma_alloc_writecombine(&pdev->dev, size, + &info->tx_buf.addr, GFP_KERNEL); + if (!info->tx_buf.area) { + err = -ENOMEM; + goto failed_tx_buf; + } + info->tx_buf.bytes = size; + + info->rx_buf.dev.type = SNDRV_DMA_TYPE_DEV; + info->rx_buf.dev.dev = &pdev->dev; + info->rx_buf.private_data = NULL; + info->rx_buf.area = dma_alloc_writecombine(&pdev->dev, size, + &info->rx_buf.addr, GFP_KERNEL); + if (!info->rx_buf.area) { + err = -ENOMEM; + goto failed_rx_buf; + } + info->rx_buf.bytes = size; + + memset(info->tx_buf.area, 0, MVF_SAI_AC97_DMABUF_SIZE); + memset(info->rx_buf.area, 0, MVF_SAI_AC97_DMABUF_SIZE); + + /* 1. Configuration of SAI clock mode */ + + /* Issue software reset and FIFO reset for Transmitter and Receiver + sections before starting configuration. */ + reg = readl(info->ac97_base + SAI_TCSR); + writel(reg | SAI_TCSR_SR, info->ac97_base + SAI_TCSR); /* Issue + software reset */ + udelay(2); + writel(reg & ~SAI_TCSR_SR, info->ac97_base + SAI_TCSR); /* Release + software reset */ + writel(reg | SAI_TCSR_FR, info->ac97_base + SAI_TCSR); /* FIFO reset */ + reg = readl(info->ac97_base + SAI_RCSR); + writel(reg | SAI_RCSR_SR, info->ac97_base + SAI_RCSR); /* Issue + software reset */ + udelay(2); + writel(reg & ~SAI_RCSR_SR, info->ac97_base + SAI_RCSR); /* Release + software reset */ + writel(reg | SAI_RCSR_FR, info->ac97_base + SAI_RCSR); /* FIFO reset */ + + /* Configure FIFO watermark. FIFO watermark is used as an indicator for + DMA trigger when read or write data from/to FIFOs. */ + /* Watermark level for all enabled transmit channels of one SAI module. + */ + writel(info->dma_params_tx.burstsize, info->ac97_base + SAI_TCR1); + writel(info->dma_params_rx.burstsize, info->ac97_base + SAI_RCR1); + + /* Configure the clocking mode, bitclock polarity, direction, and + divider. Clocking mode defines synchronous or asynchronous operation + for SAI module. Bitclock polarity configures polarity of the + bitclock. Bitclock direction configures direction of the bitclock. + Bus master has bitclock generated externally, slave has bitclock + generated internally */ + reg = readl(info->ac97_base + SAI_TCR2); + reg &= ~SAI_TCR2_SYNC_MASK; /* The transmitter must be configured + for asynchronous operation and the receiver for + synchronous operation. */ + reg &= ~SAI_TCR2_BCS; /* bit clock not swapped */ + reg &= ~SAI_TCR2_BCP; /* Bitclock is active high (drive outputs on + rising edge and sample inputs on falling edge */ + reg &= ~SAI_TCR2_BCD_MSTR; /* Bitclock is generated externally + (Slave mode) */ + writel(reg, info->ac97_base + SAI_TCR2); + reg = readl(info->ac97_base + SAI_RCR2); + reg &= ~SAI_RCR2_SYNC_MASK; /* The transmitter must be configured + for asynchronous operation and the receiver */ + reg |= SAI_RCR2_SYNC; /* for synchronous operation. */ + reg &= ~SAI_RCR2_BCS; /* bit clock not swapped */ + reg &= ~SAI_RCR2_BCP; /* Bitclock is active high (drive outputs on + rising edge and sample inputs on falling edge */ + reg &= ~SAI_RCR2_BCD_MSTR; /* Bitclock is generated externally + (Slave mode) */ + writel(reg, info->ac97_base + SAI_RCR2); + + /* Configure frame size, frame sync width, MSB first, frame sync early, + polarity, and direction + Frame size – configures the number of words in each frame. AC97 + requires 13 words per frame. + Frame sync width – configures the length of the frame sync in number + of bitclock. The sync width cannot be longer than the first word of + the frame. AC97 requires frame sync asserted for first word. */ + reg = readl(info->ac97_base + SAI_TCR4); + reg &= ~SAI_TCR4_FRSZ_MASK; /* Configures number of words in each */ + reg |= SAI_TCR4_FRSZ(12); /* frame. The value written should be + one less than the number of words in the frame. */ + reg &= ~SAI_TCR4_SYWD_MASK; /* Configures length of the frame */ + reg |= SAI_TCR4_SYWD(15); /* sync. The value written should be one + less than the number of bitclocks. + AC97 - 16 bits transmitted in first word. */ + reg |= SAI_TCR4_MF; /* MSB is transmitted first */ + reg |= SAI_TCR4_FSE; /* Frame sync asserted one bit before the first + bit of the frame */ + reg &= ~SAI_TCR4_FSP; /* A new AC-link input frame begins with a low + to high transition of SYNC. + Frame sync is active high */ + reg |= SAI_TCR4_FSD_MSTR; /* Frame sync is generated internally + (Master mode) */ + writel(reg, info->ac97_base + SAI_TCR4); + reg = readl(info->ac97_base + SAI_RCR4); + reg &= ~SAI_RCR4_FRSZ_MASK; /* Configures number of words in each */ + reg |= SAI_RCR4_FRSZ(12); /* frame. The value written should be + one less than the number of words in the frame. */ + reg &= ~SAI_RCR4_SYWD_MASK; /* Configures length of the frame */ + reg |= SAI_RCR4_SYWD(15); /* sync. The value written should be one + less than the number of bitclocks. + AC97 - 16 bits transmitted in first word. */ + reg |= SAI_RCR4_MF; /* MSB is transmitted first */ + reg |= SAI_RCR4_FSE; /* Frame sync asserted one bit before the first + bit of the frame */ + reg &= ~SAI_RCR4_FSP; /* A new AC-link input frame begins with a low + to high transition of SYNC. + Frame sync is active high */ + reg |= SAI_RCR4_FSD_MSTR; /* Frame sync is generated internally + (Master mode) */ + writel(reg, info->ac97_base + SAI_RCR4); + + /* Configure the Word 0 and next word sizes. + W0W – defines number of bits in the first word in each frame. + WNW – defines number of bits in each word for each word except the + first in the frame. */ + reg = readl(info->ac97_base + SAI_TCR5); + reg &= ~SAI_TCR5_W0W_MASK; /* Number of bits in first word in */ + reg |= SAI_TCR5_W0W(15); /* each frame. AC97 – 16-bit word is + transmitted. */ + reg &= ~SAI_TCR5_WNW_MASK; /* Number of bits in each word in */ + reg |= SAI_TCR5_WNW(19); /* each frame. AC97 – 20-bit word is + transmitted. */ + reg &= ~SAI_TCR5_FBT_MASK; /* Configures the bit index for the + first bit transmitted for each word + in the frame. */ + reg |= SAI_TCR5_FBT(19); /* The value written must be greater + than or equal to the word width when + configured for MSB First. */ + writel(reg, info->ac97_base + SAI_TCR5); + reg = readl(info->ac97_base + SAI_RCR5); + reg &= ~SAI_RCR5_W0W_MASK; /* Number of bits in first word in */ + reg |= SAI_RCR5_W0W(15); /* each frame. AC97 – 16-bit word is + transmitted. */ + reg &= ~SAI_RCR5_WNW_MASK; /* Number of bits in each word in */ + reg |= SAI_RCR5_WNW(19); /* each frame. AC97 – 20-bit word is + transmitted. */ + reg &= ~SAI_RCR5_FBT_MASK; /* Configures the bit index for the + first bit transmitted for each word + in the frame. */ + reg |= SAI_RCR5_FBT(19); /* The value written must be greater + than or equal to the word width when + configured for MSB First. */ + writel(reg, info->ac97_base + SAI_RCR5); + + /* Clear the Transmit and Receive Mask registers. */ + writel(0, info->ac97_base + SAI_TMR); /* Enable or mask word N in the + frame. */ + writel(0, info->ac97_base + SAI_RMR); /* Enable or mask word N in the + frame. */ + + /** + * mcf_edma_request_channel - Request an eDMA channel + * @channel: channel number. In case it is equal to EDMA_CHANNEL_ANY + * it will be allocated a first free eDMA channel. + * @handler: dma handler + * @error_handler: dma error handler + * @irq_level: irq level for the dma handler + * @arg: argument to pass back + * @lock: optional spinlock to hold over interrupt + * @device_id: device id + * + * Returns allocatedd channel number if success or + * a negative value if failure. + */ + err = mcf_edma_request_channel(DMA_MUX_SAI2_TX, mvf_sai_ac97_dma_irq, + NULL, EDMA_PRIO_HIGH, NULL, NULL, DRIVER_NAME); + if (err < 0) + goto failed_request_tx_dma; + info->tx_tcd_chan = err; + + err = mcf_edma_request_channel(DMA_MUX_SAI2_RX, mvf_sai_ac97_dma_irq, + NULL, EDMA_PRIO_HIGH, NULL, NULL, DRIVER_NAME); + if (err < 0) + goto failed_request_rx_dma; + info->rx_tcd_chan = err; + + /* Setup transfer control descriptor (TCD) + * channel - descriptor number + * source - source address + * dest - destination address + * attr - attributes + * soff - source offset + * nbytes - number of bytes to be transfered in minor loop + * slast - last source address adjustment + * citer - major loop count + * biter - begining minor loop count + * doff - destination offset + * dlast_sga - last destination address adjustment + * major_int - generate interrupt after each major loop + * disable_req - disable DMA request after major loop + * enable_sg - enable scatter/gather processing + */ + mcf_edma_set_tcd_params(info->tx_tcd_chan, info->tx_buf.addr, + info->dma_params_tx.dma_addr, + MCF_EDMA_TCD_ATTR_SSIZE_32BIT | + MCF_EDMA_TCD_ATTR_DSIZE_32BIT, 4, 4, -13 * 4, + 13, 13, 0, 0, 1, 0, 0); + mcf_edma_set_tcd_params(info->rx_tcd_chan, info->dma_params_rx.dma_addr, + info->rx_buf.addr, + MCF_EDMA_TCD_ATTR_SSIZE_32BIT | + MCF_EDMA_TCD_ATTR_DSIZE_32BIT, 0, 4, 0, + 13, 13, 4, -13 * 4, 1, 0, 0); + + reg = readl(info->ac97_base + SAI_TCR3); + reg |= SAI_TCR3_TCE; /* Enables a data channel for a transmit + operation. */ + writel(reg, info->ac97_base + SAI_TCR3); + reg = readl(info->ac97_base + SAI_RCR3); + reg |= SAI_RCR3_RCE; /* Enables a data channel for a receive + operation. */ + writel(reg, info->ac97_base + SAI_RCR3); + + mcf_edma_start_transfer(info->tx_tcd_chan); + mcf_edma_start_transfer(info->rx_tcd_chan); + + tcsr = readl(info->ac97_base + SAI_TCSR); + rcsr = readl(info->ac97_base + SAI_RCSR); + + /* enable transmit DMA */ + tcsr |= SAI_TCSR_FRDE; + + /* enable receive DMA */ + rcsr |= SAI_RCSR_FRDE; + + /* enable transmit/receive */ + tcsr |= SAI_TCSR_TE; + rcsr |= SAI_RCSR_RE; + + info->rd_state = RD_IDLE; + info->wr_state = WR_IDLE; + + /* In synchronous mode, receiver is enabled only when both transmitter + and receiver are enabled. It is recommended that transmitter is + enabled last and disabled first. */ + writel(rcsr, info->ac97_base + SAI_RCSR); + writel(tcsr, info->ac97_base + SAI_TCSR); + + err = snd_ac97_bus(info->card, 0, &mvf_sai_ac97_ops, NULL, &ac97_bus); + if (err) + goto failed_bus; + memset(&ac97_template, 0, sizeof(ac97_template)); + err = snd_ac97_mixer(ac97_bus, &ac97_template, &mvf_sai_ac97_ac97); + if (err) + goto failed_mixer; + + snprintf(info->card->shortname, sizeof(info->card->shortname), "%s", + snd_ac97_get_short_name(mvf_sai_ac97_ac97)); + snprintf(info->card->longname, sizeof(info->card->longname), "%s (%s)", + pdev->dev.driver->name, info->card->mixername); + + snd_card_set_dev(info->card, &pdev->dev); + err = snd_card_register(info->card); + if (err == 0) { + platform_set_drvdata(pdev, info->card); + return 0; + } + +failed: + if (info->card) + snd_card_disconnect(info->card); +failed_mixer: +failed_bus: + /* disable transmit/receive and respective DMAs */ + tcsr = readl(info->ac97_base + SAI_TCSR); + rcsr = readl(info->ac97_base + SAI_RCSR); + tcsr &= ~SAI_TCSR_FRDE; + rcsr &= ~SAI_RCSR_FRDE; + tcsr &= ~SAI_TCSR_TE; + rcsr &= ~SAI_RCSR_RE; + writel(rcsr, info->ac97_base + SAI_RCSR); + writel(tcsr, info->ac97_base + SAI_TCSR); + + mcf_edma_free_channel(DMA_MUX_SAI2_RX, NULL); +failed_request_rx_dma: + mcf_edma_free_channel(DMA_MUX_SAI2_TX, NULL); +failed_request_tx_dma: + dma_free_writecombine(&pdev->dev, MVF_SAI_AC97_DMABUF_SIZE, + info->rx_buf.area, info->rx_buf.addr); +failed_rx_buf: + dma_free_writecombine(&pdev->dev, MVF_SAI_AC97_DMABUF_SIZE, + info->tx_buf.area, info->tx_buf.addr); +failed_tx_buf: +//iomux + snd_card_free(info->card); +failed_create: + clk_disable(info->clk); + clk_put(info->clk); + + /* disable AC97 master clock */ + reg = readl(info->sai0_base + SAI_TCSR); + writel(reg & ~SAI_TCSR_BCE, info->sai0_base + SAI_TCSR); +failed_clk: + iounmap((volatile void *)MVF_SAI0_BASE_ADDR); +failed_map2: + iounmap(info->ac97_base); +failed_map: + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res) release_mem_region(res->start, resource_size(res)); +failed_region: +failed_mem: +// free_irq(info->irq, NULL); +failed_irq: + kfree(info); +failed_alloc: + return err; +} + +static int __devexit mvf_sai_ac97_dev_remove(struct platform_device *pdev) +{ + unsigned int reg; + unsigned int rcsr; + struct resource *res; + unsigned int tcsr; + + snd_card_disconnect(info->card); + + /* disable transmit/receive and respective DMAs */ + tcsr = readl(info->ac97_base + SAI_TCSR); + rcsr = readl(info->ac97_base + SAI_RCSR); + tcsr &= ~SAI_TCSR_FRDE; + rcsr &= ~SAI_RCSR_FRDE; + tcsr &= ~SAI_TCSR_TE; + rcsr &= ~SAI_RCSR_RE; + writel(rcsr, info->ac97_base + SAI_RCSR); + writel(tcsr, info->ac97_base + SAI_TCSR); + + mcf_edma_free_channel(DMA_MUX_SAI2_RX, NULL); + mcf_edma_free_channel(DMA_MUX_SAI2_TX, NULL); + dma_free_writecombine(&pdev->dev, MVF_SAI_AC97_DMABUF_SIZE, info->rx_buf.area, info->rx_buf.addr); + dma_free_writecombine(&pdev->dev, MVF_SAI_AC97_DMABUF_SIZE, info->tx_buf.area, info->tx_buf.addr); +//iomux + snd_card_free(info->card); + clk_disable(info->clk); + clk_put(info->clk); + + /* disable AC97 master clock */ + reg = readl(info->sai0_base + SAI_TCSR); + writel(reg & ~SAI_TCSR_BCE, info->sai0_base + SAI_TCSR); + + iounmap((volatile void *)MVF_SAI0_BASE_ADDR); + iounmap(info->ac97_base); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (res) release_mem_region(res->start, resource_size(res)); +// free_irq(info->irq, NULL); + kfree(info); + + return 0; +} + +static struct platform_driver mvf_sai_ac97_driver = { + .probe = mvf_sai_ac97_dev_probe, + .remove = __devexit_p(mvf_sai_ac97_dev_remove), + .driver = { + .name = "mvf-sai", + .owner = THIS_MODULE, + }, +}; + +static int __init mvf_sai_ac97_init(void) +{ + return platform_driver_register(&mvf_sai_ac97_driver); +} +module_init(mvf_sai_ac97_init); + +static void __exit mvf_sai_ac97_exit(void) +{ + platform_driver_unregister(&mvf_sai_ac97_driver); +} +module_exit(mvf_sai_ac97_exit); + +MODULE_AUTHOR("Marcel Ziswiler"); +MODULE_DESCRIPTION("Software AC97 driver for the SAI of the Vybrid"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/mvf/mvf-sai.h b/sound/soc/mvf/mvf-sai.h index 1406e28883a0..720488a5974b 100644 --- a/sound/soc/mvf/mvf-sai.h +++ b/sound/soc/mvf/mvf-sai.h @@ -13,6 +13,9 @@ #define SAI_TCSR 0x00 #define SAI_TCSR_TE (1 << 31) +#define SAI_TCSR_BCE (1 << 28) +#define SAI_TCSR_FR (1 << 25) +#define SAI_TCSR_SR (1 << 24) #define SAI_TCSR_FWF (1 << 17) #define SAI_TCSR_FRIE (1 << 8) #define SAI_TCSR_FRDE (1 << 0) @@ -20,8 +23,11 @@ #define SAI_TCR1 0x04 #define SAI_TCR2 0x08 +#define SAI_TCR2_SYNC_MASK (3 << 30) #define SAI_TCR2_SYNC (1 << 30) -#define SAI_TCR2_MSEL_MASK (0xff << 26) +#define SAI_TCR2_BCS (1 << 29) +#define SAI_TCR2_BCI (1 << 28) +#define SAI_TCR2_MSEL_MASK (3 << 26) #define SAI_TCR2_MSEL_BUS (0 << 26) #define SAI_TCR2_MSEL_MCLK1 (1 << 26) #define SAI_TCR2_MSEL_MCLK2 (2 << 26) @@ -39,7 +45,7 @@ #define SAI_TCR3_WDFL_MASK 0x1f #define SAI_TCR4 0x10 -#define SAI_TCR4_FRSZ(x) (x << 16) +#define SAI_TCR4_FRSZ(x) ((x) << 16) #define SAI_TCR4_FRSZ_MASK (0x1f << 16) #define SAI_TCR4_SYWD(x) ((x) << 8) #define SAI_TCR4_SYWD_MASK (0x1f << 8) @@ -66,6 +72,8 @@ #define SAI_RCSR 0x80 #define SAI_RCSR_RE (1 << 31) +#define SAI_RCSR_FR (1 << 25) +#define SAI_RCSR_SR (1 << 24) #define SAI_RCSR_FWF (1 << 17) #define SAI_RCSR_FRIE (1 << 8) #define SAI_RCSR_FRDE (1 << 0) @@ -73,7 +81,11 @@ #define SAI_RCR1 0x84 #define SAI_RCR2 0x88 -#define SAI_RCR2_MSEL_MASK (0xff << 26) +#define SAI_RCR2_SYNC_MASK (3 << 30) +#define SAI_RCR2_SYNC (1 << 30) +#define SAI_RCR2_BCS (1 << 29) +#define SAI_RCR2_BCI (1 << 28) +#define SAI_RCR2_MSEL_MASK (3 << 26) #define SAI_RCR2_MSEL_BUS (0 << 26) #define SAI_RCR2_MSEL_MCLK1 (1 << 26) #define SAI_RCR2_MSEL_MCLK2 (2 << 26) @@ -86,15 +98,15 @@ #define SAI_RCR2_DIV_MASK 0xff #define SAI_RCR3 0x8c -#define SAI_RCR3_TCE (1 << 16) +#define SAI_RCR3_RCE (1 << 16) #define SAI_RCR3_WDFL(x) (x) #define SAI_RCR3_WDFL_MASK 0x1f #define SAI_RCR4 0x90 /* Frame sync is active low */ -#define SAI_RCR4_FRSZ(x) (x << 16) +#define SAI_RCR4_FRSZ(x) ((x) << 16) #define SAI_RCR4_FRSZ_MASK (0x1f << 16) -#define SAI_RCR4_SYWD(x) (x << 8) +#define SAI_RCR4_SYWD(x) ((x) << 8) #define SAI_RCR4_SYWD_MASK (0x1f << 8) #define SAI_RCR4_MF (1 << 4) /* Frame sync is active low */ @@ -103,10 +115,12 @@ #define SAI_RCR4_FSD_MSTR (1 << 0) #define SAI_RCR5 0x94 -#define SAI_RCR5_WNW(x) (x << 24) +#define SAI_RCR5_WNW(x) ((x) << 24) #define SAI_RCR5_WNW_MASK (0x1f << 24) -#define SAI_RCR5_W0W(x) (x << 16) +#define SAI_RCR5_W0W(x) ((x) << 16) #define SAI_RCR5_W0W_MASK (0x1f << 16) +#define SAI_RCR5_FBT(x) ((x) << 8) +#define SAI_RCR5_FBT_MASK (0x1f << 8) #define SAI_RDR 0xa0 |