summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/arm/configs/colibri_vf_defconfig318
-rw-r--r--arch/arm/mach-mvf/Kconfig45
-rw-r--r--arch/arm/mach-mvf/Makefile1
-rw-r--r--arch/arm/mach-mvf/board-colibri_vf.c734
-rw-r--r--arch/arm/mach-mvf/clock.c316
-rw-r--r--arch/arm/mach-mvf/crm_regs.h9
-rw-r--r--arch/arm/mach-mvf/mvf_fec.c2
-rw-r--r--arch/arm/mach-mvf/system.c73
-rw-r--r--arch/arm/plat-mxc/devices/platform-mvf-adc.c4
-rw-r--r--arch/arm/plat-mxc/devices/platform-mvf-dcu.c2
-rw-r--r--arch/arm/plat-mxc/gpio-mvf.c8
-rw-r--r--arch/arm/plat-mxc/include/mach/colibri-ts.h30
-rw-r--r--arch/arm/plat-mxc/include/mach/iomux-mvf.h151
-rw-r--r--arch/arm/tools/mach-types2
-rwxr-xr-xdrivers/input/touchscreen/Kconfig20
-rwxr-xr-xdrivers/input/touchscreen/Makefile2
-rw-r--r--drivers/input/touchscreen/colibri-vf50-ts.c456
-rw-r--r--drivers/input/touchscreen/fusion_F0710A.c502
-rw-r--r--drivers/input/touchscreen/fusion_F0710A.h87
-rw-r--r--drivers/misc/mvf_adc.c381
-rw-r--r--drivers/mtd/nand/fsl_nfc.c16
-rw-r--r--drivers/mtd/nand/nand_ids.c1
-rwxr-xr-xdrivers/net/fec.c6
-rw-r--r--drivers/net/phy/micrel.c10
-rw-r--r--drivers/spi/spi_mvf_dspi.c49
-rw-r--r--drivers/tty/serial/mvf.c6
-rw-r--r--drivers/video/mvf_dcu.c277
-rw-r--r--include/linux/input/fusion_F0710A.h20
-rw-r--r--include/linux/mtd/nand.h1
-rw-r--r--include/linux/mvf_adc.h30
-rw-r--r--sound/soc/mvf/Kconfig15
-rw-r--r--sound/soc/mvf/Makefile2
-rw-r--r--sound/soc/mvf/mvf-sai-ac97.c838
-rw-r--r--sound/soc/mvf/mvf-sai.h30
34 files changed, 4158 insertions, 286 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..a19fa1bae723 100644
--- a/arch/arm/mach-mvf/Kconfig
+++ b/arch/arm/mach-mvf/Kconfig
@@ -25,6 +25,51 @@ 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 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..782d8d7b2ddd
--- /dev/null
+++ b/arch/arm/mach-mvf/board-colibri_vf.c
@@ -0,0 +1,734 @@
+/*
+ * 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,
+ MVF600_PAD18_PTA28__SDHC1_DAT2,
+ MVF600_PAD19_PTA29__SDHC1_DAT3,
+ /* 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,
+
+ /* 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,
+ MVF600_PAD45_PTC0_BL_ON,
+
+ /* 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,
+
+ /* USB */
+ MVF600_PAD83_PTD4__USBH_PEN,
+ MVF600_PAD102_PTC29__USBC_DET, /* multiplexed USB0_VBUS_DET */
+ MVF600_PAD108_PTE3__USB_OC,
+
+ /* PWM */
+ MVF600_PAD22_PTB0_FTM0CH0, //PWM<A> multiplexed MVF600_PAD52_PTC7_VID7
+ MVF600_PAD23_PTB1_FTM0CH1, //PWM<c>
+ MVF600_PAD30_PTB8_FTM1CH0, //PWM<B>
+ MVF600_PAD31_PTB9_FTM1CH1, //PWM<D> multiplexed MVF600_PAD51_PTC6_VID6
+
+ /* 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_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_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,
+};
+
+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);
+}
+
+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_pen = 8, /* PAD8 */
+};
+
+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();
+
+ mvf_add_sdhci_esdhc_imx(1, &mvfa5_sd1_data);
+
+ 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)
+{
+ mxc_iomux_v3_setup_multiple_pads(colibri_vf50_pads,
+ ARRAY_SIZE(colibri_vf50_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..7c95a2638128 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 */
@@ -1910,10 +2061,12 @@ static struct clk_lookup lookups[] = {
_REGISTER_CLOCK(NULL, "mvf-uart.2", uart_clk[0]),
_REGISTER_CLOCK(NULL, "mvf-uart.3", 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 +2102,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 +2115,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 +2130,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 +2147,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/mvf_fec.c b/arch/arm/mach-mvf/mvf_fec.c
index d51f20519c0b..55ba44bf73f0 100644
--- a/arch/arm/mach-mvf/mvf_fec.c
+++ b/arch/arm/mach-mvf/mvf_fec.c
@@ -52,7 +52,9 @@ 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);
+#if !defined(CONFIG_COLIBRI_VF)
mvf_add_fec(0, &fec_data);
+#endif
#ifdef CONFIG_FEC1
mvf_add_fec(1, &fec_data);
#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-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/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..dde7c3ce8687
--- /dev/null
+++ b/arch/arm/plat-mxc/include/mach/colibri-ts.h
@@ -0,0 +1,30 @@
+/*
+ * 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);
+ unsigned int gpio_pen;
+ int (*mux_pen_interrupt)(struct platform_device *pdev);
+ int (*mux_adc)(struct platform_device *pdev);
+};
+
+#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..9978dc02f832 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,33 @@ 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_PAD45_PTC0__RMII0_MDC \
IOMUX_PAD(0x00B4, 0x00B4, 1, 0x0000, 0, \
MVF600_ENET_PAD_CTRL | PAD_CTL_OBE_ENABLE)
@@ -188,12 +229,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 +272,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 +304,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 +384,39 @@ 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)
+
+/* 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, \
@@ -354,8 +452,55 @@ 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)
+#define MVF600_PAD5_PTA12 \
+ IOMUX_PAD(0x0014, 0x0014, 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..5b0a796d3f54
--- /dev/null
+++ b/drivers/input/touchscreen/colibri-vf50-ts.c
@@ -0,0 +1,456 @@
+/* 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
+
+#define COL_TS_GPIO_XP 0
+#define COL_TS_GPIO_XM 1
+#define COL_TS_GPIO_YP 2
+#define COL_TS_GPIO_YM 3
+#define COL_TS_GPIO_TOUCH 4
+#define COL_TS_GPIO_PULLUP 5
+
+#define COL_TS_WIRE_XP 0
+#define COL_TS_WIRE_XM 1
+#define COL_TS_WIRE_YP 2
+#define COL_TS_WIRE_YM 3
+
+struct col_ts_driver {
+ int enable_state;
+ struct gpio *control_gpio;
+};
+
+/* GPIO */
+static struct gpio col_ts_gpios[] = {
+ [COL_TS_GPIO_XP] = { 13, GPIOF_IN, "Touchscreen PX" },
+ [COL_TS_GPIO_XM] = { 5, GPIOF_IN, "Touchscreen MX" },
+ [COL_TS_GPIO_YP] = { 12, GPIOF_IN, "Touchscreen PY" },
+ [COL_TS_GPIO_YM] = { 4, GPIOF_IN, "Touchscreen MY" },
+ [COL_TS_GPIO_TOUCH] = { 8, GPIOF_IN, "Touchscreen (Touch interrupt)" },
+ [COL_TS_GPIO_PULLUP] = { 9, GPIOF_IN, "Touchscreen (Pull-up)" },
+};
+
+/* GPIOs and their FET configuration */
+static struct col_ts_driver col_ts_drivers[] = {
+ [COL_TS_WIRE_XP] = {
+ .enable_state = 0,
+ .control_gpio = &col_ts_gpios[COL_TS_GPIO_XP],
+ },
+ [COL_TS_WIRE_XM] = {
+ .enable_state = 1,
+ .control_gpio = &col_ts_gpios[COL_TS_GPIO_XM],
+ },
+ [COL_TS_WIRE_YP] = {
+ .enable_state = 0,
+ .control_gpio = &col_ts_gpios[COL_TS_GPIO_YP],
+ },
+ [COL_TS_WIRE_YM] = {
+ .enable_state = 1,
+ .control_gpio = &col_ts_gpios[COL_TS_GPIO_YM],
+ },
+};
+
+#define col_ts_init_wire(ts_gpio) \
+ gpio_direction_output(col_ts_drivers[ts_gpio].control_gpio->gpio, \
+ !col_ts_drivers[ts_gpio].enable_state)
+
+#define col_ts_enable_wire(ts_gpio) \
+ gpio_set_value(col_ts_drivers[ts_gpio].control_gpio->gpio, \
+ col_ts_drivers[ts_gpio].enable_state)
+
+#define col_ts_disable_wire(ts_gpio) \
+ gpio_set_value(col_ts_drivers[ts_gpio].control_gpio->gpio, \
+ !col_ts_drivers[ts_gpio].enable_state)
+
+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 plate1, int plate2, int adc, int adc_channel)
+{
+ int i, value = 0;
+ col_ts_enable_wire(plate1);
+ col_ts_enable_wire(plate2);
+
+ /* 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;
+
+ col_ts_disable_wire(plate1);
+ col_ts_disable_wire(plate2);
+
+ 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 0) */
+ col_ts_enable_wire(COL_TS_WIRE_YM);
+
+ /* 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;
+ 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(COL_TS_WIRE_XP, COL_TS_WIRE_XM, 1, 0);
+ if (val_x < 0)
+ continue;
+
+ /* Y-Direction */
+ val_y = adc_ts_measure(COL_TS_WIRE_YP, COL_TS_WIRE_YM, 0, 0);
+ if (val_y < 0)
+ continue;
+
+ /* Touch pressure
+ * Measure on XP/YM
+ */
+ val_z1 = adc_ts_measure(COL_TS_WIRE_YP, COL_TS_WIRE_XM, 0, 1);
+ if (val_z1 < 0)
+ continue;
+ val_z2 = adc_ts_measure(COL_TS_WIRE_YP, COL_TS_WIRE_XM, 1, 2);
+ if (val_z2 < 0)
+ continue;
+
+ /* According to datasheet of our touchscreen,
+ * resistance on X axis is 400~1200..
+ */
+ // if ((val_z2 - val_z1) < (MVF_ADC_MAX - (1<<9)) {
+ /* 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 > 1050)
+ 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 */
+ col_ts_disable_wire(COL_TS_WIRE_YM);
+
+ /* 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 */
+ col_ts_init_wire(COL_TS_WIRE_XP);
+ col_ts_init_wire(COL_TS_WIRE_XM);
+ col_ts_init_wire(COL_TS_WIRE_YP);
+ col_ts_init_wire(COL_TS_WIRE_YM);
+
+ /* 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);
+ if (adc_ts->pen_irq < 0) {
+ dev_err(dev, "Unable to get IRQ for GPIO %d\n", pdata->gpio_pen);
+ 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(gpio_to_irq(col_ts_gpios[COL_TS_GPIO_TOUCH].gpio), 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;
+
+ 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_array(col_ts_gpios, COL_TS_GPIO_PULLUP + 1);
+ if (ret) {
+ dev_err(dev, "failed to request GPIOs\n");
+ 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..8def52a718e0 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>
@@ -787,6 +788,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;
@@ -842,7 +844,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 +923,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/fec.c b/drivers/net/fec.c
index 73f3f2b49a61..8a2c60f29898 100755
--- a/drivers/net/fec.c
+++ b/drivers/net/fec.c
@@ -691,9 +691,11 @@ static void __inline__ fec_get_mac(struct net_device *ndev)
memcpy(ndev->dev_addr, iap, ETH_ALEN);
+#if !defined(CONFIG_COLIBRI_VF)
/* Adjust MAC if using macaddr */
if (iap == macaddr)
ndev->dev_addr[ETH_ALEN-1] = macaddr[ETH_ALEN-1] + fep->pdev->id;
+#endif /* !CONFIG_COLIBRI_VF */
}
/* ------------------------------------------------------------------------- */
@@ -876,6 +878,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 +900,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 +944,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 814a7daa56f0..ae35ffdf4907 100644
--- a/drivers/tty/serial/mvf.c
+++ b/drivers/tty/serial/mvf.c
@@ -373,7 +373,8 @@ 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) {
+ /* check if tty is valid, since the process might be gone... */
+ if (sport->rx_bytes && tty) {
tty_flip_buffer_push(tty);
sport->rx_bytes = 0;
}
@@ -562,6 +563,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);
@@ -586,10 +588,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)
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