diff options
author | Cliff Brake <cbrake@bec-systems.com> | 2007-10-17 16:37:11 +0000 |
---|---|---|
committer | Cliff Brake <cbrake@bec-systems.com> | 2007-10-17 16:37:11 +0000 |
commit | 6ac35c4a12465f744fadc9f7123ace504ef29f1d (patch) | |
tree | 1d287e7324ae1dccca62b0a893fd9c891bf8e6e6 /packages/linux/em-x270-2.6.23 | |
parent | d4a39a9491b84814286f0816819bc048ba9d7e68 (diff) |
em-x270: add machine/kernel support (from compulab)
em-x270 kernel recipe will likely be merged with linux_2.6.23 shortly ...
Diffstat (limited to 'packages/linux/em-x270-2.6.23')
-rw-r--r-- | packages/linux/em-x270-2.6.23/.mtn2git_empty | 0 | ||||
-rw-r--r-- | packages/linux/em-x270-2.6.23/defconfig | 1354 | ||||
-rw-r--r-- | packages/linux/em-x270-2.6.23/em-x270.patch | 12063 |
3 files changed, 13417 insertions, 0 deletions
diff --git a/packages/linux/em-x270-2.6.23/.mtn2git_empty b/packages/linux/em-x270-2.6.23/.mtn2git_empty new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/packages/linux/em-x270-2.6.23/.mtn2git_empty diff --git a/packages/linux/em-x270-2.6.23/defconfig b/packages/linux/em-x270-2.6.23/defconfig new file mode 100644 index 0000000000..3246136571 --- /dev/null +++ b/packages/linux/em-x270-2.6.23/defconfig @@ -0,0 +1,1354 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.23-rc9 +# Tue Oct 9 11:19:21 2007 +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +CONFIG_GENERIC_TIME=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_MMU=y +# CONFIG_NO_IOPORT is not set +CONFIG_GENERIC_HARDIRQS=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +# CONFIG_ARCH_HAS_ILOG2_U32 is not set +# CONFIG_ARCH_HAS_ILOG2_U64 is not set +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_ZONE_DMA=y +CONFIG_ARCH_MTD_XIP=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_LOCALVERSION="-em-x270" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_TASKSTATS is not set +# CONFIG_USER_NS is not set +# CONFIG_AUDIT is not set +CONFIG_IKCONFIG=y +CONFIG_IKCONFIG_PROC=y +CONFIG_LOG_BUF_SHIFT=17 +CONFIG_SYSFS_DEPRECATED=y +# CONFIG_RELAY is not set +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="" +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_EMBEDDED=y +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_ANON_INODES=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_SLUB_DEBUG=y +# CONFIG_SLAB is not set +CONFIG_SLUB=y +# CONFIG_SLOB is not set +CONFIG_RT_MUTEXES=y +# CONFIG_TINY_SHMEM is not set +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_KMOD=y +CONFIG_BLOCK=y +# CONFIG_LBD is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_LSF is not set +# CONFIG_BLK_DEV_BSG is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +CONFIG_DEFAULT_AS=y +# CONFIG_DEFAULT_DEADLINE is not set +# CONFIG_DEFAULT_CFQ is not set +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="anticipatory" + +# +# System Type +# +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_CLPS7500 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CO285 is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_IMX is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_PNX4008 is not set +CONFIG_ARCH_PXA=y +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP is not set + +# +# Intel PXA2xx Implementations +# +# CONFIG_ARCH_LUBBOCK is not set +# CONFIG_MACH_LOGICPD_PXA270 is not set +# CONFIG_MACH_MAINSTONE is not set +# CONFIG_ARCH_PXA_IDP is not set +# CONFIG_PXA_SHARPSL is not set +# CONFIG_MACH_TRIZEPS4 is not set +CONFIG_MACH_EM_X270=y +CONFIG_PXA27x=y +CONFIG_PXA_PWR_I2C=y + +# +# Boot options +# + +# +# Power management +# + +# +# Processor Type +# +CONFIG_CPU_32=y +CONFIG_CPU_XSCALE=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5T=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_TLB_V4WBI=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_OUTER_CACHE is not set +CONFIG_IWMMXT=y +CONFIG_XSCALE_PMU=y + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set + +# +# PCCARD (PCMCIA/CardBus) support +# +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +# CONFIG_TICK_ONESHOT is not set +# CONFIG_NO_HZ is not set +# CONFIG_HIGH_RES_TIMERS is not set +# CONFIG_PREEMPT is not set +CONFIG_HZ=100 +CONFIG_AEABI=y +CONFIG_OABI_COMPAT=y +# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +# CONFIG_SPARSEMEM_STATIC is not set +CONFIG_SPLIT_PTLOCK_CPUS=4096 +# CONFIG_RESOURCES_64BIT is not set +CONFIG_ZONE_DMA_FLAG=1 +CONFIG_BOUNCE=y +CONFIG_VIRT_TO_BUS=y +CONFIG_ALIGNMENT_TRAP=y + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="root=/dev/mtdblock2 rootfstype=jffs2 console=ttyS0,115200" +# CONFIG_XIP_KERNEL is not set +# CONFIG_KEXEC is not set + +# +# CPU Frequency scaling +# +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_TABLE=y +# CONFIG_CPU_FREQ_DEBUG is not set +CONFIG_CPU_FREQ_STAT=y +# CONFIG_CPU_FREQ_STAT_DETAILS is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y +CONFIG_CPU_FREQ_GOV_POWERSAVE=m +CONFIG_CPU_FREQ_GOV_USERSPACE=m +CONFIG_CPU_FREQ_GOV_ONDEMAND=m +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=m +CONFIG_CPU_FREQ_PXA=y + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# +CONFIG_FPE_NWFPE=y +# CONFIG_FPE_NWFPE_XP is not set +# CONFIG_FPE_FASTFPE is not set + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_BINFMT_AOUT=m +CONFIG_BINFMT_MISC=m + +# +# Power management options +# +CONFIG_PM=y +CONFIG_PM_LEGACY=y +# CONFIG_PM_DEBUG is not set +CONFIG_PM_SLEEP=y +CONFIG_SUSPEND_UP_POSSIBLE=y +CONFIG_SUSPEND=y +CONFIG_APM_EMULATION=y + +# +# Networking +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +# CONFIG_PACKET_MMAP is not set +CONFIG_UNIX=y +CONFIG_XFRM=y +# CONFIG_XFRM_USER is not set +# CONFIG_XFRM_SUB_POLICY is not set +# CONFIG_XFRM_MIGRATE is not set +# CONFIG_NET_KEY is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +CONFIG_IP_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +CONFIG_INET_XFRM_MODE_TRANSPORT=y +CONFIG_INET_XFRM_MODE_TUNNEL=y +CONFIG_INET_XFRM_MODE_BEET=y +CONFIG_INET_DIAG=y +CONFIG_INET_TCP_DIAG=y +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +# CONFIG_IPV6 is not set +# CONFIG_INET6_XFRM_TUNNEL is not set +# CONFIG_INET6_TUNNEL is not set +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETFILTER is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set +CONFIG_NET_SCH_FIFO=y + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +CONFIG_BT=m +CONFIG_BT_L2CAP=m +CONFIG_BT_SCO=m +CONFIG_BT_RFCOMM=m +# CONFIG_BT_RFCOMM_TTY is not set +CONFIG_BT_BNEP=m +# CONFIG_BT_BNEP_MC_FILTER is not set +# CONFIG_BT_BNEP_PROTO_FILTER is not set +CONFIG_BT_HIDP=m + +# +# Bluetooth device drivers +# +CONFIG_BT_HCIUSB=m +# CONFIG_BT_HCIUSB_SCO is not set +CONFIG_BT_HCIUART=m +# CONFIG_BT_HCIUART_H4 is not set +# CONFIG_BT_HCIUART_BCSP is not set +CONFIG_BT_HCIBCM203X=m +CONFIG_BT_HCIBPA10X=m +CONFIG_BT_HCIBFUSB=m +# CONFIG_BT_HCIVHCI is not set +# CONFIG_AF_RXRPC is not set + +# +# Wireless +# +CONFIG_CFG80211=m +CONFIG_WIRELESS_EXT=y +CONFIG_MAC80211=m +# CONFIG_MAC80211_LEDS is not set +CONFIG_MAC80211_DEBUGFS=y +# CONFIG_MAC80211_DEBUG is not set +CONFIG_IEEE80211=m +# CONFIG_IEEE80211_DEBUG is not set +CONFIG_IEEE80211_CRYPT_WEP=m +CONFIG_IEEE80211_CRYPT_CCMP=m +# CONFIG_IEEE80211_CRYPT_TKIP is not set +# CONFIG_IEEE80211_SOFTMAC is not set +# CONFIG_RFKILL is not set +# CONFIG_NET_9P is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +# CONFIG_DEBUG_DRIVER is not set +# CONFIG_DEBUG_DEVRES is not set +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +CONFIG_MTD_CONCAT=y +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_CMDLINE_PARTS is not set +# CONFIG_MTD_AFS_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set + +# +# RAM/ROM/Flash chip drivers +# +CONFIG_MTD_CFI=y +CONFIG_MTD_JEDECPROBE=y +CONFIG_MTD_GEN_PROBE=y +# CONFIG_MTD_CFI_ADV_OPTIONS is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +CONFIG_MTD_CFI_INTELEXT=y +CONFIG_MTD_CFI_AMDSTD=y +CONFIG_MTD_CFI_STAA=y +CONFIG_MTD_CFI_UTIL=y +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set +# CONFIG_MTD_XIP is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +CONFIG_MTD_PHYSMAP=y +CONFIG_MTD_PHYSMAP_START=0x0 +CONFIG_MTD_PHYSMAP_LEN=0x100000 +CONFIG_MTD_PHYSMAP_BANKWIDTH=2 +# CONFIG_MTD_ARM_INTEGRATOR is not set +# CONFIG_MTD_IMPA7 is not set +# CONFIG_MTD_SHARP_SL is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +CONFIG_MTD_NAND=y +# CONFIG_MTD_NAND_VERIFY_WRITE is not set +# CONFIG_MTD_NAND_ECC_SMC is not set +# CONFIG_MTD_NAND_MUSEUM_IDS is not set +# CONFIG_MTD_NAND_H1900 is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND_DISKONCHIP is not set +# CONFIG_MTD_NAND_SHARPSL is not set +# CONFIG_MTD_NAND_NANDSIM is not set +CONFIG_MTD_NAND_PLATFORM=y +# CONFIG_MTD_ONENAND is not set + +# +# UBI - Unsorted block images +# +CONFIG_MTD_UBI=m +CONFIG_MTD_UBI_WL_THRESHOLD=4096 +CONFIG_MTD_UBI_BEB_RESERVE=1 +CONFIG_MTD_UBI_GLUEBI=y + +# +# UBI debugging options +# +# CONFIG_MTD_UBI_DEBUG is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=12000 +CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024 +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +# CONFIG_IDE is not set + +# +# SCSI device support +# +# CONFIG_RAID_ATTRS is not set +CONFIG_SCSI=y +CONFIG_SCSI_DMA=y +# CONFIG_SCSI_TGT is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_SCSI_PROC_FS is not set + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +# CONFIG_CHR_DEV_ST is not set +# CONFIG_CHR_DEV_OSST is not set +# CONFIG_BLK_DEV_SR is not set +# CONFIG_CHR_DEV_SG is not set +# CONFIG_CHR_DEV_SCH is not set + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +# CONFIG_SCSI_MULTI_LUN is not set +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set +# CONFIG_SCSI_SCAN_ASYNC is not set +CONFIG_SCSI_WAIT_SCAN=m + +# +# SCSI Transports +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_ISCSI_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set +CONFIG_SCSI_LOWLEVEL=y +# CONFIG_ISCSI_TCP is not set +# CONFIG_SCSI_DEBUG is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set +CONFIG_NETDEVICES=y +# CONFIG_NETDEVICES_MULTIQUEUE is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_PHYLIB is not set +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +# CONFIG_AX88796 is not set +# CONFIG_SMC91X is not set +CONFIG_DM9000=y +# CONFIG_SMC911X is not set +# CONFIG_NETDEV_1000 is not set +# CONFIG_NETDEV_10000 is not set + +# +# Wireless LAN +# +# CONFIG_WLAN_PRE80211 is not set +# CONFIG_WLAN_80211 is not set + +# +# USB Network Adapters +# +# CONFIG_USB_CATC is not set +# CONFIG_USB_KAWETH is not set +# CONFIG_USB_PEGASUS is not set +# CONFIG_USB_RTL8150 is not set +# CONFIG_USB_USBNET_MII is not set +# CONFIG_USB_USBNET is not set +# CONFIG_WAN is not set +CONFIG_PPP=m +# CONFIG_PPP_MULTILINK is not set +# CONFIG_PPP_FILTER is not set +CONFIG_PPP_ASYNC=m +CONFIG_PPP_SYNC_TTY=m +CONFIG_PPP_DEFLATE=m +CONFIG_PPP_BSDCOMP=m +CONFIG_PPP_MPPE=m +# CONFIG_PPPOE is not set +CONFIG_PPPOL2TP=m +# CONFIG_SLIP is not set +CONFIG_SLHC=m +# CONFIG_SHAPER is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +# CONFIG_INPUT_MOUSEDEV_PSAUX is not set +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +CONFIG_KEYBOARD_PXA27x=y +# CONFIG_KEYBOARD_GPIO is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +CONFIG_INPUT_TOUCHSCREEN=y +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +CONFIG_TOUCHSCREEN_WM97XX=y +# CONFIG_TOUCHSCREEN_WM9705 is not set +CONFIG_TOUCHSCREEN_WM9712=y +# CONFIG_TOUCHSCREEN_WM9713 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_UCB1400 is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +# CONFIG_SERIO_SERPORT is not set +CONFIG_SERIO_LIBPS2=y +# CONFIG_SERIO_RAW is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_VT_HW_CONSOLE_BINDING is not set +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_PXA=y +CONFIG_SERIAL_PXA_CONSOLE=y +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_UNIX98_PTYS=y +# CONFIG_LEGACY_PTYS is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_WATCHDOG is not set +CONFIG_HW_RANDOM=m +# CONFIG_NVRAM is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +# CONFIG_I2C_CHARDEV is not set + +# +# I2C Algorithms +# +# CONFIG_I2C_ALGOBIT is not set +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set + +# +# I2C Hardware Bus support +# +# CONFIG_I2C_GPIO is not set +CONFIG_I2C_PXA=y +# CONFIG_I2C_PXA_SLAVE is not set +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Miscellaneous I2C Chip support +# +# CONFIG_SENSORS_DS1337 is not set +# CONFIG_SENSORS_DS1374 is not set +# CONFIG_DS1682 is not set +# CONFIG_SENSORS_EEPROM is not set +# CONFIG_SENSORS_PCF8574 is not set +# CONFIG_SENSORS_PCA9539 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_MAX6875 is not set +# CONFIG_SENSORS_TSL2550 is not set +CONFIG_DA9030=y +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_DEBUG_CHIP is not set + +# +# SPI support +# +# CONFIG_SPI is not set +# CONFIG_SPI_MASTER is not set +# CONFIG_W1 is not set +CONFIG_POWER_SUPPLY=y +# CONFIG_POWER_SUPPLY_DEBUG is not set +# CONFIG_PDA_POWER is not set +CONFIG_APM_POWER=y +# CONFIG_BATTERY_DS2760 is not set +# CONFIG_BATTERY_EM_X270 is not set +# CONFIG_HWMON is not set +CONFIG_MISC_DEVICES=y +# CONFIG_EEPROM_93CX6 is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_SM501 is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=m + +# +# LED drivers +# +CONFIG_LEDS_GPIO=m +CONFIG_LEDS_EM_X270=m + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_TIMER=m +CONFIG_LEDS_TRIGGER_HEARTBEAT=m + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set +# CONFIG_DVB_CORE is not set +# CONFIG_DAB is not set + +# +# Graphics support +# +CONFIG_BACKLIGHT_LCD_SUPPORT=y +CONFIG_LCD_CLASS_DEVICE=y +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_CORGI=y + +# +# Display device support +# +# CONFIG_DISPLAY_SUPPORT is not set +# CONFIG_VGASTATE is not set +CONFIG_VIDEO_OUTPUT_CONTROL=m +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_SYS_FOPS is not set +CONFIG_FB_DEFERRED_IO=y +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_S1D13XXX is not set +CONFIG_FB_PXA=y +CONFIG_FB_PXA_PARAMETERS=y +# CONFIG_FB_MBX is not set +# CONFIG_FB_VIRTUAL is not set + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set +# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set +# CONFIG_FONTS is not set +CONFIG_FONT_8x8=y +CONFIG_FONT_8x16=y +CONFIG_LOGO=y +CONFIG_LOGO_LINUX_MONO=y +CONFIG_LOGO_LINUX_VGA16=y +CONFIG_LOGO_LINUX_CLUT224=y + +# +# Sound +# +CONFIG_SOUND=y + +# +# Advanced Linux Sound Architecture +# +CONFIG_SND=y +CONFIG_SND_TIMER=y +CONFIG_SND_PCM=y +# CONFIG_SND_SEQUENCER is not set +CONFIG_SND_OSSEMUL=y +CONFIG_SND_MIXER_OSS=y +CONFIG_SND_PCM_OSS=y +CONFIG_SND_PCM_OSS_PLUGINS=y +# CONFIG_SND_DYNAMIC_MINORS is not set +CONFIG_SND_SUPPORT_OLD_API=y +CONFIG_SND_VERBOSE_PROCFS=y +# CONFIG_SND_VERBOSE_PRINTK is not set +# CONFIG_SND_DEBUG is not set + +# +# Generic devices +# +CONFIG_SND_AC97_CODEC=m +# CONFIG_SND_DUMMY is not set +# CONFIG_SND_MTPAV is not set +# CONFIG_SND_SERIAL_U16550 is not set +# CONFIG_SND_MPU401 is not set + +# +# ALSA ARM devices +# +CONFIG_SND_PXA2XX_PCM=m +CONFIG_SND_PXA2XX_AC97=m + +# +# USB devices +# +# CONFIG_SND_USB_AUDIO is not set +# CONFIG_SND_USB_CAIAQ is not set + +# +# System on Chip audio support +# +CONFIG_SND_SOC_AC97_BUS=y +CONFIG_SND_SOC=y +CONFIG_SND_PXA2XX_SOC=y +CONFIG_SND_PXA2XX_SOC_AC97=y +CONFIG_SND_PXA2XX_SOC_EM_X270=y + +# +# SoC Audio support for SuperH +# +CONFIG_SND_SOC_WM9712=y + +# +# Open Sound System +# +# CONFIG_SOUND_PRIME is not set +CONFIG_AC97_BUS=y +CONFIG_HID_SUPPORT=y +CONFIG_HID=y +# CONFIG_HID_DEBUG is not set + +# +# USB Input Devices +# +CONFIG_USB_HID=y +# CONFIG_USB_HIDINPUT_POWERBOOK is not set +# CONFIG_HID_FF is not set +# CONFIG_USB_HIDDEV is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARCH_HAS_OHCI=y +# CONFIG_USB_ARCH_HAS_EHCI is not set +CONFIG_USB=y +# CONFIG_USB_DEBUG is not set + +# +# Miscellaneous USB options +# +CONFIG_USB_DEVICEFS=y +# CONFIG_USB_DEVICE_CLASS is not set +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_SUSPEND is not set +# CONFIG_USB_PERSIST is not set +# CONFIG_USB_OTG is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_ISP116X_HCD is not set +CONFIG_USB_OHCI_HCD=y +# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set +# CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set +CONFIG_USB_OHCI_LITTLE_ENDIAN=y +# CONFIG_USB_SL811_HCD is not set +# CONFIG_USB_R8A66597_HCD is not set + +# +# USB Device Class drivers +# +CONFIG_USB_ACM=m +CONFIG_USB_PRINTER=m + +# +# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' +# + +# +# may also be needed; see USB_STORAGE Help for more information +# +CONFIG_USB_STORAGE=y +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_USBAT is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_ALAUDA is not set +# CONFIG_USB_STORAGE_KARMA is not set +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +CONFIG_USB_MDC800=m +CONFIG_USB_MICROTEK=m +# CONFIG_USB_MON is not set + +# +# USB port drivers +# + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_ADUTUX is not set +# CONFIG_USB_AUERSWALD is not set +# CONFIG_USB_RIO500 is not set +# CONFIG_USB_LEGOTOWER is not set +# CONFIG_USB_LCD is not set +# CONFIG_USB_BERRY_CHARGE is not set +# CONFIG_USB_LED is not set +# CONFIG_USB_CYPRESS_CY7C63 is not set +# CONFIG_USB_CYTHERM is not set +# CONFIG_USB_PHIDGET is not set +# CONFIG_USB_IDMOUSE is not set +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set + +# +# USB DSL modem support +# + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_UNSAFE_RESUME=y + +# +# MMC/SD Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_BOUNCE=y + +# +# MMC/SD Host Controller Drivers +# +CONFIG_MMC_PXA=y +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc1" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set + +# +# SPI RTC drivers +# + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T59 is not set +CONFIG_RTC_DRV_V3020=y + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_SA1100=y + +# +# DMA Engine support +# +# CONFIG_DMA_ENGINE is not set + +# +# DMA Clients +# + +# +# DMA Devices +# + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XIP is not set +CONFIG_EXT3_FS=y +CONFIG_EXT3_FS_XATTR=y +# CONFIG_EXT3_FS_POSIX_ACL is not set +# CONFIG_EXT3_FS_SECURITY is not set +# CONFIG_EXT4DEV_FS is not set +CONFIG_JBD=y +# CONFIG_JBD_DEBUG is not set +CONFIG_FS_MBCACHE=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +CONFIG_INOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_QUOTA is not set +CONFIG_DNOTIFY=y +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y +# CONFIG_CONFIGFS_FS is not set + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_WRITEBUFFER=y +CONFIG_JFFS2_SUMMARY=y +# CONFIG_JFFS2_FS_XATTR is not set +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +CONFIG_JFFS2_ZLIB=y +CONFIG_JFFS2_RTIME=y +# CONFIG_JFFS2_RUBIN is not set +# CONFIG_CRAMFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +CONFIG_NFS_V3=y +# CONFIG_NFS_V3_ACL is not set +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +CONFIG_ROOT_NFS=y +CONFIG_LOCKD=y +CONFIG_LOCKD_V4=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +# CONFIG_SUNRPC_BIND34 is not set +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_RPCSEC_GSS_SPKM3 is not set +CONFIG_SMB_FS=y +# CONFIG_SMB_NLS_DEFAULT is not set +# CONFIG_CIFS is not set +# CONFIG_NCP_FS is not set +# CONFIG_CODA_FS is not set +# CONFIG_AFS_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# Native Language Support +# +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +CONFIG_NLS_UTF8=y + +# +# Distributed Lock Manager +# +# CONFIG_DLM is not set + +# +# Profiling support +# +# CONFIG_PROFILING is not set + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_UNUSED_SYMBOLS is not set +CONFIG_DEBUG_FS=y +# CONFIG_HEADERS_CHECK is not set +CONFIG_DEBUG_KERNEL=y +# CONFIG_DEBUG_SHIRQ is not set +# CONFIG_DETECT_SOFTLOCKUP is not set +CONFIG_SCHED_DEBUG=y +# CONFIG_SCHEDSTATS is not set +# CONFIG_TIMER_STATS is not set +# CONFIG_SLUB_DEBUG_ON is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_LOCK_ALLOC is not set +# CONFIG_PROVE_LOCKING is not set +# CONFIG_LOCK_STAT is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_DEBUG_KOBJECT is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +CONFIG_DEBUG_INFO=y +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_LIST is not set +CONFIG_FRAME_POINTER=y +CONFIG_FORCED_INLINING=y +# CONFIG_RCU_TORTURE_TEST is not set +# CONFIG_FAULT_INJECTION is not set +CONFIG_DEBUG_USER=y +CONFIG_DEBUG_ERRORS=y +CONFIG_DEBUG_LL=y +# CONFIG_DEBUG_ICEDCC is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set +CONFIG_CRYPTO=y +CONFIG_CRYPTO_ALGAPI=m +CONFIG_CRYPTO_BLKCIPHER=m +CONFIG_CRYPTO_MANAGER=m +# CONFIG_CRYPTO_HMAC is not set +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_MD4 is not set +# CONFIG_CRYPTO_MD5 is not set +CONFIG_CRYPTO_SHA1=m +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_WP512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_GF128MUL is not set +CONFIG_CRYPTO_ECB=m +CONFIG_CRYPTO_CBC=m +CONFIG_CRYPTO_PCBC=m +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_DES is not set +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_TWOFISH is not set +# CONFIG_CRYPTO_SERPENT is not set +CONFIG_CRYPTO_AES=m +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_TEA is not set +CONFIG_CRYPTO_ARC4=m +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_ANUBIS is not set +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_TEST is not set +CONFIG_CRYPTO_HW=y + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_CRC_CCITT=m +# CONFIG_CRC16 is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_PLIST=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y diff --git a/packages/linux/em-x270-2.6.23/em-x270.patch b/packages/linux/em-x270-2.6.23/em-x270.patch new file mode 100644 index 0000000000..3c28be83c7 --- /dev/null +++ b/packages/linux/em-x270-2.6.23/em-x270.patch @@ -0,0 +1,12063 @@ + arch/arm/Kconfig | 13 +- + arch/arm/configs/em_x270_defconfig | 367 +++-- + arch/arm/mach-pxa/Kconfig | 8 + + arch/arm/mach-pxa/Makefile | 9 +- + arch/arm/mach-pxa/cpu-pxa.c | 442 ++++++ + arch/arm/mach-pxa/em-x270-devices.c | 331 +++++ + arch/arm/mach-pxa/em-x270-lcd.c | 223 +++ + arch/arm/mach-pxa/em-x270-pm.c | 892 ++++++++++++ + arch/arm/mach-pxa/em-x270.c | 127 ++- + arch/arm/mach-pxa/pwr-i2c.c | 539 +++++++ + arch/arm/mach-pxa/pxa27x.c | 6 +- + arch/arm/mach-pxa/spitz.c | 27 + + drivers/i2c/chips/Kconfig | 13 + + drivers/i2c/chips/Makefile | 1 + + drivers/i2c/chips/da9030.c | 1213 ++++++++++++++++ + drivers/i2c/chips/da9030.h | 282 ++++ + drivers/input/touchscreen/Kconfig | 43 + + drivers/input/touchscreen/Makefile | 14 + + drivers/input/touchscreen/wm9705.c | 360 +++++ + drivers/input/touchscreen/wm9712.c | 464 ++++++ + drivers/input/touchscreen/wm9713.c | 461 ++++++ + drivers/input/touchscreen/wm97xx-core.c | 859 +++++++++++ + drivers/leds/Kconfig | 6 + + drivers/leds/Makefile | 1 + + drivers/leds/leds-em-x270.c | 99 ++ + drivers/mtd/chips/jedec_probe.c | 58 +- + drivers/net/dm9000.c | 1 + + drivers/power/Kconfig | 6 + + drivers/power/Makefile | 1 + + drivers/power/em_x270_battery.c | 579 ++++++++ + drivers/usb/gadget/Kconfig | 20 + + drivers/usb/gadget/Makefile | 1 + + drivers/usb/gadget/epautoconf.c | 9 +- + drivers/usb/gadget/ether.c | 63 +- + drivers/usb/gadget/file_storage.c | 11 +- + drivers/usb/gadget/pxa27x_udc.c | 2387 +++++++++++++++++++++++++++++++ + drivers/usb/gadget/pxa27x_udc.h | 298 ++++ + drivers/usb/gadget/pxa2xx_udc.h | 7 +- + drivers/usb/gadget/serial.c | 18 +- + drivers/usb/gadget/zero.c | 13 +- + drivers/video/backlight/Kconfig | 2 +- + include/asm-arm/arch-pxa/pwr-i2c.h | 61 + + include/linux/da9030.h | 118 ++ + include/linux/usb_gadget.h | 23 +- + include/linux/wm97xx.h | 291 ++++ + sound/soc/pxa/Kconfig | 9 + + sound/soc/pxa/Makefile | 2 + + sound/soc/pxa/em-x270.c | 137 ++ + 48 files changed, 10742 insertions(+), 173 deletions(-) + +diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig +index 691aae3..cf1dbc2 100644 +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -858,7 +858,7 @@ config KEXEC + + endmenu + +-if (ARCH_SA1100 || ARCH_INTEGRATOR || ARCH_OMAP || ARCH_IMX ) ++if (ARCH_SA1100 || ARCH_INTEGRATOR || ARCH_OMAP || ARCH_IMX || ARCH_PXA) + + menu "CPU Frequency scaling" + +@@ -894,6 +894,15 @@ config CPU_FREQ_IMX + + If in doubt, say N. + ++config CPU_FREQ_PXA ++ tristate "CPUfreq driver for PXA2xx CPUs" ++ depends on CPU_FREQ && ARCH_PXA ++ default y ++ help ++ Thes enables the CPUfreq driver for PXA2xx CPUs. ++ ++ If in doubt, say Y. ++ + endmenu + + endif +@@ -1029,6 +1038,8 @@ source "drivers/spi/Kconfig" + + source "drivers/w1/Kconfig" + ++source "drivers/power/Kconfig" ++ + source "drivers/hwmon/Kconfig" + + #source "drivers/l3/Kconfig" +diff --git a/arch/arm/configs/em_x270_defconfig b/arch/arm/configs/em_x270_defconfig +index 6bea090..3246136 100644 +--- a/arch/arm/configs/em_x270_defconfig ++++ b/arch/arm/configs/em_x270_defconfig +@@ -1,13 +1,13 @@ + # + # Automatically generated make config: don't edit +-# Linux kernel version: 2.6.22 +-# Mon Jul 9 15:18:20 2007 ++# Linux kernel version: 2.6.23-rc9 ++# Tue Oct 9 11:19:21 2007 + # + CONFIG_ARM=y + CONFIG_SYS_SUPPORTS_APM_EMULATION=y + CONFIG_GENERIC_GPIO=y + CONFIG_GENERIC_TIME=y +-# CONFIG_GENERIC_CLOCKEVENTS is not set ++CONFIG_GENERIC_CLOCKEVENTS=y + CONFIG_MMU=y + # CONFIG_NO_IOPORT is not set + CONFIG_GENERIC_HARDIRQS=y +@@ -27,25 +27,20 @@ CONFIG_VECTORS_BASE=0xffff0000 + CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" + + # +-# Code maturity level options ++# General setup + # + CONFIG_EXPERIMENTAL=y + CONFIG_BROKEN_ON_SMP=y + CONFIG_INIT_ENV_ARG_LIMIT=32 +- +-# +-# General setup +-# + CONFIG_LOCALVERSION="-em-x270" + # CONFIG_LOCALVERSION_AUTO is not set + CONFIG_SWAP=y + CONFIG_SYSVIPC=y +-# CONFIG_IPC_NS is not set + CONFIG_SYSVIPC_SYSCTL=y + # CONFIG_POSIX_MQUEUE is not set + # CONFIG_BSD_PROCESS_ACCT is not set + # CONFIG_TASKSTATS is not set +-# CONFIG_UTS_NS is not set ++# CONFIG_USER_NS is not set + # CONFIG_AUDIT is not set + CONFIG_IKCONFIG=y + CONFIG_IKCONFIG_PROC=y +@@ -71,34 +66,27 @@ CONFIG_FUTEX=y + CONFIG_ANON_INODES=y + CONFIG_EPOLL=y + CONFIG_SIGNALFD=y +-CONFIG_TIMERFD=y + CONFIG_EVENTFD=y + CONFIG_SHMEM=y + CONFIG_VM_EVENT_COUNTERS=y +-CONFIG_SLAB=y +-# CONFIG_SLUB is not set ++CONFIG_SLUB_DEBUG=y ++# CONFIG_SLAB is not set ++CONFIG_SLUB=y + # CONFIG_SLOB is not set + CONFIG_RT_MUTEXES=y + # CONFIG_TINY_SHMEM is not set + CONFIG_BASE_SMALL=0 +- +-# +-# Loadable module support +-# + CONFIG_MODULES=y + CONFIG_MODULE_UNLOAD=y + CONFIG_MODULE_FORCE_UNLOAD=y + # CONFIG_MODVERSIONS is not set + # CONFIG_MODULE_SRCVERSION_ALL is not set + CONFIG_KMOD=y +- +-# +-# Block layer +-# + CONFIG_BLOCK=y + # CONFIG_LBD is not set + # CONFIG_BLK_DEV_IO_TRACE is not set + # CONFIG_LSF is not set ++# CONFIG_BLK_DEV_BSG is not set + + # + # IO Schedulers +@@ -139,6 +127,7 @@ CONFIG_DEFAULT_IOSCHED="anticipatory" + # CONFIG_ARCH_L7200 is not set + # CONFIG_ARCH_KS8695 is not set + # CONFIG_ARCH_NS9XXX is not set ++# CONFIG_ARCH_MXC is not set + # CONFIG_ARCH_PNX4008 is not set + CONFIG_ARCH_PXA=y + # CONFIG_ARCH_RPC is not set +@@ -160,6 +149,15 @@ CONFIG_ARCH_PXA=y + # CONFIG_MACH_TRIZEPS4 is not set + CONFIG_MACH_EM_X270=y + CONFIG_PXA27x=y ++CONFIG_PXA_PWR_I2C=y ++ ++# ++# Boot options ++# ++ ++# ++# Power management ++# + + # + # Processor Type +@@ -185,6 +183,7 @@ CONFIG_XSCALE_PMU=y + # + # Bus support + # ++# CONFIG_PCI_SYSCALL is not set + # CONFIG_ARCH_SUPPORTS_MSI is not set + + # +@@ -196,8 +195,9 @@ CONFIG_XSCALE_PMU=y + # Kernel Features + # + # CONFIG_TICK_ONESHOT is not set ++# CONFIG_NO_HZ is not set ++# CONFIG_HIGH_RES_TIMERS is not set + # CONFIG_PREEMPT is not set +-# CONFIG_NO_IDLE_HZ is not set + CONFIG_HZ=100 + CONFIG_AEABI=y + CONFIG_OABI_COMPAT=y +@@ -212,6 +212,8 @@ CONFIG_FLAT_NODE_MEM_MAP=y + CONFIG_SPLIT_PTLOCK_CPUS=4096 + # CONFIG_RESOURCES_64BIT is not set + CONFIG_ZONE_DMA_FLAG=1 ++CONFIG_BOUNCE=y ++CONFIG_VIRT_TO_BUS=y + CONFIG_ALIGNMENT_TRAP=y + + # +@@ -219,11 +221,28 @@ CONFIG_ALIGNMENT_TRAP=y + # + CONFIG_ZBOOT_ROM_TEXT=0x0 + CONFIG_ZBOOT_ROM_BSS=0x0 +-CONFIG_CMDLINE="" ++CONFIG_CMDLINE="root=/dev/mtdblock2 rootfstype=jffs2 console=ttyS0,115200" + # CONFIG_XIP_KERNEL is not set + # CONFIG_KEXEC is not set + + # ++# CPU Frequency scaling ++# ++CONFIG_CPU_FREQ=y ++CONFIG_CPU_FREQ_TABLE=y ++# CONFIG_CPU_FREQ_DEBUG is not set ++CONFIG_CPU_FREQ_STAT=y ++# CONFIG_CPU_FREQ_STAT_DETAILS is not set ++CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y ++# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set ++CONFIG_CPU_FREQ_GOV_PERFORMANCE=y ++CONFIG_CPU_FREQ_GOV_POWERSAVE=m ++CONFIG_CPU_FREQ_GOV_USERSPACE=m ++CONFIG_CPU_FREQ_GOV_ONDEMAND=m ++CONFIG_CPU_FREQ_GOV_CONSERVATIVE=m ++CONFIG_CPU_FREQ_PXA=y ++ ++# + # Floating point emulation + # + +@@ -238,8 +257,8 @@ CONFIG_FPE_NWFPE=y + # Userspace binary formats + # + CONFIG_BINFMT_ELF=y +-# CONFIG_BINFMT_AOUT is not set +-# CONFIG_BINFMT_MISC is not set ++CONFIG_BINFMT_AOUT=m ++CONFIG_BINFMT_MISC=m + + # + # Power management options +@@ -247,8 +266,10 @@ CONFIG_BINFMT_ELF=y + CONFIG_PM=y + CONFIG_PM_LEGACY=y + # CONFIG_PM_DEBUG is not set +-# CONFIG_PM_SYSFS_DEPRECATED is not set +-CONFIG_APM_EMULATION=m ++CONFIG_PM_SLEEP=y ++CONFIG_SUSPEND_UP_POSSIBLE=y ++CONFIG_SUSPEND=y ++CONFIG_APM_EMULATION=y + + # + # Networking +@@ -316,6 +337,7 @@ CONFIG_DEFAULT_TCP_CONG="cubic" + # QoS and/or fair queueing + # + # CONFIG_NET_SCHED is not set ++CONFIG_NET_SCH_FIFO=y + + # + # Network testing +@@ -350,9 +372,12 @@ CONFIG_BT_HCIBFUSB=m + # + # Wireless + # +-# CONFIG_CFG80211 is not set +-# CONFIG_WIRELESS_EXT is not set +-# CONFIG_MAC80211 is not set ++CONFIG_CFG80211=m ++CONFIG_WIRELESS_EXT=y ++CONFIG_MAC80211=m ++# CONFIG_MAC80211_LEDS is not set ++CONFIG_MAC80211_DEBUGFS=y ++# CONFIG_MAC80211_DEBUG is not set + CONFIG_IEEE80211=m + # CONFIG_IEEE80211_DEBUG is not set + CONFIG_IEEE80211_CRYPT_WEP=m +@@ -360,6 +385,7 @@ CONFIG_IEEE80211_CRYPT_CCMP=m + # CONFIG_IEEE80211_CRYPT_TKIP is not set + # CONFIG_IEEE80211_SOFTMAC is not set + # CONFIG_RFKILL is not set ++# CONFIG_NET_9P is not set + + # + # Device Drivers +@@ -374,10 +400,6 @@ CONFIG_FW_LOADER=y + # CONFIG_DEBUG_DRIVER is not set + # CONFIG_DEBUG_DEVRES is not set + # CONFIG_SYS_HYPERVISOR is not set +- +-# +-# Connector - unified userspace <-> kernelspace linker +-# + # CONFIG_CONNECTOR is not set + CONFIG_MTD=y + # CONFIG_MTD_DEBUG is not set +@@ -402,11 +424,10 @@ CONFIG_MTD_BLOCK=y + # + # RAM/ROM/Flash chip drivers + # +-# CONFIG_MTD_CFI is not set +-# CONFIG_MTD_JEDECPROBE is not set +-# CONFIG_MTD_CFI_NOSWAP is not set +-# CONFIG_MTD_CFI_BE_BYTE_SWAP is not set +-# CONFIG_MTD_CFI_LE_BYTE_SWAP is not set ++CONFIG_MTD_CFI=y ++CONFIG_MTD_JEDECPROBE=y ++CONFIG_MTD_GEN_PROBE=y ++# CONFIG_MTD_CFI_ADV_OPTIONS is not set + CONFIG_MTD_MAP_BANK_WIDTH_1=y + CONFIG_MTD_MAP_BANK_WIDTH_2=y + CONFIG_MTD_MAP_BANK_WIDTH_4=y +@@ -417,14 +438,25 @@ CONFIG_MTD_CFI_I1=y + CONFIG_MTD_CFI_I2=y + # CONFIG_MTD_CFI_I4 is not set + # CONFIG_MTD_CFI_I8 is not set ++CONFIG_MTD_CFI_INTELEXT=y ++CONFIG_MTD_CFI_AMDSTD=y ++CONFIG_MTD_CFI_STAA=y ++CONFIG_MTD_CFI_UTIL=y + # CONFIG_MTD_RAM is not set + # CONFIG_MTD_ROM is not set + # CONFIG_MTD_ABSENT is not set ++# CONFIG_MTD_XIP is not set + + # + # Mapping drivers for chip access + # + # CONFIG_MTD_COMPLEX_MAPPINGS is not set ++CONFIG_MTD_PHYSMAP=y ++CONFIG_MTD_PHYSMAP_START=0x0 ++CONFIG_MTD_PHYSMAP_LEN=0x100000 ++CONFIG_MTD_PHYSMAP_BANKWIDTH=2 ++# CONFIG_MTD_ARM_INTEGRATOR is not set ++# CONFIG_MTD_IMPA7 is not set + # CONFIG_MTD_SHARP_SL is not set + # CONFIG_MTD_PLATRAM is not set + +@@ -457,21 +489,17 @@ CONFIG_MTD_NAND_PLATFORM=y + # + # UBI - Unsorted block images + # +-# CONFIG_MTD_UBI is not set ++CONFIG_MTD_UBI=m ++CONFIG_MTD_UBI_WL_THRESHOLD=4096 ++CONFIG_MTD_UBI_BEB_RESERVE=1 ++CONFIG_MTD_UBI_GLUEBI=y + + # +-# Parallel port support ++# UBI debugging options + # ++# CONFIG_MTD_UBI_DEBUG is not set + # CONFIG_PARPORT is not set +- +-# +-# Plug and Play support +-# +-# CONFIG_PNPACPI is not set +- +-# +-# Block devices +-# ++CONFIG_BLK_DEV=y + # CONFIG_BLK_DEV_COW_COMMON is not set + CONFIG_BLK_DEV_LOOP=y + # CONFIG_BLK_DEV_CRYPTOLOOP is not set +@@ -490,6 +518,7 @@ CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024 + # + # CONFIG_RAID_ATTRS is not set + CONFIG_SCSI=y ++CONFIG_SCSI_DMA=y + # CONFIG_SCSI_TGT is not set + # CONFIG_SCSI_NETLINK is not set + # CONFIG_SCSI_PROC_FS is not set +@@ -519,36 +548,23 @@ CONFIG_SCSI_WAIT_SCAN=m + # CONFIG_SCSI_SPI_ATTRS is not set + # CONFIG_SCSI_FC_ATTRS is not set + # CONFIG_SCSI_ISCSI_ATTRS is not set +-# CONFIG_SCSI_SAS_ATTRS is not set + # CONFIG_SCSI_SAS_LIBSAS is not set +- +-# +-# SCSI low-level drivers +-# ++CONFIG_SCSI_LOWLEVEL=y + # CONFIG_ISCSI_TCP is not set + # CONFIG_SCSI_DEBUG is not set + # CONFIG_ATA is not set +- +-# +-# Multi-device support (RAID and LVM) +-# + # CONFIG_MD is not set +- +-# +-# Network device support +-# + CONFIG_NETDEVICES=y ++# CONFIG_NETDEVICES_MULTIQUEUE is not set + # CONFIG_DUMMY is not set + # CONFIG_BONDING is not set ++# CONFIG_MACVLAN is not set + # CONFIG_EQUALIZER is not set + # CONFIG_TUN is not set + # CONFIG_PHYLIB is not set +- +-# +-# Ethernet (10 or 100Mbit) +-# + CONFIG_NET_ETHERNET=y + CONFIG_MII=y ++# CONFIG_AX88796 is not set + # CONFIG_SMC91X is not set + CONFIG_DM9000=y + # CONFIG_SMC911X is not set +@@ -571,16 +587,22 @@ CONFIG_DM9000=y + # CONFIG_USB_USBNET_MII is not set + # CONFIG_USB_USBNET is not set + # CONFIG_WAN is not set +-# CONFIG_PPP is not set ++CONFIG_PPP=m ++# CONFIG_PPP_MULTILINK is not set ++# CONFIG_PPP_FILTER is not set ++CONFIG_PPP_ASYNC=m ++CONFIG_PPP_SYNC_TTY=m ++CONFIG_PPP_DEFLATE=m ++CONFIG_PPP_BSDCOMP=m ++CONFIG_PPP_MPPE=m ++# CONFIG_PPPOE is not set ++CONFIG_PPPOL2TP=m + # CONFIG_SLIP is not set ++CONFIG_SLHC=m + # CONFIG_SHAPER is not set + # CONFIG_NETCONSOLE is not set + # CONFIG_NETPOLL is not set + # CONFIG_NET_POLL_CONTROLLER is not set +- +-# +-# ISDN subsystem +-# + # CONFIG_ISDN is not set + + # +@@ -612,16 +634,21 @@ CONFIG_INPUT_KEYBOARD=y + # CONFIG_KEYBOARD_XTKBD is not set + # CONFIG_KEYBOARD_NEWTON is not set + # CONFIG_KEYBOARD_STOWAWAY is not set +-CONFIG_KEYBOARD_PXA27x=m ++CONFIG_KEYBOARD_PXA27x=y + # CONFIG_KEYBOARD_GPIO is not set + # CONFIG_INPUT_MOUSE is not set + # CONFIG_INPUT_JOYSTICK is not set + # CONFIG_INPUT_TABLET is not set + CONFIG_INPUT_TOUCHSCREEN=y ++# CONFIG_TOUCHSCREEN_FUJITSU is not set + # CONFIG_TOUCHSCREEN_GUNZE is not set + # CONFIG_TOUCHSCREEN_ELO is not set + # CONFIG_TOUCHSCREEN_MTOUCH is not set + # CONFIG_TOUCHSCREEN_MK712 is not set ++CONFIG_TOUCHSCREEN_WM97XX=y ++# CONFIG_TOUCHSCREEN_WM9705 is not set ++CONFIG_TOUCHSCREEN_WM9712=y ++# CONFIG_TOUCHSCREEN_WM9713 is not set + # CONFIG_TOUCHSCREEN_PENMOUNT is not set + # CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set + # CONFIG_TOUCHSCREEN_TOUCHWIN is not set +@@ -660,58 +687,91 @@ CONFIG_SERIAL_PXA_CONSOLE=y + CONFIG_SERIAL_CORE=y + CONFIG_SERIAL_CORE_CONSOLE=y + CONFIG_UNIX98_PTYS=y +-CONFIG_LEGACY_PTYS=y +-CONFIG_LEGACY_PTY_COUNT=256 +- +-# +-# IPMI +-# ++# CONFIG_LEGACY_PTYS is not set + # CONFIG_IPMI_HANDLER is not set + # CONFIG_WATCHDOG is not set + CONFIG_HW_RANDOM=m + # CONFIG_NVRAM is not set + # CONFIG_R3964 is not set + # CONFIG_RAW_DRIVER is not set ++# CONFIG_TCG_TPM is not set ++CONFIG_I2C=y ++CONFIG_I2C_BOARDINFO=y ++# CONFIG_I2C_CHARDEV is not set + + # +-# TPM devices ++# I2C Algorithms + # +-# CONFIG_TCG_TPM is not set +-# CONFIG_I2C is not set ++# CONFIG_I2C_ALGOBIT is not set ++# CONFIG_I2C_ALGOPCF is not set ++# CONFIG_I2C_ALGOPCA is not set + + # +-# SPI support ++# I2C Hardware Bus support + # +-# CONFIG_SPI is not set +-# CONFIG_SPI_MASTER is not set ++# CONFIG_I2C_GPIO is not set ++CONFIG_I2C_PXA=y ++# CONFIG_I2C_PXA_SLAVE is not set ++# CONFIG_I2C_OCORES is not set ++# CONFIG_I2C_PARPORT_LIGHT is not set ++# CONFIG_I2C_SIMTEC is not set ++# CONFIG_I2C_TAOS_EVM is not set ++# CONFIG_I2C_STUB is not set ++# CONFIG_I2C_TINY_USB is not set + + # +-# Dallas's 1-wire bus ++# Miscellaneous I2C Chip support + # +-# CONFIG_W1 is not set +-# CONFIG_HWMON is not set ++# CONFIG_SENSORS_DS1337 is not set ++# CONFIG_SENSORS_DS1374 is not set ++# CONFIG_DS1682 is not set ++# CONFIG_SENSORS_EEPROM is not set ++# CONFIG_SENSORS_PCF8574 is not set ++# CONFIG_SENSORS_PCA9539 is not set ++# CONFIG_SENSORS_PCF8591 is not set ++# CONFIG_SENSORS_MAX6875 is not set ++# CONFIG_SENSORS_TSL2550 is not set ++CONFIG_DA9030=y ++# CONFIG_I2C_DEBUG_CORE is not set ++# CONFIG_I2C_DEBUG_ALGO is not set ++# CONFIG_I2C_DEBUG_BUS is not set ++# CONFIG_I2C_DEBUG_CHIP is not set + + # +-# Misc devices ++# SPI support + # ++# CONFIG_SPI is not set ++# CONFIG_SPI_MASTER is not set ++# CONFIG_W1 is not set ++CONFIG_POWER_SUPPLY=y ++# CONFIG_POWER_SUPPLY_DEBUG is not set ++# CONFIG_PDA_POWER is not set ++CONFIG_APM_POWER=y ++# CONFIG_BATTERY_DS2760 is not set ++# CONFIG_BATTERY_EM_X270 is not set ++# CONFIG_HWMON is not set ++CONFIG_MISC_DEVICES=y ++# CONFIG_EEPROM_93CX6 is not set + + # + # Multifunction device drivers + # + # CONFIG_MFD_SM501 is not set +- +-# +-# LED devices +-# +-# CONFIG_NEW_LEDS is not set ++CONFIG_NEW_LEDS=y ++CONFIG_LEDS_CLASS=m + + # + # LED drivers + # ++CONFIG_LEDS_GPIO=m ++CONFIG_LEDS_EM_X270=m + + # + # LED Triggers + # ++CONFIG_LEDS_TRIGGERS=y ++CONFIG_LEDS_TRIGGER_TIMER=m ++CONFIG_LEDS_TRIGGER_HEARTBEAT=m + + # + # Multimedia devices +@@ -723,13 +783,17 @@ CONFIG_HW_RANDOM=m + # + # Graphics support + # +-# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++CONFIG_BACKLIGHT_LCD_SUPPORT=y ++CONFIG_LCD_CLASS_DEVICE=y ++CONFIG_BACKLIGHT_CLASS_DEVICE=y ++CONFIG_BACKLIGHT_CORGI=y + + # + # Display device support + # + # CONFIG_DISPLAY_SUPPORT is not set + # CONFIG_VGASTATE is not set ++CONFIG_VIDEO_OUTPUT_CONTROL=m + CONFIG_FB=y + # CONFIG_FIRMWARE_EDID is not set + # CONFIG_FB_DDC is not set +@@ -752,7 +816,7 @@ CONFIG_FB_DEFERRED_IO=y + # + # CONFIG_FB_S1D13XXX is not set + CONFIG_FB_PXA=y +-# CONFIG_FB_PXA_PARAMETERS is not set ++CONFIG_FB_PXA_PARAMETERS=y + # CONFIG_FB_MBX is not set + # CONFIG_FB_VIRTUAL is not set + +@@ -762,6 +826,7 @@ CONFIG_FB_PXA=y + # CONFIG_VGA_CONSOLE is not set + CONFIG_DUMMY_CONSOLE=y + CONFIG_FRAMEBUFFER_CONSOLE=y ++# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set + # CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set + # CONFIG_FONTS is not set + CONFIG_FONT_8x8=y +@@ -774,18 +839,18 @@ CONFIG_LOGO_LINUX_CLUT224=y + # + # Sound + # +-CONFIG_SOUND=m ++CONFIG_SOUND=y + + # + # Advanced Linux Sound Architecture + # +-CONFIG_SND=m +-CONFIG_SND_TIMER=m +-CONFIG_SND_PCM=m ++CONFIG_SND=y ++CONFIG_SND_TIMER=y ++CONFIG_SND_PCM=y + # CONFIG_SND_SEQUENCER is not set + CONFIG_SND_OSSEMUL=y +-CONFIG_SND_MIXER_OSS=m +-CONFIG_SND_PCM_OSS=m ++CONFIG_SND_MIXER_OSS=y ++CONFIG_SND_PCM_OSS=y + CONFIG_SND_PCM_OSS_PLUGINS=y + # CONFIG_SND_DYNAMIC_MINORS is not set + CONFIG_SND_SUPPORT_OLD_API=y +@@ -817,17 +882,23 @@ CONFIG_SND_PXA2XX_AC97=m + # + # System on Chip audio support + # +-# CONFIG_SND_SOC is not set ++CONFIG_SND_SOC_AC97_BUS=y ++CONFIG_SND_SOC=y ++CONFIG_SND_PXA2XX_SOC=y ++CONFIG_SND_PXA2XX_SOC_AC97=y ++CONFIG_SND_PXA2XX_SOC_EM_X270=y + + # +-# Open Sound System ++# SoC Audio support for SuperH + # +-# CONFIG_SOUND_PRIME is not set +-CONFIG_AC97_BUS=m ++CONFIG_SND_SOC_WM9712=y + + # +-# HID Devices ++# Open Sound System + # ++# CONFIG_SOUND_PRIME is not set ++CONFIG_AC97_BUS=y ++CONFIG_HID_SUPPORT=y + CONFIG_HID=y + # CONFIG_HID_DEBUG is not set + +@@ -838,10 +909,7 @@ CONFIG_USB_HID=y + # CONFIG_USB_HIDINPUT_POWERBOOK is not set + # CONFIG_HID_FF is not set + # CONFIG_USB_HIDDEV is not set +- +-# +-# USB support +-# ++CONFIG_USB_SUPPORT=y + CONFIG_USB_ARCH_HAS_HCD=y + CONFIG_USB_ARCH_HAS_OHCI=y + # CONFIG_USB_ARCH_HAS_EHCI is not set +@@ -855,6 +923,7 @@ CONFIG_USB_DEVICEFS=y + # CONFIG_USB_DEVICE_CLASS is not set + # CONFIG_USB_DYNAMIC_MINORS is not set + # CONFIG_USB_SUSPEND is not set ++# CONFIG_USB_PERSIST is not set + # CONFIG_USB_OTG is not set + + # +@@ -866,12 +935,13 @@ CONFIG_USB_OHCI_HCD=y + # CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set + CONFIG_USB_OHCI_LITTLE_ENDIAN=y + # CONFIG_USB_SL811_HCD is not set ++# CONFIG_USB_R8A66597_HCD is not set + + # + # USB Device Class drivers + # +-# CONFIG_USB_ACM is not set +-# CONFIG_USB_PRINTER is not set ++CONFIG_USB_ACM=m ++CONFIG_USB_PRINTER=m + + # + # NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' +@@ -896,8 +966,8 @@ CONFIG_USB_STORAGE=y + # + # USB Imaging devices + # +-# CONFIG_USB_MDC800 is not set +-# CONFIG_USB_MICROTEK is not set ++CONFIG_USB_MDC800=m ++CONFIG_USB_MICROTEK=m + # CONFIG_USB_MON is not set + + # +@@ -940,25 +1010,25 @@ CONFIG_USB_STORAGE=y + # USB Gadget Support + # + # CONFIG_USB_GADGET is not set +-CONFIG_MMC=m ++CONFIG_MMC=y + # CONFIG_MMC_DEBUG is not set +-# CONFIG_MMC_UNSAFE_RESUME is not set ++CONFIG_MMC_UNSAFE_RESUME=y + + # + # MMC/SD Card Drivers + # +-CONFIG_MMC_BLOCK=m ++CONFIG_MMC_BLOCK=y ++CONFIG_MMC_BLOCK_BOUNCE=y + + # + # MMC/SD Host Controller Drivers + # +-CONFIG_MMC_PXA=m +- +-# +-# Real Time Clock +-# ++CONFIG_MMC_PXA=y + CONFIG_RTC_LIB=y +-CONFIG_RTC_CLASS=m ++CONFIG_RTC_CLASS=y ++CONFIG_RTC_HCTOSYS=y ++CONFIG_RTC_HCTOSYS_DEVICE="rtc1" ++# CONFIG_RTC_DEBUG is not set + + # + # RTC interfaces +@@ -972,6 +1042,15 @@ CONFIG_RTC_INTF_DEV=y + # + # I2C RTC drivers + # ++# CONFIG_RTC_DRV_DS1307 is not set ++# CONFIG_RTC_DRV_DS1672 is not set ++# CONFIG_RTC_DRV_MAX6900 is not set ++# CONFIG_RTC_DRV_RS5C372 is not set ++# CONFIG_RTC_DRV_ISL1208 is not set ++# CONFIG_RTC_DRV_X1205 is not set ++# CONFIG_RTC_DRV_PCF8563 is not set ++# CONFIG_RTC_DRV_PCF8583 is not set ++# CONFIG_RTC_DRV_M41T80 is not set + + # + # SPI RTC drivers +@@ -982,14 +1061,29 @@ CONFIG_RTC_INTF_DEV=y + # + # CONFIG_RTC_DRV_CMOS is not set + # CONFIG_RTC_DRV_DS1553 is not set ++# CONFIG_RTC_DRV_STK17TA8 is not set + # CONFIG_RTC_DRV_DS1742 is not set + # CONFIG_RTC_DRV_M48T86 is not set +-CONFIG_RTC_DRV_V3020=m ++# CONFIG_RTC_DRV_M48T59 is not set ++CONFIG_RTC_DRV_V3020=y + + # + # on-CPU RTC drivers + # +-CONFIG_RTC_DRV_SA1100=m ++CONFIG_RTC_DRV_SA1100=y ++ ++# ++# DMA Engine support ++# ++# CONFIG_DMA_ENGINE is not set ++ ++# ++# DMA Clients ++# ++ ++# ++# DMA Devices ++# + + # + # File systems +@@ -1098,7 +1192,6 @@ CONFIG_SMB_FS=y + # CONFIG_NCP_FS is not set + # CONFIG_CODA_FS is not set + # CONFIG_AFS_FS is not set +-# CONFIG_9P_FS is not set + + # + # Partition Types +@@ -1167,20 +1260,22 @@ CONFIG_NLS_UTF8=y + CONFIG_ENABLE_MUST_CHECK=y + CONFIG_MAGIC_SYSRQ=y + # CONFIG_UNUSED_SYMBOLS is not set +-# CONFIG_DEBUG_FS is not set ++CONFIG_DEBUG_FS=y + # CONFIG_HEADERS_CHECK is not set + CONFIG_DEBUG_KERNEL=y + # CONFIG_DEBUG_SHIRQ is not set + # CONFIG_DETECT_SOFTLOCKUP is not set ++CONFIG_SCHED_DEBUG=y + # CONFIG_SCHEDSTATS is not set + # CONFIG_TIMER_STATS is not set +-# CONFIG_DEBUG_SLAB is not set ++# CONFIG_SLUB_DEBUG_ON is not set + # CONFIG_DEBUG_RT_MUTEXES is not set + # CONFIG_RT_MUTEX_TESTER is not set + # CONFIG_DEBUG_SPINLOCK is not set + # CONFIG_DEBUG_MUTEXES is not set + # CONFIG_DEBUG_LOCK_ALLOC is not set + # CONFIG_PROVE_LOCKING is not set ++# CONFIG_LOCK_STAT is not set + # CONFIG_DEBUG_SPINLOCK_SLEEP is not set + # CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set + # CONFIG_DEBUG_KOBJECT is not set +@@ -1202,10 +1297,6 @@ CONFIG_DEBUG_LL=y + # + # CONFIG_KEYS is not set + # CONFIG_SECURITY is not set +- +-# +-# Cryptographic options +-# + CONFIG_CRYPTO=y + CONFIG_CRYPTO_ALGAPI=m + CONFIG_CRYPTO_BLKCIPHER=m +@@ -1215,7 +1306,7 @@ CONFIG_CRYPTO_MANAGER=m + # CONFIG_CRYPTO_NULL is not set + # CONFIG_CRYPTO_MD4 is not set + # CONFIG_CRYPTO_MD5 is not set +-# CONFIG_CRYPTO_SHA1 is not set ++CONFIG_CRYPTO_SHA1=m + # CONFIG_CRYPTO_SHA256 is not set + # CONFIG_CRYPTO_SHA512 is not set + # CONFIG_CRYPTO_WP512 is not set +@@ -1243,19 +1334,17 @@ CONFIG_CRYPTO_ARC4=m + # CONFIG_CRYPTO_CRC32C is not set + # CONFIG_CRYPTO_CAMELLIA is not set + # CONFIG_CRYPTO_TEST is not set +- +-# +-# Hardware crypto devices +-# ++CONFIG_CRYPTO_HW=y + + # + # Library routines + # + CONFIG_BITREVERSE=y +-# CONFIG_CRC_CCITT is not set ++CONFIG_CRC_CCITT=m + # CONFIG_CRC16 is not set + # CONFIG_CRC_ITU_T is not set + CONFIG_CRC32=y ++# CONFIG_CRC7 is not set + # CONFIG_LIBCRC32C is not set + CONFIG_ZLIB_INFLATE=y + CONFIG_ZLIB_DEFLATE=y +diff --git a/arch/arm/mach-pxa/Kconfig b/arch/arm/mach-pxa/Kconfig +index 5ebec6d..2957cb9 100644 +--- a/arch/arm/mach-pxa/Kconfig ++++ b/arch/arm/mach-pxa/Kconfig +@@ -148,4 +148,12 @@ config PXA_SSP + tristate + help + Enable support for PXA2xx SSP ports ++ ++config PXA_PWR_I2C ++ bool "Simple Power I2C interface" ++ depends on PXA27x ++ help ++ Enable support for PXA27x Power I2C interface. This driver ++ enables very simple blocking access to Power I2C, which ++ might be useful for early access to Power Management ICs. + endif +diff --git a/arch/arm/mach-pxa/Makefile b/arch/arm/mach-pxa/Makefile +index 7d6ab5c..2d7a431 100644 +--- a/arch/arm/mach-pxa/Makefile ++++ b/arch/arm/mach-pxa/Makefile +@@ -18,7 +18,7 @@ obj-$(CONFIG_PXA_SHARP_Cxx00) += spitz.o corgi_ssp.o corgi_lcd.o sharpsl_pm.o sp + obj-$(CONFIG_MACH_AKITA) += akita-ioexp.o + obj-$(CONFIG_MACH_POODLE) += poodle.o corgi_ssp.o + obj-$(CONFIG_MACH_TOSA) += tosa.o +-obj-$(CONFIG_MACH_EM_X270) += em-x270.o ++obj-$(CONFIG_MACH_EM_X270) += em-x270.o em-x270-pm.o em-x270-lcd.o em-x270-devices.o + + # Support for blinky lights + led-y := leds.o +@@ -29,9 +29,16 @@ led-$(CONFIG_MACH_TRIZEPS4) += leds-trizeps4.o + + obj-$(CONFIG_LEDS) += $(led-y) + ++# CPU Frequency scaling ++obj-$(CONFIG_CPU_FREQ_PXA) += cpu-pxa.o ++ + # Misc features + obj-$(CONFIG_PM) += pm.o sleep.o + obj-$(CONFIG_PXA_SSP) += ssp.o ++obj-$(CONFIG_PXA_PWR_I2C) += pwr-i2c.o ++ ++#obj-m += da9030_asm.o ++#da9030_asm-y += da9030.o da9030_c.o + + ifeq ($(CONFIG_PXA27x),y) + obj-$(CONFIG_PM) += standby.o +diff --git a/arch/arm/mach-pxa/cpu-pxa.c b/arch/arm/mach-pxa/cpu-pxa.c +new file mode 100644 +index 0000000..7fa9703 +--- /dev/null ++++ b/arch/arm/mach-pxa/cpu-pxa.c +@@ -0,0 +1,442 @@ ++/* ++ * linux/arch/arm/mach-pxa/cpu-pxa.c ++ * ++ * Copyright (C) 2002,2003 Intrinsyc Software ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * History: ++ * 31-Jul-2002 : Initial version [FB] ++ * 29-Jan-2003 : added PXA255 support [FB] ++ * 20-Apr-2003 : ported to v2.5 (Dustin McIntire, Sensoria Corp.) ++ * 11-Jan-2006 : v2.6, support for PXA27x processor up to 624MHz (Bill Reese, Hewlett Packard) ++ * ++ * Note: ++ * This driver may change the memory bus clock rate, but will not do any ++ * platform specific access timing changes... for example if you have flash ++ * memory connected to CS0, you will need to register a platform specific ++ * notifier which will adjust the memory access strobes to maintain a ++ * minimum strobe width. ++ * ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/sched.h> ++#include <linux/init.h> ++#include <linux/cpufreq.h> ++ ++#include <asm/hardware.h> ++ ++#include <asm/arch/pxa-regs.h> ++ ++/* ++ * This comes from generic.h in this directory. ++ */ ++extern unsigned int get_clk_frequency_khz(int info); ++ ++#define DEBUG 0 ++ ++#ifdef DEBUG ++ static unsigned int freq_debug = DEBUG; ++ module_param(freq_debug, int, 0644); ++ MODULE_PARM_DESC(freq_debug, "Set the debug messages to on=1/off=0"); ++#else ++ #define freq_debug 0 ++#endif ++ ++typedef struct ++{ ++ unsigned int khz; /* CPU frequency */ ++ unsigned int membus; /* memory bus frequency */ ++ unsigned int cccr; /* new CCLKCFG setting */ ++ unsigned int div2; /* alter memory controller settings to divide by 2 */ ++ unsigned int cclkcfg; /* new CCLKCFG setting */ ++} pxa_freqs_t; ++ ++/* Define the refresh period in mSec for the SDRAM and the number of rows */ ++#define SDRAM_TREF 64 /* standard 64ms SDRAM */ ++#if defined(CONFIG_MACH_H4700) || defined(CONFIG_ARCH_H2200) || defined(CONFIG_MACH_MAGICIAN) ++#define SDRAM_ROWS 8192 /* hx4700 uses 2 64Mb DRAMs, 8912 rows */ ++#else ++#define SDRAM_ROWS 4096 /* 64MB=8192 32MB=4096 */ ++#endif ++#define MDREFR_DRI(x) (((x*SDRAM_TREF/SDRAM_ROWS - 31)/32)) ++ ++#define CCLKCFG_TURBO 0x1 ++#define CCLKCFG_FCS 0x2 ++#define CCLKCFG_HALFTURBO 0x4 ++#define CCLKCFG_FASTBUS 0x8 ++#ifdef CONFIG_ARCH_H5400 ++/* H5400's SAMCOP chip does not like when K2DB2 touched */ ++#define MDREFR_DB2_MASK (MDREFR_K1DB2) ++#else ++#define MDREFR_DB2_MASK (MDREFR_K2DB2 | MDREFR_K1DB2) ++#endif ++#define MDREFR_DRI_MASK 0xFFF ++#define PXA25x_CCLKCFG CCLKCFG_TURBO | CCLKCFG_FCS ++ ++/* ++ * For the PXA27x: ++ * Control variables are A, L, 2N for CCCR; B, HT, T for CLKCFG. ++ * ++ * A = 0 => memory controller clock from table 3-7, ++ * A = 1 => memory controller clock = system bus clock ++ * Run mode frequency = 13 MHz * L ++ * Turbo mode frequency = 13 MHz * L * N ++ * System bus frequency = 13 MHz * L / (B + 1) ++ * System initialized by bootldr to: ++ * ++ * In CCCR: ++ * A = 1 ++ * L = 16 oscillator to run mode ratio ++ * 2N = 6 2 * (turbo mode to run mode ratio) ++ * ++ * In CCLKCFG: ++ * B = 1 Fast bus mode ++ * HT = 0 Half-Turbo mode ++ * T = 1 Turbo mode ++ * ++ * For now, just support some of the combinations in table 3-7 of ++ * PXA27x Processor Family Developer's Manual to simplify frequency ++ * change sequences. ++ * ++ * Specify 2N in the PXA27x_CCCR macro, not N! ++ */ ++#define PXA27x_CCCR(A, L, N2) (A << 25 | N2 << 7 | L) ++#define PXA27x_CCLKCFG(B, HT, T) (B << 3 | HT << 2 | CCLKCFG_FCS | T) ++ ++#define PXA25x_CCCR(L, M, N) (L << 0 | M << 5 | N << 7) ++ ++/* ++ * Valid frequency assignments ++ */ ++static pxa_freqs_t pxa2xx_freqs[] = ++{ ++ /* CPU MEMBUS CCCR DIV2 */ ++#if defined(CONFIG_PXA25x) ++#if defined(CONFIG_PXA25x_ALTERNATE_FREQS) ++ { 99500, 99500, PXA25x_CCCR(1, 1, 2), 1, PXA25x_CCLKCFG}, /* run=99, turbo= 99, PXbus=50, SDRAM=50 */ ++ {199100, 99500, PXA25x_CCCR(1, 1, 4), 0, PXA25x_CCLKCFG}, /* run=99, turbo=199, PXbus=50, SDRAM=99 */ ++ {298500, 99500, PXA25x_CCCR(1, 1, 6), 0, PXA25x_CCLKCFG}, /* run=99, turbo=287, PXbus=50, SDRAM=99 */ ++ {298600, 99500, PXA25x_CCCR(1, 2, 3), 0, PXA25x_CCLKCFG}, /* run=199, turbo=287, PXbus=99, SDRAM=99 */ ++ {398100, 99500, PXA25x_CCCR(1, 2, 4), 0, PXA25x_CCLKCFG} /* run=199, turbo=398, PXbus=99, SDRAM=99 */ ++#else ++ { 99500, 99500, PXA25x_CCCR(1, 1, 2), 1, PXA25x_CCLKCFG}, /* run= 99, turbo= 99, PXbus=50, SDRAM=50 */ ++ {132700, 132700, PXA25x_CCCR(3, 1, 2), 1, PXA25x_CCLKCFG}, /* run=133, turbo=133, PXbus=66, SDRAM=66 */ ++ {199100, 99500, PXA25x_CCCR(1, 2, 2), 0, PXA25x_CCLKCFG}, /* run=199, turbo=199, PXbus=99, SDRAM=99 */ ++ {265400, 132700, PXA25x_CCCR(3, 2, 2), 1, PXA25x_CCLKCFG}, /* run=265, turbo=265, PXbus=133, SDRAM=66 */ ++ {331800, 165900, PXA25x_CCCR(5, 2, 2), 1, PXA25x_CCLKCFG}, /* run=331, turbo=331, PXbus=166, SDRAM=83 */ ++ {398100, 99500, PXA25x_CCCR(1, 3, 2), 0, PXA25x_CCLKCFG} /* run=398, turbo=398, PXbus=196, SDRAM=99 */ ++#endif ++#elif defined(CONFIG_PXA27x) ++ {104000, 104000, PXA27x_CCCR(1, 8, 2), 0, PXA27x_CCLKCFG(1, 0, 1)}, ++ {156000, 104000, PXA27x_CCCR(1, 8, 6), 0, PXA27x_CCLKCFG(1, 1, 1)}, ++ {208000, 208000, PXA27x_CCCR(0, 16, 2), 1, PXA27x_CCLKCFG(0, 0, 1)}, ++ {312000, 208000, PXA27x_CCCR(1, 16, 3), 1, PXA27x_CCLKCFG(1, 0, 1)}, ++ {416000, 208000, PXA27x_CCCR(1, 16, 4), 1, PXA27x_CCLKCFG(1, 0, 1)}, ++ {520000, 208000, PXA27x_CCCR(1, 16, 5), 1, PXA27x_CCLKCFG(1, 0, 1)}, ++/* {624000, 208000, PXA27x_CCCR(1, 16, 6), 1, PXA27x_CCLKCFG(1, 0, 1)} */ ++#endif ++}; ++#define NUM_FREQS (sizeof(pxa2xx_freqs)/sizeof(pxa_freqs_t)) ++ ++static struct cpufreq_frequency_table pxa2xx_freq_table[NUM_FREQS+1]; ++ ++/* Return the memory clock rate for a given cpu frequency. */ ++int pxa_cpufreq_memclk(int cpu_khz) ++{ ++ int i; ++ int freq_mem = 0; ++ ++ for (i = 0; i < NUM_FREQS; i++) { ++ if (pxa2xx_freqs[i].khz == cpu_khz) { ++ freq_mem = pxa2xx_freqs[i].membus; ++ break; ++ } ++ } ++ ++ return freq_mem; ++} ++EXPORT_SYMBOL(pxa_cpufreq_memclk); ++ ++ ++/* find a valid frequency point */ ++static int pxa_verify_policy(struct cpufreq_policy *policy) ++{ ++ int ret; ++ ++ ret=cpufreq_frequency_table_verify(policy, pxa2xx_freq_table); ++ ++ if(freq_debug) { ++ printk("Verified CPU policy: %dKhz min to %dKhz max\n", ++ policy->min, policy->max); ++ } ++ ++ return ret; ++} ++ ++static int pxa_set_target(struct cpufreq_policy *policy, ++ unsigned int target_freq, ++ unsigned int relation) ++{ ++ int idx; ++ cpumask_t cpus_allowed, allowedcpuset; ++ int cpu = policy->cpu; ++ struct cpufreq_freqs freqs; ++ unsigned long flags; ++ unsigned int unused; ++ unsigned int preset_mdrefr, postset_mdrefr, cclkcfg; ++ ++ if(freq_debug) { ++ printk ("CPU PXA: target freq %d\n", target_freq); ++ printk ("CPU PXA: relation %d\n", relation); ++ } ++ ++ /* ++ * Save this threads cpus_allowed mask. ++ */ ++ cpus_allowed = current->cpus_allowed; ++ ++ /* ++ * Bind to the specified CPU. When this call returns, ++ * we should be running on the right CPU. ++ */ ++ cpus_clear (allowedcpuset); ++ cpu_set (cpu, allowedcpuset); ++ set_cpus_allowed(current, allowedcpuset); ++ BUG_ON(cpu != smp_processor_id()); ++ ++ /* Lookup the next frequency */ ++ if (cpufreq_frequency_table_target(policy, pxa2xx_freq_table, ++ target_freq, relation, &idx)) { ++ return -EINVAL; ++ } ++ ++ freqs.old = policy->cur; ++ freqs.new = pxa2xx_freqs[idx].khz; ++ freqs.cpu = policy->cpu; ++ if(freq_debug) { ++ printk(KERN_INFO "Changing CPU frequency to %d Mhz, (SDRAM %d Mhz)\n", ++ freqs.new/1000, (pxa2xx_freqs[idx].div2) ? ++ (pxa2xx_freqs[idx].membus/2000) : ++ (pxa2xx_freqs[idx].membus/1000)); ++ } ++ ++ /* ++ * Tell everyone what we're about to do... ++ * you should add a notify client with any platform specific ++ * Vcc changing capability ++ */ ++ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); ++ ++ /* Calculate the next MDREFR. If we're slowing down the SDRAM clock ++ * we need to preset the smaller DRI before the change. If we're speeding ++ * up we need to set the larger DRI value after the change. ++ */ ++ preset_mdrefr = postset_mdrefr = MDREFR; ++ if((MDREFR & MDREFR_DRI_MASK) > MDREFR_DRI(pxa2xx_freqs[idx].membus)) { ++ preset_mdrefr = (preset_mdrefr & ~MDREFR_DRI_MASK) | ++ MDREFR_DRI(pxa2xx_freqs[idx].membus); ++ } ++ postset_mdrefr = (postset_mdrefr & ~MDREFR_DRI_MASK) | ++ MDREFR_DRI(pxa2xx_freqs[idx].membus); ++ ++ /* If we're dividing the memory clock by two for the SDRAM clock, this ++ * must be set prior to the change. Clearing the divide must be done ++ * after the change. ++ */ ++ if(pxa2xx_freqs[idx].div2) { ++ /* ++ * Potentially speeding up memory clock, so slow down the memory ++ * before speeding up the clock. ++ */ ++ preset_mdrefr |= MDREFR_DB2_MASK | MDREFR_K0DB4; ++ preset_mdrefr &= ~MDREFR_K0DB2; ++ ++ postset_mdrefr |= MDREFR_DB2_MASK | MDREFR_K0DB4; ++ postset_mdrefr &= ~MDREFR_K0DB2; ++ } else { ++ /* ++ * Potentially slowing down memory clock. Wait until after the change ++ * to speed up the memory. ++ */ ++ postset_mdrefr &= ~MDREFR_DB2_MASK; ++ postset_mdrefr &= ~MDREFR_K0DB4; ++ postset_mdrefr |= MDREFR_K0DB2; ++ } ++ ++ cclkcfg = pxa2xx_freqs[idx].cclkcfg; ++ ++ if (freq_debug) { ++ printk (KERN_INFO "CPU PXA writing 0x%08x to CCCR\n", ++ pxa2xx_freqs[idx].cccr); ++ printk (KERN_INFO "CPU PXA writing 0x%08x to CCLKCFG\n", ++ pxa2xx_freqs[idx].cclkcfg); ++ printk (KERN_INFO "CPU PXA writing 0x%08x to MDREFR before change\n", ++ preset_mdrefr); ++ printk (KERN_INFO "CPU PXA writing 0x%08x to MDREFR after change\n", ++ postset_mdrefr); ++ } ++ ++ local_irq_save(flags); ++ ++ /* Set new the CCCR */ ++ CCCR = pxa2xx_freqs[idx].cccr; ++ ++ /* ++ * Should really set both of PMCR[xIDAE] while changing the core frequency ++ */ ++ ++ /* ++ * TODO: On the PXA27x: If we're setting half-turbo mode and changing the ++ * core frequency at the same time we must split it up into two operations. ++ * The current values in the pxa2xx_freqs table don't do this, so the code ++ * is unimplemented. ++ */ ++ ++ __asm__ __volatile__(" \ ++ ldr r4, [%1] ; /* load MDREFR */ \ ++ b 2f ; \ ++ .align 5 ; \ ++1: \ ++ str %3, [%1] ; /* preset the MDREFR */ \ ++ mcr p14, 0, %2, c6, c0, 0 ; /* set CCLKCFG[FCS] */ \ ++ str %4, [%1] ; /* postset the MDREFR */ \ ++ \ ++ b 3f ; \ ++2: b 1b ; \ ++3: nop ; \ ++ " ++ : "=&r" (unused) ++ : "r" (&MDREFR), "r" (cclkcfg), \ ++ "r" (preset_mdrefr), "r" (postset_mdrefr) ++ : "r4", "r5"); ++ local_irq_restore(flags); ++ ++ if (freq_debug) { ++ printk (KERN_INFO "CPU PXA Frequency change successful\n"); ++ printk (KERN_INFO "CPU PXA new CCSR 0x%08x\n", CCSR); ++ } ++ ++ /* ++ * Restore the CPUs allowed mask. ++ */ ++ set_cpus_allowed(current, cpus_allowed); ++ ++ /* ++ * Tell everyone what we've just done... ++ * you should add a notify client with any platform specific ++ * SDRAM refresh timer adjustments ++ */ ++ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); ++ ++ return 0; ++} ++ ++static int pxa_cpufreq_init(struct cpufreq_policy *policy) ++{ ++ cpumask_t cpus_allowed, allowedcpuset; ++ unsigned int cpu = policy->cpu; ++ int i; ++ unsigned int cclkcfg; ++ ++ cpus_allowed = current->cpus_allowed; ++ ++ cpus_clear (allowedcpuset); ++ cpu_set (cpu, allowedcpuset); ++ set_cpus_allowed(current, allowedcpuset); ++ BUG_ON(cpu != smp_processor_id()); ++ ++ /* set default governor and cpuinfo */ ++ policy->governor = CPUFREQ_DEFAULT_GOVERNOR; ++ policy->cpuinfo.transition_latency = 1000; /* FIXME: 1 ms, assumed */ ++ policy->cur = get_clk_frequency_khz(0); /* current freq */ ++ ++ /* Generate the cpufreq_frequency_table struct */ ++ for(i=0;i<NUM_FREQS;i++) { ++ pxa2xx_freq_table[i].frequency = pxa2xx_freqs[i].khz; ++ pxa2xx_freq_table[i].index = i; ++ } ++ pxa2xx_freq_table[i].frequency = CPUFREQ_TABLE_END; ++ ++ /* ++ * Set the policy's minimum and maximum frequencies from the tables ++ * just constructed. This sets cpuinfo.mxx_freq, min and max. ++ */ ++ cpufreq_frequency_table_cpuinfo (policy, pxa2xx_freq_table); ++ ++ set_cpus_allowed(current, cpus_allowed); ++ printk(KERN_INFO "PXA CPU frequency change support initialized\n"); ++ ++ if (freq_debug) { ++ printk (KERN_INFO "PXA CPU initial CCCR 0x%08x\n", CCCR); ++ asm ++ ( ++ "mrc p14, 0, %0, c6, c0, 0 ; /* read CCLKCFG from CP14 */ " ++ : "=r" (cclkcfg) : ++ ); ++ printk ("PXA CPU initial CCLKCFG 0x%08x\n", cclkcfg); ++ printk ("PXA CPU initial MDREFR 0x%08x\n", MDREFR); ++ } ++ ++ return 0; ++} ++ ++static unsigned int pxa_cpufreq_get(unsigned int cpu) ++{ ++ cpumask_t cpumask_saved; ++ unsigned int cur_freq; ++ ++ cpumask_saved = current->cpus_allowed; ++ set_cpus_allowed(current, cpumask_of_cpu(cpu)); ++ BUG_ON(cpu != smp_processor_id()); ++ ++ cur_freq = get_clk_frequency_khz(0); ++ ++ set_cpus_allowed(current, cpumask_saved); ++ ++ return cur_freq; ++} ++ ++static struct cpufreq_driver pxa_cpufreq_driver = { ++ .verify = pxa_verify_policy, ++ .target = pxa_set_target, ++ .init = pxa_cpufreq_init, ++ .get = pxa_cpufreq_get, ++#if defined(CONFIG_PXA25x) ++ .name = "PXA25x", ++#elif defined(CONFIG_PXA27x) ++ .name = "PXA27x", ++#endif ++}; ++ ++static int __init pxa_cpu_init(void) ++{ ++ return cpufreq_register_driver(&pxa_cpufreq_driver); ++} ++ ++static void __exit pxa_cpu_exit(void) ++{ ++ cpufreq_unregister_driver(&pxa_cpufreq_driver); ++} ++ ++ ++MODULE_AUTHOR ("Intrinsyc Software Inc."); ++MODULE_DESCRIPTION ("CPU frequency changing driver for the PXA architecture"); ++MODULE_LICENSE("GPL"); ++module_init(pxa_cpu_init); ++module_exit(pxa_cpu_exit); ++ +diff --git a/arch/arm/mach-pxa/em-x270-devices.c b/arch/arm/mach-pxa/em-x270-devices.c +new file mode 100644 +index 0000000..d142930 +--- /dev/null ++++ b/arch/arm/mach-pxa/em-x270-devices.c +@@ -0,0 +1,331 @@ ++/* ++ * Support for CompuLab EM-X270 platfrom specific device ++ * initialization, and per-device power management functions. ++ * ++ * Copyright (C) 2007 CompuLab, Ltd. ++ * Author: Mike Rapoport <mike@compulab.co.il> ++ * ++ * 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/platform_device.h> ++#include <linux/sysfs.h> ++#include <linux/ctype.h> ++#include <linux/delay.h> ++#include <linux/da9030.h> ++#include <linux/interrupt.h> ++#include <linux/irq.h> ++ ++#include "../../../drivers/i2c/chips/da9030.h" ++ ++#include <asm/arch/hardware.h> ++#include <asm/arch/pxa-regs.h> ++ ++struct em_x270_dev_data { ++ int (*is_on)(struct device *dev); ++ int (*set_on)(struct device *dev, int on); ++ int (*get_opts)(struct device *dev, ++ struct device_attribute *attr, ++ char *buf); ++ int (*set_opts)(struct device *dev, int opts); ++}; ++ ++static void em_x270_dev_release(struct device * dev) ++{ ++ ++} ++ ++static ssize_t em_x270_dev_pm_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct em_x270_dev_data *pm_data = dev->platform_data; ++ int is_on = pm_data->is_on(dev); ++ ++ return sprintf(buf, "%d\n", is_on); ++} ++ ++static ssize_t em_x270_dev_pm_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ char *endp; ++ int on = simple_strtoul(buf, &endp, 0); ++ size_t size = endp - buf; ++ struct em_x270_dev_data *pm_data = dev->platform_data; ++ ++ pr_info("%s: %s\n", __FUNCTION__, buf); ++ ++ if (*endp && isspace(*endp)) ++ size++; ++ if (size != count) ++ return -EINVAL; ++ ++ pm_data->set_on(dev, on); ++ ++ return count; ++} ++ ++static DEVICE_ATTR(pwr_on, S_IWUSR | S_IRUGO, ++ em_x270_dev_pm_show, em_x270_dev_pm_store); ++ ++static int gprs_is_on(struct device *dev) ++{ ++ return !!(pxa_gpio_get_value(20)); ++} ++ ++static int gprs_set_on(struct device *dev, int on) ++{ ++ pr_info("%s: on = %d\n", __FUNCTION__, on); ++ if (on) { ++ pxa_gpio_mode(20 | GPIO_OUT | GPIO_DFLT_HIGH); ++ mdelay(500); ++ pxa_gpio_set_value(82, 0); ++ mdelay(500); ++ pxa_gpio_mode(82 | GPIO_OUT | GPIO_DFLT_HIGH); ++ mdelay(1000); ++ pxa_gpio_set_value(82, 0); ++ } ++ else { ++ pxa_gpio_set_value(20, 0); ++ pxa_gpio_mode(20 | GPIO_OUT); ++ } ++ ++ return 0; ++} ++ ++static struct em_x270_dev_data em_x270_gprs_data = { ++ .is_on = gprs_is_on, ++ .set_on = gprs_set_on, ++}; ++ ++static struct platform_device em_x270_gprs = { ++ .name = "gprs", ++ .id = -1, ++ .dev = { ++ .platform_data = &em_x270_gprs_data, ++ .release = em_x270_dev_release, ++ }, ++}; ++ ++static int is_wlan_on; ++ ++static int wlan_is_on(struct device *dev) ++{ ++ return is_wlan_on; ++} ++ ++static int wlan_set_on(struct device *dev, int on) ++{ ++ if (on) { ++ /* WLAN power-up sequence */ ++ /* Mask LDO17 and LDO19 tolerance events */ ++ da9030_set_reg(IRQ_MASK_C, ++ IRQ_MASK_C_LDO19 | IRQ_MASK_C_LDO17); ++ ++ /* Force I2C control over LDO17, LDO19 */ ++ da9030_set_reg(SLEEP_CONTROL, ++ APP_SLEEP_CTL_BYPASS_LDO19 | ++ APP_SLEEP_CTL_BYPASS_LDO17); ++ ++ /* Turn off LDO17 and LDO19 */ ++ da9030_set_reg(REG_CONTROL_1_17, ++ RC1_LDO16_EN | RC1_LDO15_EN | ++ RC1_LDO11_EN | RC1_LDO10_EN | RC1_BUCK2_EN); ++ da9030_set_reg(REG_CONTROL_2_18, RC2_SIMCP_EN | RC2_LDO18_EN); ++ ++ /* Set LDO17 voltage to 3V (VCC_WLAN_IO) */ ++ da9030_set_reg(LDO_17_SIMCP0, LDO_17_SIMCP0_LDO17_3V); ++ mdelay(200); ++ ++ /* Turn WLAN RF power on */ ++ pxa_gpio_mode(115 | GPIO_OUT | GPIO_DFLT_HIGH); ++ ++ /* Turn LDO17 on */ ++ da9030_set_reg(REG_CONTROL_1_17, ++ RC1_LDO17_EN| RC1_LDO16_EN | RC1_LDO15_EN | ++ RC1_LDO11_EN | RC1_LDO10_EN | RC1_BUCK2_EN); ++ mdelay(200); ++ ++ /* Turn on LDO19 */ ++ da9030_set_reg(REG_CONTROL_2_18, ++ RC2_SIMCP_EN | RC2_LDO18_EN | RC2_LDO19_EN); ++ ++ } ++ else { ++ /* FIXME: implement BGW shutdown */ ++ } ++ is_wlan_on = on; ++ ++ return 0; ++} ++ ++static struct em_x270_dev_data em_x270_wlan_data = { ++ .is_on = wlan_is_on, ++ .set_on = wlan_set_on, ++}; ++ ++static struct platform_device em_x270_wlan = { ++ .name = "wlan", ++ .id = -1, ++ .dev = { ++ .platform_data = &em_x270_wlan_data, ++ .release = em_x270_dev_release, ++ }, ++}; ++ ++static int gps_is_on(struct device *dev) ++{ ++ int val = da9030_get_reg(REG_CONTROL_1_97); ++ return !!(val & RC3_LDO3_EN); ++} ++ ++static int gps_set_on(struct device *dev, int on) ++{ ++ int val = da9030_get_reg(REG_CONTROL_1_97); ++ ++ if (on) ++ val |= RC3_LDO3_EN; ++ else ++ val &= ~RC3_LDO3_EN; ++ da9030_set_reg(REG_CONTROL_1_97, val); ++ ++ return 0; ++} ++ ++static struct em_x270_dev_data em_x270_gps_data = { ++ .is_on = gps_is_on, ++ .set_on = gps_set_on, ++}; ++ ++static struct platform_device em_x270_gps = { ++ .name = "gps", ++ .id = -1, ++ .dev = { ++ .platform_data = &em_x270_gps_data, ++ .release = em_x270_dev_release, ++ }, ++}; ++ ++static int usb_mode; ++static int usb_host_is_on(struct device *dev) ++{ ++ return usb_mode; ++} ++ ++static int usb_host_set_on(struct device *dev, int on) ++{ ++ if (on) { ++ da9030_set_reg(MISC_CONTROLB, MISCB_SESSION_VALID_ENABLE); ++ da9030_set_reg(USBPUMP, ++ USB_PUMP_EN_USBVEP | ++ USB_PUMP_EN_USBVE | ++ USB_PUMP_USBVE); ++ ++ /* enable port 2 transiever */ ++ UP2OCR = UP2OCR_HXS | UP2OCR_HXOE; ++ usb_mode = 1; ++ } ++ else { ++ UP2OCR = UP2OCR_DPPUE | UP2OCR_DPPUBE | UP2OCR_HXOE; ++ da9030_set_reg(USBPUMP, ++ USB_PUMP_EN_USBVEP | USB_PUMP_EN_USBVE); ++ usb_mode = 0; ++ } ++ ++ return 0; ++} ++ ++static struct em_x270_dev_data em_x270_usb_host_data = { ++ .is_on = usb_host_is_on, ++ .set_on = usb_host_set_on, ++}; ++ ++static struct platform_device em_x270_usb_host = { ++ .name = "usb_host", ++ .id = -1, ++ .dev = { ++ .platform_data = &em_x270_usb_host_data, ++ .release = em_x270_dev_release, ++ }, ++}; ++ ++static struct platform_device *em_x270_devices[] = { ++ &em_x270_gprs, ++ &em_x270_wlan, ++ &em_x270_gps, ++ &em_x270_usb_host, ++}; ++ ++static struct work_struct usb_work; ++static void usb_worker(struct work_struct *work) ++{ ++ usb_host_set_on(NULL, !pxa_gpio_get_value(21)); ++} ++ ++static irqreturn_t usb_irq_handler(int irq, void *regs) ++{ ++ schedule_work(&usb_work); ++ ++ pr_info("%s\n", __FUNCTION__); ++ return IRQ_HANDLED; ++} ++ ++static int em_x270_devices_init(void) ++{ ++ int i, ret; ++ ++ for (i = 0; i < ARRAY_SIZE(em_x270_devices); i++ ) { ++ ret = platform_device_register(em_x270_devices[i]); ++ if (ret) { ++ dev_dbg(&em_x270_devices[i]->dev, ++ "Registration failed: %d\n", ret); ++ continue; ++ } ++ ++ ret = device_create_file(&em_x270_devices[i]->dev, ++ &dev_attr_pwr_on); ++ if (ret) { ++ dev_dbg(&em_x270_devices[i]->dev, ++ "PWR_ON attribute failed: %d\n", ret); ++ } ++ ++ dev_dbg(&em_x270_devices[i]->dev, ++ "Registered PWR ON attribute\n"); ++ } ++ ++ /* setup USB detection irq */ ++ INIT_WORK(&usb_work, usb_worker); ++ set_irq_type(IRQ_GPIO(21), IRQT_BOTHEDGE); ++ ret = request_irq(IRQ_GPIO(21), usb_irq_handler, IRQF_DISABLED, ++ "usb detect", 0); ++ if (ret) { ++ pr_info("USB device detection disabled\n"); ++ } ++ else { ++ schedule_work(&usb_work); ++ } ++ ++ return 0; ++} ++ ++static void em_x270_devices_exit(void) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(em_x270_devices); i++ ) { ++ device_remove_file(&em_x270_devices[i]->dev, ++ &dev_attr_pwr_on); ++ platform_device_unregister(em_x270_devices[i]); ++ } ++} ++ ++late_initcall(em_x270_devices_init); ++module_exit(em_x270_devices_exit); ++ ++MODULE_AUTHOR("Mike Rapoport"); ++MODULE_DESCRIPTION("Support for platfrom specific device attributes for EM-X270"); ++MODULE_LICENSE("GPL"); +diff --git a/arch/arm/mach-pxa/em-x270-lcd.c b/arch/arm/mach-pxa/em-x270-lcd.c +new file mode 100644 +index 0000000..437efe1 +--- /dev/null ++++ b/arch/arm/mach-pxa/em-x270-lcd.c +@@ -0,0 +1,223 @@ ++/* ++ * LCD initialization and backlight managemnet for EM-X270 ++ * ++ * Copyright (C) 2007 CompuLab, Ltd. ++ * Author: Igor Vaisbein <igor@compulab.co.il> ++ * ++ * 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/platform_device.h> ++#include <linux/da9030.h> ++#include <linux/delay.h> ++ ++#include <asm/arch/hardware.h> ++#include <asm/arch/pxa-regs.h> ++#include <asm/arch/sharpsl.h> ++#include <asm/arch/ssp.h> ++ ++#include "devices.h" ++ ++#define GPIO87_nPCE_2 87 /* Card Enable for Card Space (PXA27x) */ ++#define GPIO87_nPCE_2_MD (87 | GPIO_ALT_FN_1_IN) ++#define GPIO87_USB3_1_MD (87 | GPIO_ALT_FN_3_IN) ++#define GPIO87_SSPTXD2_MD (87 | GPIO_ALT_FN_1_OUT) ++#define GPIO87_SSFRM2_MD (87 | GPIO_ALT_FN_3_OUT) ++ ++#define LCCR4 __REG(0x44000010) ++ ++#define TD035STEE1_LCD_ID 0x2008 ++ ++static void em_x270_set_bl_intensity(int intensity) ++{ ++ da9030_set_wled((intensity != 0), intensity); ++} ++ ++struct corgibl_machinfo em_x270_bl_machinfo = { ++ .max_intensity = 0x7, ++ .default_intensity = 0x3, ++ .limit_mask = 0x1, ++ .set_bl_intensity = em_x270_set_bl_intensity, ++}; ++ ++static void em_x270_bl_release(struct device * dev) ++{ ++ ++} ++ ++static struct platform_device em_x270_bl = { ++ .name = "corgi-bl", ++ .dev = { ++ .release = em_x270_bl_release, ++ .parent = &pxa_device_fb.dev, ++ .platform_data = &em_x270_bl_machinfo, ++ }, ++ .id = -1, ++}; ++ ++/* ++ * Helper functions to access LCD throuhg SPI interface ++ */ ++static void set_ssp_9bit(void) ++{ ++ int temp; ++ SSCR0 = 0xFF208; ++ SSCR0 = 0xFF288; ++ SSCR1 = 0x40000018; ++ ++ while (SSSR & 0x8) ++ temp = SSDR; ++} ++ ++static void set_ssp_18bit(void) ++{ ++ int temp; ++ SSCR0 = 0x1FF201; ++ SSCR0 = 0x1FF281; ++ SSCR1 = 0x40000018; ++ while (SSSR & 0x8) ++ temp = SSDR; ++} ++ ++static void set_ssp_rcv(void) ++{ ++ int temp; ++ SSCR0 = 0x1FF20F; ++ SSCR0 = 0x1FF28F; ++ SSCR1 = 0x40000018; ++ while (SSSR & 0x8) ++ temp = SSDR; ++} ++ ++static void send_ssp_9bit(unsigned int value) ++{ ++ int temp; ++ SSDR = (value); ++ ++ asm volatile ("mcr p15, 0, r0, c7, c10, 4":::"r0"); ++ ++ if (!(SSSR & 0x4)) ++ while ((SSSR & 0xf00) == 0) ++ ; ++ ++ while ((SSSR & 0xf00) != 0) ++ ; ++ while (SSSR & 0x10) ++ ; ++ while (SSSR & 0x8) ++ temp = SSDR; ++} ++ ++static void send_ssp_18bit(unsigned int value) ++{ ++ int temp; ++ SSDR = (value); ++ ++ asm volatile ("mcr p15, 0, r0, c7, c10, 4":::"r0"); ++ ++ if (!(SSSR & 0x4)) ++ while ((SSSR & 0xf00) == 0) ++ ; ++ ++ while ((SSSR & 0xf00) != 0) ++ ; ++ while (SSSR & 0x10) ++ ; ++ while (SSSR & 0x8) ++ temp = SSDR; ++} ++ ++static unsigned int rcv_ssp_18bit(void) ++{ ++ SSDR = ((0x04) << 23); ++ asm volatile ("mcr p15, 0, r0, c7, c10, 4":::"r0"); ++ if (!(SSSR & 0x4)) ++ while ((SSSR & 0xf00) == 0) ++ ; ++ while ((SSSR & 0xf00) != 0) ++ ; ++ while (SSSR & 0x10) ++ ; ++ return SSDR; ++} ++ ++/* LCD init sequence */ ++int em_x270_lcd_detect(void) ++{ ++ unsigned int data; ++ ++ /* Reset the LCD module */ ++ pxa_gpio_mode(GPIO87_nPCE_2 | GPIO_OUT); ++ GPCR(GPIO87_nPCE_2) |= GPIO_bit(GPIO87_nPCE_2); ++ mdelay(75); ++ GPSR(GPIO87_nPCE_2) |= GPIO_bit(GPIO87_nPCE_2); ++ mdelay(70); ++ ++ /* TD035STEE1 LCD_SSP initialization commands */ ++ set_ssp_9bit(); ++ send_ssp_9bit(0x000); ++ mdelay(5); ++ ++ send_ssp_9bit(0x000); ++ mdelay(5); ++ ++ send_ssp_9bit(0x000); ++ mdelay(5); ++ ++ set_ssp_18bit(); ++ send_ssp_18bit(0x17980); ++ mdelay(5); ++ ++ send_ssp_18bit(0x17F10); ++ mdelay(5); ++ ++ set_ssp_9bit(); ++ send_ssp_9bit(0x011); ++ mdelay(50); ++ ++ send_ssp_9bit(0x029); ++ mdelay(10); ++ ++ set_ssp_rcv(); ++ ++ /* Check for LCD ID, to enable the back-light */ ++ data = rcv_ssp_18bit(); ++ ++ if ((data & 0xFFFF) != TD035STEE1_LCD_ID) ++ return -ENODEV; ++ ++ /* enable backlight */ ++ da9030_set_wled(1, 2); ++ return 0; ++} ++ ++static int em_x270_lcd_init(void) ++{ ++ int ret; ++ pr_debug("%s\n", __FUNCTION__); ++ ret = em_x270_lcd_detect(); ++ if (ret) ++ return ret; ++ ++ /* make sure we keep LCCR4 with PCD=0 */ ++ LCCR4 = 0x0; ++ ++ return platform_device_register(&em_x270_bl); ++} ++ ++static void em_x270_lcd_exit(void) ++{ ++ pr_debug("%s\n", __FUNCTION__); ++ platform_device_unregister(&em_x270_bl); ++} ++ ++late_initcall(em_x270_lcd_init); ++module_exit(em_x270_lcd_exit); ++ ++MODULE_DESCRIPTION("EM-X270 backlight and LCD initialization driver"); ++MODULE_AUTHOR("Mike Rapoport"); ++MODULE_LICENSE("GPL"); +diff --git a/arch/arm/mach-pxa/em-x270-pm.c b/arch/arm/mach-pxa/em-x270-pm.c +new file mode 100644 +index 0000000..55ba4cd +--- /dev/null ++++ b/arch/arm/mach-pxa/em-x270-pm.c +@@ -0,0 +1,892 @@ ++/* ++ * Support for CompuLab EM-X270 platform power management ++ * ++ * Copyright (C) 2007 CompuLab, Ltd. ++ * Author: Mike Rapoport <mike@compulab.co.il> ++ * ++ * 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/kernel.h> ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/sysfs.h> ++#include <linux/pm.h> ++#include <linux/da9030.h> ++#include <linux/interrupt.h> ++#include <linux/irq.h> ++#include <linux/delay.h> ++#include <linux/ctype.h> ++ ++#include <linux/apm-emulation.h> ++#include <linux/platform_device.h> ++#include <linux/power_supply.h> ++ ++#include <linux/debugfs.h> ++#include <linux/seq_file.h> ++ ++#include "../../../drivers/i2c/chips/da9030.h" ++ ++#include <asm/arch/pm.h> ++#include <asm/arch/hardware.h> ++#include <asm/arch/pxa-regs.h> ++#include <asm/arch/pwr-i2c.h> ++ ++#define DA9030_ADDR 0x92 ++ ++#define EM_X270_BATCHK_TIME_SUSPEND (10*60) /* 10 min */ ++ ++#define VOLTAGE_MAX_DESIGN 4200000 /* 4.2V in uV */ ++#define VOLTAGE_MIN_DESIGN 3000000 /* 3V in uV */ ++ ++#define REG2VOLT(x) ((((x) * 2650) >> 8) + 2650) ++#define VOLT2REG(x) ((((x) - 2650) << 8) / 2650) ++ ++#define REG2CURR(x) ((((x) * 24000) >> 8) / 15) ++ ++#define VCHARGE_MIN_THRESHOLD VOLT2REG(3200) ++#define VCHARGE_MAX_THRESHOLD VOLT2REG(5500) ++ ++#define VBAT_LOW_THRESHOLD VOLT2REG(3600) ++#define VBAT_CRIT_THRESHOLD VOLT2REG(3400) ++ ++#define VBAT_CHARGE_START VOLT2REG(4100) ++#define VBAT_CHARGE_STOP VOLT2REG(4200) ++#define VBAT_CHARGE_RESTART VOLT2REG(4000) ++ ++#define TBAT_LOW_THRESHOLD 197 /* 0oC */ ++#define TBAT_HIGH_THRESHOLD 78 /* 45oC */ ++#define TBAT_RESUME_THRESHOLD 100 /* 35oC */ ++ ++struct em_x270_charger; ++ ++struct em_x270_charger_ops { ++ void (*get_status)(struct em_x270_charger *charger); ++ void (*set_charge)(struct em_x270_charger *charger, int on); ++ ++ s32 (*da9030_get_reg)(u32 reg); ++ s32 (*da9030_set_reg)(u32 reg, u8 val); ++}; ++ ++struct em_x270_charger { ++ struct device *dev; ++ ++ struct power_supply bat; ++ struct da9030_adc_res adc; ++ struct delayed_work work; ++ ++ int interval; ++ ++ int da9030_status; ++ int da9030_fault; ++ int mA; ++ int mV; ++ int is_on; ++ ++ struct em_x270_charger_ops *ops; ++ ++#ifdef CONFIG_DEBUG_FS ++ struct dentry *debug_file; ++#endif ++}; ++ ++static struct em_x270_charger *the_charger; ++ ++static unsigned short tbat_readings[] = { ++ 300, 244, 200, 178, 163, 152, 144, 137, 131, ++ 126, 122, 118, 114, 111, 108, 105, 103, 101, ++ 98, 96, 94, 93, 91, 89, 88, 86, 85, ++ 83, 82, 81, 79, 78, 77, 76, 75, 74, ++ 73, 72, 71, 70, 69, 68, 67, 67, 66, ++ 65, 64, 63, 63, 62, 61, 60, 60, 59, ++ 58, 58, 57, 57, 56, 55, 55, 54, 53, ++ 53, 52, 52, 51, 51, 50, 50, 49, 49, ++ 48, 48, 47, 47, 46, 46, 45, 45, 44, ++ 44, 43, 43, 42, 42, 41, 41, 41, 40, ++ 40, 39, 39, 38, 38, 38, 37, 37, 36, ++ 36, 35, 35, 35, 34, 34, 34, 33, 33, ++ 32, 32, 32, 31, 31, 30, 30, 30, 29, ++ 29, 29, 28, 28, 28, 27, 27, 26, 26, ++ 26, 25, 25, 25, 24, 24, 24, 23, 23, ++ 23, 22, 22, 21, 21, 21, 20, 20, 20, ++ 19, 19, 19, 18, 18, 18, 17, 17, 17, ++ 16, 16, 16, 15, 15, 14, 14, 14, 13, ++ 13, 13, 12, 12, 12, 11, 11, 11, 10, ++ 10, 10, 9, 9, 8, 8, 8, 7, 7, ++ 7, 6, 6, 5, 5, 5, 4, 4, 3, ++ 3, 3, 2, 2, 1, 1, 1, 0, 0, ++ -1, -1, -1, -2, -2, -3, -3, -4, -4, ++ -5, -5, -6, -6, -6, -7, -7, -8, -9, ++ -9, -10, -10, -11, -11, -12, -12, -13, -14, ++ -14, -15, -16, -16, -17, -18, -18, -19, -20, ++ -21, -21, -22, -23, -24, -25, -26, -27, -28, ++ -30, -31, -32, -34, -35, -37, -39, -41, -44, ++ -47, -50, -56, -64, ++}; ++ ++static inline int usb_host_on(void) ++{ ++ return !pxa_gpio_get_value(21); ++} ++ ++#ifdef CONFIG_DEBUG_FS ++ ++static int debug_show(struct seq_file *s, void *data) ++{ ++ struct em_x270_charger *charger = s->private; ++ ++ seq_printf(s, "charger is %s\n", charger->is_on ? "on" : "off"); ++ if (charger->da9030_status & CHRG_CHARGER_ENABLE) { ++ seq_printf(s, "iset = %dmA, vset = %dmV\n", ++ charger->mA, charger->mV); ++ } ++ ++ seq_printf(s, "vbat_res = %d (%dmV)\n", ++ charger->adc.vbat_res, REG2VOLT(charger->adc.vbat_res)); ++ seq_printf(s, "vbatmin_res = %d (%dmV)\n", ++ charger->adc.vbatmin_res, ++ REG2VOLT(charger->adc.vbatmin_res)); ++ seq_printf(s, "vbatmintxon = %d (%dmV)\n", ++ charger->adc.vbatmintxon, ++ REG2VOLT(charger->adc.vbatmintxon)); ++ seq_printf(s, "ichmax_res = %d (%dmA)\n", ++ charger->adc.ichmax_res, ++ REG2CURR(charger->adc.ichmax_res)); ++ seq_printf(s, "ichmin_res = %d (%dmA)\n", ++ charger->adc.ichmin_res, ++ REG2CURR(charger->adc.ichmin_res)); ++ seq_printf(s, "ichaverage_res = %d (%dmA)\n", ++ charger->adc.ichaverage_res, ++ REG2CURR(charger->adc.ichaverage_res)); ++ seq_printf(s, "vchmax_res = %d (%dmV)\n", ++ charger->adc.vchmax_res, ++ REG2VOLT(charger->adc.vchmax_res)); ++ seq_printf(s, "vchmin_res = %d (%dmV)\n", ++ charger->adc.vchmin_res, ++ REG2VOLT(charger->adc.vchmin_res)); ++ seq_printf(s, "tbat_res = %d (%doC)\n", charger->adc.tbat_res, ++ tbat_readings[charger->adc.tbat_res]); ++ seq_printf(s, "adc_in4_res = %d\n", charger->adc.adc_in4_res); ++ seq_printf(s, "adc_in5_res = %d\n", charger->adc.adc_in5_res); ++ ++ return 0; ++} ++ ++static int debug_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, debug_show, inode->i_private); ++} ++ ++static const struct file_operations debug_fops = { ++ .open = debug_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static struct dentry* em_x270_create_debugfs(struct em_x270_charger *charger) ++{ ++ charger->debug_file = debugfs_create_file("charger", 0666, 0, charger, ++ &debug_fops); ++ return charger->debug_file; ++} ++ ++static void em_x270_remove_debugfs(struct em_x270_charger *charger) ++{ ++ debugfs_remove(charger->debug_file); ++} ++#else ++#define em_x270_create_debugfs(x) NULL ++#define em_x270_remove_debugfs(x) do {} while(0) ++#endif ++ ++/*********************************************************************/ ++/* DA9030 access functions for suspend/resume */ ++#define DA_ADDR 0x49 ++static inline s32 __da9030_get_reg(u32 reg) ++{ ++ pr_info("%s: reg = %d\n", __FUNCTION__, reg); ++ return pxa_pwr_i2c_reg_read(DA_ADDR, reg); ++} ++ ++static inline s32 __da9030_set_reg(u32 reg, u8 val) ++{ ++ pr_info("%s: reg = %d, val = %d\n", __FUNCTION__, reg, val); ++ return pxa_pwr_i2c_reg_write(DA_ADDR, reg, val); ++} ++ ++/*********************************************************************/ ++/* helpers for charger state monnitor. Different version for stready ++ * state and suspended system ++ */ ++static void em_x270_get_charger_status(struct em_x270_charger *charger) ++{ ++ da9030_get_charger(&charger->is_on, &charger->mA, &charger->mV); ++ da9030_read_adc(&charger->adc); ++ charger->da9030_status = da9030_get_status(); ++ charger->da9030_fault = da9030_get_fault_log(); ++} ++ ++static void em_x270_set_charge(struct em_x270_charger *charger, int on) ++{ ++ if (on) { ++ pr_debug("%s: enabling charger\n", __FUNCTION__); ++ da9030_set_thresholds(TBAT_HIGH_THRESHOLD, ++ TBAT_RESUME_THRESHOLD, ++ TBAT_LOW_THRESHOLD, ++ VBAT_LOW_THRESHOLD); ++ da9030_set_reg(CCTR_CONTROL, CCTR_SET_8MIN); ++ da9030_set_charger(1, 1000, 4200); ++ da9030_set_led(3, 1, 0, 0, 0); ++ charger->is_on = 1; ++ } ++ else { ++ /* disable charger */ ++ pr_debug("%s: disabling charger\n", __FUNCTION__); ++ da9030_set_charger(0, 0, 0); ++ da9030_set_led(3, 0, 0, 0, 0); ++ charger->is_on = 0; ++ } ++} ++ ++static void em_x270_get_charger_status_suspend(struct em_x270_charger *charger) ++{ ++ s32 val; ++ ++ val = __da9030_get_reg(CHARGE_CONTROL); ++ charger->is_on = (val & CHRG_CHARGER_ENABLE) ? 1 : 0; ++ charger->mA = ((val >> 3) & 0xf) * 100; ++ charger->mV = (val & 0x7) * 50 + 4000; ++ ++ charger->adc.vbat_res = __da9030_get_reg(VBAT_RES); ++ charger->adc.vchmax_res = __da9030_get_reg(VCHMAX_RES); ++ charger->adc.vchmin_res = __da9030_get_reg(VCHMIN_RES); ++ charger->adc.tbat_res = __da9030_get_reg(TBAT_RES); ++ ++ charger->da9030_status = __da9030_get_reg(STATUS); ++ charger->da9030_fault = __da9030_get_reg(FAULT_LOG); ++} ++ ++static void em_x270_set_charge_suspend(struct em_x270_charger *charger, int on) ++{ ++ if (on) { ++ u8 val = 0; ++ int mA = 1000; ++ int mV = 4200; ++ ++ pr_debug("%s: enabling charger\n", __FUNCTION__); ++ val = CHRG_CHARGER_ENABLE; ++ val |= (mA / 100) << 3; ++ val |= (mV - 4000) / 50; ++ ++ __da9030_set_reg(CCTR_CONTROL, CCTR_SET_8MIN); ++ __da9030_set_reg(VBATMON, VBAT_LOW_THRESHOLD); ++ __da9030_set_reg(CHARGE_CONTROL, val); ++ charger->is_on = 1; ++ } ++ else { ++ /* disable charger */ ++ pr_debug("%s: disabling charger\n", __FUNCTION__); ++ __da9030_set_reg(CHARGE_CONTROL, 0); ++ charger->is_on = 0; ++ } ++} ++ ++static struct em_x270_charger_ops em_x270_charger_ops = { ++ .get_status = em_x270_get_charger_status, ++ .set_charge = em_x270_set_charge, ++ .da9030_get_reg = da9030_get_reg, ++ .da9030_set_reg = da9030_set_reg, ++}; ++ ++static struct em_x270_charger_ops em_x270_charger_ops_suspend = { ++ .get_status = em_x270_get_charger_status_suspend, ++ .set_charge = em_x270_set_charge_suspend, ++ .da9030_get_reg = __da9030_get_reg, ++ .da9030_set_reg = __da9030_set_reg, ++}; ++ ++/*********************************************************************/ ++/* charging state machine */ ++static void em_x270_check_charger_state(struct em_x270_charger *charger) ++{ ++ charger->ops->get_status(charger); ++ ++/* we wake or boot with external power on */ ++ if (!charger->is_on) { ++ if ((charger->da9030_status & STATUS_CHDET) && ++ (!usb_host_on()) && ++ (charger->adc.vbat_res < VBAT_CHARGE_START)) { ++ pr_debug("%s: vbat_res <= 4100\n", __FUNCTION__); ++ charger->ops->set_charge(charger, 1); ++ } ++ } ++ else { ++ if (charger->adc.vbat_res >= VBAT_CHARGE_STOP) { ++ pr_debug("%s: vbat_res >= 4200\n", __FUNCTION__); ++ charger->ops->set_charge(charger, 0); ++ charger->ops->da9030_set_reg(VBATMON, ++ VBAT_CHARGE_RESTART); ++ } ++ else if (charger->adc.vbat_res > VBAT_LOW_THRESHOLD) { ++ /* we are charging and passed LOW_THRESH, ++ so upate DA9030 VBAT threshold ++ */ ++ pr_debug("%s: vbat_res >= %d\n", __FUNCTION__, ++ REG2VOLT(VBAT_LOW_THRESHOLD)); ++ charger->ops->da9030_set_reg(VBATMON, ++ VBAT_LOW_THRESHOLD); ++ } ++ if (charger->adc.vchmax_res > VCHARGE_MAX_THRESHOLD || ++ charger->adc.vchmin_res < VCHARGE_MIN_THRESHOLD || ++ /* Tempreture readings are negative */ ++ charger->adc.tbat_res < TBAT_HIGH_THRESHOLD || ++ charger->adc.tbat_res > TBAT_LOW_THRESHOLD ) { ++ /* disable charger */ ++ pr_info("%s: thresholds fail\n", __FUNCTION__); ++ charger->ops->set_charge(charger, 0); ++ } ++ } ++} ++ ++static void em_x270_charging_monitor(struct work_struct *work) ++{ ++ struct em_x270_charger *charger; ++ ++ charger = container_of(work, struct em_x270_charger, work.work); ++ ++ em_x270_check_charger_state(charger); ++ ++ /* reschedule for the next time */ ++ schedule_delayed_work(&charger->work, charger->interval); ++} ++ ++void em_x270_battery_release(struct device * dev) ++{ ++} ++ ++struct em_x270_battery_thresh { ++ int voltage; ++ int percentage; ++}; ++ ++static struct em_x270_battery_thresh vbat_ranges[] = { ++ { 150, 100}, ++ { 149, 99}, ++ { 148, 98}, ++ { 147, 98}, ++ { 146, 97}, ++ { 145, 96}, ++ { 144, 96}, ++ { 143, 95}, ++ { 142, 94}, ++ { 141, 93}, ++ { 140, 92}, ++ { 139, 91}, ++ { 138, 90}, ++ { 137, 90}, ++ { 136, 89}, ++ { 135, 88}, ++ { 134, 88}, ++ { 133, 87}, ++ { 132, 86}, ++ { 131, 85}, ++ { 130, 83}, ++ { 129, 82}, ++ { 128, 81}, ++ { 127, 81}, ++ { 126, 80}, ++ { 125, 75}, ++ { 124, 74}, ++ { 123, 73}, ++ { 122, 70}, ++ { 121, 66}, ++ { 120, 65}, ++ { 119, 64}, ++ { 118, 64}, ++ { 117, 63}, ++ { 116, 59}, ++ { 115, 58}, ++ { 114, 57}, ++ { 113, 57}, ++ { 112, 56}, ++ { 111, 50}, ++ { 110, 49}, ++ { 109, 49}, ++ { 108, 48}, ++ { 107, 48}, ++ { 106, 33}, ++ { 105, 32}, ++ { 104, 32}, ++ { 103, 32}, ++ { 102, 31}, ++ { 101, 16}, ++ { 100, 15}, ++ { 99, 15}, ++ { 98, 15}, ++ { 97, 10}, ++ { 96, 9}, ++ { 95, 7}, ++ { 94, 3}, ++ { 93, 0}, ++}; ++ ++static enum power_supply_property em_x270_bat_props[] = { ++ POWER_SUPPLY_PROP_STATUS, ++ POWER_SUPPLY_PROP_HEALTH, ++ POWER_SUPPLY_PROP_TECHNOLOGY, ++ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, ++ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, ++ POWER_SUPPLY_PROP_VOLTAGE_NOW, ++ POWER_SUPPLY_PROP_CURRENT_AVG, ++ POWER_SUPPLY_PROP_CAPACITY, /* in percents! */ ++ POWER_SUPPLY_PROP_TEMP, ++ POWER_SUPPLY_PROP_MANUFACTURER, ++ POWER_SUPPLY_PROP_MODEL_NAME, ++}; ++ ++static void em_x270_bat_check_status(struct em_x270_charger *charger, ++ union power_supply_propval *val) ++{ ++ /* FIXME: below code is very crude approximation of actual ++ states, we need to take into account voltage and current ++ measurements to determine actual charger state */ ++ if (charger->da9030_status & STATUS_CHDET) { ++ if (!usb_host_on()) { ++ if (charger->is_on) { ++ val->intval = POWER_SUPPLY_STATUS_CHARGING; ++ } ++ else { ++ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; ++ } ++ } ++ } ++ else { ++ val->intval = POWER_SUPPLY_STATUS_DISCHARGING; ++ } ++} ++ ++static void em_x270_bat_check_health(struct em_x270_charger *charger, ++ union power_supply_propval *val) ++{ ++ if (charger->da9030_fault & FAULT_LOG_OVER_TEMP) { ++ val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; ++ } ++ else if (charger->da9030_fault & FAULT_LOG_VBAT_OVER) { ++ val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; ++ } ++ else { ++ val->intval = POWER_SUPPLY_HEALTH_GOOD; ++ } ++} ++ ++static int vbat_interpolate(int reg) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(vbat_ranges); i++ ) ++ if (vbat_ranges[i].voltage == reg) { ++ pr_debug("%s: voltage = %d, percentage = %d\n", ++ __FUNCTION__, vbat_ranges[i].voltage, ++ vbat_ranges[i].percentage); ++ return vbat_ranges[i].percentage; ++ } ++ ++ return 0; ++} ++ ++static int em_x270_bat_get_property(struct power_supply *psy, ++ enum power_supply_property psp, ++ union power_supply_propval *val) ++{ ++ u32 reg; ++ struct em_x270_charger *charger; ++ charger = container_of(psy, struct em_x270_charger, bat); ++ ++ switch(psp) { ++ case POWER_SUPPLY_PROP_STATUS: ++ em_x270_bat_check_status(charger, val); ++ break; ++ case POWER_SUPPLY_PROP_HEALTH: ++ em_x270_bat_check_health(charger, val); ++ break; ++ case POWER_SUPPLY_PROP_TECHNOLOGY: ++ val->intval = POWER_SUPPLY_TECHNOLOGY_LIPO; ++ break; ++ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: ++ val->intval = VOLTAGE_MAX_DESIGN; ++ break; ++ case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: ++ val->intval = VOLTAGE_MIN_DESIGN; ++ break; ++ case POWER_SUPPLY_PROP_VOLTAGE_NOW: ++ reg = charger->adc.vbat_res; ++ /* V = (reg / 256) * 2.65 + 2.65 (V) */ ++ val->intval = ((reg * 2650000) >> 8) + 2650000; ++ break; ++ case POWER_SUPPLY_PROP_CURRENT_AVG: ++ reg = charger->adc.ichaverage_res; ++ val->intval = reg; /* reg */ ++ break; ++ case POWER_SUPPLY_PROP_CAPACITY: ++ reg = charger->adc.vbat_res; ++ val->intval = vbat_interpolate(reg); ++ break; ++ case POWER_SUPPLY_PROP_TEMP: ++ reg = charger->adc.tbat_res; ++ val->intval = tbat_readings[reg]; ++ break; ++ case POWER_SUPPLY_PROP_MANUFACTURER: ++ val->strval = "MaxPower"; ++ pr_debug("%s: MFG = %s\n", __FUNCTION__, val->strval); ++ break; ++ case POWER_SUPPLY_PROP_MODEL_NAME: ++ val->strval = "LP555597P6H-FPS"; ++ pr_debug("%s: MODEL = %s\n", __FUNCTION__, val->strval); ++ break; ++ default: break; ++ } ++ ++ return 0; ++} ++ ++static void em_x270_setup_battery(struct power_supply *bat) ++{ ++ bat->name = "em-x270-battery"; ++ bat->type = POWER_SUPPLY_TYPE_BATTERY; ++ bat->properties = em_x270_bat_props; ++ bat->num_properties = ARRAY_SIZE(em_x270_bat_props); ++ bat->get_property = em_x270_bat_get_property; ++ bat->use_for_apm = 1; ++}; ++ ++static void em_x270_chiover_callback(int event, void *_charger) ++{ ++ /* disable charger */ ++ struct em_x270_charger *charger = _charger; ++/* pr_info("%s\n", __FUNCTION__); */ ++ em_x270_set_charge(charger, 0); ++} ++ ++static void em_x270_tbat_callback(int event, void *_charger) ++{ ++ /* disable charger */ ++ struct em_x270_charger *charger = _charger; ++/* pr_info("%s\n", __FUNCTION__); */ ++ em_x270_set_charge(charger, 0); ++} ++ ++static void em_x270_vbat_callback(int event, void *_charger) ++{ ++ struct em_x270_charger *charger = _charger; ++ da9030_read_adc(&charger->adc); ++ ++ if (!charger->is_on) { ++ if (charger->adc.vbat_res < VBAT_LOW_THRESHOLD) { ++ /* set VBAT threshold for critical */ ++ da9030_set_reg(VBATMON, VBAT_CRIT_THRESHOLD); ++ da9030_set_reg(VBATMON_1, VBAT_CRIT_THRESHOLD); ++ apm_queue_event(APM_LOW_BATTERY); ++ } ++ else if (charger->adc.vbat_res < VBAT_CRIT_THRESHOLD) { ++ /* notify the system of battery critical */ ++ apm_queue_event(APM_CRITICAL_SUSPEND); ++ } ++ } ++} ++ ++static void em_x270_chdet_callback(int event, void *_charger) ++{ ++ struct em_x270_charger *charger = _charger; ++ int status = da9030_get_status(); ++ pr_info("%s\n", __FUNCTION__); ++ ++/* em_x270_check_charger_state(charger); */ ++ ++ /* check if we have "real" charger or our own USB pump */ ++ if (!usb_host_on()) ++ em_x270_set_charge(charger, !!(status & CHRG_CHARGER_ENABLE)); ++} ++ ++static int em_x270_battery_probe(struct platform_device *pdev) ++{ ++ struct em_x270_charger *charger; ++ ++ pr_debug("%s\n", __FUNCTION__); ++ charger = kzalloc(sizeof(*charger), GFP_KERNEL); ++ if (charger == NULL) { ++ return -ENOMEM; ++ } ++ ++ the_charger = charger; ++ ++ charger->dev = &pdev->dev; ++ ++ charger->ops = &em_x270_charger_ops; ++ ++ charger->interval = 10 * HZ; /* 10 seconds between monotor runs */ ++ em_x270_setup_battery(&charger->bat); ++ ++ platform_set_drvdata(pdev, charger); ++ ++ da9030_enable_adc(); ++ ++ INIT_DELAYED_WORK(&charger->work, em_x270_charging_monitor); ++ schedule_delayed_work(&charger->work, charger->interval); ++ ++ charger->debug_file = em_x270_create_debugfs(charger); ++ ++ em_x270_setup_battery(&charger->bat); ++ ++ da9030_register_callback(DA9030_IRQ_CHDET, ++ em_x270_chdet_callback, ++ charger); ++ da9030_register_callback(DA9030_IRQ_VBATMON, ++ em_x270_vbat_callback, ++ charger); ++ ++ /* critical condition events */ ++ da9030_register_callback(DA9030_IRQ_CHIOVER, ++ em_x270_chiover_callback, ++ charger); ++ da9030_register_callback(DA9030_IRQ_TBAT, ++ em_x270_tbat_callback, ++ charger); ++ ++ da9030_set_thresholds(TBAT_HIGH_THRESHOLD, ++ TBAT_RESUME_THRESHOLD, ++ TBAT_LOW_THRESHOLD, ++ VBAT_LOW_THRESHOLD); ++ ++ power_supply_register(&pdev->dev, &charger->bat); ++ ++ return 0; ++} ++ ++static int em_x270_battery_remove(struct platform_device *dev) ++{ ++ struct em_x270_charger *charger = platform_get_drvdata(dev); ++ ++ pr_debug("%s\n", __FUNCTION__); ++ em_x270_remove_debugfs(charger); ++ cancel_delayed_work(&charger->work); ++ power_supply_unregister(&charger->bat); ++ ++ kfree(charger); ++ the_charger = NULL; ++ ++ return 0; ++} ++ ++static int em_x270_battery_suspend(struct platform_device *pdev, ++ pm_message_t state) ++{ ++ pr_info("%s\n", __FUNCTION__); ++ da9030_set_reg(REG_CONTROL_1_97, RC3_BUCK_EN | RC3_LDO6_EN); ++ da9030_set_reg(REG_CONTROL_2_98, 0); ++ da9030_set_reg(REG_CONTROL_2_18,RC2_LDO18_EN); ++ da9030_set_reg(REG_CONTROL_1_17, ++ RC1_LDO16_EN | RC1_LDO15_EN | RC1_BUCK2_EN); ++ da9030_set_reg(WLED_CONTROL, 0); ++ ++ da9030_set_reg(LED_1_CONTROL, 0); ++ da9030_set_reg(LED_4_CONTROL, 0); ++ ++ return 0; ++} ++ ++extern int em_x270_lcd_detect(void); ++static int em_x270_battery_resume(struct platform_device *pdev) ++{ ++ pr_info("%s\n", __FUNCTION__); ++ ++ da9030_set_reg(LED_1_CONTROL, 0xff); ++ da9030_set_reg(LED_4_CONTROL, 0xff); ++ ++ da9030_set_reg(REG_CONTROL_1_97, ++ RC3_BUCK_EN | RC3_LDO1_EN | RC3_LDO2_EN | ++ RC3_LDO3_EN | RC3_LDO6_EN | RC3_LDO7_EN); ++ da9030_set_reg(REG_CONTROL_2_98, ++ RC4_SIMCP_ENABLE | RC4_LDO11_EN | ++ RC4_LDO9_EN | RC4_LDO8_EN); ++ da9030_set_reg(REG_CONTROL_2_18, ++ RC2_SIMCP_EN | RC2_LDO18_EN | RC2_LDO19_EN); ++ da9030_set_reg(REG_CONTROL_1_17, ++ RC1_LDO17_EN| RC1_LDO16_EN | RC1_LDO15_EN | ++ RC1_LDO11_EN | RC1_LDO10_EN | RC1_BUCK2_EN); ++ ++ if (em_x270_lcd_detect() != 0) ++ pr_info("%s: LCD resume failed\n", __FUNCTION__); ++ ++ return 0; ++} ++ ++static struct platform_driver em_x270_battery_driver = { ++ .driver = { ++ .name = "em-x270-battery", ++ .owner = THIS_MODULE, ++ }, ++ .probe = em_x270_battery_probe, ++ .remove = em_x270_battery_remove, ++ ++ .suspend_late = em_x270_battery_suspend, ++ .resume_early = em_x270_battery_resume, ++}; ++ ++/**************************************************************************/ ++/* global patform power management */ ++/**************************************************************************/ ++/* suspend/resume button */ ++static irqreturn_t em_x270_suspend_irq(int irq, void *data) ++{ ++ apm_queue_event(APM_USER_SUSPEND); ++ return IRQ_HANDLED; ++} ++ ++static void em_x270_goto_sleep(suspend_state_t state, ++ unsigned long alarm_time, ++ unsigned int alarm_enable) ++{ ++ pr_info("%s\n", __FUNCTION__); ++ ++ the_charger->ops = &em_x270_charger_ops_suspend; ++ ++ RTSR &= RTSR_ALE; ++ RTAR = RCNR + EM_X270_BATCHK_TIME_SUSPEND; ++ ++ pxa_pm_enter(state); ++} ++ ++static int em_x270_enter_suspend(unsigned long alarm_time, ++ unsigned int alarm_enable) ++{ ++ s32 status = 0; ++ s32 event_a, event_b; ++ int ret; ++ int retry = 10; ++ pr_info("%s\n", __FUNCTION__); ++ ++ /* make sure power I2C state is consistent */ ++ PWRICR &= ~ICR_IUE; ++ ++ if ((PEDR & PWER_GPIO1)) { ++ /* button pressed */ ++ /* clear pending event to avoid interrupt*/ ++ PEDR &= ~PWER_GPIO1; ++ GEDR0 &= ~GPIO_bit(1); ++ ++ ret = 0; ++ } ++ else if ((PEDR & PWER_RTC)) { ++ PEDR &= ~PWER_RTC; ++ pr_debug("%s: timer alarm\n", __FUNCTION__); ++ em_x270_check_charger_state(the_charger); ++ ret = 1; ++ } ++ else if ((PEDR & PWER_GPIO0)) { ++ PEDR &= ~PWER_GPIO0; ++ GEDR0 &= ~GPIO_bit(0); ++ ++ /* we woke up because of DA9030 event */ ++ do { ++ status = the_charger->ops->da9030_get_reg(STATUS); ++ udelay(1000); ++ } while (status < 0 && retry--); ++ ++ the_charger->da9030_status = status; ++ event_a = the_charger->ops->da9030_get_reg(EVENT_A); ++ event_b = the_charger->ops->da9030_get_reg(EVENT_B); ++ ++ pr_info("%s: DA9030 wakeup: status: %x, ev_a: %x, ev_b: %x\n", ++ __FUNCTION__, the_charger->da9030_status, ++ event_a, event_b); ++ ++/* if ((the_charger->da9030_status & STATUS_CHDET)) { */ ++ if (event_a & EVENT_A_CHDET) { ++ pr_info("%s: EVENT_A_CHDET\n", __FUNCTION__); ++ em_x270_check_charger_state(the_charger); ++ } ++ else { ++ pr_info("%s: What the hell?\n", __FUNCTION__); ++ } ++ ++ ret = 1; ++ } ++ else { ++ /* wake up is not DA9030, so continue and let battery ++ driver check the charger state */ ++ ret = 0; ++ } ++ ++ /* get back to sleep */ ++ if (ret) ++ em_x270_goto_sleep(PM_SUSPEND_MEM, alarm_time, alarm_enable); ++ ++ return ret; ++} ++ ++static int em_x270_pm_enter(suspend_state_t state) ++{ ++ unsigned long alarm_time = RTAR; ++ unsigned int alarm_status = ((RTSR & RTSR_ALE) != 0); ++ ++ /* pre-suspend */ ++ pr_info("suspending emma\n"); ++ pxa_gpio_mode(38 | GPIO_OUT | GPIO_DFLT_HIGH); ++ ++ em_x270_goto_sleep(state, alarm_time, alarm_status); ++ ++ /* check if we were resumed because of charger events */ ++ while (em_x270_enter_suspend(alarm_time, alarm_status)) ++ {} ++ ++ /* make sure power I2C state is consistent */ ++ PWRICR &= ~ICR_IUE; ++ ++ /* resume */ ++ pr_info("emma is resumed\n"); ++ the_charger->ops = &em_x270_charger_ops; ++ ++ return 0; ++} ++ ++static struct pm_ops em_x270_pm_ops = { ++ .enter = em_x270_pm_enter, ++ .valid = pm_valid_only_mem, ++}; ++ ++static int em_x270_pm_init(void) ++{ ++ int ret; ++ pm_set_ops(&em_x270_pm_ops); ++ ++ set_irq_type(IRQ_GPIO(1), IRQT_RISING); ++ ++ ret = platform_driver_register(&em_x270_battery_driver); ++ if (ret) ++ return ret; ++ ++ ret = request_irq(IRQ_GPIO(1), em_x270_suspend_irq, IRQF_DISABLED, ++ "suspend button", 0); ++ if (ret) { ++ platform_driver_unregister(&em_x270_battery_driver); ++ } ++ ++ return ret; ++} ++ ++static void em_x270_pm_exit(void) ++{ ++ free_irq(IRQ_GPIO(1), em_x270_suspend_irq); ++ platform_driver_unregister(&em_x270_battery_driver); ++} ++ ++/* make sure I2C is already running */ ++late_initcall(em_x270_pm_init); ++module_exit(em_x270_pm_exit); ++ ++MODULE_DESCRIPTION("EM-X270 power manager"); ++MODULE_AUTHOR("Mike Rapoport"); ++MODULE_LICENSE("GPL"); +diff --git a/arch/arm/mach-pxa/em-x270.c b/arch/arm/mach-pxa/em-x270.c +index 3d0ad50..402d792 100644 +--- a/arch/arm/mach-pxa/em-x270.c ++++ b/arch/arm/mach-pxa/em-x270.c +@@ -18,20 +18,26 @@ + #include <linux/mtd/nand.h> + #include <linux/mtd/partitions.h> + +-#include <asm/mach-types.h> ++#include <linux/i2c.h> ++#include <linux/input.h> + ++#include <asm/mach-types.h> + #include <asm/mach/arch.h> + + #include <asm/arch/pxa-regs.h> + #include <asm/arch/pxafb.h> + #include <asm/arch/ohci.h> + #include <asm/arch/mmc.h> ++#include <asm/arch/pxa27x_keyboard.h> + #include <asm/arch/bitfield.h> + ++#include <asm/arch/udc.h> ++ ++#include <asm/arch/sharpsl.h> ++ + #include "generic.h" + + /* GPIO IRQ usage */ +-#define EM_X270_MMC_PD (105) + #define EM_X270_ETHIRQ IRQ_GPIO(41) + #define EM_X270_MMC_IRQ IRQ_GPIO(13) + +@@ -213,6 +219,68 @@ static struct platform_device em_x270_nand = { + } + }; + ++/* DA9030 */ ++static struct i2c_board_info em_x270_pmic_info = { ++ .driver_name = "da9030", ++ .type = "pmic", ++ .addr = 0x49, ++ .irq = IRQ_GPIO(0), ++}; ++ ++/* Keypad */ ++/* The Demo KeyPad has the following mapping: ++ * (0,0) (1,2) (2,1) ++ * (0,2) (1,1) (2,0) ++ * (0,1) (1,0) (2,2) ++ */ ++static struct pxa27x_keyboard_platform_data em_x270_kbd = { ++ .nr_rows = 3, ++ .nr_cols = 3, ++ .keycodes = { ++ { /* row 0 */ ++ -1, ++ -1, ++ KEY_LEFT, ++ }, ++ { /* row 1 */ ++ KEY_UP, ++ KEY_ENTER, ++ KEY_DOWN ++ }, ++ { /* row 2 */ ++ KEY_RIGHT, ++ -1, ++ -1 ++ }, ++ }, ++ .gpio_modes = { ++ (100 | GPIO_ALT_FN_1_IN), ++ (101 | GPIO_ALT_FN_1_IN), ++ (102 | GPIO_ALT_FN_1_IN), ++ (103 | GPIO_ALT_FN_2_OUT), ++ (104 | GPIO_ALT_FN_2_OUT), ++ (105 | GPIO_ALT_FN_2_OUT), ++ }, ++}; ++ ++static struct platform_device em_x270_pxa_keypad = { ++ .name = "pxa27x-keyboard", ++ .id = -1, ++ .dev = { ++ .platform_data = &em_x270_kbd, ++ }, ++}; ++ ++static struct platform_device em_x270_battery_device = { ++ .name = "em-x270-battery", ++ .id = -1, ++}; ++ ++static struct platform_device em_x270_led_device = { ++ .name = "em-x270-led", ++ .id = -1, ++}; ++ + /* platform devices */ + static struct platform_device *platform_devices[] __initdata = { + &em_x270_dm9k, +@@ -220,6 +288,9 @@ static struct platform_device *platform_devices[] __initdata = { + &em_x270_ts, + &em_x270_rtc, + &em_x270_nand, ++ &em_x270_pxa_keypad, ++ &em_x270_battery_device, ++ &em_x270_led_device, + }; + + +@@ -241,6 +312,33 @@ static struct pxaohci_platform_data em_x270_ohci_platform_data = { + .init = em_x270_ohci_init, + }; + ++/* ++ * USB Client (Gadget/UDC) ++ */ ++static void em_x270_udc_command(int cmd) ++{ ++ switch(cmd) { ++ case PXA2XX_UDC_CMD_CONNECT: ++ UP2OCR = UP2OCR_HXOE | UP2OCR_DMPUE | UP2OCR_DMPUBE; ++ break; ++ case PXA2XX_UDC_CMD_DISCONNECT: ++/* UP2OCR = UP2OCR_HXS | UP2OCR_HXOE; */ ++ //UP2OCR = UP2OCR_HXOE | UP2OCR_DMPUE | UP2OCR_DMPUBE; ++ break; ++ } ++} ++ ++static int em_x270_udc_detect(void) ++{ ++ return 1; ++} ++ ++static struct pxa2xx_udc_mach_info em_x270_udc_info __initdata = { ++ .udc_is_connected = em_x270_udc_detect, ++ .udc_command = em_x270_udc_command, ++}; ++ ++ + + static int em_x270_mci_init(struct device *dev, + irq_handler_t em_x270_detect_int, +@@ -256,9 +354,6 @@ static int em_x270_mci_init(struct device *dev, + pxa_gpio_mode(GPIO110_MMCDAT2_MD); + pxa_gpio_mode(GPIO111_MMCDAT3_MD); + +- /* EM-X270 uses GPIO13 as SD power enable */ +- pxa_gpio_mode(EM_X270_MMC_PD | GPIO_OUT); +- + err = request_irq(EM_X270_MMC_IRQ, em_x270_detect_int, + IRQF_DISABLED | IRQF_TRIGGER_FALLING, + "MMC card detect", data); +@@ -313,12 +408,19 @@ static struct pxafb_mach_info em_x270_lcd = { + .num_modes = 1, + .cmap_inverse = 0, + .cmap_static = 0, +- .lccr0 = LCCR0_PAS, +- .lccr3 = LCCR3_PixClkDiv(0x01) | LCCR3_Acb(0xff), ++ ++ .lccr0 = LCCR0_Act, ++ .lccr3 = LCCR3_PixFlEdg, + }; + + static void __init em_x270_init(void) + { ++ /* register PMIC */ ++ i2c_register_board_info(1, &em_x270_pmic_info, 1); ++ ++ /* setup DA9030 irq */ ++ set_irq_type(IRQ_GPIO(0), IRQT_FALLING); ++ + /* setup LCD */ + set_pxa_fb_info(&em_x270_lcd); + +@@ -329,6 +431,8 @@ static void __init em_x270_init(void) + pxa_set_mci_info(&em_x270_mci_platform_data); + pxa_set_ohci_info(&em_x270_ohci_platform_data); + ++ pxa_set_udc_info(&em_x270_udc_info); ++ + /* setup STUART GPIOs */ + pxa_gpio_mode(GPIO46_STRXD_MD); + pxa_gpio_mode(GPIO47_STTXD_MD); +@@ -341,9 +445,16 @@ static void __init em_x270_init(void) + + /* Setup interrupt for dm9000 */ + set_irq_type(EM_X270_ETHIRQ, IRQT_RISING); ++ ++ PCFR = 0x6; ++ PSLR = 0xff400000; ++ PMCR = 0x00000005; ++ PWER = 0x80000003; ++ PFER = 0x00000003; ++ PRER = 0x00000000; + } + +-MACHINE_START(EM_X270, "Compulab EM-x270") ++MACHINE_START(EM_X270, "Compulab EM-X270") + .boot_params = 0xa0000100, + .phys_io = 0x40000000, + .io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc, +diff --git a/arch/arm/mach-pxa/pwr-i2c.c b/arch/arm/mach-pxa/pwr-i2c.c +new file mode 100644 +index 0000000..8a501c4 +--- /dev/null ++++ b/arch/arm/mach-pxa/pwr-i2c.c +@@ -0,0 +1,539 @@ ++/* ++ * (C) Copyrihgt 2007 CompuLab, Ltd. ++ * Mike Rapoport <mike@compulab.co.il> ++ * Adaptation of U-Boot I2C driver for PXA. ++ * ++ * (C) Copyright 2000 ++ * Paolo Scaffardi, AIRVENT SAM s.p.a - RIMINI(ITALY), arsenio@tin.it ++ * ++ * (C) Copyright 2000 Sysgo Real-Time Solutions, GmbH <www.elinos.com> ++ * Marius Groeger <mgroeger@sysgo.de> ++ * ++ * (C) Copyright 2003 Pengutronix e.K. ++ * Robert Schwebel <r.schwebel@pengutronix.de> ++ * ++ * See file CREDITS for list of people who contributed to this ++ * project. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, ++ * MA 02111-1307 USA ++ * ++ * Back ported to the 8xx platform (from the 8260 platform) by ++ * Murray.Jensen@cmst.csiro.au, 27-Jan-01. ++ */ ++ ++/* #define DEBUG */ ++ ++#include <linux/kernel.h> ++#include <linux/delay.h> ++#include <linux/module.h> ++#include <linux/jiffies.h> ++ ++#include <asm/arch/hardware.h> ++#include <asm/arch/pxa-regs.h> ++#include <asm/arch/pwr-i2c.h> ++ ++#define I2C_ICR_INIT (ICR_BEIE | ICR_IRFIE | ICR_ITEIE | ICR_GCD | ICR_SCLE) ++#define I2C_ISR_INIT 0x7FF ++ ++/* Shall the current transfer have a start/stop condition? */ ++#define I2C_COND_NORMAL 0 ++#define I2C_COND_START 1 ++#define I2C_COND_STOP 2 ++ ++/* Shall the current transfer be ack/nacked or being waited for it? */ ++#define I2C_ACKNAK_WAITACK 1 ++#define I2C_ACKNAK_SENDACK 2 ++#define I2C_ACKNAK_SENDNAK 4 ++ ++/* Specify who shall transfer the data (master or slave) */ ++#define I2C_READ 0 ++#define I2C_WRITE 1 ++ ++/* All transfers are described by this data structure */ ++struct i2c_msg { ++ u8 condition; ++ u8 acknack; ++ u8 direction; ++ u8 data; ++}; ++ ++/** ++ * pxa_pwr_i2c_transfer: - reset the host controller ++ * ++ */ ++/* static void pxa_pwr_i2c_reset(void) */ ++/* { */ ++/* int i; */ ++ ++/* /\* CKEN |= CKEN_PWRI2C; *\/ */ ++/* /\* PWRICR |= PCFR_PI2C_EN; *\/ */ ++ ++/* /\* /\\* delay 250ms *\\/ *\/ */ ++/* /\* for (i = 0; i < 250; i++) *\/ */ ++/* /\* udelay(1000); *\/ */ ++ ++/* /\* PWRICR &= ~(ICR_MA | ICR_START | ICR_STOP); *\/ */ ++/* /\* PWRICR |= ICR_UR; *\/ */ ++/* /\* PWRISR = 0x7ff; *\/ */ ++ ++/* /\* PWRICR &= ~ICR_UR; *\/ */ ++/* /\* PWRICR = ICR_GCD | ICR_SCLE; *\/ */ ++/* /\* PWRICR |= ICR_IUE; *\/ */ ++/* /\* PWRICR |= 0x8000; *\/ */ ++ ++/* /\* udelay(1000); *\/ */ ++ ++/* return; */ ++ ++/* PWRICR &= ~ICR_IUE; /\* disable unit *\/ */ ++/* PWRICR |= ICR_UR; /\* reset the unit *\/ */ ++/* udelay(100); */ ++/* PWRICR &= ~ICR_IUE; /\* disable unit *\/ */ ++ ++/* CKEN |= CKEN_PWRI2C; */ ++/* PCFR |= PCFR_PI2C_EN; */ ++ ++/* PWRICR = I2C_ICR_INIT; /\* set control register values *\/ */ ++/* PWRISR = I2C_ISR_INIT; /\* set clear interrupt bits *\/ */ ++/* PWRICR |= ICR_IUE; /\* enable unit *\/ */ ++/* udelay(100); */ ++/* } */ ++ ++static void pwr_i2c_abort() ++{ ++ unsigned long timeout = 250000; ++ unsigned long time = 0; ++ ++ while ((time < timeout) && (PWRIBMR & 0x1) == 0) { ++ unsigned long icr = PWRICR; ++ ++ icr &= ~ICR_START; ++ icr |= ICR_ACKNAK | ICR_STOP | ICR_TB; ++ ++ PWRICR = icr; ++ ++ udelay(1000); ++ time += 1000; ++ } ++ ++ PWRICR &= ~(ICR_MA | ICR_START | ICR_STOP); ++} ++ ++static void pxa_pwr_i2c_reset(void) ++{ ++ pr_debug("Resetting I2C Controller Unit\n"); ++ ++ /* abort any transfer currently under way */ ++ pwr_i2c_abort(); ++ ++ /* reset according to 9.8 */ ++ PWRICR = ICR_UR; ++ PWRISR = I2C_ISR_INIT; ++ PWRISR &= ~ICR_UR; ++ ++ /* set control register values */ ++ PWRICR = I2C_ICR_INIT; ++ ++ /* enable unit */ ++ PWRICR |= ICR_IUE; ++ udelay(100); ++ ++/* CKEN |= CKEN_PWRI2C; */ ++/* PCFR |= PCFR_PI2C_EN; */ ++} ++ ++/** ++ * i2c_isr_set_cleared: - wait until certain bits of the I2C status register ++ * are set and cleared ++ * ++ * @return: 1 in case of success, 0 means timeout (no match within 10 ms). ++ */ ++static int pxa_pwr_i2c_isr_set_cleared(unsigned long set_mask, ++ unsigned long cleared_mask) ++{ ++ int timeout = 10000; ++ ++ while (((PWRISR & set_mask) != set_mask) ++ || ((PWRISR & cleared_mask) != 0)) { ++ udelay(10); ++ if (timeout-- < 0) ++ return 0; ++ } ++ ++ return 1; ++} ++ ++/** ++ * pxa_pwr_i2c_transfer: - Transfer one byte over the i2c bus ++ * ++ * This function can tranfer a byte over the i2c bus in both directions. ++ * It is used by the public API functions. ++ * ++ * @return: 0: transfer successful ++ * -EINVAL: message is empty ++ * -ETIMEDOUT: transmit timeout ++ * -E: ACK missing ++ * -ETIMEDOUT: receive timeout ++ * -EINVAL: illegal parameters ++ * -EBUSY: bus is busy and couldn't be aquired ++ */ ++int pxa_pwr_i2c_transfer(struct i2c_msg *msg) ++{ ++ int ret; ++ ++ if (!msg) ++ goto transfer_error_msg_empty; ++ ++ switch (msg->direction) { ++ case I2C_WRITE: ++ /* check if bus is not busy */ ++/* if (!pxa_pwr_i2c_isr_set_cleared(0, (ISR_IBB | ISR_UB))) */ ++/* goto transfer_error_bus_busy; */ ++ ++ /* start transmission */ ++ PWRICR &= ~ICR_START; ++ PWRICR &= ~ICR_STOP; ++ PWRIDBR = msg->data; ++ if (msg->condition == I2C_COND_START) ++ PWRICR |= ICR_START; ++ if (msg->condition == I2C_COND_STOP) ++ PWRICR |= ICR_STOP; ++ if (msg->acknack == I2C_ACKNAK_SENDNAK) ++ PWRICR |= ICR_ACKNAK; ++ if (msg->acknack == I2C_ACKNAK_SENDACK) ++ PWRICR &= ~ICR_ACKNAK; ++ PWRICR &= ~ICR_ALDIE; ++ PWRICR |= ICR_TB; ++ ++ /* transmit register empty? */ ++ if (!pxa_pwr_i2c_isr_set_cleared(ISR_ITE, 0)) ++ goto transfer_error_transmit_timeout; ++ ++ /* clear 'transmit empty' state */ ++ PWRISR |= ISR_ITE; ++ ++ /* wait for ACK from slave */ ++ if (msg->acknack == I2C_ACKNAK_WAITACK) ++ if (!pxa_pwr_i2c_isr_set_cleared(0, ISR_ACKNAK)) ++ goto transfer_error_ack_missing; ++ break; ++ case I2C_READ: ++ /* check if bus is not busy */ ++/* if (!pxa_pwr_i2c_isr_set_cleared(0, ISR_IBB)) */ ++/* goto transfer_error_bus_busy; */ ++ ++ /* start receive */ ++ PWRICR &= ~ICR_START; ++ PWRICR &= ~ICR_STOP; ++ if (msg->condition == I2C_COND_START) ++ PWRICR |= ICR_START; ++ if (msg->condition == I2C_COND_STOP) ++ PWRICR |= ICR_STOP; ++ if (msg->acknack == I2C_ACKNAK_SENDNAK) ++ PWRICR |= ICR_ACKNAK; ++ if (msg->acknack == I2C_ACKNAK_SENDACK) ++ PWRICR &= ~ICR_ACKNAK; ++ PWRICR &= ~ICR_ALDIE; ++ PWRICR |= ICR_TB; ++ ++ /* receive register full? */ ++ if (!pxa_pwr_i2c_isr_set_cleared(ISR_IRF, 0)) ++ goto transfer_error_receive_timeout; ++ ++ msg->data = PWRIDBR; ++ ++ /* clear 'receive empty' state */ ++ PWRISR |= ISR_IRF; ++ ++ break; ++ default: ++ goto transfer_error_illegal_param; ++ ++ } ++ ++ return 0; ++ ++transfer_error_msg_empty: ++ pr_debug("%s: error: 'msg' is empty\n", __FUNCTION__); ++ ret = -1; ++ goto i2c_transfer_finish; ++ ++transfer_error_transmit_timeout: ++ pr_debug("%s: error: transmit timeout\n", __FUNCTION__); ++ ret = -2; ++ goto i2c_transfer_finish; ++ ++transfer_error_ack_missing: ++ pr_debug("%s: error: ACK missing\n", __FUNCTION__); ++ ret = -3; ++ goto i2c_transfer_finish; ++ ++transfer_error_receive_timeout: ++ pr_debug("%s: error: receive timeout\n", __FUNCTION__); ++ ret = -4; ++ goto i2c_transfer_finish; ++ ++transfer_error_illegal_param: ++ pr_debug("%s: error: illegal parameters\n", __FUNCTION__); ++ ret = -5; ++ goto i2c_transfer_finish; ++ ++transfer_error_bus_busy: ++ pr_debug("%s: error: bus is busy\n", __FUNCTION__); ++ ret = -6; ++ goto i2c_transfer_finish; ++ ++i2c_transfer_finish: ++ pr_debug("%s: ISR: 0x%04x\n", __FUNCTION__, ISR); ++ return ret; ++} ++ ++/* ------------------------------------------------------------------------ */ ++/* API Functions */ ++/* ------------------------------------------------------------------------ */ ++/** ++ * i2c_probe: - Test if a chip answers for a given i2c address ++ * ++ * @chip: address of the chip which is searched for ++ * @return: 0 if a chip was found, -1 otherwhise ++ */ ++int pxa_pwr_i2c_probe(u8 chip) ++{ ++ struct i2c_msg msg; ++ int ret; ++ ++ pxa_pwr_i2c_reset(); ++ ++ msg.condition = I2C_COND_START; ++ msg.acknack = I2C_ACKNAK_WAITACK; ++ msg.direction = I2C_WRITE; ++ msg.data = (chip << 1) + 1; ++ if ((ret = pxa_pwr_i2c_transfer(&msg))) ++ return ret; ++ ++ msg.condition = I2C_COND_STOP; ++ msg.acknack = I2C_ACKNAK_SENDNAK; ++ msg.direction = I2C_READ; ++ msg.data = 0x00; ++ if ((ret = pxa_pwr_i2c_transfer(&msg))) ++ return ret; ++ ++ return 0; ++} ++ ++/** ++ * i2c_read: - Read multiple bytes from an i2c device ++ * ++ * The higher level routines take into account that this function is only ++ * called with len < page length of the device (see configuration file) ++ * ++ * @chip: address of the chip which is to be read ++ * @addr: i2c data address within the chip ++ * @alen: length of the i2c data address (1..2 bytes) ++ * @buffer: where to write the data ++ * @len: how much byte do we want to read ++ * @return: 0 in case of success ++ */ ++int pxa_pwr_i2c_read(u8 chip, uint addr, int alen, u8 * buffer, int len) ++{ ++ struct i2c_msg msg; ++ u8 addr_bytes[3]; /* lowest...highest byte of data address */ ++ int ret; ++ ++ pr_debug("%s(chip=0x%02x, addr=0x%02x, alen=0x%02x, len=0x%02x)\n", ++ __FUNCTION__, chip, addr, alen, len); ++ ++ pxa_pwr_i2c_reset(); ++ ++ /* dummy chip address write */ ++ pr_debug("%s: dummy chip address write\n", __FUNCTION__); ++ msg.condition = I2C_COND_START; ++ msg.acknack = I2C_ACKNAK_WAITACK; ++ msg.direction = I2C_WRITE; ++ msg.data = (chip << 1); ++ msg.data &= 0xFE; ++ if ((ret = pxa_pwr_i2c_transfer(&msg))) ++ return ret; ++ ++ /* ++ * send memory address bytes; ++ * alen defines how much bytes we have to send. ++ */ ++ /*addr &= ((1 << CFG_EEPROM_PAGE_WRITE_BITS)-1); */ ++ addr_bytes[0] = (u8) ((addr >> 0) & 0x000000FF); ++ addr_bytes[1] = (u8) ((addr >> 8) & 0x000000FF); ++ addr_bytes[2] = (u8) ((addr >> 16) & 0x000000FF); ++ ++ while (--alen >= 0) { ++ pr_debug("%s: send memory word address byte %1d\n", ++ __FUNCTION__, alen); ++ msg.condition = I2C_COND_NORMAL; ++ msg.acknack = I2C_ACKNAK_WAITACK; ++ msg.direction = I2C_WRITE; ++ msg.data = addr_bytes[alen]; ++ if ((ret = pxa_pwr_i2c_transfer(&msg))) ++ return ret; ++ } ++ ++ /* start read sequence */ ++ pr_debug("%s: start read sequence\n", __FUNCTION__); ++ msg.condition = I2C_COND_START; ++ msg.acknack = I2C_ACKNAK_WAITACK; ++ msg.direction = I2C_WRITE; ++ msg.data = (chip << 1); ++ msg.data |= 0x01; ++ if ((ret = pxa_pwr_i2c_transfer(&msg))) ++ return ret; ++ ++ /* read bytes; send NACK at last byte */ ++ while (len--) { ++ if (len == 0) { ++ msg.condition = I2C_COND_STOP; ++ msg.acknack = I2C_ACKNAK_SENDNAK; ++ } else { ++ msg.condition = I2C_COND_NORMAL; ++ msg.acknack = I2C_ACKNAK_SENDACK; ++ } ++ ++ msg.direction = I2C_READ; ++ msg.data = 0x00; ++ if ((ret = pxa_pwr_i2c_transfer(&msg))) ++ return ret; ++ ++ *buffer = msg.data; ++ pr_debug("%s: reading byte (0x%08x)=0x%02x\n", ++ __FUNCTION__, (unsigned int)buffer, *buffer); ++ buffer++; ++ } ++ ++ pxa_pwr_i2c_reset(); ++ return 0; ++} ++ ++/** ++ * i2c_write: - Write multiple bytes to an i2c device ++ * ++ * The higher level routines take into account that this function is only ++ * called with len < page length of the device (see configuration file) ++ * ++ * @chip: address of the chip which is to be written ++ * @addr: i2c data address within the chip ++ * @alen: length of the i2c data address (1..2 bytes) ++ * @buffer: where to find the data to be written ++ * @len: how much byte do we want to read ++ * @return: 0 in case of success ++ */ ++int pxa_pwr_i2c_write(u8 chip, uint addr, int alen, u8 * buffer, int len) ++{ ++ struct i2c_msg msg; ++ u8 addr_bytes[3]; /* lowest...highest byte of data address */ ++ int ret; ++ ++ pr_debug("%s(chip=0x%02x, addr=0x%02x, alen=0x%02x, len=0x%02x)\n", ++ __FUNCTION__, chip, addr, alen, len); ++ ++ pxa_pwr_i2c_reset(); ++ ++ /* chip address write */ ++ pr_debug("%s: chip address write\n", __FUNCTION__); ++ msg.condition = I2C_COND_START; ++ msg.acknack = I2C_ACKNAK_WAITACK; ++ msg.direction = I2C_WRITE; ++ msg.data = (chip << 1); ++ msg.data &= 0xFE; ++ if ((ret = pxa_pwr_i2c_transfer(&msg))) ++ return ret; ++ ++ /* ++ * send memory address bytes; ++ * alen defines how much bytes we have to send. ++ */ ++ addr_bytes[0] = (u8) ((addr >> 0) & 0x000000FF); ++ addr_bytes[1] = (u8) ((addr >> 8) & 0x000000FF); ++ addr_bytes[2] = (u8) ((addr >> 16) & 0x000000FF); ++ ++ while (--alen >= 0) { ++ pr_debug("%s: send memory word address\n", __FUNCTION__); ++ msg.condition = I2C_COND_NORMAL; ++ msg.acknack = I2C_ACKNAK_WAITACK; ++ msg.direction = I2C_WRITE; ++ msg.data = addr_bytes[alen]; ++ if ((ret = pxa_pwr_i2c_transfer(&msg))) ++ return ret; ++ } ++ ++ /* write bytes; send NACK at last byte */ ++ while (len--) { ++ pr_debug("%s: writing byte (0x%08x)=0x%02x\n", ++ __FUNCTION__, (unsigned int)buffer, *buffer); ++ ++ if (len == 0) ++ msg.condition = I2C_COND_STOP; ++ else ++ msg.condition = I2C_COND_NORMAL; ++ ++ msg.acknack = I2C_ACKNAK_WAITACK; ++ msg.direction = I2C_WRITE; ++ msg.data = *(buffer++); ++ ++ if ((ret = pxa_pwr_i2c_transfer(&msg))) ++ return ret; ++ } ++ ++ pxa_pwr_i2c_reset(); ++ return 0; ++} ++ ++/** ++ * pxa_pwr_i2c_reg_read: - Read single byte from an i2c device ++ * ++ * @chip: address of the chip which is to be read ++ * @reg: i2c data address within the chip ++ * @return: data in case of success, negative error code otherwise ++ */ ++s32 pxa_pwr_i2c_reg_read(u8 chip, u8 reg) ++{ ++ char buf; ++ int ret; ++ ++ pr_debug("%s(chip=0x%02x, reg=0x%02x)\n", __FUNCTION__, chip, reg); ++ ret = pxa_pwr_i2c_read(chip, reg, 1, &buf, 1); ++ if (ret == 0) ++ ret = buf; ++ ++ return ret; ++} ++ ++/** ++ * pxa_pwr_i2c_reg_write: - Write multiple bytes to an i2c device ++ * ++ * The higher level routines take into account that this function is only ++ * called with len < page length of the device (see configuration file) ++ * ++ * @chip: address of the chip which is to be written ++ * @reg: i2c data address within the chip ++ * @return: 0 in case of success, negative error code otherwise ++ */ ++s32 pxa_pwr_i2c_reg_write(u8 chip, u8 reg, u8 val) ++{ ++ pr_debug("%s(chip=0x%02x, reg=0x%02x, val=0x%02x)\n", ++ __FUNCTION__, chip, reg, val); ++ return pxa_pwr_i2c_write(chip, reg, 1, &val, 1); ++} ++ ++MODULE_DESCRIPTION("PXA Power I2C"); ++MODULE_AUTHOR("Mike Rapoport"); ++MODULE_LICENSE("GPL"); +diff --git a/arch/arm/mach-pxa/pxa27x.c b/arch/arm/mach-pxa/pxa27x.c +index 203371a..36b695f 100644 +--- a/arch/arm/mach-pxa/pxa27x.c ++++ b/arch/arm/mach-pxa/pxa27x.c +@@ -243,7 +243,11 @@ void pxa27x_cpu_pm_enter(suspend_state_t state) + case PM_SUSPEND_MEM: + /* set resume return address */ + PSPR = virt_to_phys(pxa_cpu_resume); +- pxa27x_cpu_suspend(PWRMODE_SLEEP); ++#ifdef CONFIG_MACH_EM_X270 ++ pxa27x_cpu_suspend(PWRMODE_DEEPSLEEP); ++#else ++ pxa27x_cpu_suspend(PWRMODE_SLEEP); ++#endif + break; + } + } +diff --git a/arch/arm/mach-pxa/spitz.c b/arch/arm/mach-pxa/spitz.c +index bae47e1..a16e532 100644 +--- a/arch/arm/mach-pxa/spitz.c ++++ b/arch/arm/mach-pxa/spitz.c +@@ -349,6 +349,32 @@ static struct pxamci_platform_data spitz_mci_platform_data = { + + + /* ++ * USB Client (Gadget/UDC) ++ */ ++static void spitz_udc_command(int cmd) ++{ ++ switch(cmd) { ++ case PXA2XX_UDC_CMD_CONNECT: ++ UP2OCR = UP2OCR_HXOE | UP2OCR_DMPUE | UP2OCR_DMPUBE; ++ break; ++ case PXA2XX_UDC_CMD_DISCONNECT: ++ //UP2OCR = UP2OCR_HXOE | UP2OCR_DMPUE | UP2OCR_DMPUBE; ++ break; ++ } ++} ++ ++static int spitz_udc_detect(void) ++{ ++ return 1; ++} ++ ++static struct pxa2xx_udc_mach_info spitz_udc_info __initdata = { ++ .udc_is_connected = spitz_udc_detect, ++ .udc_command = spitz_udc_command, ++}; ++ ++ ++/* + * USB Host (OHCI) + */ + static int spitz_ohci_init(struct device *dev) +@@ -499,6 +525,7 @@ static void __init common_init(void) + pxa_gpio_mode(SPITZ_GPIO_HSYNC | GPIO_IN); + + platform_add_devices(devices, ARRAY_SIZE(devices)); ++ pxa_set_udc_info(&spitz_udc_info); + pxa_set_mci_info(&spitz_mci_platform_data); + pxa_set_ohci_info(&spitz_ohci_platform_data); + pxa_set_ficp_info(&spitz_ficp_platform_data); +diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig +index 2e1c24f..f80f2b1 100644 +--- a/drivers/i2c/chips/Kconfig ++++ b/drivers/i2c/chips/Kconfig +@@ -163,4 +163,17 @@ config MENELAUS + and other features that are often used in portable devices like + cell phones and PDAs. + ++config DA9030 ++ tristate "Dialog Semiconductor DA9030 power management chip" ++ depends on EXPERIMENTAL && EMBEDDED ++ help ++ If you say yes here you get support for the Dialog ++ Semiconductor DA9030 power management chip. ++ This includes voltage regulators, lithium ion/polymer battery ++ charging, and other features that are often used in portable ++ devices like PDAs, cell phones and cameras. ++ ++ This driver can also be built as a module. If so, the module ++ will be called da9030. ++ + endmenu +diff --git a/drivers/i2c/chips/Makefile b/drivers/i2c/chips/Makefile +index ca924e1..69f545c 100644 +--- a/drivers/i2c/chips/Makefile ++++ b/drivers/i2c/chips/Makefile +@@ -15,6 +15,7 @@ obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o + obj-$(CONFIG_TPS65010) += tps65010.o + obj-$(CONFIG_MENELAUS) += menelaus.o + obj-$(CONFIG_SENSORS_TSL2550) += tsl2550.o ++obj-$(CONFIG_DA9030) += da9030.o + + ifeq ($(CONFIG_I2C_DEBUG_CHIP),y) + EXTRA_CFLAGS += -DDEBUG +diff --git a/drivers/i2c/chips/da9030.c b/drivers/i2c/chips/da9030.c +new file mode 100644 +index 0000000..9791272 +--- /dev/null ++++ b/drivers/i2c/chips/da9030.c +@@ -0,0 +1,1213 @@ ++/* ++ * Dialog Semiconductor DA9030 power management IC driver ++ * ++ * Copyright (C) 2007 Compulab, Ltd. ++ * Mike Rapoport <mike@compulab.co.il> ++ * ++ * Some parts based on menelaus.c: ++ * Copyright (C) 2004 Texas Instruments, Inc. ++ * Copyright (C) 2005, 2006 Nokia Corporation ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive for ++ * more details. ++ */ ++ ++#include <linux/module.h> ++#include <linux/ctype.h> ++#include <linux/uaccess.h> ++#include <linux/i2c.h> ++#include <linux/irq.h> ++#include <linux/interrupt.h> ++#include <linux/da9030.h> ++#include <linux/kernel_stat.h> ++#include <linux/random.h> ++#include <linux/mutex.h> ++ ++#include <linux/debugfs.h> ++#include <linux/seq_file.h> ++ ++#include "da9030.h" ++ ++#define DRIVER_NAME "da9030" ++ ++static unsigned short normal_i2c[] = { 0x49, I2C_CLIENT_END }; ++ ++I2C_CLIENT_INSMOD; ++ ++struct da9030 { ++ struct mutex lock; ++ struct i2c_client *client; ++ ++ struct work_struct event_work; ++ ++ /* there are 24 interrupts */ ++ void (*callbacks[24])(int event, void *data); ++ void *callbacks_data[24]; ++ ++ struct dentry *debug_file; ++}; ++ ++/* I hardly believe there can be more than 1 such chip in the system, ++ so keeping its global instance won't really hurt */ ++static struct da9030 *the_da9030; ++ ++unsigned char defined_regs[] = { ++ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, ++ 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, ++ 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, ++ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, ++ 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, ++ 0x50, 0x51, ++ 0x60, 0x61, 0x62, 0x63, ++ 0x80, 0x81, ++ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, ++ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, ++ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xba, ++}; ++ ++static inline int is_reg_valid(u32 reg) ++{ ++ int i; ++ for (i = 0; i < ARRAY_SIZE(defined_regs); i++) ++ if (reg == defined_regs[i]) ++ return 1; ++ ++ return 0; ++} ++ ++s32 da9030_get_reg(u32 reg) ++{ ++ if (!is_reg_valid(reg)) ++ return -EINVAL; ++ ++ return i2c_smbus_read_byte_data(the_da9030->client, reg); ++} ++EXPORT_SYMBOL(da9030_get_reg); ++ ++s32 da9030_set_reg(u32 reg, u8 val) ++{ ++ if (!is_reg_valid(reg)) ++ return -EINVAL; ++ ++ return i2c_smbus_write_byte_data(the_da9030->client, reg, val); ++} ++EXPORT_SYMBOL(da9030_set_reg); ++ ++static s32 da9030_update_reg_bits(u32 reg, int bits, int shift, int val) ++{ ++ int ret; ++ int new_val; ++ ++ mutex_lock(&the_da9030->lock); ++ ret = da9030_get_reg(reg); ++ if (ret < 0) ++ goto out; ++ ++ new_val = reg & ~(((1 << bits) - 1) << shift); ++ new_val |= (val << shift); ++ ++ ret = da9030_set_reg(reg, new_val); ++ mutex_unlock(&the_da9030->lock); ++ ++out: ++ return ret; ++} ++ ++int da9030_get_status(void) ++{ ++ s32 ret; ++ ++ mutex_lock(&the_da9030->lock); ++ ret = da9030_get_reg(STATUS); ++ mutex_unlock(&the_da9030->lock); ++ ++ return ret; ++} ++EXPORT_SYMBOL(da9030_get_status); ++ ++int da9030_get_fault_log(void) ++{ ++ s32 ret; ++ ++ mutex_lock(&the_da9030->lock); ++ ret = da9030_get_reg(FAULT_LOG); ++ mutex_unlock(&the_da9030->lock); ++ ++ return ret; ++} ++EXPORT_SYMBOL(da9030_get_fault_log); ++ ++void da9030_read_adc(struct da9030_adc_res *adc) ++{ ++ mutex_lock(&the_da9030->lock); ++ ++ adc->vbat_res = da9030_get_reg(VBAT_RES); ++ adc->vbatmin_res = da9030_get_reg(VBATMIN_RES); ++ adc->vbatmintxon = da9030_get_reg(VBATMINTXON_RES); ++ adc->ichmax_res = da9030_get_reg(ICHMAX_RES); ++ adc->ichmin_res = da9030_get_reg(ICHMIN_RES); ++ adc->ichaverage_res = da9030_get_reg(ICHAVERAGE_RES); ++ adc->vchmax_res = da9030_get_reg(VCHMAX_RES); ++ adc->vchmin_res = da9030_get_reg(VCHMIN_RES); ++ adc->tbat_res = da9030_get_reg(TBAT_RES); ++ adc->adc_in4_res = da9030_get_reg(ADC_IN4_RES); ++ adc->adc_in5_res = da9030_get_reg(ADC_IN5_RES); ++ ++ mutex_unlock(&the_da9030->lock); ++} ++EXPORT_SYMBOL(da9030_read_adc); ++ ++void da9030_enable_adc(void) ++{ ++ /* enable automatic A/D measurements */ ++ mutex_lock(&the_da9030->lock); ++ ++ da9030_set_reg(ADC_MAN_CONTROL, ++ ADC_LDO_INT_ENABLE | ADC_TBATREF_ENABLE); ++ da9030_set_reg(ADC_MAN_CONTROL_1, ++ ADC_LDO_INT_ENABLE | ADC_TBATREF_ENABLE); ++ da9030_set_reg(ADC_AUTO_CONTROL_1, ++ ADC_TBAT_ENABLE | ADC_VBAT_IN_TXON | ADC_VCH_ENABLE | ++ ADC_ICH_ENABLE | ADC_VBAT_ENABLE | ++ ADC_AUTO_SLEEP_ENABLE); ++ da9030_set_reg(ADC_AUTO_CONTROL, ++ ADC_TBAT_ENABLE | ADC_VBAT_IN_TXON | ADC_VCH_ENABLE | ++ ADC_ICH_ENABLE | ADC_VBAT_ENABLE | ++ ADC_AUTO_SLEEP_ENABLE); ++ ++ mutex_unlock(&the_da9030->lock); ++} ++EXPORT_SYMBOL(da9030_enable_adc); ++ ++void da9030_set_wled(int on, unsigned int brightness) ++{ ++ u8 val; ++ ++ mutex_lock(&the_da9030->lock); ++ ++ if (on) ++ val = WLED_CP_ENABLE | (brightness & 0x7); ++ else ++ val = da9030_get_reg(WLED_CONTROL) & ~WLED_CP_ENABLE; ++ ++ da9030_set_reg(WLED_CONTROL, val); ++ ++ mutex_unlock(&the_da9030->lock); ++} ++EXPORT_SYMBOL(da9030_set_wled); ++ ++int da9030_set_charger(int on, unsigned int mA, unsigned int mV) ++{ ++ int ret; ++ u8 val = 0; ++ if (on) { ++ if (mA >= 1500 || mV < 4000 || mV > 4350) ++ return -EINVAL; ++ ++ val = CHRG_CHARGER_ENABLE; ++ val |= (mA / 100) << 3; ++ val |= (mV - 4000) / 50; ++ } ++ ++ mutex_lock(&the_da9030->lock); ++ ret = da9030_set_reg(CHARGE_CONTROL, val); ++ mutex_unlock(&the_da9030->lock); ++ ++ return ret; ++} ++EXPORT_SYMBOL(da9030_set_charger); ++ ++void da9030_get_charger(int *on, unsigned int *mA, unsigned int *mV) ++{ ++ s32 val; ++ ++ mutex_lock(&the_da9030->lock); ++ ++ val = da9030_get_reg(CHARGE_CONTROL); ++ ++ mutex_unlock(&the_da9030->lock); ++ ++ if (on) ++ *on = (val & CHRG_CHARGER_ENABLE) ? 1 : 0; ++ ++ if (mA) ++ *mA = ((val >> 3) & 0xf) * 100; ++ ++ if (mV) ++ *mV = (val & 0x7) * 50 + 4000; ++} ++EXPORT_SYMBOL(da9030_get_charger); ++ ++int da9030_set_led(int led, int on, ++ enum da9030_led_rate rate, ++ enum da9030_led_duty_cycle duty, ++ enum da9030_led_pwm_chop pwm_chop) ++{ ++ int reg; ++ int ret; ++ ++ u8 val = 0; ++ ++ if (led > 4) ++ return -EINVAL; ++ ++ reg = LED_1_CONTROL + led; ++ if (on) { ++ val = LED_ENABLE; ++ val |= (rate & 0x3) << 5; ++ val |= (duty & 0x3) << 3; ++ val |= (pwm_chop & 0x7); ++ } ++ ++ mutex_lock(&the_da9030->lock); ++ ret = da9030_set_reg(reg, val); ++ mutex_unlock(&the_da9030->lock); ++ ++ return ret; ++} ++EXPORT_SYMBOL(da9030_set_led); ++ ++void da9030_set_thresholds(unsigned int tbathighp, unsigned int tbathighn, ++ unsigned int tbatlow, unsigned int vbatmon) ++{ ++ mutex_lock(&the_da9030->lock); ++ ++ da9030_set_reg(TBATHIGHP, tbathighp); ++ da9030_set_reg(TBATHIGHN, tbathighn); ++ da9030_set_reg(TBATLOW, tbatlow); ++ da9030_set_reg(VBATMONTXON, vbatmon); ++ da9030_set_reg(VBATMON, vbatmon); ++ ++ da9030_set_reg(TBATHIGHP_1, tbathighp); ++ da9030_set_reg(TBATHIGHN_1, tbathighn); ++ da9030_set_reg(TBATLOW_1, tbatlow); ++ da9030_set_reg(VBATMONTXMON_1, vbatmon); ++ da9030_set_reg(VBATMON_1, vbatmon); ++ ++ mutex_unlock(&the_da9030->lock); ++} ++EXPORT_SYMBOL(da9030_set_thresholds); ++ ++struct da9030_vtg_value { ++ u16 vtg; ++ u16 val; ++}; ++ ++struct da9030_vtg_value da9030_vtg_1V8_3V2[] = { ++ {1800, 0x0}, ++ {1900, 0x1}, ++ {2000, 0x2}, ++ {2100, 0x3}, ++ {2200, 0x4}, ++ {2300, 0x5}, ++ {2400, 0x6}, ++ {2500, 0x7}, ++ {2600, 0x8}, ++ {2700, 0x9}, ++ {2800, 0xa}, ++ {2900, 0xb}, ++ {3000, 0xc}, ++ {3100, 0xd}, ++ {3200, 0xe}, ++ {3200, 0xf}, ++}; ++ ++struct da9030_vtg_value da9030_vtg_1V1_2V65[] = { ++ {1100, 0x0}, ++ {1150, 0x1}, ++ {1200, 0x2}, ++ {1250, 0x3}, ++ {1300, 0x4}, ++ {1350, 0x5}, ++ {1400, 0x6}, ++ {1450, 0x7}, ++ {1500, 0x8}, ++ {1550, 0x9}, ++ {1600, 0xa}, ++ {1650, 0xb}, ++ {1700, 0xc}, ++ {1750, 0xd}, ++ {1800, 0xe}, ++ {1850, 0xf}, ++ {1900, 0x10}, ++ {1950, 0x11}, ++ {2000, 0x12}, ++ {2050, 0x13}, ++ {2100, 0x14}, ++ {2150, 0x15}, ++ {2200, 0x16}, ++ {2250, 0x17}, ++ {2300, 0x18}, ++ {2350, 0x19}, ++ {2400, 0x1a}, ++ {2450, 0x1b}, ++ {2500, 0x1c}, ++ {2550, 0x1d}, ++ {2600, 0x1e}, ++ {2650, 0x1f}, ++}; ++ ++struct da9030_vtg_value da9030_vtg_2V76_2V94[] = { ++ {2760, 0x7}, ++ {2790, 0x6}, ++ {2820, 0x5}, ++ {2850, 0x4}, ++ {2850, 0x3}, ++ {2880, 0x1}, ++ {2910, 0x2}, ++ {2940, 0x3}, ++}; ++ ++struct da9030_vtg_value da9030_vtg_0V85_1V625[] = { ++ {850, 0x0}, ++ {875, 0x1}, ++ {900, 0x2}, ++ {925, 0x3}, ++ {950, 0x4}, ++ {975, 0x5}, ++ {1000, 0x6}, ++ {1025, 0x7}, ++ {1050, 0x8}, ++ {1075, 0x9}, ++ {1100, 0xa}, ++ {1125, 0xb}, ++ {1150, 0xc}, ++ {1175, 0xd}, ++ {1200, 0xe}, ++ {1225, 0xf}, ++ {1250, 0x10}, ++ {1275, 0x11}, ++ {1300, 0x12}, ++ {1325, 0x13}, ++ {1350, 0x14}, ++ {1375, 0x15}, ++ {1400, 0x16}, ++ {1425, 0x17}, ++ {1450, 0x18}, ++ {1475, 0x19}, ++ {1500, 0x1a}, ++ {1525, 0x1b}, ++ {1550, 0x1c}, ++ {1575, 0x1d}, ++ {1600, 0x1e}, ++ {1625, 0x1f}, ++}; ++ ++struct ldo_param { ++ u8 reg; ++ u8 shift; ++ u8 bits; ++}; ++ ++struct da9030_ldo { ++ const char *name; ++ ++ struct ldo_param vtg; ++ struct ldo_param sleep; ++ struct ldo_param lock; ++ ++ /* several LDOs have two enable/disable bits */ ++ struct ldo_param enable[2]; ++ ++ struct da9030_vtg_value *values; ++ int values_count; ++}; ++ ++static struct da9030_ldo da9030_ldos[] = { ++ [0] = { ++ .name = "LDO1", ++ .vtg = { ++ .reg = LDO_1, ++ .shift = 0, ++ .bits = 5, ++ }, ++ .enable[0] = { ++ .reg = REG_CONTROL_1_97, ++ .shift = 1, ++ .bits = 1, ++ }, ++ .sleep = { ++ .reg = LDO_1, ++ .shift = 5, ++ .bits = 2, ++ }, ++ .lock = { ++ .reg = REG_SLEEP_CONTROL1, ++ .shift = 0, ++ .bits = 2, ++ }, ++ .values = da9030_vtg_1V8_3V2, ++ .values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2), ++ }, ++ [1] = { ++ .name = "LDO2", ++ .vtg = { ++ .reg = LDO_2_3, ++ .shift = 0, ++ .bits = 4, ++ }, ++ .enable[0] = { ++ .reg = REG_CONTROL_1_97, ++ .shift = 2, ++ .bits = 1, ++ }, ++ .sleep = { ++ .reg = REG_SLEEP_CONTROL1, ++ .shift = 2, ++ .bits = 2, ++ }, ++ .values = da9030_vtg_1V8_3V2, ++ .values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2), ++ }, ++ [2] = { ++ .name = "LDO3", ++ .vtg = { ++ .reg = LDO_2_3, ++ .shift = 4, ++ .bits = 4, ++ }, ++ .enable[0] = { ++ .reg = REG_CONTROL_1_97, ++ .shift = 3, ++ .bits = 1, ++ }, ++ .sleep = { ++ .reg = REG_SLEEP_CONTROL1, ++ .shift = 4, ++ .bits = 2, ++ }, ++ .values = da9030_vtg_1V8_3V2, ++ .values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2), ++ }, ++ [3] = { ++ .name = "LDO4", ++ .vtg = { ++ .reg = LDO_4_5, ++ .shift = 0, ++ .bits = 4, ++ }, ++ .enable[0] = { ++ .reg = REG_CONTROL_1_97, ++ .shift = 4, ++ .bits = 1, ++ }, ++ .sleep = { ++ .reg = REG_SLEEP_CONTROL1, ++ .shift = 6, ++ .bits = 2, ++ }, ++ .values = da9030_vtg_1V8_3V2, ++ .values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2), ++ }, ++ [4] = { ++ .name = "LDO5", ++ .vtg = { ++ .reg = LDO_4_5, ++ .shift = 4, ++ .bits = 4, ++ }, ++ .enable[0] = { ++ .reg = REG_CONTROL_1_97, ++ .shift = 5, ++ .bits = 1, ++ }, ++ .sleep = { ++ .reg = REG_SLEEP_CONTROL2, ++ .shift = 0, ++ .bits = 2, ++ }, ++ .values = da9030_vtg_1V8_3V2, ++ .values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2), ++ }, ++ [5] = { ++ .name = "LDO6", ++ .vtg = { ++ .reg = LDO_6_SIMCP, ++ .shift = 0, ++ .bits = 4, ++ }, ++ .enable[0] = { ++ .reg = REG_CONTROL_1_97, ++ .shift = 6, ++ .bits = 1, ++ }, ++ .sleep = { ++ .reg = 0, ++ .shift = 0, ++ .bits = 0, ++ }, ++ .values = da9030_vtg_1V8_3V2, ++ .values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2), ++ }, ++ [6] = { ++ .name = "LDO7", ++ .vtg = { ++ .reg = LDO_7_8, ++ .shift = 0, ++ .bits = 4, ++ }, ++ .enable[0] = { ++ .reg = REG_CONTROL_1_97, ++ .shift = 7, ++ .bits = 1, ++ }, ++ .sleep = { ++ .reg = REG_SLEEP_CONTROL2, ++ .shift = 2, ++ .bits = 2, ++ }, ++ .values = da9030_vtg_1V8_3V2, ++ .values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2), ++ }, ++ [7] = { ++ .name = "LDO8", ++ .vtg = { ++ .reg = LDO_7_8, ++ .shift = 4, ++ .bits = 4, ++ }, ++ .enable[0] = { ++ .reg = REG_CONTROL_2_98, ++ .shift = 0, ++ .bits = 1, ++ }, ++ .sleep = { ++ .reg = REG_SLEEP_CONTROL2, ++ .shift = 4, ++ .bits = 2, ++ }, ++ .values = da9030_vtg_1V8_3V2, ++ .values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2), ++ }, ++ [8] = { ++ .name = "LDO9", ++ .vtg = { ++ .reg = LDO_9_12, ++ .shift = 0, ++ .bits = 4, ++ }, ++ .enable[0] = { ++ .reg = REG_CONTROL_2_98, ++ .shift = 1, ++ .bits = 1, ++ }, ++ .sleep = { ++ .reg = REG_SLEEP_CONTROL2, ++ .shift = 6, ++ .bits = 2, ++ }, ++ .values = da9030_vtg_1V8_3V2, ++ .values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2), ++ }, ++ [9] = { ++ .name = "LDO10", ++ .vtg = { ++ .reg = LDO_10_11, ++ .shift = 0, ++ .bits = 4, ++ }, ++ .enable[0] = { ++ .reg = REG_CONTROL_1_17, ++ .shift = 1, ++ .bits = 1, ++ }, ++ .enable[1] = { ++ .reg = REG_CONTROL_2_98, ++ .shift = 2, ++ .bits = 1, ++ }, ++ .sleep = { ++ .reg = 0, ++ .shift = 0, ++ .bits = 0, ++ }, ++ .values = da9030_vtg_1V8_3V2, ++ .values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2), ++ }, ++ [10] = { ++ .name = "LDO11", ++ .vtg = { ++ .reg = LDO_10_11, ++ .shift = 4, ++ .bits = 4, ++ }, ++ .enable[0] = { ++ .reg = REG_CONTROL_1_17, ++ .shift = 2, ++ .bits = 1, ++ }, ++ .enable[1] = { ++ .reg = REG_CONTROL_2_98, ++ .shift = 3, ++ .bits = 1, ++ }, ++ .sleep = { ++ .reg = 0, ++ .shift = 0, ++ .bits = 0, ++ }, ++ .values = da9030_vtg_1V8_3V2, ++ .values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2), ++ }, ++ [11] = { ++ .name = "LDO12", ++ .vtg = { ++ .reg = LDO_9_12, ++ .shift = 4, ++ .bits = 4, ++ }, ++ .enable[0] = { ++ .reg = REG_CONTROL_2_98, ++ .shift = 4, ++ .bits = 1, ++ }, ++ .sleep = { ++ .reg = REG_SLEEP_CONTROL3, ++ .shift = 0, ++ .bits = 2, ++ }, ++ .values = da9030_vtg_1V8_3V2, ++ .values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2), ++ }, ++ [12] = { ++ .name = "LDO13", ++ .vtg = { ++ .reg = 0, ++ .shift = 0, ++ .bits = 0, ++ }, ++ .enable[0] = { ++ .reg = REG_CONTROL_1_17, ++ .shift = 3, ++ .bits = 1, ++ }, ++ .sleep = { ++ .reg = 0, ++ .shift = 0, ++ .bits = 0, ++ }, ++ .values = NULL, ++ .values_count = 0, ++ }, ++ [13] = { ++ .name = "LDO14", ++ .vtg = { ++ .reg = LDO_14_16, ++ .shift = 0, ++ .bits = 3, ++ }, ++ .enable[0] = { ++ .reg = REG_CONTROL_1_17, /* FIXME: or 2_98? */ ++ .shift = 4, ++ .bits = 1, ++ }, ++ .enable[1] = { ++ .reg = REG_CONTROL_2_98, /* FIXME: or 2_98? */ ++ .shift = 5, ++ .bits = 1, ++ }, ++ .sleep = { ++ .reg = 0, ++ .shift = 0, ++ .bits = 0, ++ }, ++ .values = da9030_vtg_2V76_2V94, ++ .values_count = ARRAY_SIZE(da9030_vtg_2V76_2V94), ++ }, ++ [14] = { ++ .name = "LDO15", ++ .vtg = { ++ .reg = LDO_15, ++ .shift = 0, ++ .bits = 5, ++ }, ++ .enable[0] = { ++ .reg = REG_CONTROL_1_17, ++ .shift = 5, ++ .bits = 1, ++ }, ++ .sleep = { ++ .reg = 0, ++ .shift = 0, ++ .bits = 0, ++ }, ++ .lock = { ++ .reg = LDO_15, ++ .shift = 5, ++ .bits = 3, ++ }, ++ .values = da9030_vtg_1V1_2V65, ++ .values_count = ARRAY_SIZE(da9030_vtg_1V1_2V65), ++ }, ++ [15] = { ++ .name = "LDO16", ++ .vtg = { ++ .reg = LDO_14_16, ++ .shift = 3, ++ .bits = 5, ++ }, ++ .enable[0] = { ++ .reg = REG_CONTROL_1_17, ++ .shift = 6, ++ .bits = 1, ++ }, ++ .sleep = { ++ .reg = 0, ++ .shift = 0, ++ .bits = 0, ++ }, ++ .values = da9030_vtg_1V1_2V65, ++ .values_count = ARRAY_SIZE(da9030_vtg_1V1_2V65), ++ }, ++ [16] = { ++ .name = "LDO17", ++ .vtg = { ++ .reg = LDO_17_SIMCP0, ++ .shift = 0, ++ .bits = 4, ++ }, ++ .enable[0] = { ++ .reg = REG_CONTROL_1_17, ++ .shift = 7, ++ .bits = 1, ++ }, ++ .sleep = { ++ .reg = 0, ++ .shift = 0, ++ .bits = 0, ++ }, ++ .values = da9030_vtg_1V8_3V2, ++ .values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2), ++ }, ++ [17] = { ++ .name = "LDO18", ++ .vtg = { ++ .reg = LDO_18_19, ++ .shift = 0, ++ .bits = 4, ++ }, ++ .enable[0] = { ++ .reg = REG_CONTROL_2_18, ++ .shift = 2, ++ .bits = 1, ++ }, ++ .sleep = { ++ .reg = 0, ++ .shift = 0, ++ .bits = 0, ++ }, ++ .values = da9030_vtg_1V8_3V2, ++ .values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2), ++ }, ++ [18] = { ++ .name = "LDO19", ++ .vtg = { ++ .reg = LDO_18_19, ++ .shift = 4, ++ .bits = 4, ++ }, ++ .enable[0] = { ++ .reg = REG_CONTROL_2_18, ++ .shift = 1, ++ .bits = 1, ++ }, ++ .sleep = { ++ .reg = 0, ++ .shift = 0, ++ .bits = 0, ++ }, ++ .values = da9030_vtg_1V8_3V2, ++ .values_count = ARRAY_SIZE(da9030_vtg_1V8_3V2), ++ }, ++}; ++ ++static int da9030_get_vtg_value(int vtg, const struct da9030_vtg_value *tbl, ++ int n) ++{ ++ int i; ++ ++ for (i = 0; i < n; i++, tbl++) ++ if (tbl->vtg == vtg) ++ return tbl->val; ++ return -EINVAL; ++} ++ ++static int da9030_set_ldo_volt(struct da9030_ldo *ldo, unsigned int mV) ++{ ++ int val; ++ ++ val = da9030_get_vtg_value(mV, ldo->values, ldo->values_count); ++ if (val < 0) ++ return val; ++ ++ return da9030_update_reg_bits(ldo->vtg.reg, ldo->vtg.bits, ++ ldo->vtg.shift, val); ++} ++ ++static int da9030_enable_ldo(struct da9030_ldo *ldo, int enable) ++{ ++ int ret; ++ ++ ret = da9030_update_reg_bits(ldo->enable[0].reg, ldo->enable[1].bits, ++ ldo->enable[0].shift, enable); ++ ++ if (ret < 0) ++ return ret; ++ ++ if (unlikely(ldo->enable[1].reg != 0)) ++ ret = da9030_update_reg_bits(ldo->enable[1].reg, ++ ldo->enable[1].bits, ++ ldo->enable[1].shift, enable); ++ ++ return ret; ++} ++ ++static int da9030_set_ldo_sleep(struct da9030_ldo *ldo, ++ enum da9030_ldo_sleep_mode sleep_mode) ++{ ++ return da9030_update_reg_bits(ldo->sleep.reg, ldo->sleep.bits, ++ ldo->sleep.shift, sleep_mode); ++} ++ ++int da9030_set_ldo(int ldo_num, int on, unsigned int mV, ++ enum da9030_ldo_sleep_mode sleep_mode) ++{ ++ struct da9030_ldo *ldo; ++ int ret; ++ ++ if (ldo_num < 0 || ldo_num > ARRAY_SIZE(da9030_ldos)) ++ return -EINVAL; ++ ++ ldo = &da9030_ldos[ldo_num]; ++ ++ ret = da9030_set_ldo_volt(ldo, mV); ++ if (ret < 0) ++ return ret; ++ ++ ret = da9030_enable_ldo(ldo, on); ++ if (ret < 0) ++ return ret; ++ ++ ret = da9030_set_ldo_sleep(ldo, sleep_mode); ++ if (ret < 0) ++ return ret; ++ ++ return 0; ++} ++EXPORT_SYMBOL(da9030_set_ldo); ++ ++int da9030_set_buck(int buck, int on, unsigned int mV, int flags) ++{ ++ /* FIXME: implement */ ++ return 0; ++} ++EXPORT_SYMBOL(da9030_set_buck); ++ ++static void da9030_disable_irq(unsigned int irq) ++{ ++ int reg, val; ++ ++ if (irq < 8) { ++ reg = IRQ_MASK_A; ++ val = 1 << irq; ++ } else if (irq < 16) { ++ reg = IRQ_MASK_B; ++ val = 1 << (irq - 8); ++ } else { ++ reg = IRQ_MASK_C; ++ val = 1 << (irq - 16); ++ } ++ ++ i2c_smbus_write_byte_data( ++ the_da9030->client, reg, ++ i2c_smbus_read_byte_data(the_da9030->client, reg) | val); ++} ++ ++static void da9030_enable_irq(unsigned int irq) ++{ ++ int reg, val; ++ ++ if (irq < 8) { ++ reg = IRQ_MASK_A; ++ val = 1 << irq; ++ } else if (irq < 16) { ++ reg = IRQ_MASK_B; ++ val = 1 << (irq - 8); ++ } else { ++ reg = IRQ_MASK_C; ++ val = 1 << (irq - 16); ++ } ++ ++ i2c_smbus_write_byte_data( ++ the_da9030->client, reg, ++ i2c_smbus_read_byte_data(the_da9030->client, reg) & ~val); ++} ++ ++int da9030_register_callback(int event, ++ void (*callback)(int event, void *data), ++ void *data) ++{ ++ int ret; ++ ++ if (event < 0 || event > 23) ++ return -EINVAL; ++ ++ mutex_lock(&the_da9030->lock); ++ if (the_da9030->callbacks[event]) ++ ret = -EBUSY; ++ else { ++ the_da9030->callbacks[event] = callback; ++ the_da9030->callbacks_data[event] = data; ++ da9030_enable_irq(event); ++ ret = 0; ++ } ++ mutex_unlock(&the_da9030->lock); ++ ++ return ret; ++} ++EXPORT_SYMBOL(da9030_register_callback); ++ ++void da9030_unregister_callback(int event) ++{ ++ mutex_lock(&the_da9030->lock); ++ ++ da9030_disable_irq(event); ++ the_da9030->callbacks[event] = NULL; ++ the_da9030->callbacks_data[event] = 0; ++ ++ mutex_unlock(&the_da9030->lock); ++} ++EXPORT_SYMBOL(da9030_unregister_callback); ++ ++#ifdef CONFIG_DEBUG_FS ++#define MAX_BUF 256 ++ ++static int da9030_debug_show(struct seq_file *s, void *data) ++{ ++ int i, res = 0; ++ struct da9030 *da9030 = s->private; ++ struct i2c_client *client = da9030->client; ++ ++ seq_printf(s, "DA9030 state: da = %p, cl = %p, s->private = %p\n", ++ da9030, client, s->private); ++ ++ for (i = 0; i < ARRAY_SIZE(defined_regs); i++) { ++ res = i2c_smbus_read_byte_data(client, defined_regs[i]); ++ seq_printf(s, "%02x %x\n", defined_regs[i], res); ++ } ++ return 0; ++} ++ ++static int da9030_debug_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, da9030_debug_show, inode->i_private); ++} ++ ++ssize_t da9030_debug_write(struct file *f, const char __user *buf, size_t len, ++ loff_t *off) ++{ ++ char buffer[MAX_BUF]; ++ int reg, val; ++ char *endp; ++ struct da9030 *da9030 = ((struct seq_file *)f->private_data)->private; ++ struct i2c_client *client = da9030->client; ++ ++ if (len > MAX_BUF) { ++ printk(KERN_INFO "%s: large buffer\n", __FUNCTION__); ++ len = MAX_BUF; ++ } ++ ++ if (copy_from_user(buffer, buf, len)) { ++ printk(KERN_INFO "%s: copy_from_user failed\n", __FUNCTION__); ++ return -EFAULT; ++ } ++ buffer[len] = '\0'; ++ ++ reg = simple_strtoul(buffer, &endp, 0); ++ while (endp && isspace(*endp)) ++ endp++; ++ ++ val = simple_strtoul(endp, 0, 0); ++ ++ i2c_smbus_write_byte_data(client, reg, val); ++ ++ return len; ++} ++ ++static const struct file_operations debug_fops = { ++ .open = da9030_debug_open, ++ .read = seq_read, ++ .write = da9030_debug_write, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static struct dentry *da9030_create_debugfs(struct da9030 *da9030) ++{ ++ da9030->debug_file = debugfs_create_file("da9030", 0666, 0, da9030, ++ &debug_fops); ++ return da9030->debug_file; ++} ++ ++static void da9030_remove_debugfs(struct da9030 *da9030) ++{ ++ debugfs_remove(da9030->debug_file); ++} ++#else ++#define da9030_create_debugfs(x) NULL ++#define da9030_remove_debugfs(x) do {} while (0) ++#endif ++ ++static irqreturn_t da9030_irq(int irq, void *_da9030) ++{ ++ struct da9030 *da9030 = _da9030; ++ ++ (void)schedule_work(&da9030->event_work); ++ ++ return IRQ_HANDLED; ++} ++ ++static void da9030_irq_worker(struct work_struct *work) ++{ ++ struct da9030 *da9030 = container_of(work, struct da9030, event_work); ++ void (*callback)(int event, void *data); ++ u32 pending = 0; ++ u32 mask = 0; ++ int i; ++ ++ while (1) { ++ pending = (i2c_smbus_read_byte_data(da9030->client, ++ EVENT_A)) | ++ ((i2c_smbus_read_byte_data(da9030->client, ++ EVENT_B)) << 8) | ++ ((i2c_smbus_read_byte_data(da9030->client, ++ EVENT_C)) << 16); ++ ++ mask = (i2c_smbus_read_byte_data(da9030->client, ++ IRQ_MASK_A)) | ++ ((i2c_smbus_read_byte_data(da9030->client, ++ IRQ_MASK_B)) << 8) | ++ ((i2c_smbus_read_byte_data(da9030->client, ++ IRQ_MASK_C)) << 16); ++ pending &= ~mask; ++ ++ if (!pending) ++ return; ++ ++ while (pending) { ++ i = __ffs(pending); ++ callback = da9030->callbacks[i]; ++ if (callback) ++ callback(i, da9030->callbacks_data[i]); ++ pending &= ~(1 << i); ++ } ++ } ++} ++ ++static inline void da9030_disable_interrupts(struct da9030 *da9030) ++{ ++ /* clear pending interruts */ ++ (void)i2c_smbus_read_byte_data(da9030->client, EVENT_A); ++ (void)i2c_smbus_read_byte_data(da9030->client, EVENT_B); ++ (void)i2c_smbus_read_byte_data(da9030->client, EVENT_C); ++ ++ /* disable interrupts */ ++ i2c_smbus_write_byte_data(da9030->client, IRQ_MASK_A, 0xff); ++ i2c_smbus_write_byte_data(da9030->client, IRQ_MASK_B, 0xff); ++ i2c_smbus_write_byte_data(da9030->client, IRQ_MASK_C, 0xff); ++} ++ ++static int da9030_probe(struct i2c_client *client) ++{ ++ int ret; ++ struct da9030 *da9030; ++ ++ if (the_da9030) { ++ dev_dbg(&client->dev, "only one %s for now\n", ++ DRIVER_NAME); ++ return -ENODEV; ++ } ++ ++ ret = i2c_smbus_read_byte_data(client, CHIP_ID); ++ ++ dev_info(&client->dev, "initialized chip revision %x\n", ret); ++ ++ da9030 = kzalloc(sizeof(struct da9030), GFP_KERNEL); ++ if (da9030 == NULL) { ++ dev_err(&client->dev, "insufficient memory\n"); ++ return -ENOMEM; ++ } ++ the_da9030 = da9030; ++ da9030->client = client; ++ ++ mutex_init(&the_da9030->lock); ++ ++ da9030_disable_interrupts(da9030); ++ INIT_WORK(&da9030->event_work, da9030_irq_worker); ++ ++ ret = request_irq(client->irq, da9030_irq, ++ IRQF_DISABLED, DRIVER_NAME, da9030); ++ ++ if (ret) { ++ kfree(da9030); ++ dev_err(&client->dev, ++ "failed to allocate irq %d\n", ++ client->irq); ++ return ret; ++ } ++ ++ i2c_set_clientdata(client, da9030); ++ ++ da9030->debug_file = da9030_create_debugfs(da9030); ++ ++ return 0; ++} ++ ++/* static int da9030_detach_adapter(struct i2c_adapter *a) */ ++static int da9030_remove(struct i2c_client *client) ++{ ++ struct da9030 *da9030 = i2c_get_clientdata(client); ++ ++ da9030_remove_debugfs(da9030); ++ ++ free_irq(da9030->client->irq, da9030); ++ kfree(da9030); ++ i2c_set_clientdata(client, NULL); ++ the_da9030 = NULL; ++ ++ return 0; ++} ++ ++static struct i2c_driver da9030_driver = { ++ .driver = { ++ .name = "da9030", ++ .owner = THIS_MODULE, ++ }, ++ ++ .probe = da9030_probe, ++ .remove = da9030_remove, ++}; ++ ++static int da9030_init(void) ++{ ++ i2c_add_driver(&da9030_driver); ++ return 0; ++} ++ ++static void da9030_exit(void) ++{ ++ i2c_del_driver(&da9030_driver); ++} ++ ++/* NOTE: this MUST be initialized before the other parts of the system ++ * that rely on it ... but after the i2c bus on which this relies. ++ * That is, much earlier than on PC-type systems, which don't often use ++ * I2C as a core system bus. ++ */ ++subsys_initcall(da9030_init); ++module_exit(da9030_exit); ++ ++MODULE_DESCRIPTION("DA9030 power manager driver"); ++MODULE_AUTHOR("Mike Rapoport, Compulab"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/i2c/chips/da9030.h b/drivers/i2c/chips/da9030.h +new file mode 100644 +index 0000000..4163156 +--- /dev/null ++++ b/drivers/i2c/chips/da9030.h +@@ -0,0 +1,282 @@ ++/* DA9030 Register definintions */ ++ ++#define CHIP_ID 0x00 ++ ++#define EVENT_A 0x01 ++#define EVENT_A_CHIOVER (1 << 7) ++#define EVENT_A_VBAT_MON_TXON (1 << 6) ++#define EVENT_A_VBAT_MON (1 << 5) ++#define EVENT_A_TBAT (1 << 4) ++#define EVENT_A_CHDET (1 << 3) ++#define EVENT_A_EXTON (1 << 2) ++#define EVENT_A_PWREN1 (1 << 1) ++#define EVENT_A_ONKEY_N (1 << 0) ++ ++#define EVENT_B 0x02 ++#define EVENT_B_WDOG_INT (1 << 7) ++#define EVENT_B_SRP_DETECT (1 << 6) ++#define EVENT_B_SESSION_VALID (1 << 5) ++#define EVENT_B_VBUS_VALID_4_0 (1 << 4) ++#define EVENT_B_VBUS_VALID_4_4 (1 << 3) ++#define EVENT_B_ADC_READY (1 << 2) ++#define EVENT_B_CCTO (1 << 1) ++#define EVENT_B_TCTO (1 << 0) ++ ++#define EVENT_C 0x03 ++#define EVENT_C_ADC_IN5 (1 << 7) ++#define EVENT_C_ADC_IN4 (1 << 6) ++#define EVENT_C_BUCK2 (1 << 5) ++#define EVENT_C_LDO19 (1 << 4) ++#define EVENT_C_LDO18 (1 << 3) ++#define EVENT_C_LDO17 (1 << 2) ++#define EVENT_C_LDO16 (1 << 1) ++#define EVENT_C_LDO15 (1 << 0) ++ ++#define STATUS 0x04 ++#define STATUS_MCLK_DETECT (1 << 7) ++#define STATUS_VBAT_MON_TXON (1 << 6) ++#define STATUS_VBAT_MON (1 << 5) ++#define STATUS_TBAT (1 << 4) ++#define STATUS_CHDET (1 << 3) ++#define STATUS_EXTON (1 << 2) ++#define STATUS_PWREN1 (1 << 1) ++#define STATUS_ONKEY_N (1 << 0) ++ ++#define IRQ_MASK_A 0x05 ++#define IRQ_MASK_A_CHIOVER (1 << 7) ++#define IRQ_MASK_A_VBAT_MON_TXON (1 << 6) ++#define IRQ_MASK_A_VBAT_MON (1 << 5) ++#define IRQ_MASK_A_TBAT (1 << 4) ++#define IRQ_MASK_A_CHDET (1 << 3) ++#define IRQ_MASK_A_EXTON (1 << 2) ++#define IRQ_MASK_A_PWREN1 (1 << 1) ++#define IRQ_MASK_A_ONKEY_N (1 << 0) ++ ++#define IRQ_MASK_B 0x06 ++#define IRQ_MASK_B_WDOG_INT (1 << 7) ++#define IRQ_MASK_B_SRP_DETECT (1 << 6) ++#define IRQ_MASK_B_SESSION_VALID (1 << 5) ++#define IRQ_MASK_B_VBUS_VALID_4_0 (1 << 4) ++#define IRQ_MASK_B_VBUS_VALID_4_4 (1 << 3) ++#define IRQ_MASK_B_ADC_READY (1 << 2) ++#define IRQ_MASK_B_CCTO (1 << 1) ++#define IRQ_MASK_B_TCTO (1 << 0) ++ ++#define IRQ_MASK_C 0x07 ++#define IRQ_MASK_C_ADC_IN5 (1 << 7) ++#define IRQ_MASK_C_ADC_IN4 (1 << 6) ++#define IRQ_MASK_C_BUCK2 (1 << 5) ++#define IRQ_MASK_C_LDO19 (1 << 4) ++#define IRQ_MASK_C_LDO18 (1 << 3) ++#define IRQ_MASK_C_LDO17 (1 << 2) ++#define IRQ_MASK_C_LDO16 (1 << 1) ++#define IRQ_MASK_C_LDO15 (1 << 0) ++ ++#define SYS_CTRL_A 0x08 ++#define SYS_CONTROL_A_SLEEP_N_PIN_ENABLE 0x1 ++#define SYS_CONTROL_A_SHUT_DOWN (1<<1) ++#define SYS_CONTROL_A_HWRES_ENABLE (1<<2) ++#define SYS_CONTROL_A_WDOG_ACTION (1<<3) ++#define SYS_CONTROL_A_WATCHDOG (1<<7) ++ ++#define SYS_CONTROL_B 0x09 ++#define SYS_CLTR_B_SLEEP_13MHZ_EN (1 << 4) ++#define SYS_CLTR_B_AUTO_CLK_SWITCH (1 << 3) ++ ++#define FAULT_LOG 0x0a ++#define FAULT_LOG_OVER_TEMP (1 << 7) ++#define FAULT_LOG_VBAT_OVER (1 << 4) ++ ++#define LDO_10_11 0x10 ++#define LDO_10_11_LDO10_3V (0xc) ++#define LDO_10_11_LDO11_3V2 (0xe << 4) ++ ++#define LDO_15 0x11 ++#define LDO_14_16 0x12 ++#define LDO_18_19 0x13 ++#define LDO_18_19_LDO18_3V2 (0xe) ++ ++#define LDO_17_SIMCP0 0x14 ++#define LDO_17_SIMCP0_LDO17_3V 0x0F ++ ++#define BUCK_2_DVC1 0x15 ++#define BUCK_2_GO (1 << 7) ++#define BUCK_2_SLEEP (1 << 6) ++#define BUCK_2_TRIM_1V5 (0x1a) ++ ++#define BUCK_2_DVC2 0x16 ++ ++#define REG_CONTROL_1_17 0x17 ++#define RC1_LDO17_EN (1 << 7) ++#define RC1_LDO16_EN (1 << 6) ++#define RC1_LDO15_EN (1 << 5) ++#define RC1_LDO14_EN (1 << 4) ++#define RC1_LDO13_EN (1 << 3) ++#define RC1_LDO11_EN (1 << 2) ++#define RC1_LDO10_EN (1 << 1) ++#define RC1_BUCK2_EN (1 << 0) ++ ++#define REG_CONTROL_2_18 0x18 ++#define RC2_SIMCP_EN (1 << 6) ++#define RC2_LDO19_EN (1 << 1) ++#define RC2_LDO18_EN (1 << 0) ++ ++#define REG_CONTROL_3_ ++ ++#define USBPUMP 0x19 ++#define USB_PUMP_EN_USBVEP (1 << 7) ++#define USB_PUMP_EN_USBVE (1 << 6) ++#define USB_PUMP_SPR_DETECT (1 << 5) ++#define USB_PUMP_SESSION_VALID (1 << 4) ++#define USB_PUMP_VBUS_VALID_4_0 (1 << 3) ++#define USB_PUMP_VBUS_VALID_4_4 (1 << 2) ++#define USB_PUMP_USBVEP (1 << 1) ++#define USB_PUMP_USBVE (1 << 0) ++ ++#define SLEEP_CONTROL 0x1a ++#define APP_SLEEP_CTL_PWR_EN (1 << 7) ++#define APP_SLEEP_CTL_SYS_EN (1 << 6) ++#define APP_SLEEP_CTL_BYPASS_LDO19 (1 << 2) ++#define APP_SLEEP_CTL_BYPASS_LDO18 (1 << 1) ++#define APP_SLEEP_CTL_BYPASS_LDO17 (1 << 0) ++ ++#define STARTUP_CONTROL 0x1b ++#define STARTUP_CTL_LDO11_PWR_SYS (1 << 3) ++#define STARTUP_CTL_LDO10_PWR_SYS (1 << 2) ++#define STARTUP_CTL_LDO11_START (1 << 1) ++#define STARTUP_CTL_LDO10_START (1 << 0) ++ ++#define LED_1_CONTROL 0x20 ++#define LED_2_CONTROL 0x21 ++#define LED_3_CONTROL 0x22 ++#define LED_4_CONTROL 0x23 ++#define LEDPC_CONTROL 0x24 ++#define LED_ENABLE (1 << 7) ++ ++#define WLED_CONTROL 0x25 ++#define WLED_CP_ENABLE (1 << 6) ++#define WLED_ISET_10 (3) ++ ++#define MISC_CONTROLA 0x26 ++#define MISC_CONTROLB 0x27 ++#define MISCB_SESSION_VALID_ENABLE (1 << 3) ++#define MISCB_USB_INT_RISING (1 << 2) ++#define MISCB_I2C_ADDRESS (1 << 1) ++#define MISCB_STARTUP_SEQUENCE (1 << 0) ++ ++#define CHARGE_CONTROL 0x28 ++#define CHRG_CHARGER_ENABLE (1 << 7) ++ ++#define CCTR_CONTROL 0x29 ++#define CCTR_SET_8MIN 0x01 ++ ++#define TCTR_CONTROL 0x2a ++#define CHARGE_PULSE 0x2b ++ ++#define ADC_MAN_CONTROL 0x30 ++ ++#define ADC_AUTO_CONTROL 0x31 ++#define ADC_IN5_EN (1 << 7) ++#define ADC_IN4_EN (1 << 6) ++#define ADC_TBAT_ENABLE (1 << 5) ++#define ADC_VBAT_IN_TXON (1 << 4) ++#define ADC_VCH_ENABLE (1 << 3) ++#define ADC_ICH_ENABLE (1 << 2) ++#define ADC_VBAT_ENABLE (1 << 1) ++#define ADC_AUTO_SLEEP_ENABLE (1 << 0) ++ ++#define VBATMON 0x32 ++#define VBATMONTXON 0x33 ++#define TBATHIGHP 0x34 ++#define TBATHIGHN 0x35 ++#define TBATLOW 0x36 ++#define ADC_IN4_MIN 0x37 ++#define ADC_IN4_MAX 0x38 ++#define ADC_IN5_MIN 0x39 ++#define ADC_IN5_MAX 0x3a ++ ++#define VBAT_RES 0x41 ++#define VBATMIN_RES 0x42 ++#define VBATMINTXON 0x43 ++#define ICHMAX_RES 0x44 ++#define ICHMIN_RES 0x45 ++#define ICHAVERAGE_RES 0x46 ++#define VCHMAX_RES 0x47 ++#define VCHMIN_RES 0x48 ++#define TBAT_RES 0x49 ++#define ADC_IN4_RES 0x4a ++#define ADC_IN5_RES 0x4b ++ ++#define LDO_1 0x90 ++#define LDO_1_UNLOCK (0x5 << 5) ++#define LDO_1_TRIM_3V (0x12) ++ ++#define LDO_2_3 0x91 ++#define LDO_2_3_LDO2_3V2 (0xe << 4) ++#define LDO_2_3_LDO3_3V (0xc) ++ ++#define LDO_4_5 0x92 ++#define LDO_6_SIMCP 0x93 ++#define LDO_6_SIMCP_LDO6_3V2 (0xe) ++ ++#define LDO_7_8 0x94 ++#define LDO_9_12 0x95 ++#define BUCK 0x96 ++#define REG_CONTROL_1_97 0x97 ++#define RC3_LDO7_EN (1 << 7) ++#define RC3_LDO6_EN (1 << 6) ++#define RC3_LDO5_EN (1 << 5) ++#define RC3_LDO4_EN (1 << 4) ++#define RC3_LDO3_EN (1 << 3) ++#define RC3_LDO2_EN (1 << 2) ++#define RC3_LDO1_EN (1 << 1) ++#define RC3_BUCK_EN (1 << 0) ++ ++#define REG_CONTROL_2_98 0x98 ++#define RC4_SLEEP (1 << 7) ++#define RC4_SIMCP_ENABLE (1 << 6) ++#define RC4_LDO14_EN (1 << 5) ++#define RC4_LDO12_EN (1 << 4) ++#define RC4_LDO11_EN (1 << 3) ++#define RC4_LDO10_EN (1 << 2) ++#define RC4_LDO9_EN (1 << 1) ++#define RC4_LDO8_EN (1 << 0) ++ ++#define REG_SLEEP_CONTROL1 0x99 ++#define REG_SLEEP_CONTROL2 0x9a ++#define REG_SLEEP_CONTROL3 0x9b ++ ++#define ADC_MAN_CONTROL_1 0xa0 ++#define ADC_DEBOUNCE_VBATMON_TXON (1 << 7) ++#define ADC_DEBOUNCE_VBATMON (1 << 6) ++#define ADC_TBATREF_ENABLE (1 << 5) ++#define ADC_LDO_INT_ENABLE (1 << 4) ++#define ADC_MAN_CONV (1 << 3) ++#define ADC_MUX_VBAT (0) ++ ++#define ADC_AUTO_CONTROL_1 0xa1 ++#define ADC_IN5_EN (1 << 7) ++#define ADC_IN4_EN (1 << 6) ++#define ADC_TBAT_ENABLE (1 << 5) ++#define ADC_VBAT_IN_TXON (1 << 4) ++#define ADC_VCH_ENABLE (1 << 3) ++#define ADC_ICH_ENABLE (1 << 2) ++#define ADC_VBAT_ENABLE (1 << 1) ++#define ADC_AUTO_SLEEP_ENABLE (1 << 0) ++ ++#define VBATMON_1 0xa2 ++#define VBATMONTXMON_1 0xa3 ++#define TBATHIGHP_1 0xa4 ++#define TBATHIGHN_1 0xa5 ++#define TBATLOW_1 0xa6 ++#define MAN_RES 0xb0 ++#define VBAT_RES_1 0xb1 ++#define VBATMIN_RES_1 0xb2 ++#define VBATMINTXON_RES 0xb3 ++#define ICHMAX_RES_1 0xb4 ++#define ICHMIN_RES_1 0xb5 ++#define ICHAVERAGE_RES_1 0xb6 ++#define VCHMAX_RES_1 0xb7 ++#define VCHMIN_RES_1 0xb8 ++#define TBAT_RES_1 0xb9 ++#define ADC_IN4_RES_1 0xba +diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig +index f929fcd..174ea2e 100644 +--- a/drivers/input/touchscreen/Kconfig ++++ b/drivers/input/touchscreen/Kconfig +@@ -126,6 +126,49 @@ config TOUCHSCREEN_HP600 + To compile this driver as a module, choose M here: the + module will be called hp680_ts_input. + ++config TOUCHSCREEN_WM97XX ++ tristate "Support for WM97xx AC97 touchscreen controllers" ++ depends AC97_BUS ++ ++choice ++ prompt "WM97xx codec type" ++ depends TOUCHSCREEN_WM97XX ++ ++config TOUCHSCREEN_WM9705 ++ bool "WM9705 Touchscreen interface support" ++ depends on TOUCHSCREEN_WM97XX ++ help ++ Say Y here if you have the wm9705 touchscreen. ++ ++ If unsure, say N. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called wm9705. ++ ++config TOUCHSCREEN_WM9712 ++ bool "WM9712 Touchscreen interface support" ++ depends on TOUCHSCREEN_WM97XX ++ help ++ Say Y here if you have the wm9712 touchscreen. ++ ++ If unsure, say N. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called wm9712. ++ ++config TOUCHSCREEN_WM9713 ++ bool "WM9713 Touchscreen interface support" ++ depends on TOUCHSCREEN_WM97XX ++ help ++ Say Y here if you have the wm9713 touchscreen. ++ ++ If unsure, say N. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called wm9713. ++ ++endchoice ++ + config TOUCHSCREEN_PENMOUNT + tristate "Penmount serial touchscreen" + select SERIO +diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile +index 5de8933..3a059b1 100644 +--- a/drivers/input/touchscreen/Makefile ++++ b/drivers/input/touchscreen/Makefile +@@ -3,6 +3,7 @@ + # + + # Each configuration option enables a list of files. ++wm97xx-ts-objs := wm97xx-core.o + + obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o + obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o +@@ -18,3 +19,16 @@ obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o + obj-$(CONFIG_TOUCHSCREEN_TOUCHRIGHT) += touchright.o + obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o + obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o ++obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o ++ ++ifeq ($(CONFIG_TOUCHSCREEN_WM9713),y) ++wm97xx-ts-objs += wm9713.o ++endif ++ ++ifeq ($(CONFIG_TOUCHSCREEN_WM9712),y) ++wm97xx-ts-objs += wm9712.o ++endif ++ ++ifeq ($(CONFIG_TOUCHSCREEN_WM9705),y) ++wm97xx-ts-objs += wm9705.o ++endif +diff --git a/drivers/input/touchscreen/wm9705.c b/drivers/input/touchscreen/wm9705.c +new file mode 100644 +index 0000000..1dae63d +--- /dev/null ++++ b/drivers/input/touchscreen/wm9705.c +@@ -0,0 +1,360 @@ ++/* ++ * wm9705.c -- Codec driver for Wolfson WM9705 AC97 Codec. ++ * ++ * Copyright 2003, 2004, 2005, 2006 Wolfson Microelectronics PLC. ++ * Author: Liam Girdwood ++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com ++ * Parts Copyright : Ian Molton <spyro@f2s.com> ++ * Andrew Zabolotny <zap@homelink.ru> ++ * Russell King <rmk@arm.linux.org.uk> ++ * ++ * 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. ++ * ++ * Revision history ++ * 6th Sep 2006 Mike Arthur <linux@wolfsonmicro.com> ++ * Added pre and post sample calls. ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/version.h> ++#include <linux/kernel.h> ++#include <linux/input.h> ++#include <linux/delay.h> ++#include <linux/bitops.h> ++#include <linux/wm97xx.h> ++ ++#define TS_NAME "wm97xx" ++#define WM9705_VERSION "0.62" ++#define DEFAULT_PRESSURE 0xb0c0 ++ ++/* ++ * Debug ++ */ ++#if 0 ++#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg) ++#else ++#define dbg(format, arg...) ++#endif ++#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg) ++#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg) ++#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg) ++ ++/* ++ * Module parameters ++ */ ++ ++/* ++ * Set current used for pressure measurement. ++ * ++ * Set pil = 2 to use 400uA ++ * pil = 1 to use 200uA and ++ * pil = 0 to disable pressure measurement. ++ * ++ * This is used to increase the range of values returned by the adc ++ * when measureing touchpanel pressure. ++ */ ++static int pil = 0; ++module_param(pil, int, 0); ++MODULE_PARM_DESC(pil, "Set current used for pressure measurement."); ++ ++/* ++ * Set threshold for pressure measurement. ++ * ++ * Pen down pressure below threshold is ignored. ++ */ ++static int pressure = DEFAULT_PRESSURE & 0xfff; ++module_param(pressure, int, 0); ++MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement."); ++ ++/* ++ * Set adc sample delay. ++ * ++ * For accurate touchpanel measurements, some settling time may be ++ * required between the switch matrix applying a voltage across the ++ * touchpanel plate and the ADC sampling the signal. ++ * ++ * This delay can be set by setting delay = n, where n is the array ++ * position of the delay in the array delay_table below. ++ * Long delays > 1ms are supported for completeness, but are not ++ * recommended. ++ */ ++static int delay = 4; ++module_param(delay, int, 0); ++MODULE_PARM_DESC(delay, "Set adc sample delay."); ++ ++/* ++ * Pen detect comparator threshold. ++ * ++ * 0 to Vmid in 15 steps, 0 = use zero power comparator with Vmid threshold ++ * i.e. 1 = Vmid/15 threshold ++ * 15 = Vmid/1 threshold ++ * ++ * Adjust this value if you are having problems with pen detect not ++ * detecting any down events. ++ */ ++static int pdd = 8; ++module_param(pdd, int, 0); ++MODULE_PARM_DESC(pdd, "Set pen detect comparator threshold"); ++ ++/* ++ * Set adc mask function. ++ * ++ * Sources of glitch noise, such as signals driving an LCD display, may feed ++ * through to the touch screen plates and affect measurement accuracy. In ++ * order to minimise this, a signal may be applied to the MASK pin to delay or ++ * synchronise the sampling. ++ * ++ * 0 = No delay or sync ++ * 1 = High on pin stops conversions ++ * 2 = Edge triggered, edge on pin delays conversion by delay param (above) ++ * 3 = Edge triggered, edge on pin starts conversion after delay param ++ */ ++static int mask = 0; ++module_param(mask, int, 0); ++MODULE_PARM_DESC(mask, "Set adc mask function."); ++ ++/* ++ * ADC sample delay times in uS ++ */ ++static const int delay_table[] = { ++ 21, // 1 AC97 Link frames ++ 42, // 2 ++ 84, // 4 ++ 167, // 8 ++ 333, // 16 ++ 667, // 32 ++ 1000, // 48 ++ 1333, // 64 ++ 2000, // 96 ++ 2667, // 128 ++ 3333, // 160 ++ 4000, // 192 ++ 4667, // 224 ++ 5333, // 256 ++ 6000, // 288 ++ 0 // No delay, switch matrix always on ++}; ++ ++/* ++ * Delay after issuing a POLL command. ++ * ++ * The delay is 3 AC97 link frames + the touchpanel settling delay ++ */ ++static inline void poll_delay(int d) ++{ ++ udelay (3 * AC97_LINK_FRAME + delay_table [d]); ++} ++ ++/* ++ * set up the physical settings of the WM9705 ++ */ ++static void init_wm9705_phy(struct wm97xx* wm) ++{ ++ u16 dig1 = 0, dig2 = WM97XX_RPR; ++ ++ /* ++ * mute VIDEO and AUX as they share X and Y touchscreen ++ * inputs on the WM9705 ++ */ ++ wm97xx_reg_write(wm, AC97_AUX, 0x8000); ++ wm97xx_reg_write(wm, AC97_VIDEO, 0x8000); ++ ++ /* touchpanel pressure current*/ ++ if (pil == 2) { ++ dig2 |= WM9705_PIL; ++ dbg("setting pressure measurement current to 400uA."); ++ } else if (pil) ++ dbg("setting pressure measurement current to 200uA."); ++ if(!pil) ++ pressure = 0; ++ ++ /* polling mode sample settling delay */ ++ if (delay!=4) { ++ if (delay < 0 || delay > 15) { ++ dbg("supplied delay out of range."); ++ delay = 4; ++ } ++ } ++ dig1 &= 0xff0f; ++ dig1 |= WM97XX_DELAY(delay); ++ dbg("setting adc sample delay to %d u Secs.", delay_table[delay]); ++ ++ /* WM9705 pdd */ ++ dig2 |= (pdd & 0x000f); ++ dbg("setting pdd to Vmid/%d", 1 - (pdd & 0x000f)); ++ ++ /* mask */ ++ dig2 |= ((mask & 0x3) << 4); ++ ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2); ++} ++ ++static int wm9705_digitiser_ioctl(struct wm97xx* wm, int cmd) ++{ ++ switch(cmd) { ++ case WM97XX_DIG_START: ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig[2] | WM97XX_PRP_DET_DIG); ++ wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */ ++ break; ++ case WM97XX_DIG_STOP: ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig[2] & ~WM97XX_PRP_DET_DIG); ++ break; ++ case WM97XX_AUX_PREPARE: ++ memcpy(wm->dig_save, wm->dig, sizeof(wm->dig)); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, 0); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG); ++ break; ++ case WM97XX_DIG_RESTORE: ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, wm->dig_save[1]); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig_save[2]); ++ break; ++ case WM97XX_PHY_INIT: ++ init_wm9705_phy(wm); ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static inline int is_pden (struct wm97xx* wm) ++{ ++ return wm->dig[2] & WM9705_PDEN; ++} ++ ++/* ++ * Read a sample from the WM9705 adc in polling mode. ++ */ ++static int wm9705_poll_sample (struct wm97xx* wm, int adcsel, int *sample) ++{ ++ int timeout = 5 * delay; ++ ++ if (!wm->pen_probably_down) { ++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (!(data & WM97XX_PEN_DOWN)) ++ return RC_PENUP; ++ wm->pen_probably_down = 1; ++ } ++ ++ /* set up digitiser */ ++ if (adcsel & 0x8000) ++ adcsel = ((adcsel & 0x7fff) + 3) << 12; ++ ++ if (wm->mach_ops && wm->mach_ops->pre_sample) ++ wm->mach_ops->pre_sample(adcsel); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, adcsel | WM97XX_POLL | WM97XX_DELAY(delay)); ++ ++ /* wait 3 AC97 time slots + delay for conversion */ ++ poll_delay (delay); ++ ++ /* wait for POLL to go low */ ++ while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) && timeout) { ++ udelay(AC97_LINK_FRAME); ++ timeout--; ++ } ++ ++ if (timeout <= 0) { ++ /* If PDEN is set, we can get a timeout when pen goes up */ ++ if (is_pden(wm)) ++ wm->pen_probably_down = 0; ++ else ++ dbg ("adc sample timeout"); ++ return RC_PENUP; ++ } ++ ++ *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (wm->mach_ops && wm->mach_ops->post_sample) ++ wm->mach_ops->post_sample(adcsel); ++ ++ /* check we have correct sample */ ++ if ((*sample & WM97XX_ADCSEL_MASK) != adcsel) { ++ dbg ("adc wrong sample, read %x got %x", adcsel, ++ *sample & WM97XX_ADCSEL_MASK); ++ return RC_PENUP; ++ } ++ ++ if (!(*sample & WM97XX_PEN_DOWN)) { ++ wm->pen_probably_down = 0; ++ return RC_PENUP; ++ } ++ ++ return RC_VALID; ++} ++ ++/* ++ * Sample the WM9705 touchscreen in polling mode ++ */ ++static int wm9705_poll_touch(struct wm97xx* wm, struct wm97xx_data *data) ++{ ++ int rc; ++ ++ if ((rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_X, &data->x)) != RC_VALID) ++ return rc; ++ if ((rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_Y, &data->y)) != RC_VALID) ++ return rc; ++ if (pil) { ++ if ((rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_PRES, &data->p)) != RC_VALID) ++ return rc; ++ } else ++ data->p = DEFAULT_PRESSURE; ++ ++ return RC_VALID; ++} ++ ++/* ++ * Enable WM9705 continuous mode, i.e. touch data is streamed across an AC97 slot ++ */ ++static int wm9705_acc_enable (struct wm97xx* wm, int enable) ++{ ++ u16 dig1, dig2; ++ int ret = 0; ++ ++ dig1 = wm->dig[1]; ++ dig2 = wm->dig[2]; ++ ++ if (enable) { ++ /* continous mode */ ++ if (wm->mach_ops->acc_startup && (ret = wm->mach_ops->acc_startup(wm)) < 0) ++ return ret; ++ dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK | ++ WM97XX_DELAY_MASK | WM97XX_SLT_MASK); ++ dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN | ++ WM97XX_DELAY (delay) | ++ WM97XX_SLT (wm->acc_slot) | ++ WM97XX_RATE (wm->acc_rate); ++ if (pil) ++ dig1 |= WM97XX_ADCSEL_PRES; ++ dig2 |= WM9705_PDEN; ++ } else { ++ dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN); ++ dig2 &= ~WM9705_PDEN; ++ if (wm->mach_ops->acc_shutdown) ++ wm->mach_ops->acc_shutdown(wm); ++ } ++ ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2); ++ return ret; ++} ++ ++struct wm97xx_codec_drv wm97xx_codec = { ++ .id = WM9705_ID2, ++ .name = "wm9705", ++ .poll_sample = wm9705_poll_sample, ++ .poll_touch = wm9705_poll_touch, ++ .acc_enable = wm9705_acc_enable, ++ .digitiser_ioctl = wm9705_digitiser_ioctl, ++}; ++ ++EXPORT_SYMBOL_GPL(wm97xx_codec); ++ ++/* Module information */ ++MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); ++MODULE_DESCRIPTION("WM9705 Touch Screen Driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/input/touchscreen/wm9712.c b/drivers/input/touchscreen/wm9712.c +new file mode 100644 +index 0000000..99433e9 +--- /dev/null ++++ b/drivers/input/touchscreen/wm9712.c +@@ -0,0 +1,464 @@ ++/* ++ * wm9712.c -- Codec driver for Wolfson WM9712 AC97 Codecs. ++ * ++ * Copyright 2003, 2004, 2005, 2006 Wolfson Microelectronics PLC. ++ * Author: Liam Girdwood ++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com ++ * Parts Copyright : Ian Molton <spyro@f2s.com> ++ * Andrew Zabolotny <zap@homelink.ru> ++ * Russell King <rmk@arm.linux.org.uk> ++ * ++ * 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. ++ * ++ * Revision history ++ * 4th Jul 2005 Initial version. ++ * 6th Sep 2006 Mike Arthur <linux@wolfsonmicro.com> ++ * Added pre and post sample calls. ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/version.h> ++#include <linux/kernel.h> ++#include <linux/input.h> ++#include <linux/delay.h> ++#include <linux/bitops.h> ++#include <linux/wm97xx.h> ++ ++#define TS_NAME "wm97xx" ++#define WM9712_VERSION "0.61" ++#define DEFAULT_PRESSURE 0xb0c0 ++ ++/* ++ * Debug ++ */ ++#if 0 ++#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg) ++#else ++#define dbg(format, arg...) ++#endif ++#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg) ++#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg) ++#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg) ++ ++/* ++ * Module parameters ++ */ ++ ++/* ++ * Set internal pull up for pen detect. ++ * ++ * Pull up is in the range 1.02k (least sensitive) to 64k (most sensitive) ++ * i.e. pull up resistance = 64k Ohms / rpu. ++ * ++ * Adjust this value if you are having problems with pen detect not ++ * detecting any down event. ++ */ ++static int rpu = 8; ++module_param(rpu, int, 0); ++MODULE_PARM_DESC(rpu, "Set internal pull up resitor for pen detect."); ++ ++/* ++ * Set current used for pressure measurement. ++ * ++ * Set pil = 2 to use 400uA ++ * pil = 1 to use 200uA and ++ * pil = 0 to disable pressure measurement. ++ * ++ * This is used to increase the range of values returned by the adc ++ * when measureing touchpanel pressure. ++ */ ++static int pil = 0; ++module_param(pil, int, 0); ++MODULE_PARM_DESC(pil, "Set current used for pressure measurement."); ++ ++/* ++ * Set threshold for pressure measurement. ++ * ++ * Pen down pressure below threshold is ignored. ++ */ ++static int pressure = DEFAULT_PRESSURE & 0xfff; ++module_param(pressure, int, 0); ++MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement."); ++ ++/* ++ * Set adc sample delay. ++ * ++ * For accurate touchpanel measurements, some settling time may be ++ * required between the switch matrix applying a voltage across the ++ * touchpanel plate and the ADC sampling the signal. ++ * ++ * This delay can be set by setting delay = n, where n is the array ++ * position of the delay in the array delay_table below. ++ * Long delays > 1ms are supported for completeness, but are not ++ * recommended. ++ */ ++static int delay = 3; ++module_param(delay, int, 0); ++MODULE_PARM_DESC(delay, "Set adc sample delay."); ++ ++/* ++ * Set five_wire = 1 to use a 5 wire touchscreen. ++ * ++ * NOTE: Five wire mode does not allow for readback of pressure. ++ */ ++static int five_wire; ++module_param(five_wire, int, 0); ++MODULE_PARM_DESC(five_wire, "Set to '1' to use 5-wire touchscreen."); ++ ++/* ++ * Set adc mask function. ++ * ++ * Sources of glitch noise, such as signals driving an LCD display, may feed ++ * through to the touch screen plates and affect measurement accuracy. In ++ * order to minimise this, a signal may be applied to the MASK pin to delay or ++ * synchronise the sampling. ++ * ++ * 0 = No delay or sync ++ * 1 = High on pin stops conversions ++ * 2 = Edge triggered, edge on pin delays conversion by delay param (above) ++ * 3 = Edge triggered, edge on pin starts conversion after delay param ++ */ ++static int mask = 0; ++module_param(mask, int, 0); ++MODULE_PARM_DESC(mask, "Set adc mask function."); ++ ++/* ++ * Coordinate Polling Enable. ++ * ++ * Set to 1 to enable coordinate polling. e.g. x,y[,p] is sampled together ++ * for every poll. ++ */ ++static int coord = 0; ++module_param(coord, int, 0); ++MODULE_PARM_DESC(coord, "Polling coordinate mode"); ++ ++/* ++ * ADC sample delay times in uS ++ */ ++static const int delay_table[] = { ++ 21, // 1 AC97 Link frames ++ 42, // 2 ++ 84, // 4 ++ 167, // 8 ++ 333, // 16 ++ 667, // 32 ++ 1000, // 48 ++ 1333, // 64 ++ 2000, // 96 ++ 2667, // 128 ++ 3333, // 160 ++ 4000, // 192 ++ 4667, // 224 ++ 5333, // 256 ++ 6000, // 288 ++ 0 // No delay, switch matrix always on ++}; ++ ++/* ++ * Delay after issuing a POLL command. ++ * ++ * The delay is 3 AC97 link frames + the touchpanel settling delay ++ */ ++static inline void poll_delay(int d) ++{ ++ udelay (3 * AC97_LINK_FRAME + delay_table [d]); ++} ++ ++/* ++ * set up the physical settings of the WM9712 ++ */ ++static void init_wm9712_phy(struct wm97xx* wm) ++{ ++ u16 dig1 = 0; ++ u16 dig2 = WM97XX_RPR | WM9712_RPU(1); ++ ++ /* WM9712 rpu */ ++ if (rpu) { ++ dig2 &= 0xffc0; ++ dig2 |= WM9712_RPU(rpu); ++ dbg("setting pen detect pull-up to %d Ohms",64000 / rpu); ++ } ++ ++ /* touchpanel pressure current*/ ++ if (pil == 2) { ++ dig2 |= WM9712_PIL; ++ dbg("setting pressure measurement current to 400uA."); ++ } else if (pil) ++ dbg("setting pressure measurement current to 200uA."); ++ if(!pil) ++ pressure = 0; ++ ++ /* WM9712 five wire */ ++ if (five_wire) { ++ dig2 |= WM9712_45W; ++ dbg("setting 5-wire touchscreen mode."); ++ } ++ ++ /* polling mode sample settling delay */ ++ if (delay < 0 || delay > 15) { ++ dbg("supplied delay out of range."); ++ delay = 4; ++ } ++ dig1 &= 0xff0f; ++ dig1 |= WM97XX_DELAY(delay); ++ dbg("setting adc sample delay to %d u Secs.", delay_table[delay]); ++ ++ /* mask */ ++ dig2 |= ((mask & 0x3) << 6); ++ if (mask) { ++ u16 reg; ++ /* Set GPIO4 as Mask Pin*/ ++ reg = wm97xx_reg_read(wm, AC97_MISC_AFE); ++ wm97xx_reg_write(wm, AC97_MISC_AFE, reg | WM97XX_GPIO_4); ++ reg = wm97xx_reg_read(wm, AC97_GPIO_CFG); ++ wm97xx_reg_write(wm, AC97_GPIO_CFG, reg | WM97XX_GPIO_4); ++ } ++ ++ /* wait - coord mode */ ++ if(coord) ++ dig2 |= WM9712_WAIT; ++ ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2); ++} ++ ++static int wm9712_digitiser_ioctl(struct wm97xx* wm, int cmd) ++{ ++ u16 dig2 = wm->dig[2]; ++ ++ switch(cmd) { ++ case WM97XX_DIG_START: ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2 | WM97XX_PRP_DET_DIG); ++ wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */ ++ break; ++ case WM97XX_DIG_STOP: ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2 & ~WM97XX_PRP_DET_DIG); ++ break; ++ case WM97XX_AUX_PREPARE: ++ memcpy(wm->dig_save, wm->dig, sizeof(wm->dig)); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, 0); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG); ++ break; ++ case WM97XX_DIG_RESTORE: ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, wm->dig_save[1]); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig_save[2]); ++ break; ++ case WM97XX_PHY_INIT: ++ init_wm9712_phy(wm); ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static inline int is_pden (struct wm97xx* wm) ++{ ++ return wm->dig[2] & WM9712_PDEN; ++} ++ ++/* ++ * Read a sample from the WM9712 adc in polling mode. ++ */ ++static int wm9712_poll_sample (struct wm97xx* wm, int adcsel, int *sample) ++{ ++ int timeout = 5 * delay; ++ ++ if (!wm->pen_probably_down) { ++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (!(data & WM97XX_PEN_DOWN)) ++ return RC_PENUP; ++ wm->pen_probably_down = 1; ++ } ++ ++ /* set up digitiser */ ++ if (adcsel & 0x8000) ++ adcsel = ((adcsel & 0x7fff) + 3) << 12; ++ ++ if (wm->mach_ops && wm->mach_ops->pre_sample) ++ wm->mach_ops->pre_sample(adcsel); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, adcsel | WM97XX_POLL | WM97XX_DELAY(delay)); ++ ++ /* wait 3 AC97 time slots + delay for conversion */ ++ poll_delay (delay); ++ ++ /* wait for POLL to go low */ ++ while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) && timeout) { ++ udelay(AC97_LINK_FRAME); ++ timeout--; ++ } ++ ++ if (timeout <= 0) { ++ /* If PDEN is set, we can get a timeout when pen goes up */ ++ if (is_pden(wm)) ++ wm->pen_probably_down = 0; ++ else ++ dbg ("adc sample timeout"); ++ return RC_PENUP; ++ } ++ ++ *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (wm->mach_ops && wm->mach_ops->post_sample) ++ wm->mach_ops->post_sample(adcsel); ++ ++ /* check we have correct sample */ ++ if ((*sample & WM97XX_ADCSEL_MASK) != adcsel) { ++ dbg ("adc wrong sample, read %x got %x", adcsel, ++ *sample & WM97XX_ADCSEL_MASK); ++ return RC_PENUP; ++ } ++ ++ if (!(*sample & WM97XX_PEN_DOWN)) { ++ wm->pen_probably_down = 0; ++ return RC_PENUP; ++ } ++ ++ return RC_VALID; ++} ++ ++/* ++ * Read a coord from the WM9712 adc in polling mode. ++ */ ++static int wm9712_poll_coord (struct wm97xx* wm, struct wm97xx_data *data) ++{ ++ int timeout = 5 * delay; ++ ++ if (!wm->pen_probably_down) { ++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (!(data & WM97XX_PEN_DOWN)) ++ return RC_PENUP; ++ wm->pen_probably_down = 1; ++ } ++ ++ /* set up digitiser */ ++ if (wm->mach_ops && wm->mach_ops->pre_sample) ++ wm->mach_ops->pre_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y); ++ ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, ++ WM97XX_COO | WM97XX_POLL | WM97XX_DELAY(delay)); ++ ++ /* wait 3 AC97 time slots + delay for conversion and read x */ ++ poll_delay(delay); ++ data->x = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ /* wait for POLL to go low */ ++ while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) && timeout) { ++ udelay(AC97_LINK_FRAME); ++ timeout--; ++ } ++ ++ if (timeout <= 0) { ++ /* If PDEN is set, we can get a timeout when pen goes up */ ++ if (is_pden(wm)) ++ wm->pen_probably_down = 0; ++ else ++ dbg ("adc sample timeout"); ++ return RC_PENUP; ++ } ++ ++ /* read back y data */ ++ data->y = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (pil) ++ data->p = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ else ++ data->p = DEFAULT_PRESSURE; ++ ++ if (wm->mach_ops && wm->mach_ops->post_sample) ++ wm->mach_ops->post_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y); ++ ++ /* check we have correct sample */ ++ if (!(data->x & WM97XX_ADCSEL_X) || !(data->y & WM97XX_ADCSEL_Y)) ++ goto err; ++ if(pil && !(data->p & WM97XX_ADCSEL_PRES)) ++ goto err; ++ ++ if (!(data->x & WM97XX_PEN_DOWN)) { ++ wm->pen_probably_down = 0; ++ return RC_PENUP; ++ } ++ return RC_VALID; ++err: ++ return RC_PENUP; ++} ++ ++/* ++ * Sample the WM9712 touchscreen in polling mode ++ */ ++static int wm9712_poll_touch(struct wm97xx* wm, struct wm97xx_data *data) ++{ ++ int rc; ++ ++ if(coord) { ++ if((rc = wm9712_poll_coord(wm, data)) != RC_VALID) ++ return rc; ++ } else { ++ if ((rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_X, &data->x)) != RC_VALID) ++ return rc; ++ ++ if ((rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_Y, &data->y)) != RC_VALID) ++ return rc; ++ ++ if (pil && !five_wire) { ++ if ((rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_PRES, &data->p)) != RC_VALID) ++ return rc; ++ } else ++ data->p = DEFAULT_PRESSURE; ++ } ++ return RC_VALID; ++} ++ ++/* ++ * Enable WM9712 continuous mode, i.e. touch data is streamed across an AC97 slot ++ */ ++static int wm9712_acc_enable (struct wm97xx* wm, int enable) ++{ ++ u16 dig1, dig2; ++ int ret = 0; ++ ++ dig1 = wm->dig[1]; ++ dig2 = wm->dig[2]; ++ ++ if (enable) { ++ /* continous mode */ ++ if (wm->mach_ops->acc_startup && (ret = wm->mach_ops->acc_startup(wm)) < 0) ++ return ret; ++ dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK | ++ WM97XX_DELAY_MASK | WM97XX_SLT_MASK); ++ dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN | ++ WM97XX_DELAY (delay) | ++ WM97XX_SLT (wm->acc_slot) | ++ WM97XX_RATE (wm->acc_rate); ++ if (pil) ++ dig1 |= WM97XX_ADCSEL_PRES; ++ dig2 |= WM9712_PDEN; ++ } else { ++ dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN); ++ dig2 &= ~WM9712_PDEN; ++ if (wm->mach_ops->acc_shutdown) ++ wm->mach_ops->acc_shutdown(wm); ++ } ++ ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2); ++ return 0; ++} ++ ++struct wm97xx_codec_drv wm97xx_codec = { ++ .id = WM9712_ID2, ++ .name = "wm9712", ++ .poll_sample = wm9712_poll_sample, ++ .poll_touch = wm9712_poll_touch, ++ .acc_enable = wm9712_acc_enable, ++ .digitiser_ioctl = wm9712_digitiser_ioctl, ++}; ++ ++EXPORT_SYMBOL_GPL(wm97xx_codec); ++ ++/* Module information */ ++MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); ++MODULE_DESCRIPTION("WM9712 Touch Screen Driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/input/touchscreen/wm9713.c b/drivers/input/touchscreen/wm9713.c +new file mode 100644 +index 0000000..42f5f30 +--- /dev/null ++++ b/drivers/input/touchscreen/wm9713.c +@@ -0,0 +1,461 @@ ++/* ++ * wm9713.c -- Codec touch driver for Wolfson WM9713 AC97 Codec. ++ * ++ * Copyright 2003, 2004, 2005, 2006 Wolfson Microelectronics PLC. ++ * Author: Liam Girdwood ++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com ++ * Parts Copyright : Ian Molton <spyro@f2s.com> ++ * Andrew Zabolotny <zap@homelink.ru> ++ * Russell King <rmk@arm.linux.org.uk> ++ * ++ * 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. ++ * ++ * Revision history ++ * 6th Sep 2006 Mike Arthur <linux@wolfsonmicro.com> ++ * Added pre and post sample calls. ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/version.h> ++#include <linux/kernel.h> ++#include <linux/input.h> ++#include <linux/delay.h> ++#include <linux/bitops.h> ++#include <linux/wm97xx.h> ++ ++#define TS_NAME "wm97xx" ++#define WM9713_VERSION "0.53" ++#define DEFAULT_PRESSURE 0xb0c0 ++ ++/* ++ * Debug ++ */ ++#if 0 ++#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg) ++#else ++#define dbg(format, arg...) ++#endif ++#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg) ++#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg) ++#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg) ++ ++/* ++ * Module parameters ++ */ ++ ++/* ++ * Set internal pull up for pen detect. ++ * ++ * Pull up is in the range 1.02k (least sensitive) to 64k (most sensitive) ++ * i.e. pull up resistance = 64k Ohms / rpu. ++ * ++ * Adjust this value if you are having problems with pen detect not ++ * detecting any down event. ++ */ ++static int rpu = 8; ++module_param(rpu, int, 0); ++MODULE_PARM_DESC(rpu, "Set internal pull up resitor for pen detect."); ++ ++/* ++ * Set current used for pressure measurement. ++ * ++ * Set pil = 2 to use 400uA ++ * pil = 1 to use 200uA and ++ * pil = 0 to disable pressure measurement. ++ * ++ * This is used to increase the range of values returned by the adc ++ * when measureing touchpanel pressure. ++ */ ++static int pil = 0; ++module_param(pil, int, 0); ++MODULE_PARM_DESC(pil, "Set current used for pressure measurement."); ++ ++/* ++ * Set threshold for pressure measurement. ++ * ++ * Pen down pressure below threshold is ignored. ++ */ ++static int pressure = DEFAULT_PRESSURE & 0xfff; ++module_param(pressure, int, 0); ++MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement."); ++ ++/* ++ * Set adc sample delay. ++ * ++ * For accurate touchpanel measurements, some settling time may be ++ * required between the switch matrix applying a voltage across the ++ * touchpanel plate and the ADC sampling the signal. ++ * ++ * This delay can be set by setting delay = n, where n is the array ++ * position of the delay in the array delay_table below. ++ * Long delays > 1ms are supported for completeness, but are not ++ * recommended. ++ */ ++static int delay = 4; ++module_param(delay, int, 0); ++MODULE_PARM_DESC(delay, "Set adc sample delay."); ++ ++/* ++ * Set adc mask function. ++ * ++ * Sources of glitch noise, such as signals driving an LCD display, may feed ++ * through to the touch screen plates and affect measurement accuracy. In ++ * order to minimise this, a signal may be applied to the MASK pin to delay or ++ * synchronise the sampling. ++ * ++ * 0 = No delay or sync ++ * 1 = High on pin stops conversions ++ * 2 = Edge triggered, edge on pin delays conversion by delay param (above) ++ * 3 = Edge triggered, edge on pin starts conversion after delay param ++ */ ++static int mask = 0; ++module_param(mask, int, 0); ++MODULE_PARM_DESC(mask, "Set adc mask function."); ++ ++/* ++ * Coordinate Polling Enable. ++ * ++ * Set to 1 to enable coordinate polling. e.g. x,y[,p] is sampled together ++ * for every poll. ++ */ ++static int coord = 0; ++module_param(coord, int, 0); ++MODULE_PARM_DESC(coord, "Polling coordinate mode"); ++ ++/* ++ * ADC sample delay times in uS ++ */ ++static const int delay_table[] = { ++ 21, // 1 AC97 Link frames ++ 42, // 2 ++ 84, // 4 ++ 167, // 8 ++ 333, // 16 ++ 667, // 32 ++ 1000, // 48 ++ 1333, // 64 ++ 2000, // 96 ++ 2667, // 128 ++ 3333, // 160 ++ 4000, // 192 ++ 4667, // 224 ++ 5333, // 256 ++ 6000, // 288 ++ 0 // No delay, switch matrix always on ++}; ++ ++/* ++ * Delay after issuing a POLL command. ++ * ++ * The delay is 3 AC97 link frames + the touchpanel settling delay ++ */ ++static inline void poll_delay(int d) ++{ ++ udelay (3 * AC97_LINK_FRAME + delay_table [d]); ++} ++ ++/* ++ * set up the physical settings of the WM9713 ++ */ ++static void init_wm9713_phy(struct wm97xx* wm) ++{ ++ u16 dig1 = 0, dig2, dig3; ++ ++ /* default values */ ++ dig2 = WM97XX_DELAY(4) | WM97XX_SLT(5); ++ dig3= WM9712_RPU(1); ++ ++ /* rpu */ ++ if (rpu) { ++ dig3 &= 0xffc0; ++ dig3 |= WM9712_RPU(rpu); ++ info("setting pen detect pull-up to %d Ohms",64000 / rpu); ++ } ++ ++ /* touchpanel pressure */ ++ if (pil == 2) { ++ dig3 |= WM9712_PIL; ++ info("setting pressure measurement current to 400uA."); ++ } else if (pil) ++ info ("setting pressure measurement current to 200uA."); ++ if(!pil) ++ pressure = 0; ++ ++ /* sample settling delay */ ++ if (delay < 0 || delay > 15) { ++ info ("supplied delay out of range."); ++ delay = 4; ++ info("setting adc sample delay to %d u Secs.", delay_table[delay]); ++ } ++ dig2 &= 0xff0f; ++ dig2 |= WM97XX_DELAY(delay); ++ ++ /* mask */ ++ dig3 |= ((mask & 0x3) << 4); ++ if(coord) ++ dig3 |= WM9713_WAIT; ++ ++ wm->misc = wm97xx_reg_read(wm, 0x5a); ++ ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, dig2); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, dig3); ++ wm97xx_reg_write(wm, AC97_GPIO_STICKY, 0x0); ++} ++ ++static int wm9713_digitiser_ioctl(struct wm97xx* wm, int cmd) ++{ ++ u16 val = 0; ++ ++ switch(cmd){ ++ case WM97XX_DIG_START: ++ val = wm97xx_reg_read(wm, AC97_EXTENDED_MID); ++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, val & 0x7fff); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2] | WM97XX_PRP_DET_DIG); ++ wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */ ++ break; ++ case WM97XX_DIG_STOP: ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2] & ~WM97XX_PRP_DET_DIG); ++ val = wm97xx_reg_read(wm, AC97_EXTENDED_MID); ++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, val | 0x8000); ++ break; ++ case WM97XX_AUX_PREPARE: ++ memcpy(wm->dig_save, wm->dig, sizeof(wm->dig)); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, 0); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, 0); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, WM97XX_PRP_DET_DIG); ++ break; ++ case WM97XX_DIG_RESTORE: ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, wm->dig_save[0]); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, wm->dig_save[1]); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig_save[2]); ++ break; ++ case WM97XX_PHY_INIT: ++ init_wm9713_phy(wm); ++ break; ++ default: ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++static inline int is_pden (struct wm97xx* wm) ++{ ++ return wm->dig[2] & WM9713_PDEN; ++} ++ ++/* ++ * Read a sample from the WM9713 adc in polling mode. ++ */ ++static int wm9713_poll_sample (struct wm97xx* wm, int adcsel, int *sample) ++{ ++ u16 dig1; ++ int timeout = 5 * delay; ++ ++ if (!wm->pen_probably_down) { ++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (!(data & WM97XX_PEN_DOWN)) ++ return RC_PENUP; ++ wm->pen_probably_down = 1; ++ } ++ ++ /* set up digitiser */ ++ if (adcsel & 0x8000) ++ adcsel = 1 << ((adcsel & 0x7fff) + 3); ++ ++ dig1 = wm97xx_reg_read(wm, AC97_WM9713_DIG1); ++ dig1 &= ~WM9713_ADCSEL_MASK; ++ ++ if (wm->mach_ops && wm->mach_ops->pre_sample) ++ wm->mach_ops->pre_sample(adcsel); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1 | adcsel |WM9713_POLL); ++ ++ /* wait 3 AC97 time slots + delay for conversion */ ++ poll_delay(delay); ++ ++ /* wait for POLL to go low */ ++ while ((wm97xx_reg_read(wm, AC97_WM9713_DIG1) & WM9713_POLL) && timeout) { ++ udelay(AC97_LINK_FRAME); ++ timeout--; ++ } ++ ++ if (timeout <= 0) { ++ /* If PDEN is set, we can get a timeout when pen goes up */ ++ if (is_pden(wm)) ++ wm->pen_probably_down = 0; ++ else ++ dbg ("adc sample timeout"); ++ return RC_PENUP; ++ } ++ ++ *sample =wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (wm->mach_ops && wm->mach_ops->post_sample) ++ wm->mach_ops->post_sample(adcsel); ++ ++ /* check we have correct sample */ ++ if ((*sample & WM97XX_ADCSRC_MASK) != ffs(adcsel >> 1) << 12) { ++ dbg ("adc wrong sample, read %x got %x", adcsel, ++ *sample & WM97XX_ADCSRC_MASK); ++ return RC_PENUP; ++ } ++ ++ if (!(*sample & WM97XX_PEN_DOWN)) { ++ wm->pen_probably_down = 0; ++ return RC_PENUP; ++ } ++ ++ return RC_VALID; ++} ++ ++/* ++ * Read a coordinate from the WM9713 adc in polling mode. ++ */ ++static int wm9713_poll_coord (struct wm97xx* wm, struct wm97xx_data *data) ++{ ++ u16 dig1; ++ int timeout = 5 * delay; ++ ++ if (!wm->pen_probably_down) { ++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (!(data & WM97XX_PEN_DOWN)) ++ return RC_PENUP; ++ wm->pen_probably_down = 1; ++ } ++ ++ /* set up digitiser */ ++ dig1 = wm97xx_reg_read(wm, AC97_WM9713_DIG1); ++ dig1 &= ~WM9713_ADCSEL_MASK; ++ if(pil) ++ dig1 |= WM97XX_ADCSEL_PRES; ++ ++ if (wm->mach_ops && wm->mach_ops->pre_sample) ++ wm->mach_ops->pre_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1 | WM9713_POLL | WM9713_COO); ++ ++ /* wait 3 AC97 time slots + delay for conversion */ ++ poll_delay(delay); ++ data->x = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ /* wait for POLL to go low */ ++ while ((wm97xx_reg_read(wm, AC97_WM9713_DIG1) & WM9713_POLL) && timeout) { ++ udelay(AC97_LINK_FRAME); ++ timeout--; ++ } ++ ++ if (timeout <= 0) { ++ /* If PDEN is set, we can get a timeout when pen goes up */ ++ if (is_pden(wm)) ++ wm->pen_probably_down = 0; ++ else ++ dbg ("adc sample timeout"); ++ return RC_PENUP; ++ } ++ ++ /* read back data */ ++ data->y = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (pil) ++ data->p = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ else ++ data->p = DEFAULT_PRESSURE; ++ ++ if (wm->mach_ops && wm->mach_ops->post_sample) ++ wm->mach_ops->post_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y); ++ ++ /* check we have correct sample */ ++ if (!(data->x & WM97XX_ADCSEL_X) || !(data->y & WM97XX_ADCSEL_Y)) ++ goto err; ++ if(pil && !(data->p & WM97XX_ADCSEL_PRES)) ++ goto err; ++ ++ if (!(data->x & WM97XX_PEN_DOWN)) { ++ wm->pen_probably_down = 0; ++ return RC_PENUP; ++ } ++ return RC_VALID; ++err: ++ return RC_PENUP; ++} ++ ++/* ++ * Sample the WM9713 touchscreen in polling mode ++ */ ++static int wm9713_poll_touch(struct wm97xx* wm, struct wm97xx_data *data) ++{ ++ int rc; ++ ++ if(coord) { ++ if((rc = wm9713_poll_coord(wm, data)) != RC_VALID) ++ return rc; ++ } else { ++ if ((rc = wm9713_poll_sample(wm, WM9713_ADCSEL_X, &data->x)) != RC_VALID) ++ return rc; ++ if ((rc = wm9713_poll_sample(wm, WM9713_ADCSEL_Y, &data->y)) != RC_VALID) ++ return rc; ++ if (pil) { ++ if ((rc = wm9713_poll_sample(wm, WM9713_ADCSEL_PRES, &data->p)) != RC_VALID) ++ return rc; ++ } else ++ data->p = DEFAULT_PRESSURE; ++ } ++ return RC_VALID; ++} ++ ++/* ++ * Enable WM9713 continuous mode, i.e. touch data is streamed across an AC97 slot ++ */ ++static int wm9713_acc_enable (struct wm97xx* wm, int enable) ++{ ++ u16 dig1, dig2, dig3; ++ int ret = 0; ++ ++ dig1 = wm->dig[0]; ++ dig2 = wm->dig[1]; ++ dig3 = wm->dig[2]; ++ ++ if (enable) { ++ /* continous mode */ ++ if (wm->mach_ops->acc_startup && ++ (ret = wm->mach_ops->acc_startup(wm)) < 0) ++ return ret; ++ ++ dig1 &= ~WM9713_ADCSEL_MASK; ++ dig1 |= WM9713_CTC | WM9713_COO | WM9713_ADCSEL_X | WM9713_ADCSEL_Y; ++ if (pil) ++ dig1 |= WM9713_ADCSEL_PRES; ++ dig2 &= ~(WM97XX_DELAY_MASK | WM97XX_SLT_MASK | WM97XX_CM_RATE_MASK); ++ dig2 |= WM97XX_SLEN | WM97XX_DELAY (delay) | ++ WM97XX_SLT (wm->acc_slot) | WM97XX_RATE (wm->acc_rate); ++ dig3 |= WM9713_PDEN; ++ } else { ++ dig1 &= ~(WM9713_CTC | WM9713_COO); ++ dig2 &= ~WM97XX_SLEN; ++ dig3 &= ~WM9713_PDEN; ++ if (wm->mach_ops->acc_shutdown) ++ wm->mach_ops->acc_shutdown(wm); ++ } ++ ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, dig2); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, dig3); ++ return ret; ++} ++ ++struct wm97xx_codec_drv wm97xx_codec = { ++ .id = WM9713_ID2, ++ .name = "wm9713", ++ .poll_sample = wm9713_poll_sample, ++ .poll_touch = wm9713_poll_touch, ++ .acc_enable = wm9713_acc_enable, ++ .digitiser_ioctl = wm9713_digitiser_ioctl, ++}; ++ ++EXPORT_SYMBOL_GPL(wm97xx_codec); ++ ++/* Module information */ ++MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); ++MODULE_DESCRIPTION("WM9713 Touch Screen Driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c +new file mode 100644 +index 0000000..87179d2 +--- /dev/null ++++ b/drivers/input/touchscreen/wm97xx-core.c +@@ -0,0 +1,859 @@ ++/* ++ * wm97xx-core.c -- Touch screen driver core for Wolfson WM9705, WM9712 ++ * and WM9713 AC97 Codecs. ++ * ++ * Copyright 2003, 2004, 2005, 2006 Wolfson Microelectronics PLC. ++ * Author: Liam Girdwood ++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com ++ * Parts Copyright : Ian Molton <spyro@f2s.com> ++ * Andrew Zabolotny <zap@homelink.ru> ++ * Russell King <rmk@arm.linux.org.uk> ++ * ++ * 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. ++ * ++ * Notes: ++ * ++ * Features: ++ * - supports WM9705, WM9712, WM9713 ++ * - polling mode ++ * - continuous mode (arch-dependent) ++ * - adjustable rpu/dpp settings ++ * - adjustable pressure current ++ * - adjustable sample settle delay ++ * - 4 and 5 wire touchscreens (5 wire is WM9712 only) ++ * - pen down detection ++ * - battery monitor ++ * - sample AUX adc's ++ * - power management ++ * - codec GPIO ++ * - codec event notification ++ * Todo ++ * - Support for async sampling control for noisy LCD's. ++ * ++ * Revision history ++ * 7th May 2003 Initial version. ++ * 6th June 2003 Added non module support and AC97 registration. ++ * 18th June 2003 Added AUX adc sampling. ++ * 23rd June 2003 Did some minimal reformatting, fixed a couple of ++ * codec_mutexing bugs and noted a race to fix. ++ * 24th June 2003 Added power management and fixed race condition. ++ * 10th July 2003 Changed to a misc device. ++ * 31st July 2003 Moved TS_EVENT and TS_CAL to wm97xx.h ++ * 8th Aug 2003 Added option for read() calling wm97xx_sample_touch() ++ * because some ac97_read/ac_97_write call schedule() ++ * 7th Nov 2003 Added Input touch event interface, stanley.cai@intel.com ++ * 13th Nov 2003 Removed h3600 touch interface, added interrupt based ++ * pen down notification and implemented continous mode ++ * on XScale arch. ++ * 16th Nov 2003 Ian Molton <spyro@f2s.com> ++ * Modified so that it suits the new 2.6 driver model. ++ * 25th Jan 2004 Andrew Zabolotny <zap@homelink.ru> ++ * Implemented IRQ-driven pen down detection, implemented ++ * the private API meant to be exposed to platform-specific ++ * drivers, reorganized the driver so that it supports ++ * an arbitrary number of devices. ++ * 1st Feb 2004 Moved continuous mode handling to a separate ++ * architecture-dependent file. For now only PXA ++ * built-in AC97 controller is supported (pxa-ac97-wm97xx.c). ++ * 11th Feb 2004 Reduced CPU usage by keeping a cached copy of both ++ * digitizer registers instead of reading them every time. ++ * A reorganization of the whole code for better ++ * error handling. ++ * 17th Apr 2004 Added BMON support. ++ * 17th Nov 2004 Added codec GPIO, codec event handling (real and virtual ++ * GPIOs) and 2.6 power management. ++ * 29th Nov 2004 Added WM9713 support. ++ * 4th Jul 2005 Moved codec specific code out to seperate files. ++ * 6th Sep 2006 Mike Arthur <linux@wolfsonmicro.com> ++ * Added bus interface. ++ */ ++ ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/version.h> ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/delay.h> ++#include <linux/string.h> ++#include <linux/proc_fs.h> ++#include <linux/pm.h> ++#include <linux/interrupt.h> ++#include <linux/bitops.h> ++#include <linux/workqueue.h> ++#include <linux/device.h> ++#include <linux/freezer.h> ++#include <linux/wm97xx.h> ++#include <asm/uaccess.h> ++#include <asm/io.h> ++ ++#include <asm/arch/irqs.h> ++ ++#define TS_NAME "wm97xx" ++#define WM_CORE_VERSION "0.64" ++#define DEFAULT_PRESSURE 0xb0c0 ++ ++ ++/* ++ * Touchscreen absolute values ++ * ++ * These parameters are used to help the input layer discard out of ++ * range readings and reduce jitter etc. ++ * ++ * o min, max:- indicate the min and max values your touch screen returns ++ * o fuzz:- use a higher number to reduce jitter ++ * ++ * The default values correspond to Mainstone II in QVGA mode ++ * ++ * Please read ++ * Documentation/input/input-programming.txt for more details. ++ */ ++ ++static int abs_x[3] = {350,3900,5}; ++module_param_array(abs_x, int, NULL, 0); ++MODULE_PARM_DESC(abs_x, "Touchscreen absolute X min, max, fuzz"); ++ ++static int abs_y[3] = {320,3750,40}; ++module_param_array(abs_y, int, NULL, 0); ++MODULE_PARM_DESC(abs_y, "Touchscreen absolute Y min, max, fuzz"); ++ ++static int abs_p[3] = {0,150,4}; ++module_param_array(abs_p, int, NULL, 0); ++MODULE_PARM_DESC(abs_p, "Touchscreen absolute Pressure min, max, fuzz"); ++ ++/* ++ * Debug ++ */ ++#if 0 ++#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg) ++#else ++#define dbg(format, arg...) ++#endif ++#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg) ++#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg) ++#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg) ++ ++/* ++ * wm97xx IO access, all IO locking done by AC97 layer ++ */ ++int wm97xx_reg_read(struct wm97xx *wm, u16 reg) ++{ ++ if (wm->ac97) ++ return wm->ac97->bus->ops->read(wm->ac97, reg); ++ else ++ return -1; ++} ++EXPORT_SYMBOL_GPL(wm97xx_reg_read); ++ ++void wm97xx_reg_write(struct wm97xx *wm, u16 reg, u16 val) ++{ ++ /* cache digitiser registers */ ++ if(reg >= AC97_WM9713_DIG1 && reg <= AC97_WM9713_DIG3) ++ wm->dig[(reg - AC97_WM9713_DIG1) >> 1] = val; ++ ++ /* cache gpio regs */ ++ if(reg >= AC97_GPIO_CFG && reg <= AC97_MISC_AFE) ++ wm->gpio[(reg - AC97_GPIO_CFG) >> 1] = val; ++ ++ /* wm9713 irq reg */ ++ if(reg == 0x5a) ++ wm->misc = val; ++ ++ if (wm->ac97) ++ wm->ac97->bus->ops->write(wm->ac97, reg, val); ++} ++EXPORT_SYMBOL_GPL(wm97xx_reg_write); ++ ++/** ++ * wm97xx_read_aux_adc - Read the aux adc. ++ * @wm: wm97xx device. ++ * @adcsel: codec ADC to be read ++ * ++ * Reads the selected AUX ADC. ++ */ ++ ++int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel) ++{ ++ int power_adc = 0, auxval; ++ u16 power = 0; ++ ++ /* get codec */ ++ mutex_lock(&wm->codec_mutex); ++ ++ /* When the touchscreen is not in use, we may have to power up the AUX ADC ++ * before we can use sample the AUX inputs-> ++ */ ++ if (wm->id == WM9713_ID2 && ++ (power = wm97xx_reg_read(wm, AC97_EXTENDED_MID)) & 0x8000) { ++ power_adc = 1; ++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, power & 0x7fff); ++ } ++ ++ /* Prepare the codec for AUX reading */ ++ wm->codec->digitiser_ioctl(wm, WM97XX_AUX_PREPARE); ++ ++ /* Turn polling mode on to read AUX ADC */ ++ wm->pen_probably_down = 1; ++ wm->codec->poll_sample(wm, adcsel, &auxval); ++ ++ if (power_adc) ++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, power | 0x8000); ++ ++ wm->codec->digitiser_ioctl(wm, WM97XX_DIG_RESTORE); ++ ++ wm->pen_probably_down = 0; ++ ++ mutex_unlock(&wm->codec_mutex); ++ return auxval & 0xfff; ++} ++EXPORT_SYMBOL_GPL(wm97xx_read_aux_adc); ++ ++/** ++ * wm97xx_get_gpio - Get the status of a codec GPIO. ++ * @wm: wm97xx device. ++ * @gpio: gpio ++ * ++ * Get the status of a codec GPIO pin ++ */ ++ ++wm97xx_gpio_status_t wm97xx_get_gpio(struct wm97xx *wm, u32 gpio) ++{ ++ u16 status; ++ wm97xx_gpio_status_t ret; ++ ++ mutex_lock(&wm->codec_mutex); ++ status = wm97xx_reg_read(wm, AC97_GPIO_STATUS); ++ ++ if (status & gpio) ++ ret = WM97XX_GPIO_HIGH; ++ else ++ ret = WM97XX_GPIO_LOW; ++ ++ mutex_unlock(&wm->codec_mutex); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(wm97xx_get_gpio); ++ ++/** ++ * wm97xx_set_gpio - Set the status of a codec GPIO. ++ * @wm: wm97xx device. ++ * @gpio: gpio ++ * ++ * ++ * Set the status of a codec GPIO pin ++ */ ++ ++void wm97xx_set_gpio(struct wm97xx *wm, u32 gpio, ++ wm97xx_gpio_status_t status) ++{ ++ u16 reg; ++ ++ mutex_lock(&wm->codec_mutex); ++ reg = wm97xx_reg_read(wm, AC97_GPIO_STATUS); ++ ++ if (status & WM97XX_GPIO_HIGH) ++ reg |= gpio; ++ else ++ reg &= ~gpio; ++ ++ if (wm->id == WM9712_ID2) ++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg << 1); ++ else ++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg); ++ mutex_unlock(&wm->codec_mutex); ++} ++EXPORT_SYMBOL_GPL(wm97xx_set_gpio); ++ ++/* ++ * Codec GPIO pin configuration, this set's pin direction, polarity, ++ * stickyness and wake up. ++ */ ++void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio, wm97xx_gpio_dir_t dir, ++ wm97xx_gpio_pol_t pol, wm97xx_gpio_sticky_t sticky, ++ wm97xx_gpio_wake_t wake) ++{ ++ u16 reg; ++ ++ mutex_lock(&wm->codec_mutex); ++ reg = wm97xx_reg_read(wm, AC97_GPIO_POLARITY); ++ ++ if (pol == WM97XX_GPIO_POL_HIGH) ++ reg |= gpio; ++ else ++ reg &= ~gpio; ++ ++ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, reg); ++ reg = wm97xx_reg_read(wm, AC97_GPIO_STICKY); ++ ++ if (sticky == WM97XX_GPIO_STICKY) ++ reg |= gpio; ++ else ++ reg &= ~gpio; ++ ++ wm97xx_reg_write(wm, AC97_GPIO_STICKY, reg); ++ reg = wm97xx_reg_read(wm, AC97_GPIO_WAKEUP); ++ ++ if (wake == WM97XX_GPIO_WAKE) ++ reg |= gpio; ++ else ++ reg &= ~gpio; ++ ++ wm97xx_reg_write(wm, AC97_GPIO_WAKEUP, reg); ++ reg = wm97xx_reg_read(wm, AC97_GPIO_CFG); ++ ++ if (dir == WM97XX_GPIO_IN) ++ reg |= gpio; ++ else ++ reg &= ~gpio; ++ ++ wm97xx_reg_write(wm, AC97_GPIO_CFG, reg); ++ mutex_unlock(&wm->codec_mutex); ++} ++EXPORT_SYMBOL_GPL(wm97xx_config_gpio); ++ ++/* ++ * Handle a pen down interrupt. ++ */ ++static void wm97xx_pen_irq_worker(struct work_struct *w) ++{ ++/* struct wm97xx *wm = (struct wm97xx *) ptr; */ ++ struct wm97xx *wm = container_of(w, struct wm97xx, pen_event_work); ++ ++ /* do we need to enable the touch panel reader */ ++ if (wm->id == WM9705_ID2) { ++ if (wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD) & WM97XX_PEN_DOWN) ++ wm->pen_is_down = 1; ++ else ++ wm->pen_is_down = 0; ++ wake_up_interruptible(&wm->pen_irq_wait); ++ } else { ++ u16 status, pol; ++ mutex_lock(&wm->codec_mutex); ++ status = wm97xx_reg_read(wm, AC97_GPIO_STATUS); ++ pol = wm97xx_reg_read(wm, AC97_GPIO_POLARITY); ++ ++ if (WM97XX_GPIO_13 & pol & status) { ++ wm->pen_is_down = 1; ++ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, pol & ~WM97XX_GPIO_13); ++ } else { ++ wm->pen_is_down = 0; ++ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, pol | WM97XX_GPIO_13); ++ } ++ ++ if (wm->id == WM9712_ID2) ++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, (status & ~WM97XX_GPIO_13) << 1); ++ else ++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, status & ~WM97XX_GPIO_13); ++ mutex_unlock(&wm->codec_mutex); ++ wake_up_interruptible(&wm->pen_irq_wait); ++ } ++ ++ if (!wm->pen_is_down && wm->mach_ops && wm->mach_ops->acc_enabled) ++ wm->mach_ops->acc_pen_up(wm); ++ enable_irq(wm->pen_irq); ++} ++ ++/* ++ * Codec PENDOWN irq handler ++ * ++ * We have to disable the codec interrupt in the handler because it can ++ * take upto 1ms to clear the interrupt source. The interrupt is then enabled ++ * again in the slow handler when the source has been cleared. ++ */ ++static irqreturn_t wm97xx_pen_interrupt(int irq, void *dev_id) ++{ ++ struct wm97xx *wm = (struct wm97xx *) dev_id; ++ disable_irq(wm->pen_irq); ++ queue_work(wm->pen_irq_workq, &wm->pen_event_work); ++ return IRQ_HANDLED; ++} ++ ++/* ++ * initialise pen IRQ handler and workqueue ++ */ ++static int wm97xx_init_pen_irq(struct wm97xx *wm) ++{ ++ u16 reg; ++ ++ INIT_WORK(&wm->pen_event_work, wm97xx_pen_irq_worker/* , wm */); ++ if ((wm->pen_irq_workq = ++ create_singlethread_workqueue("kwm97pen")) == NULL) { ++ err("could not create pen irq work queue"); ++ wm->pen_irq = 0; ++ return -EINVAL; ++ } ++ ++ ++ wm->pen_irq = IRQ_GPIO(90); ++ if (request_irq (wm->pen_irq, wm97xx_pen_interrupt, SA_SHIRQ, "wm97xx-pen", wm)) { ++ err("could not register codec pen down interrupt, will poll for pen down"); ++ destroy_workqueue(wm->pen_irq_workq); ++ wm->pen_irq = 0; ++ return -EINVAL; ++ } ++ ++ /* enable PEN down on wm9712/13 */ ++ if (wm->id != WM9705_ID2) { ++ reg = wm97xx_reg_read(wm, AC97_MISC_AFE); ++ wm97xx_reg_write(wm, AC97_MISC_AFE, reg & 0xfffb); ++ reg = wm97xx_reg_read(wm, 0x5a); ++ wm97xx_reg_write(wm, 0x5a, reg & ~0x0001); ++ } ++ ++ return 0; ++} ++ ++/* Private struct for communication between struct wm97xx_tshread ++ * and wm97xx_read_samples */ ++struct ts_state { ++ int sleep_time; ++ int min_sleep_time; ++}; ++ ++static int wm97xx_read_samples(struct wm97xx *wm, struct ts_state *state) ++{ ++ struct wm97xx_data data; ++ int rc; ++ ++ mutex_lock(&wm->codec_mutex); ++ ++ if (wm->mach_ops && wm->mach_ops->acc_enabled) ++ rc = wm->mach_ops->acc_pen_down(wm); ++ else ++ rc = wm->codec->poll_touch(wm, &data); ++ ++ if (rc & RC_PENUP) { ++ if (wm->pen_is_down) { ++ wm->pen_is_down = 0; ++ dbg("pen up"); ++ input_report_abs(wm->input_dev, ABS_PRESSURE, 0); ++ input_sync(wm->input_dev); ++ } else if (!(rc & RC_AGAIN)) { ++ /* We need high frequency updates only while pen is down, ++ * the user never will be able to touch screen faster than ++ * a few times per second... On the other hand, when the ++ * user is actively working with the touchscreen we don't ++ * want to lose the quick response. So we will slowly ++ * increase sleep time after the pen is up and quicky ++ * restore it to ~one task switch when pen is down again. ++ */ ++ if (state->sleep_time < HZ / 10) ++ state->sleep_time++; ++ } ++ ++ } else if (rc & RC_VALID) { ++ dbg("pen down: x=%x:%d, y=%x:%d, pressure=%x:%d\n", ++ data.x >> 12, data.x & 0xfff, data.y >> 12, ++ data.y & 0xfff, data.p >> 12, data.p & 0xfff); ++ input_report_abs(wm->input_dev, ABS_X, data.x & 0xfff); ++ input_report_abs(wm->input_dev, ABS_Y, data.y & 0xfff); ++ input_report_abs(wm->input_dev, ABS_PRESSURE, data.p & 0xfff); ++ input_sync(wm->input_dev); ++ wm->pen_is_down = 1; ++ state->sleep_time = state->min_sleep_time; ++ } else if (rc & RC_PENDOWN) { ++ dbg("pen down"); ++ wm->pen_is_down = 1; ++ state->sleep_time = state->min_sleep_time; ++ } ++ ++ mutex_unlock(&wm->codec_mutex); ++ return rc; ++} ++ ++/* ++* The touchscreen sample reader thread. ++*/ ++static int wm97xx_ts_read(void *data) ++{ ++ int rc; ++ struct ts_state state; ++ struct wm97xx *wm = (struct wm97xx *) data; ++ ++ /* set up thread context */ ++ wm->ts_task = current; ++ daemonize("kwm97xxts"); ++ ++ if (wm->codec == NULL) { ++ wm->ts_task = NULL; ++ printk(KERN_ERR "codec is NULL, bailing\n"); ++ } ++ ++ complete(&wm->ts_init); ++ wm->pen_is_down = 0; ++ state.min_sleep_time = HZ >= 100 ? HZ / 100 : 1; ++ if (state.min_sleep_time < 1) ++ state.min_sleep_time = 1; ++ state.sleep_time = state.min_sleep_time; ++ ++ /* touch reader loop */ ++ while (wm->ts_task) { ++ do { ++ try_to_freeze(); ++ rc = wm97xx_read_samples(wm, &state); ++ } while (rc & RC_AGAIN); ++ if (!wm->pen_is_down && wm->pen_irq) { ++ /* Nice, we don't have to poll for pen down event */ ++ wait_event_interruptible(wm->pen_irq_wait, wm->pen_is_down); ++ } else { ++ set_task_state(current, TASK_INTERRUPTIBLE); ++ schedule_timeout(state.sleep_time); ++ } ++ } ++ complete_and_exit(&wm->ts_exit, 0); ++} ++ ++/** ++ * wm97xx_ts_input_open - Open the touch screen input device. ++ * @idev: Input device to be opened. ++ * ++ * Called by the input sub system to open a wm97xx touchscreen device. ++ * Starts the touchscreen thread and touch digitiser. ++ */ ++static int wm97xx_ts_input_open(struct input_dev *idev) ++{ ++ int ret = 0; ++ struct wm97xx *wm = (struct wm97xx *) idev->private; ++ ++ mutex_lock(&wm->codec_mutex); ++ /* first time opened ? */ ++ if (wm->ts_use_count++ == 0) { ++ /* start touchscreen thread */ ++ init_completion(&wm->ts_init); ++ init_completion(&wm->ts_exit); ++ ret = kernel_thread(wm97xx_ts_read, wm, CLONE_KERNEL); ++ ++ if (ret >= 0) { ++ wait_for_completion(&wm->ts_init); ++ if (wm->ts_task == NULL) ++ ret = -EINVAL; ++ } else { ++ mutex_unlock(&wm->codec_mutex); ++ return ret; ++ } ++ ++ /* start digitiser */ ++ if (wm->mach_ops && wm->mach_ops->acc_enabled) ++ wm->codec->acc_enable(wm, 1); ++ wm->codec->digitiser_ioctl(wm, WM97XX_DIG_START); ++ ++ /* init pen down/up irq handling */ ++ if (wm->pen_irq) { ++ wm97xx_init_pen_irq(wm); ++ ++ if (wm->pen_irq == 0) { ++ /* we failed to get an irq for pen down events, ++ * so we resort to polling. kickstart the reader */ ++ wm->pen_is_down = 1; ++ wake_up_interruptible(&wm->pen_irq_wait); ++ } ++ } ++ } ++ ++ mutex_unlock(&wm->codec_mutex); ++ return 0; ++} ++ ++/** ++ * wm97xx_ts_input_close - Close the touch screen input device. ++ * @idev: Input device to be closed. ++ * ++ * Called by the input sub system to close a wm97xx touchscreen device. ++ * Kills the touchscreen thread and stops the touch digitiser. ++ */ ++ ++static void wm97xx_ts_input_close(struct input_dev *idev) ++{ ++ struct wm97xx *wm = (struct wm97xx *) idev->private; ++ ++ mutex_lock(&wm->codec_mutex); ++ if (--wm->ts_use_count == 0) { ++ /* destroy workqueues and free irqs */ ++ if (wm->pen_irq) { ++ free_irq(wm->pen_irq, wm); ++ destroy_workqueue(wm->pen_irq_workq); ++ } ++ ++ /* kill thread */ ++ if (wm->ts_task) { ++ wm->ts_task = NULL; ++ wm->pen_is_down = 1; ++ mutex_unlock(&wm->codec_mutex); ++ wake_up_interruptible(&wm->pen_irq_wait); ++ wait_for_completion(&wm->ts_exit); ++ wm->pen_is_down = 0; ++ mutex_lock(&wm->codec_mutex); ++ } ++ ++ /* stop digitiser */ ++ wm->codec->digitiser_ioctl(wm, WM97XX_DIG_STOP); ++ if (wm->mach_ops && wm->mach_ops->acc_enabled) ++ wm->codec->acc_enable(wm, 0); ++ } ++ mutex_unlock(&wm->codec_mutex); ++} ++ ++/* ++ * Bus interface to allow for client drivers codec access ++ * e.g. battery monitor ++ */ ++static int wm97xx_bus_match(struct device *dev, struct device_driver *drv) ++{ ++ return !(strcmp(dev->bus_id,drv->name)); ++} ++ ++/* ++ * The AC97 audio driver will do all the Codec suspend and resume ++ * tasks. This is just for anything machine specific or extra. ++ */ ++static int wm97xx_bus_suspend(struct device *dev, pm_message_t state) ++{ ++ int ret = 0; ++ ++ if (dev->driver && dev->driver->suspend) ++ ret = dev->driver->suspend(dev, state); ++ ++ return ret; ++} ++ ++static int wm97xx_bus_resume(struct device *dev) ++{ ++ int ret = 0; ++ ++ if (dev->driver && dev->driver->resume) ++ ret = dev->driver->resume(dev); ++ ++ return ret; ++} ++ ++struct bus_type wm97xx_bus_type = { ++ .name = "wm97xx", ++ .match = wm97xx_bus_match, ++ .suspend = wm97xx_bus_suspend, ++ .resume = wm97xx_bus_resume, ++}; ++EXPORT_SYMBOL_GPL(wm97xx_bus_type); ++ ++static void wm97xx_release(struct device *dev) ++{ ++ kfree(dev); ++} ++ ++static int wm97xx_probe(struct device *dev) ++{ ++ struct wm97xx* wm; ++ int ret = 0, id = 0; ++ ++ if (!(wm = kzalloc(sizeof(struct wm97xx), GFP_KERNEL))) ++ return -ENOMEM; ++ mutex_init(&wm->codec_mutex); ++ ++ init_waitqueue_head(&wm->pen_irq_wait); ++ wm->dev = dev; ++ dev->driver_data = wm; ++ wm->ac97 = to_ac97_t(dev); ++ ++ /* check that we have a supported codec */ ++ if ((id = wm97xx_reg_read(wm, AC97_VENDOR_ID1)) != WM97XX_ID1) { ++ err("could not find a wm97xx, found a %x instead\n", id); ++ kfree(wm); ++ return -ENODEV; ++ } ++ ++ wm->id = wm97xx_reg_read(wm, AC97_VENDOR_ID2); ++ if(wm->id != wm97xx_codec.id) { ++ err("could not find a the selected codec, please build for wm97%2x", wm->id & 0xff); ++ kfree(wm); ++ return -ENODEV; ++ } ++ ++ if((wm->input_dev = input_allocate_device()) == NULL) { ++ kfree(wm); ++ return -ENOMEM; ++ } ++ ++ /* set up touch configuration */ ++ info("detected a wm97%2x codec", wm->id & 0xff); ++ wm->input_dev->name = "wm97xx touchscreen"; ++ wm->input_dev->open = wm97xx_ts_input_open; ++ wm->input_dev->close = wm97xx_ts_input_close; ++ set_bit(EV_ABS, wm->input_dev->evbit); ++ set_bit(ABS_X, wm->input_dev->absbit); ++ set_bit(ABS_Y, wm->input_dev->absbit); ++ set_bit(ABS_PRESSURE, wm->input_dev->absbit); ++ wm->input_dev->absmax[ABS_X] = abs_x[1]; ++ wm->input_dev->absmax[ABS_Y] = abs_y[1]; ++ wm->input_dev->absmax[ABS_PRESSURE] = abs_p[1]; ++ wm->input_dev->absmin[ABS_X] = abs_x[0]; ++ wm->input_dev->absmin[ABS_Y] = abs_y[0]; ++ wm->input_dev->absmin[ABS_PRESSURE] = abs_p[0]; ++ wm->input_dev->absfuzz[ABS_X] = abs_x[2]; ++ wm->input_dev->absfuzz[ABS_Y] = abs_y[2]; ++ wm->input_dev->absfuzz[ABS_PRESSURE] = abs_p[2]; ++ wm->input_dev->private = wm; ++ wm->codec = &wm97xx_codec; ++ if((ret = input_register_device(wm->input_dev)) < 0) { ++ kfree(wm); ++ return -ENOMEM; ++ } ++ ++ /* set up physical characteristics */ ++ wm->codec->digitiser_ioctl(wm, WM97XX_PHY_INIT); ++ ++ /* load gpio cache */ ++ wm->gpio[0] = wm97xx_reg_read(wm, AC97_GPIO_CFG); ++ wm->gpio[1] = wm97xx_reg_read(wm, AC97_GPIO_POLARITY); ++ wm->gpio[2] = wm97xx_reg_read(wm, AC97_GPIO_STICKY); ++ wm->gpio[3] = wm97xx_reg_read(wm, AC97_GPIO_WAKEUP); ++ wm->gpio[4] = wm97xx_reg_read(wm, AC97_GPIO_STATUS); ++ wm->gpio[5] = wm97xx_reg_read(wm, AC97_MISC_AFE); ++ ++ /* register our battery device */ ++ if (!(wm->battery_dev = kzalloc(sizeof(struct device), GFP_KERNEL))) { ++ ret = -ENOMEM; ++ goto batt_err; ++ } ++ wm->battery_dev->bus = &wm97xx_bus_type; ++ strcpy(wm->battery_dev->bus_id,"wm97xx-battery"); ++ wm->battery_dev->driver_data = wm; ++ wm->battery_dev->parent = dev; ++ wm->battery_dev->release = wm97xx_release; ++ if((ret = device_register(wm->battery_dev)) < 0) ++ goto batt_reg_err; ++ ++ /* register our extended touch device (for machine specific extensions) */ ++ if (!(wm->touch_dev = kzalloc(sizeof(struct device), GFP_KERNEL))) { ++ ret = -ENOMEM; ++ goto touch_err; ++ } ++ wm->touch_dev->bus = &wm97xx_bus_type; ++ strcpy(wm->touch_dev->bus_id,"wm97xx-touchscreen"); ++ wm->touch_dev->driver_data = wm; ++ wm->touch_dev->parent = dev; ++ wm->touch_dev->release = wm97xx_release; ++ if((ret = device_register(wm->touch_dev)) < 0) ++ goto touch_reg_err; ++ ++ return ret; ++ ++touch_reg_err: ++ kfree(wm->touch_dev); ++touch_err: ++ device_unregister(wm->battery_dev); ++batt_reg_err: ++ kfree(wm->battery_dev); ++batt_err: ++ input_unregister_device(wm->input_dev); ++ kfree(wm); ++ return ret; ++} ++ ++static int wm97xx_remove(struct device *dev) ++{ ++ struct wm97xx *wm = dev_get_drvdata(dev); ++ ++ /* Stop touch reader thread */ ++ if (wm->ts_task) { ++ wm->ts_task = NULL; ++ wm->pen_is_down = 1; ++ wake_up_interruptible(&wm->pen_irq_wait); ++ wait_for_completion(&wm->ts_exit); ++ } ++ device_unregister(wm->battery_dev); ++ device_unregister(wm->touch_dev); ++ input_unregister_device(wm->input_dev); ++ ++ kfree(wm); ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++int wm97xx_resume(struct device* dev) ++{ ++ struct wm97xx *wm = dev_get_drvdata(dev); ++ ++ /* restore digitiser and gpio's */ ++ if(wm->id == WM9713_ID2) { ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, wm->dig[0]); ++ wm97xx_reg_write(wm, 0x5a, wm->misc); ++ if(wm->ts_use_count) { ++ u16 reg = wm97xx_reg_read(wm, AC97_EXTENDED_MID) & 0x7fff; ++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, reg); ++ } ++ } ++ ++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, wm->dig[1]); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2]); ++ ++ wm97xx_reg_write(wm, AC97_GPIO_CFG, wm->gpio[0]); ++ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, wm->gpio[1]); ++ wm97xx_reg_write(wm, AC97_GPIO_STICKY, wm->gpio[2]); ++ wm97xx_reg_write(wm, AC97_GPIO_WAKEUP, wm->gpio[3]); ++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, wm->gpio[4]); ++ wm97xx_reg_write(wm, AC97_MISC_AFE, wm->gpio[5]); ++ ++ return 0; ++} ++ ++#else ++#define wm97xx_resume NULL ++#endif ++ ++/* ++ * Machine specific operations ++ */ ++int wm97xx_register_mach_ops(struct wm97xx *wm, struct wm97xx_mach_ops *mach_ops) ++{ ++ mutex_lock(&wm->codec_mutex); ++ if(wm->mach_ops) { ++ mutex_unlock(&wm->codec_mutex); ++ return -EINVAL; ++ } ++ wm->mach_ops = mach_ops; ++ mutex_unlock(&wm->codec_mutex); ++ return 0; ++} ++EXPORT_SYMBOL_GPL(wm97xx_register_mach_ops); ++ ++void wm97xx_unregister_mach_ops(struct wm97xx *wm) ++{ ++ mutex_lock(&wm->codec_mutex); ++ wm->mach_ops = NULL; ++ mutex_unlock(&wm->codec_mutex); ++} ++EXPORT_SYMBOL_GPL(wm97xx_unregister_mach_ops); ++ ++static struct device_driver wm97xx_driver = { ++ .name = "ac97", ++ .bus = &ac97_bus_type, ++ .owner = THIS_MODULE, ++ .probe = wm97xx_probe, ++ .remove = wm97xx_remove, ++ .resume = wm97xx_resume, ++}; ++ ++static int __init wm97xx_init(void) ++{ ++ int ret; ++ ++ info("version %s liam.girdwood@wolfsonmicro.com", WM_CORE_VERSION); ++ if((ret = bus_register(&wm97xx_bus_type)) < 0) ++ return ret; ++ return driver_register(&wm97xx_driver); ++} ++ ++static void __exit wm97xx_exit(void) ++{ ++ driver_unregister(&wm97xx_driver); ++ bus_unregister(&wm97xx_bus_type); ++} ++ ++module_init(wm97xx_init); ++module_exit(wm97xx_exit); ++ ++/* Module information */ ++MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); ++MODULE_DESCRIPTION("WM97xx Core - Touch Screen / AUX ADC / GPIO Driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/leds/Kconfig b/drivers/leds/Kconfig +index 4468cb3..01b4828 100644 +--- a/drivers/leds/Kconfig ++++ b/drivers/leds/Kconfig +@@ -101,6 +101,12 @@ config LEDS_GPIO + outputs. To be useful the particular board must have LEDs + and they must be connected to the GPIO lines. + ++config LEDS_EM_X270 ++ tristate "LED Support for the CompuLab EM-X270" ++ depends on LEDS_CLASS && MACH_EM_X270 ++ help ++ This option enables support for the LEDs on CompuLab EM-X270. ++ + comment "LED Triggers" + + config LEDS_TRIGGERS +diff --git a/drivers/leds/Makefile b/drivers/leds/Makefile +index f8995c9..6d3941e 100644 +--- a/drivers/leds/Makefile ++++ b/drivers/leds/Makefile +@@ -17,6 +17,7 @@ obj-$(CONFIG_LEDS_WRAP) += leds-wrap.o + obj-$(CONFIG_LEDS_H1940) += leds-h1940.o + obj-$(CONFIG_LEDS_COBALT) += leds-cobalt.o + obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o ++obj-$(CONFIG_LEDS_EM_X270) += leds-em-x270.o + + # LED Triggers + obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o +diff --git a/drivers/leds/leds-em-x270.c b/drivers/leds/leds-em-x270.c +new file mode 100644 +index 0000000..485e7da +--- /dev/null ++++ b/drivers/leds/leds-em-x270.c +@@ -0,0 +1,99 @@ ++/* ++ * LED Triggers Core ++ * ++ * Copyright 2007 CompuLab, Ltd. ++ * Author: Mike Rapoport <mike@compulab.co.il> ++ * ++ * Based on Corgi leds driver: ++ * Copyright 2005-2006 Openedhand Ltd. ++ * Author: Richard Purdie <rpurdie@openedhand.com> ++ * ++ * 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/kernel.h> ++#include <linux/init.h> ++#include <linux/platform_device.h> ++#include <linux/leds.h> ++#include <linux/da9030.h> ++ ++static void em_x270_led_orange_set(struct led_classdev *led_cdev, ++ enum led_brightness value) ++{ ++ int on = 1; ++ int pwm_chop = 0; ++ ++ pr_info("%s: value = %d\n", __FUNCTION__, value); ++ if (value == LED_OFF) ++ on = 0; ++ else if (value == LED_HALF) ++ pwm_chop = 4; ++ ++ da9030_set_led(3, on, 0, 0, pwm_chop); ++} ++ ++static struct led_classdev em_x270_orange_led = { ++ .name = "em_x270:orange", ++ .default_trigger = "em-x270-battery.0-charging-or-full", ++ .brightness_set = em_x270_led_orange_set, ++}; ++ ++#ifdef CONFIG_PM ++static int em_x270_led_suspend(struct platform_device *dev, pm_message_t state) ++{ ++#ifdef CONFIG_LEDS_TRIGGERS ++ if (em_x270_orange_led.trigger && strcmp(em_x270_orange_led.trigger->name, "em-x270-battery-charging-or-full")) ++#endif ++ led_classdev_suspend(&em_x270_orange_led); ++ return 0; ++} ++ ++static int em_x270_led_resume(struct platform_device *dev) ++{ ++ led_classdev_resume(&em_x270_orange_led); ++ return 0; ++} ++#endif ++ ++static int em_x270_led_probe(struct platform_device *pdev) ++{ ++ return led_classdev_register(&pdev->dev, &em_x270_orange_led); ++} ++ ++static int em_x270_led_remove(struct platform_device *pdev) ++{ ++ led_classdev_unregister(&em_x270_orange_led); ++ return 0; ++} ++ ++static struct platform_driver em_x270_led_driver = { ++ .probe = em_x270_led_probe, ++ .remove = em_x270_led_remove, ++#ifdef CONFIG_PM ++ .suspend = em_x270_led_suspend, ++ .resume = em_x270_led_resume, ++#endif ++ .driver = { ++ .name = "em-x270-led", ++ }, ++}; ++ ++static int __init em_x270_led_init(void) ++{ ++ return platform_driver_register(&em_x270_led_driver); ++} ++ ++static void __exit em_x270_led_exit(void) ++{ ++ platform_driver_unregister(&em_x270_led_driver); ++} ++ ++module_init(em_x270_led_init); ++module_exit(em_x270_led_exit); ++ ++MODULE_AUTHOR("Mike Rapoport <mike@compulab.co.il>"); ++MODULE_DESCRIPTION("EX-X270 LED driver"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c +index 58e561e..7050e71 100644 +--- a/drivers/mtd/chips/jedec_probe.c ++++ b/drivers/mtd/chips/jedec_probe.c +@@ -38,7 +38,7 @@ + #define MANUFACTURER_ST 0x0020 + #define MANUFACTURER_TOSHIBA 0x0098 + #define MANUFACTURER_WINBOND 0x00da +- ++#define CONTINUATION_CODE 0x007f + + /* AMD */ + #define AM29DL800BB 0x22C8 +@@ -68,6 +68,10 @@ + #define AT49BV32X 0x00C8 + #define AT49BV32XT 0x00C9 + ++/* Eon */ ++#define EN29SL800BB 0x226B ++#define EN29SL800BT 0x22EA ++ + /* Fujitsu */ + #define MBM29F040C 0x00A4 + #define MBM29LV650UE 0x22D7 +@@ -632,6 +636,40 @@ static const struct amd_flash_info jedec_table[] = { + ERASEINFO(0x02000,8) + } + }, { ++ .mfr_id = 0x1c, ++ .dev_id = EN29SL800BT, ++ .name = "Eon EN29SL800BT", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x02AA, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000, 15), ++ ERASEINFO(0x08000, 1), ++ ERASEINFO(0x02000, 2), ++ ERASEINFO(0x04000, 1), ++ } ++ }, { ++ .mfr_id = 0x1c, ++ .dev_id = EN29SL800BB, ++ .name = "Eon EN29SL800BB", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x02AA, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x04000, 1), ++ ERASEINFO(0x02000, 2), ++ ERASEINFO(0x08000, 1), ++ ERASEINFO(0x10000, 15), ++ } ++ }, { + .mfr_id = MANUFACTURER_FUJITSU, + .dev_id = MBM29F040C, + .name = "Fujitsu MBM29F040C", +@@ -1769,9 +1807,21 @@ static inline u32 jedec_read_mfr(struct map_info *map, __u32 base, + { + map_word result; + unsigned long mask; +- u32 ofs = cfi_build_cmd_addr(0, cfi_interleave(cfi), cfi->device_type); +- mask = (1 << (cfi->device_type * 8)) -1; +- result = map_read(map, base + ofs); ++ int bank = 0; ++ ++ /* According to JEDEC "Standard Manufacturer's Identification Code" ++ * (http://www.jedec.org/download/search/jep106W.pdf) ++ * several first banks can contain 0x7f instead of actual ID ++ */ ++ do { ++ u32 ofs = cfi_build_cmd_addr(0 + (bank << 8), ++ cfi_interleave(cfi), ++ cfi->device_type); ++ mask = (1 << (cfi->device_type * 8)) -1; ++ result = map_read(map, base + ofs); ++ bank++; ++ } while ((result.x[0] & mask) == CONTINUATION_CODE); ++ + return result.x[0] & mask; + } + +diff --git a/drivers/net/dm9000.c b/drivers/net/dm9000.c +index 738aa59..88dbbdf 100644 +--- a/drivers/net/dm9000.c ++++ b/drivers/net/dm9000.c +@@ -546,6 +546,7 @@ dm9000_probe(struct platform_device *pdev) + + if (id_val != DM9000_ID) { + printk("%s: wrong id: 0x%08x\n", CARDNAME, id_val); ++ ret = -ENODEV; + goto release; + } + +diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig +index 58c806e..3a16d9c 100644 +--- a/drivers/power/Kconfig ++++ b/drivers/power/Kconfig +@@ -49,4 +49,10 @@ config BATTERY_OLPC + help + Say Y to enable support for the battery on the OLPC laptop. + ++config BATTERY_EM_X270 ++ tristate "EM-X270 battery" ++ depends on DA9030 && MACH_EM_X270 ++ help ++ Say Y here to enable support for battery on EM-X270. ++ + endif # POWER_SUPPLY +diff --git a/drivers/power/Makefile b/drivers/power/Makefile +index 6413ded..dc9585e 100644 +--- a/drivers/power/Makefile ++++ b/drivers/power/Makefile +@@ -20,3 +20,4 @@ obj-$(CONFIG_APM_POWER) += apm_power.o + obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o + obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o + obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o ++obj-$(CONFIG_BATTERY_EM_X270) += em_x270_battery.o +diff --git a/drivers/power/em_x270_battery.c b/drivers/power/em_x270_battery.c +new file mode 100644 +index 0000000..2630c68 +--- /dev/null ++++ b/drivers/power/em_x270_battery.c +@@ -0,0 +1,579 @@ ++/* ++ * Power managemnet implementation for EM-X270 ++ * ++ * Copyright (C) 2007 CompuLab, Ltd. ++ * Author: Mike Rapoport <mike@compulab.co.il> ++ * ++ * 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. ++ * ++ */ ++ ++#define DEBUG ++ ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/pda_power.h> ++#include <linux/apm-emulation.h> ++#include <linux/da9030.h> ++#include <linux/power_supply.h> ++#include <linux/pm.h> ++ ++#include <linux/debugfs.h> ++#include <linux/seq_file.h> ++ ++#include "../i2c/chips/da9030.h" ++ ++#define VOLTAGE_MAX_DESIGN 4200000 /* 4.2V in uV */ ++#define VOLTAGE_MIN_DESIGN 3000000 /* 3V in uV */ ++ ++/* #define VCHARGE_MIN_THRESHOLD 72 /\* 4.5V *\/ */ ++ ++#define VCHARGE_MIN_THRESHOLD 60 /* ?.?V */ ++#define VCHARGE_MAX_THRESHOLD 89 /* 5.5V */ ++#define TBAT_LOW_THRESHOLD 197 /* 0oC */ ++#define TBAT_HIGH_THRESHOLD 78 /* 45oC */ ++#define TBAT_RESUME_THRESHOLD 100 /* 35oC */ ++#define VBAT_LOW_THRESHOLD 82 /* 3.498V */ ++#define VBAT_CRIT_THRESHOLD 62 /* 3.291V */ ++ ++struct da9030_charger { ++ struct device *dev; ++ ++ struct power_supply bat; ++ struct da9030_adc_res adc; ++ struct delayed_work work; ++ ++ int interval; ++ int status; ++ int mA; ++ int mV; ++ ++ int is_charging:1; ++ ++#ifdef CONFIG_DEBUG_FS ++ struct dentry *debug_file; ++#endif ++}; ++ ++static unsigned short tbat_readings[] = { ++ 300, 244, 200, 178, 163, 152, 144, 137, 131, ++ 126, 122, 118, 114, 111, 108, 105, 103, 101, ++ 98, 96, 94, 93, 91, 89, 88, 86, 85, ++ 83, 82, 81, 79, 78, 77, 76, 75, 74, ++ 73, 72, 71, 70, 69, 68, 67, 67, 66, ++ 65, 64, 63, 63, 62, 61, 60, 60, 59, ++ 58, 58, 57, 57, 56, 55, 55, 54, 53, ++ 53, 52, 52, 51, 51, 50, 50, 49, 49, ++ 48, 48, 47, 47, 46, 46, 45, 45, 44, ++ 44, 43, 43, 42, 42, 41, 41, 41, 40, ++ 40, 39, 39, 38, 38, 38, 37, 37, 36, ++ 36, 35, 35, 35, 34, 34, 34, 33, 33, ++ 32, 32, 32, 31, 31, 30, 30, 30, 29, ++ 29, 29, 28, 28, 28, 27, 27, 26, 26, ++ 26, 25, 25, 25, 24, 24, 24, 23, 23, ++ 23, 22, 22, 21, 21, 21, 20, 20, 20, ++ 19, 19, 19, 18, 18, 18, 17, 17, 17, ++ 16, 16, 16, 15, 15, 14, 14, 14, 13, ++ 13, 13, 12, 12, 12, 11, 11, 11, 10, ++ 10, 10, 9, 9, 8, 8, 8, 7, 7, ++ 7, 6, 6, 5, 5, 5, 4, 4, 3, ++ 3, 3, 2, 2, 1, 1, 1, 0, 0, ++ -1, -1, -1, -2, -2, -3, -3, -4, -4, ++ -5, -5, -6, -6, -6, -7, -7, -8, -9, ++ -9, -10, -10, -11, -11, -12, -12, -13, -14, ++ -14, -15, -16, -16, -17, -18, -18, -19, -20, ++ -21, -21, -22, -23, -24, -25, -26, -27, -28, ++ -30, -31, -32, -34, -35, -37, -39, -41, -44, ++ -47, -50, -56, -64, ++}; ++ ++#ifdef CONFIG_DEBUG_FS ++ ++#define REG2VOLT(x) ((((x) * 2650) >> 8) + 2650) ++ ++static int debug_show(struct seq_file *s, void *data) ++{ ++ struct da9030_charger *charger = s->private; ++ ++ seq_printf(s, "charger is %s\n", ++ charger->status & CHRG_CHARGER_ENABLE ? "on" : "off"); ++ if (charger->status & CHRG_CHARGER_ENABLE) { ++ seq_printf(s, "iset = %dmA, vset = %dmV\n", ++ charger->mA, charger->mV); ++ } ++ ++ seq_printf(s, "vbat_res = %d (%dmV)\n", ++ charger->adc.vbat_res, REG2VOLT(charger->adc.vbat_res)); ++ seq_printf(s, "vbatmin_res = %d (%dmV)\n", ++ charger->adc.vbatmin_res, ++ REG2VOLT(charger->adc.vbatmin_res)); ++ seq_printf(s, "vbatmintxon = %d (%dmV)\n", ++ charger->adc.vbatmintxon, ++ REG2VOLT(charger->adc.vbatmintxon)); ++ seq_printf(s, "ichmax_res = %d\n", charger->adc.ichmax_res); ++ seq_printf(s, "ichmin_res = %d\n", charger->adc.ichmin_res); ++ seq_printf(s, "ichaverage_res = %d\n", charger->adc.ichaverage_res); ++ seq_printf(s, "vchmax_res = %d (%dmV)\n", ++ charger->adc.vchmax_res, ++ REG2VOLT(charger->adc.vchmax_res)); ++ seq_printf(s, "vchmin_res = %d (%dmV)\n", ++ charger->adc.vchmin_res, ++ REG2VOLT(charger->adc.vchmin_res)); ++ seq_printf(s, "tbat_res = %d (%doC\n", charger->adc.tbat_res, ++ tbat_readings[charger->adc.tbat_res]); ++ seq_printf(s, "adc_in4_res = %d\n", charger->adc.adc_in4_res); ++ seq_printf(s, "adc_in5_res = %d\n", charger->adc.adc_in5_res); ++ ++ return 0; ++} ++ ++static int debug_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, debug_show, inode->i_private); ++} ++ ++static const struct file_operations debug_fops = { ++ .open = debug_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static struct dentry* da9030_create_debugfs(struct da9030_charger *charger) ++{ ++ charger->debug_file = debugfs_create_file("charger", 0666, 0, charger, ++ &debug_fops); ++ return charger->debug_file; ++} ++ ++static void da9030_remove_debugfs(struct da9030_charger *charger) ++{ ++ debugfs_remove(charger->debug_file); ++} ++#else ++#define da9030_create_debugfs(x) NULL ++#define da9030_remove_debugfs(x) do {} while(0) ++#endif ++ ++static void da9030_set_charge(struct da9030_charger *charger, int on) ++{ ++/* int status = da9030_get_status(); */ ++ if (on) { ++ pr_debug("%s: enabling charger\n", __FUNCTION__); ++ da9030_set_thresholds(TBAT_HIGH_THRESHOLD, ++ TBAT_RESUME_THRESHOLD, ++ TBAT_LOW_THRESHOLD); ++ da9030_set_charger(1, 1000, 4200); ++ charger->is_charging = 1; ++ } ++ else { ++ /* disable charger */ ++ pr_debug("%s: disabling charger\n", __FUNCTION__); ++ da9030_set_charger(0, 0, 0); ++ charger->is_charging = 0; ++ } ++} ++ ++static void da9030_charging_monitor(struct work_struct *work) ++{ ++ struct da9030_charger *charger; ++ int is_on; ++ unsigned int mA, mV; ++ ++ charger = container_of(work, struct da9030_charger, work.work); ++ ++ da9030_get_charger(&is_on, &mA, &mV); ++ da9030_read_adc(&charger->adc); ++ ++ charger->status = da9030_get_status(); ++ charger->mA = mA; ++ charger->mV = mV; ++ ++ /* we wake or boot with external power on */ ++ if (!is_on && (charger->status & STATUS_CHDET)) { ++ da9030_set_charge(charger, 1); ++ return; ++ } ++ ++ if (is_on) { ++/* pr_info("%s: mA = %d, mV = %d\n", __FUNCTION__, mA, mV); */ ++/* pr_info("%s: vchmin_res = %d, vchmax_res = %d\n", */ ++/* __FUNCTION__, charger->adc.vchmin_res, */ ++/* charger->adc.vchmax_res); */ ++/* pr_info("%s: tbat_res = %d\n", */ ++/* __FUNCTION__, charger->adc.tbat_res); */ ++ if (charger->adc.vbat_res > VBAT_LOW_THRESHOLD) { ++ /* update VBAT threshlods ? */ ++ da9030_set_reg(VBATMON, VBAT_LOW_THRESHOLD); ++ } ++ if (charger->adc.vchmax_res > VCHARGE_MAX_THRESHOLD || ++ charger->adc.vchmin_res < VCHARGE_MIN_THRESHOLD || ++ /* Tempreture readings are negative */ ++ charger->adc.tbat_res < TBAT_HIGH_THRESHOLD || ++ charger->adc.tbat_res > TBAT_LOW_THRESHOLD) { ++ /* disable charger */ ++ da9030_set_charge(charger, 0); ++ } ++ } ++ ++ /* reschedule for the next time */ ++ schedule_delayed_work(&charger->work, charger->interval); ++} ++ ++void da9030_battery_release(struct device * dev) ++{ ++} ++ ++static void da9030_external_power_changed(struct power_supply *psy) ++{ ++/* struct da9030_charger *charger; */ ++ ++/* charger = container_of(psy, struct da9030_charger, bat); */ ++/* pr_info("%s:\n", __FUNCTION__); */ ++/* da9030_set_charge(charger); */ ++} ++ ++struct da9030_battery_thresh { ++ int voltage; ++ int percentage; ++}; ++ ++static struct da9030_battery_thresh vbat_ranges[] = { ++ { 150, 100}, ++ { 149, 99}, ++ { 148, 98}, ++ { 147, 98}, ++ { 146, 97}, ++ { 145, 96}, ++ { 144, 96}, ++ { 143, 95}, ++ { 142, 94}, ++ { 141, 93}, ++ { 140, 92}, ++ { 139, 91}, ++ { 138, 90}, ++ { 137, 90}, ++ { 136, 89}, ++ { 135, 88}, ++ { 134, 88}, ++ { 133, 87}, ++ { 132, 86}, ++ { 131, 85}, ++ { 130, 83}, ++ { 129, 82}, ++ { 128, 81}, ++ { 127, 81}, ++ { 126, 80}, ++ { 125, 75}, ++ { 124, 74}, ++ { 123, 73}, ++ { 122, 70}, ++ { 121, 66}, ++ { 120, 65}, ++ { 119, 64}, ++ { 118, 64}, ++ { 117, 63}, ++ { 116, 59}, ++ { 115, 58}, ++ { 114, 57}, ++ { 113, 57}, ++ { 112, 56}, ++ { 111, 50}, ++ { 110, 49}, ++ { 109, 49}, ++ { 108, 48}, ++ { 107, 48}, ++ { 106, 33}, ++ { 105, 32}, ++ { 104, 32}, ++ { 103, 32}, ++ { 102, 31}, ++ { 101, 16}, ++ { 100, 15}, ++ { 99, 15}, ++ { 98, 15}, ++ { 97, 10}, ++ { 96, 9}, ++ { 95, 7}, ++ { 94, 3}, ++ { 93, 0}, ++}; ++ ++static enum power_supply_property da9030_bat_props[] = { ++ POWER_SUPPLY_PROP_STATUS, ++ POWER_SUPPLY_PROP_HEALTH, ++ POWER_SUPPLY_PROP_TECHNOLOGY, ++ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, ++ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, ++ POWER_SUPPLY_PROP_VOLTAGE_NOW, ++ POWER_SUPPLY_PROP_CURRENT_AVG, ++ POWER_SUPPLY_PROP_CAPACITY, /* in percents! */ ++ POWER_SUPPLY_PROP_TEMP, ++ POWER_SUPPLY_PROP_MANUFACTURER, ++ POWER_SUPPLY_PROP_MODEL_NAME, ++}; ++ ++static void da9030_bat_check_status(union power_supply_propval *val) ++{ ++ int charger_on; ++ int status = da9030_get_status(); ++ ++ da9030_get_charger(&charger_on, 0, 0); ++ ++ /* FIXME: below code is very crude approximation of actual ++ states, we need to take into account voltage and current ++ measurements to determine actual charger state */ ++ if (status & STATUS_CHDET) { ++ if (charger_on) { ++ val->intval = POWER_SUPPLY_STATUS_CHARGING; ++ } ++ else { ++ val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; ++ } ++ } ++ else { ++ val->intval = POWER_SUPPLY_STATUS_DISCHARGING; ++ } ++} ++ ++static void da9030_bat_check_health(union power_supply_propval *val) ++{ ++ int fault_log = da9030_get_fault_log(); ++ ++ if (fault_log & FAULT_LOG_OVER_TEMP) { ++ val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; ++ } ++ else if (fault_log & FAULT_LOG_VBAT_OVER) { ++ val->intval = POWER_SUPPLY_HEALTH_OVERVOLTAGE; ++ } ++ else { ++ val->intval = POWER_SUPPLY_HEALTH_GOOD; ++ } ++} ++ ++static int vbat_interpolate(int reg) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(vbat_ranges); i++ ) ++ if (vbat_ranges[i].voltage == reg) { ++ pr_debug("%s: voltage = %d, percentage = %d\n", ++ __FUNCTION__, vbat_ranges[i].voltage, ++ vbat_ranges[i].percentage); ++ return vbat_ranges[i].percentage; ++ } ++ ++ return 0; ++} ++ ++static int da9030_bat_get_property(struct power_supply *psy, ++ enum power_supply_property psp, ++ union power_supply_propval *val) ++{ ++ u32 reg; ++ struct da9030_charger *charger; ++ charger = container_of(psy, struct da9030_charger, bat); ++ ++ switch(psp) { ++ case POWER_SUPPLY_PROP_STATUS: ++ da9030_bat_check_status(val); ++ break; ++ case POWER_SUPPLY_PROP_HEALTH: ++ da9030_bat_check_health(val); ++ break; ++ case POWER_SUPPLY_PROP_TECHNOLOGY: ++ val->intval = POWER_SUPPLY_TECHNOLOGY_LIPO; ++ break; ++ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: ++ val->intval = VOLTAGE_MAX_DESIGN; ++ break; ++ case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: ++ val->intval = VOLTAGE_MIN_DESIGN; ++ break; ++ case POWER_SUPPLY_PROP_VOLTAGE_NOW: ++ reg = charger->adc.vbat_res; ++ /* V = (reg / 256) * 2.65 + 2.65 (V) */ ++ val->intval = ((reg * 2650000) >> 8) + 2650000; ++ break; ++ case POWER_SUPPLY_PROP_CURRENT_AVG: ++ reg = charger->adc.ichaverage_res; ++ val->intval = reg; /* reg */ ++ break; ++ case POWER_SUPPLY_PROP_CAPACITY: ++ reg = charger->adc.vbat_res; ++ val->intval = vbat_interpolate(reg); ++ break; ++ case POWER_SUPPLY_PROP_TEMP: ++ reg = charger->adc.tbat_res; ++ val->intval = tbat_readings[reg]; ++ break; ++ case POWER_SUPPLY_PROP_MANUFACTURER: ++ val->strval = "MaxPower"; ++ pr_debug("%s: MFG = %s\n", __FUNCTION__, val->strval); ++ break; ++ case POWER_SUPPLY_PROP_MODEL_NAME: ++ val->strval = "LP555597P6H-FPS"; ++ pr_debug("%s: MODEL = %s\n", __FUNCTION__, val->strval); ++ break; ++ default: break; ++ } ++ ++ return 0; ++} ++ ++static void da9030_setup_battery(struct power_supply *bat) ++{ ++ bat->name = "em-x270-battery"; ++ bat->type = POWER_SUPPLY_TYPE_BATTERY; ++ bat->properties = da9030_bat_props; ++ bat->num_properties = ARRAY_SIZE(da9030_bat_props); ++ bat->get_property = da9030_bat_get_property; ++ bat->use_for_apm = 1; ++ bat->external_power_changed = da9030_external_power_changed; ++}; ++ ++static void da9030_chiover_callback(int event, void *_charger) ++{ ++ /* disable charger */ ++ struct da9030_charger *charger = _charger; ++ da9030_set_charge(charger, 0); ++} ++ ++static void da9030_tbat_callback(int event, void *_charger) ++{ ++ /* disable charger */ ++ struct da9030_charger *charger = _charger; ++ da9030_set_charge(charger, 0); ++} ++ ++static void da9030_vbat_callback(int event, void *_charger) ++{ ++ struct da9030_charger *charger = _charger; ++ da9030_read_adc(&charger->adc); ++ ++ if (charger->is_charging) { ++ if (charger->adc.vbat_res < VBAT_LOW_THRESHOLD) { ++ /* set VBAT threshold for critical */ ++ da9030_set_reg(VBATMON, VBAT_CRIT_THRESHOLD); ++ } ++ else if (charger->adc.vbat_res < VBAT_CRIT_THRESHOLD) { ++ /* notify the system of battery critical */ ++ apm_queue_event(APM_CRITICAL_SUSPEND); ++ } ++ } ++} ++ ++static void da9030_ccto_callback(int event, void *_charger) ++{ ++ /* x3 */ ++} ++ ++static void da9030_tcto_callback(int event, void *_charger) ++{ ++ /* x3 */ ++} ++ ++static void da9030_chdet_callback(int event, void *_charger) ++{ ++ struct da9030_charger *charger = _charger; ++ int status = da9030_get_status(); ++ da9030_set_charge(charger, !!(status & CHRG_CHARGER_ENABLE)); ++} ++ ++static int da9030_battery_probe(struct platform_device *pdev) ++{ ++ struct da9030_charger *charger; ++ ++ pr_debug("%s\n", __FUNCTION__); ++ charger = kzalloc(sizeof(*charger), GFP_KERNEL); ++ if (charger == NULL) { ++ return -ENOMEM; ++ } ++ ++ charger->dev = &pdev->dev; ++ ++ charger->interval = 10 * HZ; /* 10 seconds between monotor runs */ ++ da9030_setup_battery(&charger->bat); ++ ++ platform_set_drvdata(pdev, charger); ++ ++ da9030_enable_adc(); ++ ++ INIT_DELAYED_WORK(&charger->work, da9030_charging_monitor); ++ schedule_delayed_work(&charger->work, charger->interval); ++ ++ charger->debug_file = da9030_create_debugfs(charger); ++ ++ da9030_setup_battery(&charger->bat); ++ ++ da9030_register_callback(DA9030_IRQ_CHDET, ++ da9030_chdet_callback, ++ charger); ++ da9030_register_callback(DA9030_IRQ_VBATMON, ++ da9030_vbat_callback, ++ charger); ++ ++ /* critical condition events */ ++ da9030_register_callback(DA9030_IRQ_CHIOVER, ++ da9030_chiover_callback, ++ charger); ++ da9030_register_callback(DA9030_IRQ_TBAT, ++ da9030_tbat_callback, ++ charger); ++ ++ /* timer events */ ++ da9030_register_callback(DA9030_IRQ_TCTO, ++ da9030_tcto_callback, ++ charger); ++ da9030_register_callback(DA9030_IRQ_CCTO, ++ da9030_ccto_callback, ++ charger); ++ ++ power_supply_register(&pdev->dev, &charger->bat); ++ ++ return 0; ++} ++ ++static int da9030_battery_remove(struct platform_device *dev) ++{ ++ struct da9030_charger *charger = platform_get_drvdata(dev); ++ ++ pr_debug("%s\n", __FUNCTION__); ++ da9030_remove_debugfs(charger); ++ cancel_delayed_work(&charger->work); ++ power_supply_unregister(&charger->bat); ++ kfree(charger); ++ return 0; ++} ++ ++static struct platform_driver da9030_battery_driver = { ++ .driver = { ++ .name = "da9030-battery", ++ .owner = THIS_MODULE, ++ }, ++ .probe = da9030_battery_probe, ++ .remove = da9030_battery_remove, ++}; ++ ++static int da9030_battery_init(void) ++{ ++ pr_debug("%s\n", __FUNCTION__); ++ ++ return platform_driver_register(&da9030_battery_driver); ++} ++ ++static void da9030_battery_exit(void) ++{ ++ pr_debug("%s\n", __FUNCTION__); ++ ++ platform_driver_unregister(&da9030_battery_driver); ++} ++ ++module_init(da9030_battery_init); ++module_exit(da9030_battery_exit); ++ ++MODULE_DESCRIPTION("DA9030 charger driver"); ++MODULE_AUTHOR("Mike Rapoport"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig +index 767aed5..4c44a7a 100644 +--- a/drivers/usb/gadget/Kconfig ++++ b/drivers/usb/gadget/Kconfig +@@ -195,6 +195,26 @@ config USB_M66592 + default USB_GADGET + select USB_GADGET_SELECTED + ++config USB_GADGET_PXA27X ++ boolean "PXA 27x" ++ depends on ARCH_PXA && PXA27x ++ help ++ Intel's PXA 27x series XScale ARM-5TE processors include ++ an integrated full speed USB 1.1 device controller. ++ ++ It has 23 endpoints, as well as endpoint zero (for control ++ transfers). ++ ++ Say "y" to link the driver statically, or "m" to build a ++ dynamically linked module called "pxa27x_udc" and force all ++ gadget drivers to also be dynamically linked. ++ ++config USB_PXA27X ++ tristate ++ depends on USB_GADGET_PXA27X ++ default USB_GADGET ++ select USB_GADGET_SELECTED ++ + config USB_GADGET_GOKU + boolean "Toshiba TC86C001 'Goku-S'" + depends on PCI +diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile +index 1bc0f03..b8743bf 100644 +--- a/drivers/usb/gadget/Makefile ++++ b/drivers/usb/gadget/Makefile +@@ -9,6 +9,7 @@ obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o + obj-$(CONFIG_USB_NET2280) += net2280.o + obj-$(CONFIG_USB_AMD5536UDC) += amd5536udc.o + obj-$(CONFIG_USB_PXA2XX) += pxa2xx_udc.o ++obj-$(CONFIG_USB_PXA27X) += pxa27x_udc.o + obj-$(CONFIG_USB_GOKU) += goku_udc.o + obj-$(CONFIG_USB_OMAP) += omap_udc.o + obj-$(CONFIG_USB_LH7A40X) += lh7a40x_udc.o +diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c +index 3aa46cf..d7d5550 100644 +--- a/drivers/usb/gadget/epautoconf.c ++++ b/drivers/usb/gadget/epautoconf.c +@@ -228,14 +228,19 @@ find_ep (struct usb_gadget *gadget, const char *name) + * + * On failure, this returns a null endpoint descriptor. + */ +-struct usb_ep * __devinit usb_ep_autoconfig ( ++struct usb_ep * usb_ep_autoconfig ( + struct usb_gadget *gadget, +- struct usb_endpoint_descriptor *desc ++ struct usb_endpoint_descriptor *desc, ++ struct usb_endpoint_config *epconfig, int numconfigs + ) + { + struct usb_ep *ep; + u8 type; + ++ /* Use device specific ep allocation code if provided */ ++ if (gadget->ops->ep_alloc) ++ return gadget->ops->ep_alloc(gadget, desc, epconfig, numconfigs); ++ + type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + + /* First, apply chip-specific "best usage" knowledge. +diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c +index 593e235..87aa9fb 100644 +--- a/drivers/usb/gadget/ether.c ++++ b/drivers/usb/gadget/ether.c +@@ -1350,6 +1350,10 @@ static void rndis_response_complete (struct usb_ep *ep, struct usb_request *req) + /* done sending after USB_CDC_GET_ENCAPSULATED_RESPONSE */ + } + ++#ifdef CONFIG_USB_GADGET_PXA27X ++int write_ep0_zlp(void); ++#endif ++ + static void rndis_command_complete (struct usb_ep *ep, struct usb_request *req) + { + struct eth_dev *dev = ep->driver_data; +@@ -1360,6 +1364,10 @@ static void rndis_command_complete (struct usb_ep *ep, struct usb_request *req) + status = rndis_msg_parser (dev->rndis_config, (u8 *) req->buf); + if (status < 0) + ERROR(dev, "%s: rndis parse error %d\n", __FUNCTION__, status); ++ ++#ifdef CONFIG_USB_GADGET_PXA27X ++ write_ep0_zlp(); ++#endif + spin_unlock(&dev->lock); + } + +@@ -2287,7 +2295,8 @@ eth_bind (struct usb_gadget *gadget) + struct eth_dev *dev; + struct net_device *net; + u8 cdc = 1, zlp = 1, rndis = 1; +- struct usb_ep *in_ep, *out_ep, *status_ep = NULL; ++ struct usb_ep *in_ep = NULL , *out_ep = NULL, *status_ep = NULL; ++ struct usb_endpoint_config ep_config[2]; + int status = -ENOMEM; + int gcnum; + +@@ -2386,7 +2395,30 @@ eth_bind (struct usb_gadget *gadget) + + /* all we really need is bulk IN/OUT */ + usb_ep_autoconfig_reset (gadget); +- in_ep = usb_ep_autoconfig (gadget, &fs_source_desc); ++ ++ ep_config[0].config = DEV_CONFIG_VALUE; ++#if defined(DEV_CONFIG_CDC) ++ ep_config[0].interface = data_intf.bInterfaceNumber; ++ ep_config[0].altinterface = data_intf.bAlternateSetting; ++#else /* DEV_CONFIG_SUBSET */ ++ ep_config[0].interface = subset_data_intf.bInterfaceNumber; ++ ep_config[0].altinterface = subset_data_intf.bAlternateSetting; ++#endif ++ ++#ifdef CONFIG_USB_ETH_RNDIS ++ ep_config[1].config = DEV_RNDIS_CONFIG_VALUE; ++#ifdef CONFIG_USB_GADGET_PXA27X ++ ep_config[1].interface = 0; ++#else ++ ep_config[1].interface = rndis_data_intf.bInterfaceNumber; ++#endif ++ ep_config[1].altinterface = rndis_data_intf.bAlternateSetting; ++ ++ in_ep = usb_ep_autoconfig(gadget, &fs_source_desc, &ep_config[0], 2); ++#else ++ in_ep = usb_ep_autoconfig(gadget, &fs_source_desc, &ep_config[0], 1); ++#endif ++ + if (!in_ep) { + autoconf_fail: + dev_err (&gadget->dev, +@@ -2396,7 +2428,12 @@ autoconf_fail: + } + in_ep->driver_data = in_ep; /* claim */ + +- out_ep = usb_ep_autoconfig (gadget, &fs_sink_desc); ++#ifdef CONFIG_USB_ETH_RNDIS ++ out_ep = usb_ep_autoconfig(gadget, &fs_sink_desc, &ep_config[0], 2); ++#else ++ out_ep = usb_ep_autoconfig(gadget, &fs_sink_desc, &ep_config[0], 1); ++#endif ++ + if (!out_ep) + goto autoconf_fail; + out_ep->driver_data = out_ep; /* claim */ +@@ -2406,7 +2443,25 @@ autoconf_fail: + * Since some hosts expect one, try to allocate one anyway. + */ + if (cdc || rndis) { +- status_ep = usb_ep_autoconfig (gadget, &fs_status_desc); ++#ifdef DEV_CONFIG_CDC ++ ep_config[0].config = DEV_CONFIG_VALUE; ++ ep_config[0].interface = control_intf.bInterfaceNumber; ++ ep_config[0].altinterface = control_intf.bAlternateSetting; ++#endif ++#ifdef CONFIG_USB_ETH_RNDIS ++ ep_config[1].config = DEV_RNDIS_CONFIG_VALUE; ++ ep_config[1].interface = rndis_control_intf.bInterfaceNumber; ++ ep_config[1].altinterface = rndis_control_intf.bAlternateSetting; ++#endif ++ ++#if defined(DEV_CONFIG_CDC) && defined(CONFIG_USB_ETH_RNDIS) ++ status_ep = usb_ep_autoconfig(gadget, &fs_status_desc, &ep_config[0], 2); ++#elif defined(CONFIG_USB_ETH_RNDIS) ++ status_ep = usb_ep_autoconfig(gadget, &fs_status_desc, &ep_config[1], 1); ++#else ++ status_ep = usb_ep_autoconfig(gadget, &fs_status_desc, &ep_config[0], 1); ++#endif ++ + if (status_ep) { + status_ep->driver_data = status_ep; /* claim */ + } else if (rndis) { +diff --git a/drivers/usb/gadget/file_storage.c b/drivers/usb/gadget/file_storage.c +index 965ad7b..b9cd8c9 100644 +--- a/drivers/usb/gadget/file_storage.c ++++ b/drivers/usb/gadget/file_storage.c +@@ -3841,6 +3841,7 @@ static int __init fsg_bind(struct usb_gadget *gadget) + struct usb_ep *ep; + struct usb_request *req; + char *pathbuf, *p; ++ struct usb_endpoint_config ep_config; + + fsg->gadget = gadget; + set_gadget_data(gadget, fsg); +@@ -3911,21 +3912,25 @@ static int __init fsg_bind(struct usb_gadget *gadget) + } + + /* Find all the endpoints we will use */ ++ ep_config.config = CONFIG_VALUE; ++ ep_config.interface = intf_desc.bInterfaceNumber; ++ ep_config.altinterface = intf_desc.bAlternateSetting; ++ + usb_ep_autoconfig_reset(gadget); +- ep = usb_ep_autoconfig(gadget, &fs_bulk_in_desc); ++ ep = usb_ep_autoconfig(gadget, &fs_bulk_in_desc, &ep_config, 1); + if (!ep) + goto autoconf_fail; + ep->driver_data = fsg; // claim the endpoint + fsg->bulk_in = ep; + +- ep = usb_ep_autoconfig(gadget, &fs_bulk_out_desc); ++ ep = usb_ep_autoconfig(gadget, &fs_bulk_out_desc, &ep_config, 1); + if (!ep) + goto autoconf_fail; + ep->driver_data = fsg; // claim the endpoint + fsg->bulk_out = ep; + + if (transport_is_cbi()) { +- ep = usb_ep_autoconfig(gadget, &fs_intr_in_desc); ++ ep = usb_ep_autoconfig(gadget, &fs_intr_in_desc, &ep_config, 1); + if (!ep) + goto autoconf_fail; + ep->driver_data = fsg; // claim the endpoint +diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c +new file mode 100644 +index 0000000..d4270d4 +--- /dev/null ++++ b/drivers/usb/gadget/pxa27x_udc.c +@@ -0,0 +1,2387 @@ ++/* ++ * Handles the Intel 27x USB Device Controller (UDC) ++ * ++ * Copyright (C) 2002 Intrinsyc, Inc. (Frank Becker) ++ * Copyright (C) 2003 Robert Schwebel, Pengutronix ++ * Copyright (C) 2003 Benedikt Spranger, Pengutronix ++ * Copyright (C) 2003 David Brownell ++ * Copyright (C) 2003 Joshua Wise ++ * Copyright (C) 2004 Intel Corporation ++ * Copyright (C) 2005 SDG Systems, LLC (Aric Blumer) ++ * Copyright (C) 2005-2006 Openedhand Ltd. (Richard Purdie) ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ */ ++ ++#undef DEBUG ++//#define DEBUG 1 ++//#define VERBOSE DBG_VERBOSE ++ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/ioport.h> ++#include <linux/types.h> ++#include <linux/version.h> ++#include <linux/errno.h> ++#include <linux/platform_device.h> ++#include <linux/delay.h> ++#include <linux/sched.h> ++#include <linux/slab.h> ++#include <linux/init.h> ++#include <linux/timer.h> ++#include <linux/list.h> ++#include <linux/interrupt.h> ++#include <linux/proc_fs.h> ++#include <linux/mm.h> ++#include <linux/device.h> ++#include <linux/dma-mapping.h> ++ ++#include <asm/byteorder.h> ++#include <asm/dma.h> ++#include <asm/io.h> ++#include <asm/irq.h> ++#include <asm/system.h> ++#include <asm/mach-types.h> ++#include <asm/unaligned.h> ++#include <asm/hardware.h> ++#include <asm/arch/pxa-regs.h> ++ ++#include <linux/usb/ch9.h> ++#include <linux/usb_gadget.h> ++ ++#include <asm/arch/udc.h> ++ ++/* ++ * This driver handles the USB Device Controller (UDC) in Intel's PXA 27x ++ * series processors. ++ * ++ * Such controller drivers work with a gadget driver. The gadget driver ++ * returns descriptors, implements configuration and data protocols used ++ * by the host to interact with this device, and allocates endpoints to ++ * the different protocol interfaces. The controller driver virtualizes ++ * usb hardware so that the gadget drivers will be more portable. ++ * ++ * This UDC hardware wants to implement a bit too much USB protocol. The ++ * biggest issue is that the endpoints have to be setup before the controller ++ * can be enabled and each endpoint can only have one configuration, interface ++ * and alternative interface number. Once enabled, these cannot be changed ++ * without a controller reset. ++ * ++ * Intel Errata #22 mentions issues when changing alternate interface. ++ * The exact meaning of this remains uncertain as gadget drivers using alternate ++ * interfaces such as CDC-Ethernet appear to work... ++ */ ++ ++#define DRIVER_VERSION "01-01-2006" ++#define DRIVER_DESC "PXA 27x USB Device Controller driver" ++ ++static const char driver_name [] = "pxa27x_udc"; ++ ++static const char ep0name [] = "ep0"; ++ ++ ++#define USE_DMA ++//#undef USE_DMA ++ ++#ifdef CONFIG_PROC_FS ++#define UDC_PROC_FILE ++#endif ++ ++#include "pxa27x_udc.h" ++ ++#ifdef USE_DMA ++static int use_dma = 1; ++module_param(use_dma, bool, 0); ++MODULE_PARM_DESC(use_dma, "true to use dma"); ++ ++static void dma_nodesc_handler(int dmach, void *_ep); ++static void kick_dma(struct pxa27x_ep *ep, struct pxa27x_request *req); ++ ++#define DMASTR " (dma support)" ++ ++#else /* !USE_DMA */ ++#define DMASTR " (pio only)" ++#endif ++ ++#define UDCISR0_IR0 0x3 ++#define UDCISR_INT_MASK (UDC_INT_FIFOERROR | UDC_INT_PACKETCMP) ++#define UDCICR_INT_MASK UDCISR_INT_MASK ++ ++#define UDCCSR_MASK (UDCCSR_FST | UDCCSR_DME) ++ ++static void pxa27x_ep_fifo_flush(struct usb_ep *ep); ++static void nuke(struct pxa27x_ep *, int status); ++static void udc_init_ep(struct pxa27x_udc *dev); ++ ++ ++/* ++ * Endpoint Functions ++ */ ++static void pio_irq_enable(int ep_num) ++{ ++ if (ep_num < 16) ++ UDCICR0 |= 3 << (ep_num * 2); ++ else { ++ ep_num -= 16; ++ UDCICR1 |= 3 << (ep_num * 2); ++ } ++} ++ ++static void pio_irq_disable(int ep_num) ++{ ++ ep_num &= 0xf; ++ if (ep_num < 16) ++ UDCICR0 &= ~(3 << (ep_num * 2)); ++ else { ++ ep_num -= 16; ++ UDCICR1 &= ~(3 << (ep_num * 2)); ++ } ++} ++ ++/* The UDCCR reg contains mask and interrupt status bits, ++ * so using '|=' isn't safe as it may ack an interrupt. ++ */ ++#define UDCCR_MASK_BITS (UDCCR_OEN | UDCCR_UDE) ++ ++static inline void udc_set_mask_UDCCR(int mask) ++{ ++ UDCCR = (UDCCR & UDCCR_MASK_BITS) | (mask & UDCCR_MASK_BITS); ++} ++ ++static inline void udc_clear_mask_UDCCR(int mask) ++{ ++ UDCCR = (UDCCR & UDCCR_MASK_BITS) & ~(mask & UDCCR_MASK_BITS); ++} ++ ++static inline void udc_ack_int_UDCCR(int mask) ++{ ++ /* udccr contains the bits we dont want to change */ ++ __u32 udccr = UDCCR & UDCCR_MASK_BITS; ++ ++ UDCCR = udccr | (mask & ~UDCCR_MASK_BITS); ++} ++ ++/* ++ * Endpoint enable/disable ++ * ++ * Not much to do here as the ep_alloc function sets up most things. Once ++ * enabled, not much of the pxa27x configuration can be changed. ++ * ++ */ ++static int pxa27x_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) ++{ ++ struct pxa27x_virt_ep *virt_ep = container_of(_ep, struct pxa27x_virt_ep, usb_ep); ++ struct pxa27x_ep *ep = virt_ep->pxa_ep; ++ struct pxa27x_udc *dev; ++ ++ if (!_ep || !desc || _ep->name == ep0name ++ || desc->bDescriptorType != USB_DT_ENDPOINT ++ || ep->fifo_size < le16_to_cpu(desc->wMaxPacketSize)) { ++ dev_err(ep->dev->dev, "%s, bad ep or descriptor\n", __FUNCTION__); ++ return -EINVAL; ++ } ++ ++ /* xfer types must match, except that interrupt ~= bulk */ ++ if( ep->ep_type != USB_ENDPOINT_XFER_BULK ++ && desc->bmAttributes != USB_ENDPOINT_XFER_INT) { ++ dev_err(ep->dev->dev, "%s, %s type mismatch\n", __FUNCTION__, _ep->name); ++ return -EINVAL; ++ } ++ ++ /* hardware _could_ do smaller, but driver doesn't */ ++ if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK ++ && le16_to_cpu (desc->wMaxPacketSize) ++ != BULK_FIFO_SIZE) ++ || !desc->wMaxPacketSize) { ++ dev_err(ep->dev->dev, "%s, bad %s maxpacket\n", __FUNCTION__, _ep->name); ++ return -ERANGE; ++ } ++ ++ dev = ep->dev; ++ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { ++ dev_err(ep->dev->dev, "%s, bogus device state\n", __FUNCTION__); ++ return -ESHUTDOWN; ++ } ++ ++ ep->desc = desc; ++ ep->dma = -1; ++ ep->stopped = 0; ++ ep->pio_irqs = ep->dma_irqs = 0; ++ ep->usb_ep->maxpacket = le16_to_cpu(desc->wMaxPacketSize); ++ ++ /* flush fifo (mostly for OUT buffers) */ ++ pxa27x_ep_fifo_flush(_ep); ++ ++ /* ... reset halt state too, if we could ... */ ++ ++#ifdef USE_DMA ++ /* for (some) bulk and ISO endpoints, try to get a DMA channel and ++ * bind it to the endpoint. otherwise use PIO. ++ */ ++ dev_dbg(ep->dev->dev, "%s: called attributes=%d\n", __FUNCTION__, ep->ep_type); ++ switch (ep->ep_type) { ++ case USB_ENDPOINT_XFER_ISOC: ++ if (le16_to_cpu(desc->wMaxPacketSize) % 32) ++ break; ++ // fall through ++ case USB_ENDPOINT_XFER_BULK: ++ if (!use_dma || !ep->reg_drcmr) ++ break; ++ ep->dma = pxa_request_dma((char *)_ep->name, (le16_to_cpu(desc->wMaxPacketSize) > 64) ++ ? DMA_PRIO_MEDIUM : DMA_PRIO_LOW, dma_nodesc_handler, ep); ++ if (ep->dma >= 0) { ++ *ep->reg_drcmr = DRCMR_MAPVLD | ep->dma; ++ dev_dbg(ep->dev->dev, "%s using dma%d\n", _ep->name, ep->dma); ++ } ++ default: ++ break; ++ } ++#endif ++ DBG(DBG_VERBOSE, "enabled %s\n", _ep->name); ++ return 0; ++} ++ ++static int pxa27x_ep_disable(struct usb_ep *_ep) ++{ ++ struct pxa27x_virt_ep *virt_ep = container_of(_ep, struct pxa27x_virt_ep, usb_ep); ++ struct pxa27x_ep *ep = virt_ep->pxa_ep; ++ unsigned long flags; ++ ++ if (!_ep || !ep->desc) { ++ dev_err(ep->dev->dev, "%s, %s not enabled\n", __FUNCTION__, ++ _ep ? _ep->name : NULL); ++ return -EINVAL; ++ } ++ local_irq_save(flags); ++ nuke(ep, -ESHUTDOWN); ++ ++#ifdef USE_DMA ++ if (ep->dma >= 0) { ++ *ep->reg_drcmr = 0; ++ pxa_free_dma(ep->dma); ++ ep->dma = -1; ++ } ++#endif ++ ++ /* flush fifo (mostly for IN buffers) */ ++ pxa27x_ep_fifo_flush(_ep); ++ ++ ep->desc = 0; ++ ep->stopped = 1; ++ ++ local_irq_restore(flags); ++ DBG(DBG_VERBOSE, "%s disabled\n", _ep->name); ++ return 0; ++} ++ ++ ++ ++/* for the pxa27x, these can just wrap kmalloc/kfree. gadget drivers ++ * must still pass correctly initialized endpoints, since other controller ++ * drivers may care about how it's currently set up (dma issues etc). ++ */ ++ ++/* ++ * pxa27x_ep_alloc_request - allocate a request data structure ++ */ ++static struct usb_request * ++pxa27x_ep_alloc_request(struct usb_ep *_ep, unsigned gfp_flags) ++{ ++ struct pxa27x_request *req; ++ ++ req = kzalloc(sizeof *req, gfp_flags); ++ if (!req) ++ return 0; ++ ++ INIT_LIST_HEAD(&req->queue); ++ return &req->req; ++} ++ ++ ++/* ++ * pxa27x_ep_free_request - deallocate a request data structure ++ */ ++static void ++pxa27x_ep_free_request(struct usb_ep *_ep, struct usb_request *_req) ++{ ++ struct pxa27x_request *req; ++ ++ req = container_of(_req, struct pxa27x_request, req); ++ WARN_ON(!list_empty(&req->queue)); ++ kfree(req); ++} ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * done - retire a request; caller blocked irqs ++ */ ++static void done(struct pxa27x_ep *ep, struct pxa27x_request *req, int status) ++{ ++ list_del_init(&req->queue); ++ if (likely (req->req.status == -EINPROGRESS)) ++ req->req.status = status; ++ else ++ status = req->req.status; ++ ++ if (status && status != -ESHUTDOWN) ++ DBG(DBG_VERBOSE, "complete %s req %p stat %d len %u/%u\n", ++ ep->usb_ep->name, &req->req, status, ++ req->req.actual, req->req.length); ++ ++ /* don't modify queue heads during completion callback */ ++ req->req.complete(ep->usb_ep, &req->req); ++} ++ ++ ++static inline void ep0_idle(struct pxa27x_udc *dev) ++{ ++ dev->ep0state = EP0_IDLE; ++} ++ ++static int write_packet(volatile u32 *uddr, struct pxa27x_request *req, unsigned max) ++{ ++ u32 *buf; ++ int length, count, remain; ++ ++ buf = (u32*)(req->req.buf + req->req.actual); ++ prefetch(buf); ++ ++ /* how big will this packet be? */ ++ length = min(req->req.length - req->req.actual, max); ++ req->req.actual += length; ++ ++ remain = length & 0x3; ++ count = length & ~(0x3); ++ ++ //dev_dbg(ep->dev->dev, "Length %d, Remain %d, Count %d\n",length, remain, count); ++ ++ while (likely(count)) { ++ //dev_dbg(ep->dev->dev, "Sending:0x%x\n", *buf); ++ *uddr = *buf++; ++ count -= 4; ++ } ++ ++ if (remain) { ++ volatile u8* reg=(u8*)uddr; ++ char *rd =(u8*)buf; ++ ++ while (remain--) { ++ *reg=*rd++; ++ } ++ } ++ ++ return length; ++} ++ ++/* ++ * write to an IN endpoint fifo, as many packets as possible. ++ * irqs will use this to write the rest later. ++ * caller guarantees at least one packet buffer is ready (or a zlp). ++ */ ++static int ++write_fifo(struct pxa27x_ep *ep, struct pxa27x_request *req) ++{ ++ unsigned max; ++ ++ max = le16_to_cpu(ep->desc->wMaxPacketSize); ++ do { ++ int count, is_last, is_short; ++ ++ //dev_dbg(ep->dev->dev, "write_fifo7 %x\n", *ep->reg_udccsr); ++ ++ if (*ep->reg_udccsr & UDCCSR_PC) { ++ //dev_dbg(ep->dev->dev, "Transmit Complete\n"); ++ *ep->reg_udccsr = UDCCSR_PC | (*ep->reg_udccsr & UDCCSR_MASK); ++ } ++ ++ if (*ep->reg_udccsr & UDCCSR_TRN) { ++ //dev_dbg(ep->dev->dev, "Clearing Underrun\n"); ++ *ep->reg_udccsr = UDCCSR_TRN | (*ep->reg_udccsr & UDCCSR_MASK); ++ } ++ //dev_dbg(ep->dev->dev, "write_fifo8 %x\n", *ep->reg_udccsr); ++ ++ count = write_packet(ep->reg_udcdr, req, max); ++ ++ /* last packet is usually short (or a zlp) */ ++ if (unlikely (count != max)) ++ is_last = is_short = 1; ++ else { ++ if (likely(req->req.length != req->req.actual) ++ || req->req.zero) ++ is_last = 0; ++ else ++ is_last = 1; ++ /* interrupt/iso maxpacket may not fill the fifo */ ++ is_short = unlikely (max < ep->fifo_size); ++ } ++ ++ //dev_dbg(ep->dev->dev, "write_fifo0 %x\n", *ep->reg_udccsr); ++ ++ dev_dbg(ep->dev->dev, "wrote %s count:%d bytes%s%s %d left %p\n", ++ ep->usb_ep->name, count, ++ is_last ? "/L" : "", is_short ? "/S" : "", ++ req->req.length - req->req.actual, &req->req); ++ ++ /* let loose that packet. maybe try writing another one, ++ * double buffering might work. ++ */ ++ ++ if (is_short) ++ *ep->reg_udccsr = UDCCSR_SP | (*ep->reg_udccsr & UDCCSR_MASK); ++ ++ dev_dbg(ep->dev->dev, "write_fifo0.5 %x\n", *ep->reg_udccsr); ++ ++ /* requests complete when all IN data is in the FIFO */ ++ if (is_last) { ++ done(ep, req, 0); ++ if (list_empty(&ep->queue) || unlikely(ep->dma >= 0)) { ++ pio_irq_disable(ep->pxa_ep_num); ++ //dev_dbg(ep->dev->dev, "write_fifo1 %x\n", *ep->reg_udccsr); ++#ifdef USE_DMA ++ /* unaligned data and zlps couldn't use dma */ ++ if (unlikely(!list_empty(&ep->queue))) { ++ req = list_entry(ep->queue.next, ++ struct pxa27x_request, queue); ++ kick_dma(ep,req); ++ return 0; ++ } ++#endif ++ } ++ //dev_dbg(ep->dev->dev, "write_fifo2 %x\n", *ep->reg_udccsr); ++ return 1; ++ } ++ ++ // TODO experiment: how robust can fifo mode tweaking be? ++ // double buffering is off in the default fifo mode, which ++ // prevents TFS from being set here. ++ ++ } while (*ep->reg_udccsr & UDCCSR_FS); ++ //dev_dbg(ep->dev->dev, "write_fifo2 %x\n", *ep->reg_udccsr); ++ return 0; ++} ++ ++/* caller asserts req->pending (ep0 irq status nyet cleared); starts ++ * ep0 data stage. these chips want very simple state transitions. ++ */ ++static inline ++void ep0start(struct pxa27x_udc *dev, u32 flags, const char *tag) ++{ ++ UDCCSR0 = flags|UDCCSR0_SA|UDCCSR0_OPC; ++ UDCISR0 = UDCICR_INT(0, UDC_INT_FIFOERROR | UDC_INT_PACKETCMP); ++ dev->req_pending = 0; ++ DBG(DBG_VERY_NOISY, "%s %s, %02x/%02x\n", ++ __FUNCTION__, tag, UDCCSR0, flags); ++} ++ ++static int ++write_ep0_fifo(struct pxa27x_ep *ep, struct pxa27x_request *req) ++{ ++ unsigned count; ++ int is_short; ++ ++ count = write_packet(&UDCDR0, req, EP0_FIFO_SIZE); ++ ep->dev->stats.write.bytes += count; ++ ++ /* last packet "must be" short (or a zlp) */ ++ is_short = (count != EP0_FIFO_SIZE); ++ ++ DBG(DBG_VERY_NOISY, "ep0in %d bytes %d left %p\n", count, ++ req->req.length - req->req.actual, &req->req); ++ ++ if (unlikely (is_short)) { ++ if (ep->dev->req_pending) ++ ep0start(ep->dev, UDCCSR0_IPR, "short IN"); ++ else ++ UDCCSR0 = UDCCSR0_IPR; ++ ++ count = req->req.length; ++ done(ep, req, 0); ++ ep0_idle(ep->dev); ++#if 0 ++ /* This seems to get rid of lost status irqs in some cases: ++ * host responds quickly, or next request involves config ++ * change automagic, or should have been hidden, or ... ++ * ++ * FIXME get rid of all udelays possible... ++ */ ++ if (count >= EP0_FIFO_SIZE) { ++ count = 100; ++ do { ++ if ((UDCCSR0 & UDCCSR0_OPC) != 0) { ++ /* clear OPC, generate ack */ ++ UDCCSR0 = UDCCSR0_OPC; ++ break; ++ } ++ count--; ++ udelay(1); ++ } while (count); ++ } ++#endif ++ } else if (ep->dev->req_pending) ++ ep0start(ep->dev, 0, "IN"); ++ return is_short; ++} ++ ++ ++/* ++ * read_fifo - unload packet(s) from the fifo we use for usb OUT ++ * transfers and put them into the request. caller should have made ++ * sure there's at least one packet ready. ++ * ++ * returns true if the request completed because of short packet or the ++ * request buffer having filled (and maybe overran till end-of-packet). ++ */ ++static int read_fifo(struct pxa27x_ep *ep, struct pxa27x_request *req) ++{ ++ for (;;) { ++ u32 *buf; ++ int bufferspace, count, is_short; ++ ++ /* make sure there's a packet in the FIFO.*/ ++ if (unlikely ((*ep->reg_udccsr & UDCCSR_PC) == 0)) ++ break; ++ buf =(u32*) (req->req.buf + req->req.actual); ++ prefetchw(buf); ++ bufferspace = req->req.length - req->req.actual; ++ ++ /* read all bytes from this packet */ ++ if (likely (*ep->reg_udccsr & UDCCSR_BNE)) { ++ count = 0x3ff & *ep->reg_udcbcr; ++ req->req.actual += min(count, bufferspace); ++ } else /* zlp */ ++ count = 0; ++ ++ is_short = (count < ep->usb_ep->maxpacket); ++ dev_dbg(ep->dev->dev, "read %s udccsr:%02x, count:%d bytes%s req %p %d/%d\n", ++ ep->usb_ep->name, *ep->reg_udccsr, count, ++ is_short ? "/S" : "", ++ &req->req, req->req.actual, req->req.length); ++ ++ count = min(count, bufferspace); ++ while (likely (count > 0)) { ++ *buf++ = *ep->reg_udcdr; ++ count -= 4; ++ } ++ dev_dbg(ep->dev->dev, "Buf:0x%p\n", req->req.buf); ++ ++ *ep->reg_udccsr = UDCCSR_PC; ++ /* RPC/RSP/RNE could now reflect the other packet buffer */ ++ ++ /* completion */ ++ if (is_short || req->req.actual == req->req.length) { ++ done(ep, req, 0); ++ if (list_empty(&ep->queue)) ++ pio_irq_disable(ep->pxa_ep_num); ++ return 1; ++ } ++ ++ /* finished that packet. the next one may be waiting... */ ++ } ++ return 0; ++} ++ ++/* ++ * special ep0 version of the above. no UBCR0 or double buffering; status ++ * handshaking is magic. most device protocols don't need control-OUT. ++ * CDC vendor commands (and RNDIS), mass storage CB/CBI, and some other ++ * protocols do use them. ++ */ ++static int read_ep0_fifo(struct pxa27x_ep *ep, struct pxa27x_request *req) ++{ ++ u32 *buf, word; ++ unsigned bufferspace; ++ ++ buf = (u32*) (req->req.buf + req->req.actual); ++ bufferspace = req->req.length - req->req.actual; ++ ++ while (UDCCSR0 & UDCCSR0_RNE) { ++ word = UDCDR0; ++ ++ if (unlikely (bufferspace == 0)) { ++ /* this happens when the driver's buffer ++ * is smaller than what the host sent. ++ * discard the extra data. ++ */ ++ if (req->req.status != -EOVERFLOW) ++ dev_info(ep->dev->dev, "%s overflow\n", ep->usb_ep->name); ++ req->req.status = -EOVERFLOW; ++ } else { ++ *buf++ = word; ++ req->req.actual += 4; ++ bufferspace -= 4; ++ } ++ } ++ ++ UDCCSR0 = UDCCSR0_OPC ; ++ ++ /* completion */ ++ if (req->req.actual >= req->req.length) ++ return 1; ++ ++ /* finished that packet. the next one may be waiting... */ ++ return 0; ++} ++ ++#ifdef USE_DMA ++ ++#define MAX_IN_DMA ((DCMD_LENGTH + 1) - BULK_FIFO_SIZE) ++static void kick_dma(struct pxa27x_ep *ep, struct pxa27x_request *req) ++{ ++ u32 dcmd = 0; ++ u32 len = req->req.length; ++ u32 buf = req->req.dma; ++ u32 fifo = io_v2p((u32)ep->reg_udcdr); ++ ++ buf += req->req.actual; ++ len -= req->req.actual; ++ ep->dma_con = 0; ++ ++ DMSG("%s: req:0x%p length:%d, actual:%d dma:%d\n", ++ __FUNCTION__, &req->req, req->req.length, ++ req->req.actual,ep->dma); ++ ++ /* no-descriptor mode can be simple for bulk-in, iso-in, iso-out */ ++ DCSR(ep->dma) = DCSR_NODESC; ++ if (buf & 0x3) ++ DALGN |= 1 << ep->dma; ++ else ++ DALGN &= ~(1 << ep->dma); ++ ++ if (ep->dir_in) { ++ DSADR(ep->dma) = buf; ++ DTADR(ep->dma) = fifo; ++ if (len > MAX_IN_DMA) { ++ len= MAX_IN_DMA; ++ ep->dma_con =1 ; ++ } else if (len >= ep->usb_ep->maxpacket) { ++ if ((ep->dma_con = (len % ep->usb_ep->maxpacket) != 0)) ++ len = ep->usb_ep->maxpacket; ++ } ++ dcmd = len | DCMD_BURST32 | DCMD_WIDTH4 | DCMD_ENDIRQEN ++ | DCMD_FLOWTRG | DCMD_INCSRCADDR; ++ } else { ++ DSADR(ep->dma) = fifo; ++ DTADR(ep->dma) = buf; ++ dcmd = len | DCMD_BURST32 | DCMD_WIDTH4 | DCMD_ENDIRQEN ++ | DCMD_FLOWSRC | DCMD_INCTRGADDR; ++ } ++ *ep->reg_udccsr = UDCCSR_DME; ++ DCMD(ep->dma) = dcmd; ++ DCSR(ep->dma) = DCSR_NODESC | DCSR_EORIRQEN \ ++ | ((ep->dir_in) ? DCSR_STOPIRQEN : 0); ++ *ep->reg_drcmr = ep->dma | DRCMR_MAPVLD; ++ DCSR(ep->dma) |= DCSR_RUN; ++} ++ ++static void cancel_dma(struct pxa27x_ep *ep) ++{ ++ struct pxa27x_request *req; ++ u32 tmp; ++ ++ if (DCSR(ep->dma) == 0 || list_empty(&ep->queue)) ++ return; ++ ++ DMSG("hehe dma:%d,dcsr:0x%x\n", ep->dma, DCSR(ep->dma)); ++ DCSR(ep->dma) = 0; ++ while ((DCSR(ep->dma) & DCSR_STOPSTATE) == 0) ++ cpu_relax(); ++ ++ req = list_entry(ep->queue.next, struct pxa27x_request, queue); ++ tmp = DCMD(ep->dma) & DCMD_LENGTH; ++ req->req.actual = req->req.length - tmp; ++ ++ /* the last tx packet may be incomplete, so flush the fifo. ++ * FIXME correct req.actual if we can ++ */ ++ *ep->reg_udccsr = UDCCSR_FEF; ++} ++ ++static void dma_nodesc_handler(int dmach, void *_ep) ++{ ++ struct pxa27x_ep *ep = _ep; ++ struct pxa27x_request *req, *req_next; ++ u32 dcsr, tmp, completed; ++ ++ local_irq_disable(); ++ ++ req = list_entry(ep->queue.next, struct pxa27x_request, queue); ++ ++ DMSG("%s, buf:0x%p\n",__FUNCTION__, req->req.buf); ++ ++ ep->dma_irqs++; ++ ep->dev->stats.irqs++; ++ ++ completed = 0; ++ ++ dcsr = DCSR(dmach); ++ DCSR(ep->dma) &= ~DCSR_RUN; ++ ++ if (dcsr & DCSR_BUSERR) { ++ DCSR(dmach) = DCSR_BUSERR; ++ dev_err(ep->dev->dev, "DMA Bus Error\n"); ++ req->req.status = -EIO; ++ completed = 1; ++ } else if (dcsr & DCSR_ENDINTR) { ++ DCSR(dmach) = DCSR_ENDINTR; ++ if (ep->dir_in) { ++ tmp = req->req.length - req->req.actual; ++ /* Last packet is a short one*/ ++ if (tmp < ep->usb_ep->maxpacket) { ++ int count = 0; ++ ++ *ep->reg_udccsr = UDCCSR_SP | \ ++ (*ep->reg_udccsr & UDCCSR_MASK); ++ /*Wait for packet out */ ++ while( (count++ < 10000) && \ ++ !(*ep->reg_udccsr & UDCCSR_FS)); ++ if (count >= 10000) ++ DMSG("Failed to send packet\n"); ++ else ++ DMSG("%s: short packet sent len:%d," ++ "length:%d,actual:%d\n", __FUNCTION__, ++ tmp, req->req.length, req->req.actual); ++ req->req.actual = req->req.length; ++ completed = 1; ++ /* There are still packets to transfer */ ++ } else if ( ep->dma_con) { ++ DMSG("%s: more packets,length:%d,actual:%d\n", ++ __FUNCTION__,req->req.length, ++ req->req.actual); ++ req->req.actual += ep->usb_ep->maxpacket; ++ completed = 0; ++ } else { ++ DMSG("%s: no more packets,length:%d," ++ "actual:%d\n", __FUNCTION__, ++ req->req.length, req->req.actual); ++ req->req.actual = req->req.length; ++ completed = 1; ++ } ++ } else { ++ req->req.actual = req->req.length; ++ completed = 1; ++ } ++ } else if (dcsr & DCSR_EORINTR) { //Only happened in OUT DMA ++ int remain,udccsr ; ++ ++ DCSR(dmach) = DCSR_EORINTR; ++ remain = DCMD(dmach) & DCMD_LENGTH; ++ req->req.actual = req->req.length - remain; ++ ++ udccsr = *ep->reg_udccsr; ++ if (udccsr & UDCCSR_SP) { ++ *ep->reg_udccsr = UDCCSR_PC | (udccsr & UDCCSR_MASK); ++ completed = 1; ++ } ++ DMSG("%s: length:%d actual:%d\n", ++ __FUNCTION__, req->req.length, req->req.actual); ++ } else ++ DMSG("%s: Others dma:%d DCSR:0x%x DCMD:0x%x\n", ++ __FUNCTION__, dmach, DCSR(dmach), DCMD(dmach)); ++ ++ if (likely(completed)) { ++ if (req->queue.next != &ep->queue) { ++ req_next = list_entry(req->queue.next, ++ struct pxa27x_request, queue); ++ kick_dma(ep, req_next); ++ } ++ done(ep, req, 0); ++ } else { ++ kick_dma(ep, req); ++ } ++ ++ local_irq_enable(); ++} ++ ++#endif ++/*-------------------------------------------------------------------------*/ ++ ++static int ++pxa27x_ep_queue(struct usb_ep *_ep, struct usb_request *_req, unsigned gfp_flags) ++{ ++ struct pxa27x_virt_ep *virt_ep; ++ struct pxa27x_ep *ep; ++ struct pxa27x_request *req; ++ struct pxa27x_udc *dev; ++ unsigned long flags; ++ ++ virt_ep = container_of(_ep, struct pxa27x_virt_ep, usb_ep); ++ ep = virt_ep->pxa_ep; ++ ++ req = container_of(_req, struct pxa27x_request, req); ++ if (unlikely (!_req || !_req->complete || !_req->buf|| ++ !list_empty(&req->queue))) { ++ DMSG("%s, bad params\n", __FUNCTION__); ++ return -EINVAL; ++ } ++ ++ if (unlikely (!_ep || (!ep->desc && _ep->name != ep0name))) { ++ DMSG("%s, bad ep\n", __FUNCTION__); ++ return -EINVAL; ++ } ++ ++ DMSG("%s, ep point %d is queue\n", __FUNCTION__, ep->ep_num); ++ ++ dev = ep->dev; ++ if (unlikely (!dev->driver ++ || dev->gadget.speed == USB_SPEED_UNKNOWN)) { ++ DMSG("%s, bogus device state\n", __FUNCTION__); ++ return -ESHUTDOWN; ++ } ++ ++ /* iso is always one packet per request, that's the only way ++ * we can report per-packet status. that also helps with dma. ++ */ ++ if (unlikely (ep->ep_type == USB_ENDPOINT_XFER_ISOC ++ && req->req.length > le16_to_cpu ++ (ep->desc->wMaxPacketSize))) ++ return -EMSGSIZE; ++ ++#ifdef USE_DMA ++ // FIXME caller may already have done the dma mapping ++ if (ep->dma >= 0) { ++ _req->dma = dma_map_single(dev->dev, _req->buf, _req->length, ++ (ep->dir_in) ? DMA_TO_DEVICE : DMA_FROM_DEVICE); ++ } ++#endif ++ ++ DBG(DBG_NOISY, "%s queue req %p, len %d buf %p\n", ++ _ep->name, _req, _req->length, _req->buf); ++ ++ local_irq_save(flags); ++ ++ _req->status = -EINPROGRESS; ++ _req->actual = 0; ++ ++ /* kickstart this i/o queue? */ ++ if (list_empty(&ep->queue) && !ep->stopped) { ++ if (ep->desc == 0 /* ep0 */) { ++ unsigned length = _req->length; ++ ++ switch (dev->ep0state) { ++ case EP0_IN_DATA_PHASE: ++ dev->stats.write.ops++; ++ if (write_ep0_fifo(ep, req)) ++ req = 0; ++ break; ++ ++ case EP0_OUT_DATA_PHASE: ++ dev->stats.read.ops++; ++ if (dev->req_pending) ++ ep0start(dev, UDCCSR0_IPR, "OUT"); ++ if (length == 0 || ((UDCCSR0 & UDCCSR0_RNE) != 0 ++ && read_ep0_fifo(ep, req))) { ++ ep0_idle(dev); ++ done(ep, req, 0); ++ req = 0; ++ } ++ break; ++ case EP0_NO_ACTION: ++ ep0_idle(dev); ++ req=0; ++ break; ++ default: ++ DMSG("ep0 i/o, odd state %d\n", dev->ep0state); ++ local_irq_restore (flags); ++ return -EL2HLT; ++ } ++#ifdef USE_DMA ++ /* either start dma or prime pio pump */ ++ } else if (ep->dma >= 0) { ++ kick_dma(ep, req); ++#endif ++ /* can the FIFO can satisfy the request immediately? */ ++ } else if (ep->dir_in && (*ep->reg_udccsr & UDCCSR_FS) != 0 ++ && write_fifo(ep, req)) { ++ req = 0; ++ } else if ((*ep->reg_udccsr & UDCCSR_FS) != 0 ++ && read_fifo(ep, req)) { ++ req = 0; ++ } ++ DMSG("req:%p,ep->desc:%p,ep->dma:%d\n", req, ep->desc, ep->dma); ++ if (likely (req && ep->desc) && ep->dma < 0) ++ pio_irq_enable(ep->pxa_ep_num); ++ } ++ ++ /* pio or dma irq handler advances the queue. */ ++ if (likely (req != 0)) ++ list_add_tail(&req->queue, &ep->queue); ++ local_irq_restore(flags); ++ ++ return 0; ++} ++ ++ ++/* ++ * nuke - dequeue ALL requests ++ */ ++static void nuke(struct pxa27x_ep *ep, int status) ++{ ++ struct pxa27x_request *req; ++ ++ /* called with irqs blocked */ ++#ifdef USE_DMA ++ if (ep->dma >= 0 && !ep->stopped) ++ cancel_dma(ep); ++#endif ++ while (!list_empty(&ep->queue)) { ++ req = list_entry(ep->queue.next, struct pxa27x_request, queue); ++ done(ep, req, status); ++ } ++ if (ep->desc) ++ pio_irq_disable(ep->pxa_ep_num); ++} ++ ++ ++/* dequeue JUST ONE request */ ++static int pxa27x_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) ++{ ++ struct pxa27x_virt_ep *virt_ep = container_of(_ep, struct pxa27x_virt_ep, usb_ep); ++ struct pxa27x_ep *ep = virt_ep->pxa_ep; ++ struct pxa27x_request *req; ++ unsigned long flags; ++ ++ if (!_ep || _ep->name == ep0name) ++ return -EINVAL; ++ ++ local_irq_save(flags); ++ ++ /* make sure it's actually queued on this endpoint */ ++ list_for_each_entry(req, &ep->queue, queue) { ++ if (&req->req == _req) ++ break; ++ } ++ if (&req->req != _req) { ++ local_irq_restore(flags); ++ return -EINVAL; ++ } ++ ++#ifdef USE_DMA ++ if (ep->dma >= 0 && ep->queue.next == &req->queue && !ep->stopped) { ++ cancel_dma(ep); ++ done(ep, req, -ECONNRESET); ++ /* restart i/o */ ++ if (!list_empty(&ep->queue)) { ++ req = list_entry(ep->queue.next, ++ struct pxa27x_request, queue); ++ kick_dma(ep, req); ++ } ++ } else ++#endif ++ done(ep, req, -ECONNRESET); ++ ++ local_irq_restore(flags); ++ return 0; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static int pxa27x_ep_set_halt(struct usb_ep *_ep, int value) ++{ ++ struct pxa27x_virt_ep *virt_ep = container_of(_ep, struct pxa27x_virt_ep, usb_ep); ++ struct pxa27x_ep *ep = virt_ep->pxa_ep; ++ unsigned long flags; ++ ++ DMSG("%s is called\n", __FUNCTION__); ++ if (unlikely (!_ep || (!ep->desc && _ep->name != ep0name)) ++ || ep->ep_type == USB_ENDPOINT_XFER_ISOC) { ++ DMSG("%s, bad ep\n", __FUNCTION__); ++ return -EINVAL; ++ } ++ if (value == 0) { ++ /* this path (reset toggle+halt) is needed to implement ++ * SET_INTERFACE on normal hardware. but it can't be ++ * done from software on the PXA UDC, and the hardware ++ * forgets to do it as part of SET_INTERFACE automagic. ++ */ ++ DMSG("only host can clear %s halt\n", _ep->name); ++ return -EROFS; ++ } ++ ++ local_irq_save(flags); ++ ++ if (ep->dir_in && ((*ep->reg_udccsr & UDCCSR_FS) == 0 ++ || !list_empty(&ep->queue))) { ++ local_irq_restore(flags); ++ return -EAGAIN; ++ } ++ ++ /* FST bit is the same for control, bulk in, bulk out, interrupt in */ ++ *ep->reg_udccsr = UDCCSR_FST|UDCCSR_FEF; ++ ++ /* ep0 needs special care */ ++ if (!ep->desc) { ++ start_watchdog(ep->dev); ++ ep->dev->req_pending = 0; ++ ep->dev->ep0state = EP0_STALL; ++ ++ /* and bulk/intr endpoints like dropping stalls too */ ++ } else { ++ unsigned i; ++ for (i = 0; i < 1000; i += 20) { ++ if (*ep->reg_udccsr & UDCCSR_SST) ++ break; ++ udelay(20); ++ } ++ } ++ local_irq_restore(flags); ++ ++ DBG(DBG_VERBOSE, "%s halt\n", _ep->name); ++ return 0; ++} ++ ++static int pxa27x_ep_fifo_status(struct usb_ep *_ep) ++{ ++ struct pxa27x_virt_ep *virt_ep = container_of(_ep, struct pxa27x_virt_ep, usb_ep); ++ struct pxa27x_ep *ep = virt_ep->pxa_ep; ++ ++ if (!_ep) { ++ DMSG("%s, bad ep\n", __FUNCTION__); ++ return -ENODEV; ++ } ++ /* pxa can't report unclaimed bytes from IN fifos */ ++ if (ep->dir_in) ++ return -EOPNOTSUPP; ++ if (ep->dev->gadget.speed == USB_SPEED_UNKNOWN ++ || (*ep->reg_udccsr & UDCCSR_FS) == 0) ++ return 0; ++ else ++ return (*ep->reg_udcbcr & 0xfff) + 1; ++} ++ ++static void pxa27x_ep_fifo_flush(struct usb_ep *_ep) ++{ ++ struct pxa27x_virt_ep *virt_ep = container_of(_ep, struct pxa27x_virt_ep, usb_ep); ++ struct pxa27x_ep *ep = virt_ep->pxa_ep; ++ ++ DMSG("pxa27x_ep_fifo_flush\n"); ++ ++ if (!_ep || _ep->name == ep0name || !list_empty(&ep->queue)) { ++ DMSG("%s, bad ep\n", __FUNCTION__); ++ return; ++ } ++ ++ /* toggle and halt bits stay unchanged */ ++ ++ /* for OUT, just read and discard the FIFO contents. */ ++ if (!ep->dir_in) { ++ while (((*ep->reg_udccsr) & UDCCSR_BNE) != 0) ++ (void) *ep->reg_udcdr; ++ return; ++ } ++ ++ /* most IN status is the same, but ISO can't stall */ ++ *ep->reg_udccsr = UDCCSR_PC|UDCCSR_FST|UDCCSR_TRN ++ | (ep->ep_type == USB_ENDPOINT_XFER_ISOC) ++ ? 0 : UDCCSR_SST; ++} ++ ++ ++static struct usb_ep_ops pxa27x_ep_ops = { ++ .enable = pxa27x_ep_enable, ++ .disable = pxa27x_ep_disable, ++ ++ .alloc_request = pxa27x_ep_alloc_request, ++ .free_request = pxa27x_ep_free_request, ++ ++ .queue = pxa27x_ep_queue, ++ .dequeue = pxa27x_ep_dequeue, ++ ++ .set_halt = pxa27x_ep_set_halt, ++ .fifo_status = pxa27x_ep_fifo_status, ++ .fifo_flush = pxa27x_ep_fifo_flush, ++}; ++ ++ ++/* --------------------------------------------------------------------------- ++ * device-scoped parts of the api to the usb controller hardware ++ * --------------------------------------------------------------------------- ++ */ ++ ++static inline unsigned int validate_fifo_size(u8 bmAttributes) ++{ ++ switch (bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { ++ case USB_ENDPOINT_XFER_CONTROL: ++ return EP0_FIFO_SIZE; ++ break; ++ case USB_ENDPOINT_XFER_ISOC: ++ return ISO_FIFO_SIZE; ++ break; ++ case USB_ENDPOINT_XFER_BULK: ++ return BULK_FIFO_SIZE; ++ break; ++ case USB_ENDPOINT_XFER_INT: ++ return INT_FIFO_SIZE; ++ break; ++ default: ++ break; ++ } ++} ++ ++static void pxa27x_ep_free(struct usb_gadget *gadget, struct usb_ep *_ep) ++{ ++ struct pxa27x_udc *dev = the_controller; ++ struct pxa27x_virt_ep *virt_ep; ++ int i; ++ ++ virt_ep = container_of(_ep, struct pxa27x_virt_ep, usb_ep); ++ ++ for (i = 1; i < UDC_EP_NUM; i++) { ++ if (dev->ep[i].usb_ep == &virt_ep->usb_ep) { ++ if (dev->ep[i].desc) { ++ virt_ep->pxa_ep = &dev->ep[i]; ++ pxa27x_ep_disable(&virt_ep->usb_ep); ++ } ++ dev->ep[i].usb_ep = NULL; ++ } ++ } ++ ++ if (!list_empty(&virt_ep->usb_ep.ep_list)) ++ list_del_init(&virt_ep->usb_ep.ep_list); ++ ++ kfree(virt_ep->usb_ep.name); ++ kfree(virt_ep); ++} ++ ++static void pxa27x_ep_freeall(struct usb_gadget *gadget) ++{ ++ struct pxa27x_udc *dev = the_controller; ++ int i; ++ ++ for (i = 1; i < UDC_EP_NUM; i++) { ++ if(dev->ep[i].usb_ep) ++ pxa27x_ep_free(gadget, dev->ep[i].usb_ep); ++ } ++} ++ ++#define NAME_SIZE 18 ++ ++static int pxa27x_find_free_ep(struct pxa27x_udc *dev) ++{ ++ int i; ++ for (i = 1; i < UDC_EP_NUM; i++) { ++ if(!dev->ep[i].assigned) ++ return i; ++ } ++ return -1; ++} ++ ++/* ++ * Endpoint Allocation/Configuration ++ * ++ * pxa27x endpoint configuration is fixed when the device is enabled. Any pxa ++ * endpoint is only active in one configuration, interface and alternate ++ * interface combination so to support gadget drivers, we map one usb_ep to ++ * one of several pxa ep's. One pxa endpoint is assigned per configuration ++ * combination. ++ */ ++static struct usb_ep* pxa27x_ep_alloc(struct usb_gadget *gadget, struct usb_endpoint_descriptor *desc, ++ struct usb_endpoint_config *epconfig, int configs) ++{ ++ struct pxa27x_udc *dev = the_controller; ++ struct pxa27x_virt_ep *virt_ep; ++ unsigned int i, fifo_size; ++ char *name; ++ ++ if (unlikely(configs < 1)) { ++ dev_err(dev->dev, "%s: Error in config data\n", __FUNCTION__); ++ return NULL; ++ } ++ ++ virt_ep = kmalloc(sizeof(struct pxa27x_virt_ep), GFP_KERNEL); ++ name = kmalloc(NAME_SIZE, GFP_KERNEL); ++ if (!virt_ep || !name) { ++ dev_err(dev->dev, "%s: -ENOMEM\n", __FUNCTION__); ++ kfree(name); ++ kfree(virt_ep); ++ return NULL; ++ } ++ ++ if (!(desc->wMaxPacketSize)) { ++ fifo_size = validate_fifo_size(desc->bmAttributes); ++ desc->wMaxPacketSize = fifo_size; ++ } else { ++ fifo_size = desc->wMaxPacketSize; ++ } ++ ++ DMSG("pxa27x_ep_alloc: bLength: %d, bDescriptorType: %x, bEndpointAddress: %x,\n" ++ " bmAttributes: %x, wMaxPacketSize: %d\n", desc->bLength, ++ desc->bDescriptorType, desc->bEndpointAddress, desc->bmAttributes, ++ desc->wMaxPacketSize); ++ ++ if (!(desc->bEndpointAddress & 0xF)) ++ desc->bEndpointAddress |= dev->ep_num; ++ ++ for (i = 0; i < configs; i++) ++ { ++ struct pxa27x_ep *pxa_ep; ++ int j; ++ ++ DMSG("pxa27x_ep_alloc: config: %d, interface: %d, altinterface: %x,\n", ++ epconfig->config, epconfig->interface, epconfig->altinterface); ++ ++ j = pxa27x_find_free_ep(dev); ++ ++ if (unlikely(j < 0)) { ++ dev_err(dev->dev, "pxa27x_ep_alloc: Failed to find a spare endpoint\n"); ++ pxa27x_ep_free(gadget, &virt_ep->usb_ep); ++ return NULL; ++ } ++ ++ pxa_ep = &dev->ep[j]; ++ ++ if (i == 0) ++ virt_ep->pxa_ep = pxa_ep; ++ ++ pxa_ep->assigned = 1; ++ pxa_ep->ep_num = dev->ep_num; ++ pxa_ep->pxa_ep_num = j; ++ pxa_ep->usb_ep = &virt_ep->usb_ep; ++ pxa_ep->dev = dev; ++ pxa_ep->desc = desc; ++ pxa_ep->pio_irqs = pxa_ep->dma_irqs = 0; ++ pxa_ep->dma = -1; ++ ++ pxa_ep->fifo_size = fifo_size; ++ pxa_ep->dir_in = (desc->bEndpointAddress & USB_DIR_IN) ? 1 : 0; ++ pxa_ep->ep_type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; ++ pxa_ep->stopped = 1; ++ pxa_ep->dma_con = 0; ++ pxa_ep->config = epconfig->config; ++ pxa_ep->interface = epconfig->interface; ++ pxa_ep->aisn = epconfig->altinterface; ++ ++ pxa_ep->reg_udccsr = &UDCCSR0 + j; ++ pxa_ep->reg_udcbcr = &UDCBCR0 + j; ++ pxa_ep->reg_udcdr = &UDCDR0 + j ; ++ pxa_ep->reg_udccr = &UDCCRA - 1 + j; ++#ifdef USE_DMA ++ pxa_ep->reg_drcmr = &DRCMR24 + j; ++#endif ++ ++ /* Configure UDCCR */ ++ *pxa_ep->reg_udccr = ((pxa_ep->config << UDCCONR_CN_S) & UDCCONR_CN) ++ | ((pxa_ep->interface << UDCCONR_IN_S) & UDCCONR_IN) ++ | ((pxa_ep->aisn << UDCCONR_AISN_S) & UDCCONR_AISN) ++ | ((dev->ep_num << UDCCONR_EN_S) & UDCCONR_EN) ++ | ((pxa_ep->ep_type << UDCCONR_ET_S) & UDCCONR_ET) ++ | ((pxa_ep->dir_in) ? UDCCONR_ED : 0) ++ | ((min(pxa_ep->fifo_size, (unsigned)desc->wMaxPacketSize) << UDCCONR_MPS_S ) & UDCCONR_MPS) ++ | UDCCONR_EE; ++// | UDCCONR_DE | UDCCONR_EE; ++ ++ ++ ++#ifdef USE_DMA ++ /* Only BULK use DMA */ ++ if ((pxa_ep->ep_type & USB_ENDPOINT_XFERTYPE_MASK)\ ++ == USB_ENDPOINT_XFER_BULK) ++ *pxa_ep->reg_udccsr = UDCCSR_DME; ++#endif ++ ++ DMSG("UDCCR: 0x%p is 0x%x\n", pxa_ep->reg_udccr,*pxa_ep->reg_udccr); ++ ++ epconfig++; ++ } ++ ++ /* Fill ep name*/ ++ switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { ++ case USB_ENDPOINT_XFER_BULK: ++ sprintf(name, "ep%d%s-bulk", dev->ep_num, ++ ((desc->bEndpointAddress & USB_DIR_IN) ? "in":"out")); ++ break; ++ case USB_ENDPOINT_XFER_INT: ++ sprintf(name, "ep%d%s-intr", dev->ep_num, ++ ((desc->bEndpointAddress & USB_DIR_IN) ? "in":"out")); ++ break; ++ default: ++ sprintf(name, "ep%d%s", dev->ep_num, ++ ((desc->bEndpointAddress & USB_DIR_IN) ? "in":"out")); ++ break; ++ } ++ ++ virt_ep->desc = desc; ++ virt_ep->usb_ep.name = name; ++ virt_ep->usb_ep.ops = &pxa27x_ep_ops; ++ virt_ep->usb_ep.maxpacket = min((ushort)fifo_size, desc->wMaxPacketSize); ++ ++ list_add_tail(&virt_ep->usb_ep.ep_list, &gadget->ep_list); ++ ++ dev->ep_num++; ++ return &virt_ep->usb_ep; ++} ++ ++static int pxa27x_udc_get_frame(struct usb_gadget *_gadget) ++{ ++ return (UDCFNR & 0x7FF); ++} ++ ++static int pxa27x_udc_wakeup(struct usb_gadget *_gadget) ++{ ++ /* host may not have enabled remote wakeup */ ++ if ((UDCCR & UDCCR_DWRE) == 0) ++ return -EHOSTUNREACH; ++ udc_set_mask_UDCCR(UDCCR_UDR); ++ return 0; ++} ++ ++static const struct usb_gadget_ops pxa27x_udc_ops = { ++ .ep_alloc = pxa27x_ep_alloc, ++ .get_frame = pxa27x_udc_get_frame, ++ .wakeup = pxa27x_udc_wakeup, ++ // current versions must always be self-powered ++}; ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++#ifdef UDC_PROC_FILE ++ ++static const char proc_node_name [] = "driver/udc"; ++ ++static int ++udc_proc_read(char *page, char **start, off_t off, int count, ++ int *eof, void *_dev) ++{ ++ char *buf = page; ++ struct pxa27x_udc *dev = _dev; ++ char *next = buf; ++ unsigned size = count; ++ unsigned long flags; ++ int i, t; ++ u32 tmp; ++ ++ if (off != 0) ++ return 0; ++ ++ local_irq_save(flags); ++ ++ /* basic device status */ ++ t = scnprintf(next, size, DRIVER_DESC "\n" ++ "%s version: %s\nGadget driver: %s\n", ++ driver_name, DRIVER_VERSION DMASTR, ++ dev->driver ? dev->driver->driver.name : "(none)"); ++ size -= t; ++ next += t; ++ ++ /* registers for device and ep0 */ ++ t = scnprintf(next, size, ++ "uicr %02X.%02X, usir %02X.%02x, ufnr %02X\n", ++ UDCICR1, UDCICR0, UDCISR1, UDCISR0, UDCFNR); ++ size -= t; ++ next += t; ++ ++ tmp = UDCCR; ++ t = scnprintf(next, size,"udccr %02X =%s%s%s%s%s%s%s%s%s%s, con=%d,inter=%d,altinter=%d\n", tmp, ++ (tmp & UDCCR_OEN) ? " oen":"", ++ (tmp & UDCCR_AALTHNP) ? " aalthnp":"", ++ (tmp & UDCCR_AHNP) ? " rem" : "", ++ (tmp & UDCCR_BHNP) ? " rstir" : "", ++ (tmp & UDCCR_DWRE) ? " dwre" : "", ++ (tmp & UDCCR_SMAC) ? " smac" : "", ++ (tmp & UDCCR_EMCE) ? " emce" : "", ++ (tmp & UDCCR_UDR) ? " udr" : "", ++ (tmp & UDCCR_UDA) ? " uda" : "", ++ (tmp & UDCCR_UDE) ? " ude" : "", ++ (tmp & UDCCR_ACN) >> UDCCR_ACN_S, ++ (tmp & UDCCR_AIN) >> UDCCR_AIN_S, ++ (tmp & UDCCR_AAISN)>> UDCCR_AAISN_S ); ++ ++ size -= t; ++ next += t; ++ ++ tmp = UDCCSR0; ++ t = scnprintf(next, size, ++ "udccsr0 %02X =%s%s%s%s%s%s%s\n", tmp, ++ (tmp & UDCCSR0_SA) ? " sa" : "", ++ (tmp & UDCCSR0_RNE) ? " rne" : "", ++ (tmp & UDCCSR0_FST) ? " fst" : "", ++ (tmp & UDCCSR0_SST) ? " sst" : "", ++ (tmp & UDCCSR0_DME) ? " dme" : "", ++ (tmp & UDCCSR0_IPR) ? " ipr" : "", ++ (tmp & UDCCSR0_OPC) ? " opc" : ""); ++ size -= t; ++ next += t; ++ ++ if (!dev->driver) ++ goto done; ++ ++ t = scnprintf(next, size, "ep0 IN %lu/%lu, OUT %lu/%lu\nirqs %lu\n\n", ++ dev->stats.write.bytes, dev->stats.write.ops, ++ dev->stats.read.bytes, dev->stats.read.ops, ++ dev->stats.irqs); ++ size -= t; ++ next += t; ++ ++ /* dump endpoint queues */ ++ for (i = 0; i < UDC_EP_NUM; i++) { ++ struct pxa27x_ep *ep = &dev->ep [i]; ++ struct pxa27x_request *req; ++ int t; ++ ++ if (i != 0) { ++ const struct usb_endpoint_descriptor *d; ++ ++ d = ep->desc; ++ if (!d) ++ continue; ++ tmp = *dev->ep [i].reg_udccsr; ++ t = scnprintf(next, size, ++ "%d max %d %s udccs %02x udccr:0x%x\n", ++ i, le16_to_cpu (d->wMaxPacketSize), ++ (ep->dma >= 0) ? "dma" : "pio", tmp, ++ *dev->ep[i].reg_udccr); ++ /* TODO translate all five groups of udccs bits! */ ++ ++ } else /* ep0 should only have one transfer queued */ ++ t = scnprintf(next, size, "ep0 max 16 pio irqs %lu\n", ++ ep->pio_irqs); ++ if (t <= 0 || t > size) ++ goto done; ++ size -= t; ++ next += t; ++ ++ if (list_empty(&ep->queue)) { ++ t = scnprintf(next, size, "\t(nothing queued)\n"); ++ if (t <= 0 || t > size) ++ goto done; ++ size -= t; ++ next += t; ++ continue; ++ } ++ list_for_each_entry(req, &ep->queue, queue) { ++#ifdef USE_DMA ++ if (ep->dma >= 0 && req->queue.prev == &ep->queue) ++ t = scnprintf(next, size, "\treq %p len %d/%d " ++ "buf %p (dma%d dcmd %08x)\n", ++ &req->req, req->req.actual, ++ req->req.length, req->req.buf, ++ ep->dma, DCMD(ep->dma) ++ /* low 13 bits == bytes-to-go */); ++ else ++#endif ++ t = scnprintf(next, size, ++ "\treq %p len %d/%d buf %p\n", ++ &req->req, req->req.actual, ++ req->req.length, req->req.buf); ++ if (t <= 0 || t > size) ++ goto done; ++ size -= t; ++ next += t; ++ } ++ } ++ ++done: ++ local_irq_restore(flags); ++ *eof = 1; ++ return count - size; ++} ++ ++#define create_proc_files() \ ++ create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev) ++#define remove_proc_files() \ ++ remove_proc_entry(proc_node_name, NULL) ++ ++#else /* !UDC_PROC_FILE */ ++#define create_proc_files() do {} while (0) ++#define remove_proc_files() do {} while (0) ++ ++#endif /* UDC_PROC_FILE */ ++ ++/* "function" sysfs attribute */ ++static ssize_t show_function(struct device *_dev, struct device_attribute *attr, char *buf) ++{ ++ struct pxa27x_udc *dev = dev_get_drvdata(_dev); ++ ++ if (!dev->driver || !dev->driver->function ++ || strlen(dev->driver->function) > PAGE_SIZE) ++ return 0; ++ return scnprintf(buf, PAGE_SIZE, "%s\n", dev->driver->function); ++} ++static DEVICE_ATTR(function, S_IRUGO, show_function, NULL); ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * udc_disable - disable USB device controller ++ */ ++static void udc_disable(struct pxa27x_udc *dev) ++{ ++ UDCICR0 = UDCICR1 = 0x00000000; ++ ++ udc_clear_mask_UDCCR(UDCCR_UDE); ++ ++ /* Disable clock for USB device */ ++ pxa_set_cken(CKEN_USB, 0); ++ ++ ep0_idle(dev); ++ dev->gadget.speed = USB_SPEED_UNKNOWN; ++ dev->mach->udc_command(PXA2XX_UDC_CMD_DISCONNECT); ++} ++ ++ ++/* ++ * udc_reinit - initialize software state ++ */ ++static void udc_reinit(struct pxa27x_udc *dev) ++{ ++ u32 i; ++ ++ dev->ep0state = EP0_IDLE; ++ ++ /* basic endpoint records init */ ++ for (i = 0; i < UDC_EP_NUM; i++) { ++ struct pxa27x_ep *ep = &dev->ep[i]; ++ ++ ep->stopped = 0; ++ ep->pio_irqs = ep->dma_irqs = 0; ++ } ++ dev->configuration = 0; ++ dev->interface = 0; ++ dev->alternate = 0; ++ /* the rest was statically initialized, and is read-only */ ++} ++ ++/* until it's enabled, this UDC should be completely invisible ++ * to any USB host. ++ */ ++static void udc_enable(struct pxa27x_udc *dev) ++{ ++ udc_clear_mask_UDCCR(UDCCR_UDE); ++ ++ /* Enable clock for USB device */ ++ pxa_set_cken(CKEN_USB, 1); ++ ++ UDCICR0 = UDCICR1 = 0; ++ ++ ep0_idle(dev); ++ dev->gadget.speed = USB_SPEED_FULL; ++ dev->stats.irqs = 0; ++ ++ udc_set_mask_UDCCR(UDCCR_UDE); ++ udelay(2); ++ if (UDCCR & UDCCR_EMCE) ++ dev_err(dev->dev, "There are error in configuration, udc disabled\n"); ++ ++ /* caller must be able to sleep in order to cope ++ * with startup transients. ++ */ ++ msleep(100); ++ ++ /* enable suspend/resume and reset irqs */ ++ UDCICR1 = UDCICR1_IECC | UDCICR1_IERU | UDCICR1_IESU | UDCICR1_IERS; ++ ++ /* enable ep0 irqs */ ++ UDCICR0 = UDCICR_INT(0,UDCICR_INT_MASK); ++ ++ DMSG("Connecting\n"); ++ /* RPFIXME */ ++ UP2OCR = UP2OCR_HXOE | UP2OCR_DPPUE | UP2OCR_DPPUBE; ++ //dev->mach->udc_command(PXA2XX_UDC_CMD_CONNECT); ++} ++ ++ ++/* when a driver is successfully registered, it will receive ++ * control requests including set_configuration(), which enables ++ * non-control requests. then usb traffic follows until a ++ * disconnect is reported. then a host may connect again, or ++ * the driver might get unbound. ++ */ ++int usb_gadget_register_driver(struct usb_gadget_driver *driver) ++{ ++ struct pxa27x_udc *dev = the_controller; ++ int retval; ++ ++ if (!driver || driver->speed != USB_SPEED_FULL || !driver->bind ++ || !driver->unbind || !driver->disconnect || !driver->setup) ++ return -EINVAL; ++ if (!dev) ++ return -ENODEV; ++ if (dev->driver) ++ return -EBUSY; ++ ++ udc_disable(dev); ++ udc_init_ep(dev); ++ udc_reinit(dev); ++ ++ /* first hook up the driver ... */ ++ dev->driver = driver; ++ dev->gadget.dev.driver = &driver->driver; ++ dev->ep_num = 1; ++ ++ retval = device_add(&dev->gadget.dev); ++ if (retval) { ++ DMSG("device_add error %d\n", retval); ++ goto add_fail; ++ } ++ retval = driver->bind(&dev->gadget); ++ if (retval) { ++ DMSG("bind to driver %s --> error %d\n", ++ driver->driver.name, retval); ++ goto bind_fail; ++ } ++ retval = device_create_file(dev->dev, &dev_attr_function); ++ if (retval) { ++ DMSG("device_create_file failed: %d\n", retval); ++ goto create_file_fail; ++ } ++ ++ /* ... then enable host detection and ep0; and we're ready ++ * for set_configuration as well as eventual disconnect. ++ * NOTE: this shouldn't power up until later. ++ */ ++ DMSG("registered gadget driver '%s'\n", driver->driver.name); ++ udc_enable(dev); ++ dump_state(dev); ++ return 0; ++ ++create_file_fail: ++ driver->unbind(&dev->gadget); ++bind_fail: ++ device_del(&dev->gadget.dev); ++add_fail: ++ dev->driver = 0; ++ dev->gadget.dev.driver = 0; ++ return retval; ++} ++EXPORT_SYMBOL(usb_gadget_register_driver); ++ ++static void ++stop_activity(struct pxa27x_udc *dev, struct usb_gadget_driver *driver) ++{ ++ int i; ++ ++ DMSG("Trace path 1\n"); ++ /* don't disconnect drivers more than once */ ++ if (dev->gadget.speed == USB_SPEED_UNKNOWN) ++ driver = 0; ++ dev->gadget.speed = USB_SPEED_UNKNOWN; ++ ++ /* prevent new request submissions, kill any outstanding requests */ ++ for (i = 0; i < UDC_EP_NUM; i++) { ++ struct pxa27x_ep *ep = &dev->ep[i]; ++ ++ ep->stopped = 1; ++ nuke(ep, -ESHUTDOWN); ++ } ++ del_timer_sync(&dev->timer); ++ ++ /* report disconnect; the driver is already quiesced */ ++ if (driver) ++ driver->disconnect(&dev->gadget); ++ ++ /* re-init driver-visible data structures */ ++ udc_reinit(dev); ++} ++ ++int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) ++{ ++ struct pxa27x_udc *dev = the_controller; ++ ++ if (!dev) ++ return -ENODEV; ++ if (!driver || driver != dev->driver) ++ return -EINVAL; ++ ++ local_irq_disable(); ++ udc_disable(dev); ++ stop_activity(dev, driver); ++ local_irq_enable(); ++ ++ driver->unbind(&dev->gadget); ++ pxa27x_ep_freeall(&dev->gadget); ++ dev->driver = 0; ++ ++ device_del(&dev->gadget.dev); ++ device_remove_file(dev->dev, &dev_attr_function); ++ ++ DMSG("unregistered gadget driver '%s'\n", driver->driver.name); ++ dump_state(dev); ++ return 0; ++} ++EXPORT_SYMBOL(usb_gadget_unregister_driver); ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++static inline void clear_ep_state(struct pxa27x_udc *dev) ++{ ++ unsigned i; ++ ++ /* hardware SET_{CONFIGURATION,INTERFACE} automagic resets endpoint ++ * fifos, and pending transactions mustn't be continued in any case. ++ */ ++ for (i = 1; i < UDC_EP_NUM; i++) ++ nuke(&dev->ep[i], -ECONNABORTED); ++} ++ ++static void udc_watchdog(unsigned long _dev) ++{ ++ struct pxa27x_udc *dev = (void *)_dev; ++ ++ local_irq_disable(); ++ if (dev->ep0state == EP0_STALL ++ && (UDCCSR0 & UDCCSR0_FST) == 0 ++ && (UDCCSR0 & UDCCSR0_SST) == 0) { ++ UDCCSR0 = UDCCSR0_FST|UDCCSR0_FTF; ++ DBG(DBG_VERBOSE, "ep0 re-stall\n"); ++ start_watchdog(dev); ++ } ++ local_irq_enable(); ++} ++ ++static void handle_ep0(struct pxa27x_udc *dev) ++{ ++ u32 udccsr0 = UDCCSR0; ++ struct pxa27x_ep *ep = &dev->ep[0]; ++ struct pxa27x_request *req; ++ union { ++ struct usb_ctrlrequest r; ++ u8 raw[8]; ++ u32 word[2]; ++ } u; ++ ++ if (list_empty(&ep->queue)) ++ req = 0; ++ else ++ req = list_entry(ep->queue.next, struct pxa27x_request, queue); ++ ++ /* clear stall status */ ++ if (udccsr0 & UDCCSR0_SST) { ++ nuke(ep, -EPIPE); ++ UDCCSR0 = UDCCSR0_SST; ++ del_timer(&dev->timer); ++ ep0_idle(dev); ++ } ++ ++ /* previous request unfinished? non-error iff back-to-back ... */ ++ if ((udccsr0 & UDCCSR0_SA) != 0 && dev->ep0state != EP0_IDLE) { ++ nuke(ep, 0); ++ del_timer(&dev->timer); ++ ep0_idle(dev); ++ } ++ ++ switch (dev->ep0state) { ++ case EP0_NO_ACTION: ++ dev_info(dev->dev, "%s: Busy\n", __FUNCTION__); ++ /*Fall through */ ++ case EP0_IDLE: ++ /* late-breaking status? */ ++ udccsr0 = UDCCSR0; ++ ++ /* start control request? */ ++ if (likely((udccsr0 & (UDCCSR0_OPC|UDCCSR0_SA|UDCCSR0_RNE)) ++ == (UDCCSR0_OPC|UDCCSR0_SA|UDCCSR0_RNE))) { ++ int i; ++ ++ nuke(ep, -EPROTO); ++ /* read SETUP packet */ ++ for (i = 0; i < 2; i++) { ++ if (unlikely(!(UDCCSR0 & UDCCSR0_RNE))) { ++bad_setup: ++ DMSG("SETUP %d!\n", i); ++ goto stall; ++ } ++ u.word [i] = UDCDR0; ++ } ++ if (unlikely((UDCCSR0 & UDCCSR0_RNE) != 0)) ++ goto bad_setup; ++ ++ le16_to_cpus(&u.r.wValue); ++ le16_to_cpus(&u.r.wIndex); ++ le16_to_cpus(&u.r.wLength); ++ ++ DBG(DBG_VERBOSE, "SETUP %02x.%02x v%04x i%04x l%04x\n", ++ u.r.bRequestType, u.r.bRequest, ++ u.r.wValue, u.r.wIndex, u.r.wLength); ++ /* cope with automagic for some standard requests. */ ++ dev->req_std = (u.r.bRequestType & USB_TYPE_MASK) ++ == USB_TYPE_STANDARD; ++ dev->req_config = 0; ++ dev->req_pending = 1; ++#if 0 ++ switch (u.r.bRequest) { ++ /* hardware was supposed to hide this */ ++ case USB_REQ_SET_CONFIGURATION: ++ case USB_REQ_SET_INTERFACE: ++ case USB_REQ_SET_ADDRESS: ++ dev_err(dev->dev, "Should not come here\n"); ++ break; ++ } ++ ++#endif ++ if (u.r.bRequestType & USB_DIR_IN) ++ dev->ep0state = EP0_IN_DATA_PHASE; ++ else ++ dev->ep0state = EP0_OUT_DATA_PHASE; ++ i = dev->driver->setup(&dev->gadget, &u.r); ++ ++ if (i < 0) { ++ /* hardware automagic preventing STALL... */ ++ if (dev->req_config) { ++ /* hardware sometimes neglects to tell ++ * tell us about config change events, ++ * so later ones may fail... ++ */ ++ WARN("config change %02x fail %d?\n", ++ u.r.bRequest, i); ++ return; ++ /* TODO experiment: if has_cfr, ++ * hardware didn't ACK; maybe we ++ * could actually STALL! ++ */ ++ } ++ DBG(DBG_VERBOSE, "protocol STALL, " ++ "%02x err %d\n", UDCCSR0, i); ++stall: ++ /* the watchdog timer helps deal with cases ++ * where udc seems to clear FST wrongly, and ++ * then NAKs instead of STALLing. ++ */ ++ ep0start(dev, UDCCSR0_FST|UDCCSR0_FTF, "stall"); ++ start_watchdog(dev); ++ dev->ep0state = EP0_STALL; ++ ++ /* deferred i/o == no response yet */ ++ } else if (dev->req_pending) { ++ if (likely(dev->ep0state == EP0_IN_DATA_PHASE ++ || dev->req_std || u.r.wLength)) ++ ep0start(dev, 0, "defer"); ++ else ++ ep0start(dev, UDCCSR0_IPR, "defer/IPR"); ++ } ++ ++ /* expect at least one data or status stage irq */ ++ return; ++ ++ } else { ++ /* some random early IRQ: ++ * - we acked FST ++ * - IPR cleared ++ * - OPC got set, without SA (likely status stage) ++ */ ++ UDCCSR0 = udccsr0 & (UDCCSR0_SA|UDCCSR0_OPC); ++ } ++ break; ++ case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR etc */ ++ if (udccsr0 & UDCCSR0_OPC) { ++ UDCCSR0 = UDCCSR0_OPC|UDCCSR0_FTF; ++ DBG(DBG_VERBOSE, "ep0in premature status\n"); ++ if (req) ++ done(ep, req, 0); ++ ep0_idle(dev); ++ } else /* irq was IPR clearing */ { ++ if (req) { ++ /* this IN packet might finish the request */ ++ (void) write_ep0_fifo(ep, req); ++ } /* else IN token before response was written */ ++ } ++ break; ++ case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR etc */ ++ if (udccsr0 & UDCCSR0_OPC) { ++ if (req) { ++ /* this OUT packet might finish the request */ ++ if (read_ep0_fifo(ep, req)) ++ done(ep, req, 0); ++ /* else more OUT packets expected */ ++ } /* else OUT token before read was issued */ ++ } else /* irq was IPR clearing */ { ++ DBG(DBG_VERBOSE, "ep0out premature status\n"); ++ if (req) ++ done(ep, req, 0); ++ ep0_idle(dev); ++ } ++ break; ++ case EP0_STALL: ++ UDCCSR0 = UDCCSR0_FST; ++ break; ++ } ++ UDCISR0 = UDCISR_INT(0, UDCISR_INT_MASK); ++} ++ ++ ++static void handle_ep(struct pxa27x_ep *ep) ++{ ++ struct pxa27x_request *req; ++ int completed; ++ u32 udccsr=0; ++ ++ DMSG("%s is called\n", __FUNCTION__); ++ do { ++ completed = 0; ++ if (likely (!list_empty(&ep->queue))) { ++ req = list_entry(ep->queue.next, ++ struct pxa27x_request, queue); ++ } else ++ req = 0; ++ ++// udccsr = *ep->reg_udccsr; ++ DMSG("%s: req:%p, udcisr0:0x%x udccsr %p:0x%x\n", __FUNCTION__, ++ req, UDCISR0, ep->reg_udccsr, *ep->reg_udccsr); ++ if (unlikely(ep->dir_in)) { ++ udccsr = (UDCCSR_SST | UDCCSR_TRN) & *ep->reg_udccsr; ++ if (unlikely (udccsr)) ++ *ep->reg_udccsr = udccsr; ++ ++ if (req && likely ((*ep->reg_udccsr & UDCCSR_FS) != 0)) ++ completed = write_fifo(ep, req); ++ ++ } else { ++ udccsr = (UDCCSR_SST | UDCCSR_TRN) & *ep->reg_udccsr; ++ if (unlikely(udccsr)) ++ *ep->reg_udccsr = udccsr; ++ ++ /* fifos can hold packets, ready for reading... */ ++ if (likely(req)) { ++ completed = read_fifo(ep, req); ++ } else { ++ pio_irq_disable (ep->pxa_ep_num); ++ //*ep->reg_udccsr = UDCCSR_FEF; ++ DMSG("%s: no req for out data\n", ++ __FUNCTION__); ++ } ++ } ++ ep->pio_irqs++; ++ } while (completed); ++} ++ ++static void pxa27x_update_eps(struct pxa27x_udc *dev) ++{ ++ struct pxa27x_virt_ep *virt_ep; ++ int i; ++ ++ for (i = 1; i < UDC_EP_NUM; i++) { ++ if(!dev->ep[i].assigned || !dev->ep[i].usb_ep) ++ continue; ++ virt_ep = container_of(dev->ep[i].usb_ep, struct pxa27x_virt_ep, usb_ep); ++ ++ DMSG("%s, Updating eps %d:%d, %d:%d, %d:%d, %p,%p\n", __FUNCTION__, dev->ep[i].config, dev->configuration ++ ,dev->ep[i].interface, dev->interface, dev->ep[i].aisn, dev->alternate, virt_ep->pxa_ep, &dev->ep[i]); ++ ++ if(dev->ep[i].config == dev->configuration && virt_ep->pxa_ep != &dev->ep[i]) { ++ if ((dev->ep[i].interface == dev->interface && ++ dev->ep[i].aisn == dev->alternate) || virt_ep->pxa_ep->config != dev->configuration) { ++ ++ if (virt_ep->pxa_ep->desc) { ++ DMSG("%s, Changing end point to %d (en/dis)\n", __FUNCTION__, i); ++ pxa27x_ep_disable(&virt_ep->usb_ep); ++ virt_ep->pxa_ep = &dev->ep[i]; ++ pxa27x_ep_enable(&virt_ep->usb_ep, virt_ep->desc); ++ } else { ++ DMSG("%s, Changing end point to %d (no en/dis)\n", __FUNCTION__, i); ++ virt_ep->pxa_ep = &dev->ep[i]; ++ } ++ } ++ } ++ } ++} ++ ++static void pxa27x_change_configuration(struct pxa27x_udc *dev) ++{ ++ struct usb_ctrlrequest req ; ++ ++ pxa27x_update_eps(dev); ++ ++ req.bRequestType = 0; ++ req.bRequest = USB_REQ_SET_CONFIGURATION; ++ req.wValue = dev->configuration; ++ req.wIndex = 0; ++ req.wLength = 0; ++ ++ dev->ep0state = EP0_NO_ACTION; ++ dev->driver->setup(&dev->gadget, &req); ++} ++ ++static void pxa27x_change_interface(struct pxa27x_udc *dev) ++{ ++ struct usb_ctrlrequest req; ++ ++ pxa27x_update_eps(dev); ++ ++ req.bRequestType = USB_RECIP_INTERFACE; ++ req.bRequest = USB_REQ_SET_INTERFACE; ++ req.wValue = dev->alternate; ++ req.wIndex = dev->interface; ++ req.wLength = 0; ++ ++ dev->ep0state = EP0_NO_ACTION; ++ dev->driver->setup(&dev->gadget, &req); ++} ++ ++/* ++ * pxa27x_udc_irq - interrupt handler ++ * ++ * avoid delays in ep0 processing. the control handshaking isn't always ++ * under software control (pxa250c0 and the pxa255 are better), and delays ++ * could cause usb protocol errors. ++ */ ++static irqreturn_t pxa27x_udc_irq(int irq, void *_dev) ++{ ++ struct pxa27x_udc *dev = _dev; ++ int handled; ++ ++ dev->stats.irqs++; ++ ++ DBG(DBG_VERBOSE, "Interrupt, UDCISR0:0x%08x, UDCISR1:0x%08x, " ++ "UDCCR:0x%08x\n", UDCISR0, UDCISR1, UDCCR); ++ do { ++ u32 udcir = UDCISR1 & 0xF8000000; ++ ++ handled = 0; ++ ++ /* SUSpend Interrupt Request */ ++ if (unlikely(udcir & UDCISR1_IRSU)) { ++ UDCISR1 = UDCISR1_IRSU; ++ handled = 1; ++ DBG(DBG_VERBOSE, "USB suspend\n"); ++ if (dev->gadget.speed != USB_SPEED_UNKNOWN ++ && dev->driver ++ && dev->driver->suspend) ++ dev->driver->suspend(&dev->gadget); ++ ep0_idle(dev); ++ } ++ ++ /* RESume Interrupt Request */ ++ if (unlikely(udcir & UDCISR1_IRRU)) { ++ UDCISR1 = UDCISR1_IRRU; ++ handled = 1; ++ DBG(DBG_VERBOSE, "USB resume\n"); ++ ++ if (dev->gadget.speed != USB_SPEED_UNKNOWN ++ && dev->driver ++ && dev->driver->resume) ++ dev->driver->resume(&dev->gadget); ++ } ++ ++ if (unlikely(udcir & UDCISR1_IRCC)) { ++ unsigned config, interface, alternate; ++ ++ handled = 1; ++ DBG(DBG_VERBOSE, "USB SET_CONFIGURATION or " ++ "SET_INTERFACE command received\n"); ++ ++ config = (UDCCR & UDCCR_ACN) >> UDCCR_ACN_S; ++ ++ if (dev->configuration != config) { ++ dev->configuration = config; ++ pxa27x_change_configuration(dev) ; ++ } ++ ++ interface = (UDCCR & UDCCR_AIN) >> UDCCR_AIN_S; ++ alternate = (UDCCR & UDCCR_AAISN) >> UDCCR_AAISN_S; ++ ++ if ((dev->interface != interface) || (dev->alternate != alternate)) { ++ dev->interface = interface; ++ dev->alternate = alternate; ++ pxa27x_change_interface(dev); ++ } ++ ++ UDCCR |= UDCCR_SMAC; ++ ++ UDCISR1 = UDCISR1_IRCC; ++ DMSG("%s: con:%d,inter:%d,alt:%d\n", ++ __FUNCTION__, config,interface, alternate); ++ } ++ ++ /* ReSeT Interrupt Request - USB reset */ ++ if (unlikely(udcir & UDCISR1_IRRS)) { ++ UDCISR1 = UDCISR1_IRRS; ++ handled = 1; ++ ++ if ((UDCCR & UDCCR_UDA) == 0) { ++ DBG(DBG_VERBOSE, "USB reset start\n"); ++ ++ /* reset driver and endpoints, ++ * in case that's not yet done ++ */ ++ stop_activity(dev, dev->driver); ++ } ++ INFO("USB reset\n"); ++ dev->gadget.speed = USB_SPEED_FULL; ++ memset(&dev->stats, 0, sizeof dev->stats); ++ ++ } else { ++ u32 udcisr0 = UDCISR0 ; ++ u32 udcisr1 = UDCISR1 & 0xFFFF; ++ int i; ++ ++ if (unlikely (!udcisr0 && !udcisr1)) ++ continue; ++ ++ DBG(DBG_VERY_NOISY, "irq %02x.%02x\n", udcisr1,udcisr0); ++ ++ /* control traffic */ ++ if (udcisr0 & UDCISR0_IR0) { ++ dev->ep[0].pio_irqs++; ++ handle_ep0(dev); ++ handled = 1; ++ } ++ ++ udcisr0 >>= 2; ++ /* endpoint data transfers */ ++ for (i = 1; udcisr0!=0 && i < 16; udcisr0>>=2,i++) { ++ UDCISR0 = UDCISR_INT(i, UDCISR_INT_MASK); ++ ++ if (udcisr0 & UDC_INT_FIFOERROR) ++ dev_err(dev->dev, " Endpoint %d Fifo error\n", i); ++ if (udcisr0 & UDC_INT_PACKETCMP) { ++ handle_ep(&dev->ep[i]); ++ handled = 1; ++ } ++ ++ } ++ ++ for (i = 0; udcisr1!=0 && i < 8; udcisr1 >>= 2, i++) { ++ UDCISR1 = UDCISR_INT(i, UDCISR_INT_MASK); ++ ++ if (udcisr1 & UDC_INT_FIFOERROR) { ++ dev_err(dev->dev, "Endpoint %d fifo error\n", (i+16)); ++ } ++ ++ if (udcisr1 & UDC_INT_PACKETCMP) { ++ handle_ep(&dev->ep[i+16]); ++ handled = 1; ++ } ++ } ++ } ++ ++ /* we could also ask for 1 msec SOF (SIR) interrupts */ ++ ++ } while (handled); ++ return IRQ_HANDLED; ++} ++ ++int write_ep0_zlp(void) ++{ ++ UDCCSR0 = UDCCSR0_IPR; ++ return 0; ++} ++EXPORT_SYMBOL(write_ep0_zlp); ++ ++static void udc_init_ep(struct pxa27x_udc *dev) ++{ ++ int i; ++ ++ INIT_LIST_HEAD(&dev->gadget.ep_list); ++ INIT_LIST_HEAD(&dev->gadget.ep0->ep_list); ++ ++ for (i = 0; i < UDC_EP_NUM; i++) { ++ struct pxa27x_ep *ep = &dev->ep[i]; ++ ++ ep->dma = -1; ++ if (i != 0) { ++ memset(ep, 0, sizeof(*ep)); ++ } ++ INIT_LIST_HEAD(&ep->queue); ++ } ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void nop_release(struct device *dev) ++{ ++ DMSG("%s %s\n", __FUNCTION__, dev->bus_id); ++} ++ ++/* this uses load-time allocation and initialization (instead of ++ * doing it at run-time) to save code, eliminate fault paths, and ++ * be more obviously correct. ++ */ ++ ++static struct pxa27x_udc memory = { ++ .gadget = { ++ .ops = &pxa27x_udc_ops, ++ .ep0 = &memory.virt_ep0.usb_ep, ++ .name = driver_name, ++ .dev = { ++ .bus_id = "gadget", ++ .release = nop_release, ++ }, ++ }, ++ ++ /* control endpoint */ ++ .virt_ep0 = { ++ .pxa_ep = &memory.ep[0], ++ .usb_ep = { ++ .name = ep0name, ++ .ops = &pxa27x_ep_ops, ++ .maxpacket = EP0_FIFO_SIZE, ++ }, ++ }, ++ ++ .ep[0] = { ++ .usb_ep = &memory.virt_ep0.usb_ep, ++ .dev = &memory, ++ .reg_udccsr = &UDCCSR0, ++ .reg_udcdr = &UDCDR0, ++ }, ++}; ++ ++static int __init pxa27x_udc_probe(struct platform_device *_dev) ++{ ++ struct pxa27x_udc *dev = &memory; ++ int retval; ++ ++ /* other non-static parts of init */ ++ dev->dev = &_dev->dev; ++ dev->mach = _dev->dev.platform_data; ++ ++ /* RPFIXME */ ++ UP2OCR = UP2OCR_HXOE | UP2OCR_DPPUE | UP2OCR_DPPUBE; ++ ++ init_timer(&dev->timer); ++ dev->timer.function = udc_watchdog; ++ dev->timer.data = (unsigned long) dev; ++ ++ device_initialize(&dev->gadget.dev); ++ dev->gadget.dev.parent = &_dev->dev; ++ dev->gadget.dev.dma_mask = _dev->dev.dma_mask; ++ ++ the_controller = dev; ++ platform_set_drvdata(_dev, dev); ++ ++ udc_disable(dev); ++ udc_init_ep(dev); ++ udc_reinit(dev); ++ ++ /* irq setup after old hardware state is cleaned up */ ++ retval = request_irq(IRQ_USB, pxa27x_udc_irq, ++ SA_INTERRUPT, driver_name, dev); ++ if (retval != 0) { ++ dev_err(dev->dev, "%s: can't get irq %i, err %d\n", ++ driver_name, IRQ_USB, retval); ++ return -EBUSY; ++ } ++ dev->got_irq = 1; ++ ++ create_proc_files(); ++ ++ return 0; ++} ++ ++static int pxa27x_udc_remove(struct platform_device *_dev) ++{ ++ struct pxa27x_udc *dev = platform_get_drvdata(_dev); ++ ++ udc_disable(dev); ++ remove_proc_files(); ++ usb_gadget_unregister_driver(dev->driver); ++ ++ pxa27x_ep_freeall(&dev->gadget); ++ ++ if (dev->got_irq) { ++ free_irq(IRQ_USB, dev); ++ dev->got_irq = 0; ++ } ++ platform_set_drvdata(_dev, 0); ++ the_controller = 0; ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static void pxa27x_udc_shutdown(struct platform_device *_dev) ++{ ++ struct pxa27x_udc *dev = platform_get_drvdata(_dev); ++ ++ udc_disable(dev); ++} ++ ++static int pxa27x_udc_suspend(struct platform_device *_dev, pm_message_t state) ++{ ++ int i; ++ struct pxa27x_udc *dev = platform_get_drvdata(_dev); ++ ++ DMSG("%s is called\n", __FUNCTION__); ++ ++ dev->udccsr0 = UDCCSR0; ++ for(i=1; (i<UDC_EP_NUM); i++) { ++ if (dev->ep[i].assigned) { ++ struct pxa27x_ep *ep = &dev->ep[i]; ++ ep->udccsr_value = *ep->reg_udccsr; ++ ep->udccr_value = *ep->reg_udccr; ++ DMSG("EP%d, udccsr:0x%x, udccr:0x%x\n", ++ i, *ep->reg_udccsr, *ep->reg_udccr); ++ } ++ } ++ ++ udc_clear_mask_UDCCR(UDCCR_UDE); ++ pxa_set_cken(CKEN_USB, 0); ++ ++ return 0; ++} ++ ++static int pxa27x_udc_resume(struct platform_device *_dev) ++{ ++ int i; ++ struct pxa27x_udc *dev = platform_get_drvdata(_dev); ++ ++ DMSG("%s is called\n", __FUNCTION__); ++ UDCCSR0 = dev->udccsr0 & (UDCCSR0_FST | UDCCSR0_DME); ++ for (i=1; i < UDC_EP_NUM; i++) { ++ if (dev->ep[i].assigned) { ++ struct pxa27x_ep *ep = &dev->ep[i]; ++ *ep->reg_udccsr = ep->udccsr_value; ++ *ep->reg_udccr = ep->udccr_value; ++ DMSG("EP%d, udccsr:0x%x, udccr:0x%x\n", ++ i, *ep->reg_udccsr, *ep->reg_udccr); ++ } ++ } ++ ++ udc_enable(dev); ++ ++ /* OTGPH bit is set when sleep mode is entered. ++ * it indicates that OTG pad is retaining its state. ++ * Upon exit from sleep mode and before clearing OTGPH, ++ * Software must configure the USB OTG pad, UDC, and UHC ++ * to the state they were in before entering sleep mode.*/ ++ PSSR |= PSSR_OTGPH; ++ ++ return 0; ++} ++#endif ++ ++/*-------------------------------------------------------------------------*/ ++ ++static struct platform_driver udc_driver = { ++ .driver = { ++ .name = "pxa2xx-udc", ++ }, ++ .probe = pxa27x_udc_probe, ++ .remove = pxa27x_udc_remove, ++#ifdef CONFIG_PM ++ .shutdown = pxa27x_udc_shutdown, ++ .suspend = pxa27x_udc_suspend, ++ .resume = pxa27x_udc_resume ++#endif ++}; ++ ++static int __init udc_init(void) ++{ ++ printk(KERN_INFO "%s: version %s\n", driver_name, DRIVER_VERSION); ++ return platform_driver_register(&udc_driver); ++} ++module_init(udc_init); ++ ++static void __exit udc_exit(void) ++{ ++ platform_driver_unregister(&udc_driver); ++} ++module_exit(udc_exit); ++ ++MODULE_DESCRIPTION(DRIVER_DESC); ++MODULE_AUTHOR("Frank Becker, Robert Schwebel, David Brownell"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/usb/gadget/pxa27x_udc.h b/drivers/usb/gadget/pxa27x_udc.h +new file mode 100644 +index 0000000..d4377cf +--- /dev/null ++++ b/drivers/usb/gadget/pxa27x_udc.h +@@ -0,0 +1,298 @@ ++/* ++ * linux/drivers/usb/gadget/pxa27x_udc.h ++ * Intel PXA27x on-chip full speed USB device controller ++ * ++ * Copyright (C) 2003 Robert Schwebel <r.schwebel@pengutronix.de>, Pengutronix ++ * Copyright (C) 2003 David Brownell ++ * Copyright (C) 2004 Intel Corporation ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef __LINUX_USB_GADGET_PXA27X_H ++#define __LINUX_USB_GADGET_PXA27X_H ++ ++#include <linux/types.h> ++ ++struct pxa27x_udc; ++ ++struct pxa27x_ep { ++ struct pxa27x_udc *dev; ++ struct usb_ep *usb_ep; ++ const struct usb_endpoint_descriptor *desc; ++ ++ struct list_head queue; ++ unsigned long pio_irqs; ++ unsigned long dma_irqs; ++ ++ unsigned pxa_ep_num; ++ int dma; ++ unsigned fifo_size; ++ unsigned ep_type; ++ ++ unsigned stopped : 1; ++ unsigned dma_con : 1; ++ unsigned dir_in : 1; ++ unsigned assigned : 1; ++ ++ unsigned ep_num; ++ unsigned config; ++ unsigned interface; ++ unsigned aisn; ++ /* UDCCSR = UDC Control/Status Register for this EP ++ * UBCR = UDC Byte Count Remaining (contents of OUT fifo) ++ * UDCDR = UDC Endpoint Data Register (the fifo) ++ * UDCCR = UDC Endpoint Configuration Registers ++ * DRCM = DMA Request Channel Map ++ */ ++ volatile u32 *reg_udccsr; ++ volatile u32 *reg_udcbcr; ++ volatile u32 *reg_udcdr; ++ volatile u32 *reg_udccr; ++#ifdef USE_DMA ++ volatile u32 *reg_drcmr; ++#define drcmr(n) .reg_drcmr = & DRCMR ## n , ++#else ++#define drcmr(n) ++#endif ++ ++#ifdef CONFIG_PM ++ unsigned udccsr_value; ++ unsigned udccr_value; ++#endif ++}; ++ ++struct pxa27x_virt_ep { ++ struct usb_ep usb_ep; ++ const struct usb_endpoint_descriptor *desc; ++ struct pxa27x_ep *pxa_ep; ++}; ++ ++struct pxa27x_request { ++ struct usb_request req; ++ struct list_head queue; ++}; ++ ++enum ep0_state { ++ EP0_IDLE, ++ EP0_IN_DATA_PHASE, ++ EP0_OUT_DATA_PHASE, ++// EP0_END_XFER, ++ EP0_STALL, ++ EP0_NO_ACTION ++}; ++ ++#define EP0_FIFO_SIZE ((unsigned)16) ++#define BULK_FIFO_SIZE ((unsigned)64) ++#define ISO_FIFO_SIZE ((unsigned)256) ++#define INT_FIFO_SIZE ((unsigned)8) ++ ++struct udc_stats { ++ struct ep0stats { ++ unsigned long ops; ++ unsigned long bytes; ++ } read, write; ++ unsigned long irqs; ++}; ++ ++#define UDC_EP_NUM 24 ++ ++ ++struct pxa27x_udc { ++ struct usb_gadget gadget; ++ struct usb_gadget_driver *driver; ++ ++ enum ep0_state ep0state; ++ struct udc_stats stats; ++ unsigned got_irq : 1, ++ has_cfr : 1, ++ req_pending : 1, ++ req_std : 1, ++ req_config : 1; ++ ++#define start_watchdog(dev) mod_timer(&dev->timer, jiffies + (HZ/200)) ++ struct timer_list timer; ++ ++ struct device *dev; ++ struct pxa2xx_udc_mach_info *mach; ++ u64 dma_mask; ++ struct pxa27x_virt_ep virt_ep0; ++ struct pxa27x_ep ep[UDC_EP_NUM]; ++ unsigned int ep_num; ++ ++ unsigned configuration, ++ interface, ++ alternate; ++#ifdef CONFIG_PM ++ unsigned udccsr0; ++#endif ++}; ++ ++static struct pxa27x_udc *the_controller; ++ ++#if 0 ++/*-------------------------------------------------------------------------*/ ++ ++ ++/* one GPIO should be used to detect host disconnect */ ++static inline int is_usb_connected(void) ++{ ++ if (!the_controller->mach->udc_is_connected) ++ return 1; ++ return the_controller->mach->udc_is_connected(); ++} ++ ++/* one GPIO should force the host to see this device (or not) */ ++static inline void make_usb_disappear(void) ++{ ++ if (!the_controller->mach->udc_command) ++ return; ++ the_controller->mach->udc_command(PXA27X_UDC_CMD_DISCONNECT); ++} ++ ++static inline void let_usb_appear(void) ++{ ++ if (!the_controller->mach->udc_command) ++ return; ++ the_controller->mach->udc_command(PXA2XX_UDC_CMD_CONNECT); ++} ++#endif ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * Debugging support vanishes in non-debug builds. DBG_NORMAL should be ++ * mostly silent during normal use/testing, with no timing side-effects. ++ */ ++#define DBG_NORMAL 1 /* error paths, device state transitions */ ++#define DBG_VERBOSE 2 /* add some success path trace info */ ++#define DBG_NOISY 3 /* ... even more: request level */ ++#define DBG_VERY_NOISY 4 /* ... even more: packet level */ ++ ++#ifdef DEBUG ++static const char *state_name[] = { ++ "EP0_IDLE", ++ "EP0_IN_DATA_PHASE", "EP0_OUT_DATA_PHASE", ++ "EP0_END_XFER", "EP0_STALL" ++}; ++ ++#define DMSG(stuff...) printk(KERN_ERR "udc: " stuff) ++ ++#ifdef VERBOSE ++# define UDC_DEBUG DBG_VERBOSE ++#else ++# define UDC_DEBUG DBG_NORMAL ++#endif ++ ++static void __attribute__ ((__unused__)) ++dump_udccr(const char *label) ++{ ++ u32 udccr = UDCCR; ++ DMSG("%s 0x%08x =%s%s%s%s%s%s%s%s%s%s, con=%d,inter=%d,altinter=%d\n", ++ label, udccr, ++ (udccr & UDCCR_OEN) ? " oen":"", ++ (udccr & UDCCR_AALTHNP) ? " aalthnp":"", ++ (udccr & UDCCR_AHNP) ? " rem" : "", ++ (udccr & UDCCR_BHNP) ? " rstir" : "", ++ (udccr & UDCCR_DWRE) ? " dwre" : "", ++ (udccr & UDCCR_SMAC) ? " smac" : "", ++ (udccr & UDCCR_EMCE) ? " emce" : "", ++ (udccr & UDCCR_UDR) ? " udr" : "", ++ (udccr & UDCCR_UDA) ? " uda" : "", ++ (udccr & UDCCR_UDE) ? " ude" : "", ++ (udccr & UDCCR_ACN) >> UDCCR_ACN_S, ++ (udccr & UDCCR_AIN) >> UDCCR_AIN_S, ++ (udccr & UDCCR_AAISN)>> UDCCR_AAISN_S ); ++} ++ ++static void __attribute__ ((__unused__)) ++dump_udccsr0(const char *label) ++{ ++ u32 udccsr0 = UDCCSR0; ++ ++ DMSG("%s %s 0x%08x =%s%s%s%s%s%s%s\n", ++ label, state_name[the_controller->ep0state], udccsr0, ++ (udccsr0 & UDCCSR0_SA) ? " sa" : "", ++ (udccsr0 & UDCCSR0_RNE) ? " rne" : "", ++ (udccsr0 & UDCCSR0_FST) ? " fst" : "", ++ (udccsr0 & UDCCSR0_SST) ? " sst" : "", ++ (udccsr0 & UDCCSR0_DME) ? " dme" : "", ++ (udccsr0 & UDCCSR0_IPR) ? " ipr" : "", ++ (udccsr0 & UDCCSR0_OPC) ? " opr" : ""); ++} ++ ++static void __attribute__ ((__unused__)) ++dump_state(struct pxa27x_udc *dev) ++{ ++ unsigned i; ++ ++ DMSG("%s, udcicr %02X.%02X, udcsir %02X.%02x, udcfnr %02X\n", ++ state_name[dev->ep0state], ++ UDCICR1, UDCICR0, UDCISR1, UDCISR0, UDCFNR); ++ dump_udccr("udccr"); ++ ++ if (!dev->driver) { ++ DMSG("no gadget driver bound\n"); ++ return; ++ } else ++ DMSG("ep0 driver '%s'\n", dev->driver->driver.name); ++ ++ ++ dump_udccsr0 ("udccsr0"); ++ DMSG("ep0 IN %lu/%lu, OUT %lu/%lu\n", ++ dev->stats.write.bytes, dev->stats.write.ops, ++ dev->stats.read.bytes, dev->stats.read.ops); ++ ++ for (i = 1; i < UDC_EP_NUM; i++) { ++ if (dev->ep[i].assigned) ++ DMSG ("udccs%d = %02x\n", i, *dev->ep->reg_udccsr); ++ } ++} ++ ++#if 0 ++static void dump_regs(u8 ep) ++{ ++ DMSG("EP:%d UDCCSR:0x%08x UDCBCR:0x%08x\n UDCCR:0x%08x\n", ++ ep,UDCCSN(ep), UDCBCN(ep), UDCCN(ep)); ++} ++static void dump_req (struct pxa27x_request *req) ++{ ++ struct usb_request *r = &req->req; ++ ++ DMSG("%s: buf:0x%08x length:%d dma:0x%08x actual:%d\n", ++ __FUNCTION__, (unsigned)r->buf, r->length, ++ r->dma, r->actual); ++} ++#endif ++ ++#else ++ ++#define DMSG(stuff...) do{}while(0) ++ ++#define dump_udccr(x) do{}while(0) ++#define dump_udccsr0(x) do{}while(0) ++#define dump_state(x) do{}while(0) ++ ++#define UDC_DEBUG ((unsigned)4) ++ ++#endif ++ ++#define DBG(lvl, stuff...) do{if ((lvl) <= UDC_DEBUG) DMSG(stuff);}while(0) ++ ++#define WARN(stuff...) printk(KERN_WARNING "udc: " stuff) ++#define INFO(stuff...) printk(KERN_INFO "udc: " stuff) ++ ++ ++#endif /* __LINUX_USB_GADGET_PXA27X_H */ +diff --git a/drivers/usb/gadget/pxa2xx_udc.h b/drivers/usb/gadget/pxa2xx_udc.h +index 0e5d0e6..9483a49 100644 +--- a/drivers/usb/gadget/pxa2xx_udc.h ++++ b/drivers/usb/gadget/pxa2xx_udc.h +@@ -206,7 +206,8 @@ dump_state(struct pxa2xx_udc *dev) + unsigned i; + + DMSG("%s %s, uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n", +- is_usb_connected() ? "host " : "disconnected", ++ //is_usb_connected() ? "host " : "disconnected", ++ "host ", + state_name[dev->ep0state], + UICR1, UICR0, USIR1, USIR0, UFNRH, UFNRL); + dump_udccr("udccr"); +@@ -223,8 +224,8 @@ dump_state(struct pxa2xx_udc *dev) + } else + DMSG("ep0 driver '%s'\n", dev->driver->driver.name); + +- if (!is_usb_connected()) +- return; ++ //if (!is_usb_connected()) ++ // return; + + dump_udccs0 ("udccs0"); + DMSG("ep0 IN %lu/%lu, OUT %lu/%lu\n", +diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/serial.c +index ce4d2e0..5dac23f 100644 +--- a/drivers/usb/gadget/serial.c ++++ b/drivers/usb/gadget/serial.c +@@ -1356,6 +1356,7 @@ static int __init gs_bind(struct usb_gadget *gadget) + struct usb_ep *ep; + struct gs_dev *dev; + int gcnum; ++ struct usb_endpoint_config ep_config[2]; + + /* Some controllers can't support CDC ACM: + * - sh doesn't support multiple interfaces or configs; +@@ -1376,22 +1377,33 @@ static int __init gs_bind(struct usb_gadget *gadget) + __constant_cpu_to_le16(GS_VERSION_NUM|0x0099); + } + ++ ep_config[0].config = GS_BULK_CONFIG_ID; ++ ep_config[0].interface = gs_bulk_interface_desc.bInterfaceNumber; ++ ep_config[0].altinterface = gs_bulk_interface_desc.bAlternateSetting; ++ ep_config[1].config = GS_ACM_CONFIG_ID; ++ ep_config[1].interface = gs_data_interface_desc.bInterfaceNumber; ++ ep_config[1].altinterface = gs_data_interface_desc.bAlternateSetting; ++ + usb_ep_autoconfig_reset(gadget); + +- ep = usb_ep_autoconfig(gadget, &gs_fullspeed_in_desc); ++ ep = usb_ep_autoconfig(gadget, &gs_fullspeed_in_desc, &ep_config[0], 2); + if (!ep) + goto autoconf_fail; + EP_IN_NAME = ep->name; + ep->driver_data = ep; /* claim the endpoint */ + +- ep = usb_ep_autoconfig(gadget, &gs_fullspeed_out_desc); ++ ep = usb_ep_autoconfig(gadget, &gs_fullspeed_out_desc, &ep_config[0], 2); + if (!ep) + goto autoconf_fail; + EP_OUT_NAME = ep->name; + ep->driver_data = ep; /* claim the endpoint */ + + if (use_acm) { +- ep = usb_ep_autoconfig(gadget, &gs_fullspeed_notify_desc); ++ ep_config[0].config = GS_ACM_CONFIG_ID; ++ ep_config[0].interface = gs_control_interface_desc.bInterfaceNumber; ++ ep_config[0].altinterface = gs_control_interface_desc.bAlternateSetting; ++ ++ ep = usb_ep_autoconfig(gadget, &gs_fullspeed_notify_desc, &ep_config[0], 1); + if (!ep) { + printk(KERN_ERR "gs_bind: cannot run ACM on %s\n", gadget->name); + goto autoconf_fail; +diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c +index fcfe869..1e40d46 100644 +--- a/drivers/usb/gadget/zero.c ++++ b/drivers/usb/gadget/zero.c +@@ -1142,6 +1142,7 @@ zero_bind (struct usb_gadget *gadget) + struct zero_dev *dev; + struct usb_ep *ep; + int gcnum; ++ struct usb_endpoint_config ep_config[2]; + + /* FIXME this can't yet work right with SH ... it has only + * one configuration, numbered one. +@@ -1154,7 +1155,15 @@ zero_bind (struct usb_gadget *gadget) + * but there may also be important quirks to address. + */ + usb_ep_autoconfig_reset (gadget); +- ep = usb_ep_autoconfig (gadget, &fs_source_desc); ++ ++ ep_config[0].config = CONFIG_SOURCE_SINK; ++ ep_config[0].interface = source_sink_intf.bInterfaceNumber; ++ ep_config[0].altinterface = source_sink_intf.bAlternateSetting; ++ ep_config[1].config = CONFIG_LOOPBACK; ++ ep_config[1].interface = loopback_intf.bInterfaceNumber; ++ ep_config[1].altinterface = loopback_intf.bAlternateSetting; ++ ++ ep = usb_ep_autoconfig(gadget, &fs_source_desc, &ep_config[0], 2); + if (!ep) { + autoconf_fail: + printk (KERN_ERR "%s: can't autoconfigure on %s\n", +@@ -1164,7 +1173,7 @@ autoconf_fail: + EP_IN_NAME = ep->name; + ep->driver_data = ep; /* claim */ + +- ep = usb_ep_autoconfig (gadget, &fs_sink_desc); ++ ep = usb_ep_autoconfig(gadget, &fs_sink_desc, &ep_config[0], 2); + if (!ep) + goto autoconf_fail; + EP_OUT_NAME = ep->name; +diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig +index 2580f5f..12e4b91 100644 +--- a/drivers/video/backlight/Kconfig ++++ b/drivers/video/backlight/Kconfig +@@ -40,7 +40,7 @@ config BACKLIGHT_CLASS_DEVICE + + config BACKLIGHT_CORGI + tristate "Sharp Corgi Backlight Driver (SL Series)" +- depends on BACKLIGHT_CLASS_DEVICE && PXA_SHARPSL ++ depends on BACKLIGHT_CLASS_DEVICE && (PXA_SHARPSL || MACH_EM_X270) + default y + help + If you have a Sharp Zaurus SL-C7xx, SL-Cxx00 or SL-6000x say y to enable the +diff --git a/include/asm-arm/arch-pxa/pwr-i2c.h b/include/asm-arm/arch-pxa/pwr-i2c.h +new file mode 100644 +index 0000000..6bad486 +--- /dev/null ++++ b/include/asm-arm/arch-pxa/pwr-i2c.h +@@ -0,0 +1,61 @@ ++/* ++ * (C) Copyright 2007 CompuLab, Ltd ++ * Mike Rapoport <mike@compulab.co.il> ++ * ++ * Simple Power I2C interface for PXA processors. ++ * Based on U-Boot PXA I2C 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, 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., 59 Temple Place, Suite 330, Boston, ++ * MA 02111-1307 USA ++ * ++ */ ++ ++#ifndef __PXA_PWR_I2C_H__ ++#define __PXA_PWR_I2C_H__ ++ ++/* ++ * Configuration items. ++ */ ++#define I2C_RXTX_LEN 128 /* maximum tx/rx buffer length */ ++ ++/* ++ * Probe the given I2C chip address. Returns 0 if a chip responded, ++ * not 0 on failure. ++ */ ++extern int pxa_pwr_i2c_probe(u8 chip); ++ ++/* ++ * Read/Write interface: ++ * chip: I2C chip address, range 0..127 ++ * addr: Memory (register) address within the chip ++ * alen: Number of bytes to use for addr (typically 1, 2 for larger ++ * memories, 0 for register type devices with only one ++ * register) ++ * buffer: Where to read/write the data ++ * len: How many bytes to read/write ++ * ++ * Returns: 0 on success, not 0 on failure ++ */ ++extern int pxa_pwr_i2c_read(u8 chip, uint addr, int alen, u8 *buffer, int len); ++extern int pxa_pwr_i2c_write(u8 chip, uint addr, int alen, ++ u8 *buffer, int len); ++ ++/* ++ * Utility routines to read/write registers. ++ */ ++extern s32 pxa_pwr_i2c_reg_read (u8 chip, u8 reg); ++extern s32 pxa_pwr_i2c_reg_write(u8 chip, u8 reg, u8 val); ++ ++#endif /* __PXA_PWR_I2C_H___ */ +diff --git a/include/linux/da9030.h b/include/linux/da9030.h +new file mode 100644 +index 0000000..6eb89e2 +--- /dev/null ++++ b/include/linux/da9030.h +@@ -0,0 +1,118 @@ ++#ifndef _DA9030_H ++#define _DA9030_H ++ ++/* DA9030 has 24 possible interrupts */ ++#define DA9030_IRQ_COUNT 24 ++ ++/* EVENT A */ ++#define DA9030_IRQ_ONKEY_EN 0 ++#define DA9030_IRQ_PWREN1 1 ++#define DA9030_IRQ_EXTON 2 ++#define DA9030_IRQ_CHDET 3 ++#define DA9030_IRQ_TBAT 4 ++#define DA9030_IRQ_VBATMON 5 ++#define DA9030_IRQ_VBATMONTXON 6 ++#define DA9030_IRQ_CHIOVER 7 ++ ++/* EVENT B */ ++#define DA9030_IRQ_TCTO 8 ++#define DA9030_IRQ_CCTO 9 ++#define DA9030_IRQ_ADC_READY 10 ++#define DA9030_IRQ_VBUS_4_4 11 ++#define DA9030_IRQ_VBUS_4_0 12 ++#define DA9030_IRQ_SESSION_VALID 13 ++#define DA9030_IRQ_SRP_DETECT 14 ++#define DA9030_IRQ_WDOG_INTR 15 ++ ++/* EVENT C */ ++#define DA9030_IRQ_LDO15 16 ++#define DA9030_IRQ_LDO16 17 ++#define DA9030_IRQ_LDO17 18 ++#define DA9030_IRQ_LDO18 19 ++#define DA9030_IRQ_LDO19 20 ++#define DA9030_IRQ_BUCK2 21 ++#define DA9030_IRQ_ADC_IN4 22 ++#define DA9030_IRQ_ADC_IN5 23 ++ ++enum da9030_ldo_sleep_mode { ++ DA9030_LDO_SLEEP_KEEP = 0x0, ++ DA9030_LDO_SLEEP_SHUT_DOWN = 0x1, ++ DA9030_LDO_SLEEP_POWER_UP = 0x2, ++ DA9030_LDO_SLEEP_HIGH_Z = 0x3, ++}; ++ ++enum da9030_led_rate { ++ DA9030_LED_RATE_ON = 0x0, ++ DA9030_LED_RATE_0_52 = 0x1, ++ DA9030_LED_RATE_1_05 = 0x2, ++ DA9030_LED_RATE_2_1 = 0x3, ++}; ++ ++enum da9030_led_duty_cycle { ++ DA9030_LED_DUTY_1_16 = 0x0, ++ DA9030_LED_DUTY_1_8 = 0x1, ++ DA9030_LED_DUTY_1_4 = 0x2, ++ DA9030_LED_DUTY_1_2 = 0x3, ++}; ++ ++enum da9030_led_pwm_chop { ++ DA9030_LED_PWM_8_8 = 0x0, ++ DA9030_LED_PWM_7_8 = 0x1, ++ DA9030_LED_PWM_6_8 = 0x2, ++ DA9030_LED_PWM_5_8 = 0x3, ++ DA9030_LED_PWM_4_8 = 0x4, ++ DA9030_LED_PWM_3_8 = 0x5, ++ DA9030_LED_PWM_2_8 = 0x6, ++ DA9030_LED_PWM_1_8 = 0x7, ++}; ++ ++struct da9030_adc_res { ++ int vbat_res; ++ int vbatmin_res; ++ int vbatmintxon; ++ int ichmax_res; ++ int ichmin_res; ++ int ichaverage_res; ++ int vchmax_res; ++ int vchmin_res; ++ int tbat_res; ++ int adc_in4_res; ++ int adc_in5_res; ++}; ++ ++extern int da9030_get_status(void); ++extern int da9030_get_fault_log(void); ++ ++extern void da9030_enable_adc(void); ++extern void da9030_read_adc(struct da9030_adc_res *adc); ++ ++extern void da9030_set_wled(int on, unsigned int brightness); ++ ++extern int da9030_set_led(int led, int on, ++ enum da9030_led_rate rate, ++ enum da9030_led_duty_cycle duty, ++ enum da9030_led_pwm_chop pwm_chop); ++ ++extern int da9030_set_charger(int on, unsigned int mA, unsigned int mV); ++extern void da9030_get_charger(int *on, unsigned int *mA, unsigned int *mV); ++ ++extern int da9030_set_ldo(int ldo, int on, unsigned int mV, ++ enum da9030_ldo_sleep_mode sleep_mode); ++extern int da9030_set_buck(int buck, int on, unsigned int mV, int flags); ++ ++extern void da9030_set_thresholds(unsigned int tbathighp, ++ unsigned int tbathighn, ++ unsigned int tbatlow, ++ unsigned int vbatmon); ++ ++extern int da9030_register_callback(int event, ++ void (*callback)(int event, void *data), ++ void *data); ++extern void da9030_unregister_callback(int event); ++ ++/* Keep these for now, although they are unsafe. When I'll see what ++ other methods are needed from DA9030, I'll remove these */ ++extern s32 da9030_get_reg(u32 reg); ++extern s32 da9030_set_reg(u32 reg, u8 val); ++ ++#endif +diff --git a/include/linux/usb_gadget.h b/include/linux/usb_gadget.h +index 4f59b2a..792c568 100644 +--- a/include/linux/usb_gadget.h ++++ b/include/linux/usb_gadget.h +@@ -397,10 +397,28 @@ usb_ep_fifo_flush (struct usb_ep *ep) + + struct usb_gadget; + ++/** ++ * struct usb_endpoint_config - possible configurations of a given endpoint ++ * @config: the configuration number ++ * @interface: the interface number ++ * @altinterface: the altinterface number ++ * ++ * Used as an array to pass information about the possible configurations ++ * of a given endpoint to the bus controller. ++ */ ++struct usb_endpoint_config { ++ u8 config; ++ u8 interface; ++ u8 altinterface; ++}; ++ + /* the rest of the api to the controller hardware: device operations, + * which don't involve endpoints (or i/o). + */ + struct usb_gadget_ops { ++ struct usb_ep* (*ep_alloc)(struct usb_gadget *, ++ struct usb_endpoint_descriptor *, ++ struct usb_endpoint_config *, int); + int (*get_frame)(struct usb_gadget *); + int (*wakeup)(struct usb_gadget *); + int (*set_selfpowered) (struct usb_gadget *, int is_selfpowered); +@@ -824,7 +842,10 @@ int usb_gadget_config_buf(const struct usb_config_descriptor *config, + /* utility wrapping a simple endpoint selection policy */ + + extern struct usb_ep *usb_ep_autoconfig (struct usb_gadget *, +- struct usb_endpoint_descriptor *) __devinit; ++ struct usb_endpoint_descriptor *, ++ struct usb_endpoint_config *, ++ int numconfigs ++); + + extern void usb_ep_autoconfig_reset (struct usb_gadget *) __devinit; + +diff --git a/include/linux/wm97xx.h b/include/linux/wm97xx.h +new file mode 100644 +index 0000000..354e533 +--- /dev/null ++++ b/include/linux/wm97xx.h +@@ -0,0 +1,291 @@ ++ ++/* ++ * Register bits and API for Wolfson WM97xx series of codecs ++ */ ++ ++#ifndef _LINUX_WM97XX_H ++#define _LINUX_WM97XX_H ++ ++#include <sound/driver.h> ++#include <sound/core.h> ++#include <sound/pcm.h> ++#include <sound/ac97_codec.h> ++#include <sound/initval.h> ++#include <linux/types.h> ++#include <linux/list.h> ++#include <linux/input.h> /* Input device layer */ ++ ++/* ++ * WM97xx AC97 Touchscreen registers ++ */ ++#define AC97_WM97XX_DIGITISER1 0x76 ++#define AC97_WM97XX_DIGITISER2 0x78 ++#define AC97_WM97XX_DIGITISER_RD 0x7a ++#define AC97_WM9713_DIG1 0x74 ++#define AC97_WM9713_DIG2 AC97_WM97XX_DIGITISER1 ++#define AC97_WM9713_DIG3 AC97_WM97XX_DIGITISER2 ++ ++/* ++ * WM97xx register bits ++ */ ++#define WM97XX_POLL 0x8000 /* initiate a polling measurement */ ++#define WM97XX_ADCSEL_X 0x1000 /* x coord measurement */ ++#define WM97XX_ADCSEL_Y 0x2000 /* y coord measurement */ ++#define WM97XX_ADCSEL_PRES 0x3000 /* pressure measurement */ ++#define WM97XX_ADCSEL_MASK 0x7000 ++#define WM97XX_COO 0x0800 /* enable coordinate mode */ ++#define WM97XX_CTC 0x0400 /* enable continuous mode */ ++#define WM97XX_CM_RATE_93 0x0000 /* 93.75Hz continuous rate */ ++#define WM97XX_CM_RATE_187 0x0100 /* 187.5Hz continuous rate */ ++#define WM97XX_CM_RATE_375 0x0200 /* 375Hz continuous rate */ ++#define WM97XX_CM_RATE_750 0x0300 /* 750Hz continuous rate */ ++#define WM97XX_CM_RATE_8K 0x00f0 /* 8kHz continuous rate */ ++#define WM97XX_CM_RATE_12K 0x01f0 /* 12kHz continuous rate */ ++#define WM97XX_CM_RATE_24K 0x02f0 /* 24kHz continuous rate */ ++#define WM97XX_CM_RATE_48K 0x03f0 /* 48kHz continuous rate */ ++#define WM97XX_CM_RATE_MASK 0x03f0 ++#define WM97XX_RATE(i) (((i & 3) << 8) | ((i & 4) ? 0xf0 : 0)) ++#define WM97XX_DELAY(i) ((i << 4) & 0x00f0) /* sample delay times */ ++#define WM97XX_DELAY_MASK 0x00f0 ++#define WM97XX_SLEN 0x0008 /* slot read back enable */ ++#define WM97XX_SLT(i) ((i - 5) & 0x7) /* touchpanel slot selection (5-11) */ ++#define WM97XX_SLT_MASK 0x0007 ++#define WM97XX_PRP_DETW 0x4000 /* pen detect on, digitiser off, wake up */ ++#define WM97XX_PRP_DET 0x8000 /* pen detect on, digitiser off, no wake up */ ++#define WM97XX_PRP_DET_DIG 0xc000 /* pen detect on, digitiser on */ ++#define WM97XX_RPR 0x2000 /* wake up on pen down */ ++#define WM97XX_PEN_DOWN 0x8000 /* pen is down */ ++#define WM97XX_ADCSRC_MASK 0x7000 /* ADC source mask */ ++ ++#define WM97XX_AUX_ID1 0x8001 ++#define WM97XX_AUX_ID2 0x8002 ++#define WM97XX_AUX_ID3 0x8003 ++#define WM97XX_AUX_ID4 0x8004 ++ ++ ++/* WM9712 Bits */ ++#define WM9712_45W 0x1000 /* set for 5-wire touchscreen */ ++#define WM9712_PDEN 0x0800 /* measure only when pen down */ ++#define WM9712_WAIT 0x0200 /* wait until adc is read before next sample */ ++#define WM9712_PIL 0x0100 /* current used for pressure measurement. set 400uA else 200uA */ ++#define WM9712_MASK_HI 0x0040 /* hi on mask pin (47) stops conversions */ ++#define WM9712_MASK_EDGE 0x0080 /* rising/falling edge on pin delays sample */ ++#define WM9712_MASK_SYNC 0x00c0 /* rising/falling edge on mask initiates sample */ ++#define WM9712_RPU(i) (i&0x3f) /* internal pull up on pen detect (64k / rpu) */ ++#define WM9712_PD(i) (0x1 << i) /* power management */ ++ ++/* WM9712 Registers */ ++#define AC97_WM9712_POWER 0x24 ++#define AC97_WM9712_REV 0x58 ++ ++/* WM9705 Bits */ ++#define WM9705_PDEN 0x1000 /* measure only when pen is down */ ++#define WM9705_PINV 0x0800 /* inverts sense of pen down output */ ++#define WM9705_BSEN 0x0400 /* BUSY flag enable, pin47 is 1 when busy */ ++#define WM9705_BINV 0x0200 /* invert BUSY (pin47) output */ ++#define WM9705_WAIT 0x0100 /* wait until adc is read before next sample */ ++#define WM9705_PIL 0x0080 /* current used for pressure measurement. set 400uA else 200uA */ ++#define WM9705_PHIZ 0x0040 /* set PHONE and PCBEEP inputs to high impedance */ ++#define WM9705_MASK_HI 0x0010 /* hi on mask stops conversions */ ++#define WM9705_MASK_EDGE 0x0020 /* rising/falling edge on pin delays sample */ ++#define WM9705_MASK_SYNC 0x0030 /* rising/falling edge on mask initiates sample */ ++#define WM9705_PDD(i) (i & 0x000f) /* pen detect comparator threshold */ ++ ++ ++/* WM9713 Bits */ ++#define WM9713_PDPOL 0x0400 /* Pen down polarity */ ++#define WM9713_POLL 0x0200 /* initiate a polling measurement */ ++#define WM9713_CTC 0x0100 /* enable continuous mode */ ++#define WM9713_ADCSEL_X 0x0002 /* X measurement */ ++#define WM9713_ADCSEL_Y 0x0004 /* Y measurement */ ++#define WM9713_ADCSEL_PRES 0x0008 /* Pressure measurement */ ++#define WM9713_COO 0x0001 /* enable coordinate mode */ ++#define WM9713_PDEN 0x0800 /* measure only when pen down */ ++#define WM9713_ADCSEL_MASK 0x00fe /* ADC selection mask */ ++#define WM9713_WAIT 0x0200 /* coordinate wait */ ++ ++/* AUX ADC ID's */ ++#define TS_COMP1 0x0 ++#define TS_COMP2 0x1 ++#define TS_BMON 0x2 ++#define TS_WIPER 0x3 ++ ++/* ID numbers */ ++#define WM97XX_ID1 0x574d ++#define WM9712_ID2 0x4c12 ++#define WM9705_ID2 0x4c05 ++#define WM9713_ID2 0x4c13 ++ ++/* Codec GPIO's */ ++#define WM97XX_MAX_GPIO 16 ++#define WM97XX_GPIO_1 (1 << 1) ++#define WM97XX_GPIO_2 (1 << 2) ++#define WM97XX_GPIO_3 (1 << 3) ++#define WM97XX_GPIO_4 (1 << 4) ++#define WM97XX_GPIO_5 (1 << 5) ++#define WM97XX_GPIO_6 (1 << 6) ++#define WM97XX_GPIO_7 (1 << 7) ++#define WM97XX_GPIO_8 (1 << 8) ++#define WM97XX_GPIO_9 (1 << 9) ++#define WM97XX_GPIO_10 (1 << 10) ++#define WM97XX_GPIO_11 (1 << 11) ++#define WM97XX_GPIO_12 (1 << 12) ++#define WM97XX_GPIO_13 (1 << 13) ++#define WM97XX_GPIO_14 (1 << 14) ++#define WM97XX_GPIO_15 (1 << 15) ++ ++ ++#define AC97_LINK_FRAME 21 /* time in uS for AC97 link frame */ ++ ++ ++/*---------------- Return codes from sample reading functions ---------------*/ ++ ++/* More data is available; call the sample gathering function again */ ++#define RC_AGAIN 0x00000001 ++/* The returned sample is valid */ ++#define RC_VALID 0x00000002 ++/* The pen is up (the first RC_VALID without RC_PENUP means pen is down) */ ++#define RC_PENUP 0x00000004 ++/* The pen is down (RC_VALID implies RC_PENDOWN, but sometimes it is helpful ++ to tell the handler that the pen is down but we don't know yet his coords, ++ so the handler should not sleep or wait for pendown irq) */ ++#define RC_PENDOWN 0x00000008 ++ ++/* The wm97xx driver provides a private API for writing platform-specific ++ * drivers. ++ */ ++ ++/* The structure used to return arch specific sampled data into */ ++struct wm97xx_data { ++ int x; ++ int y; ++ int p; ++}; ++ ++/* Codec GPIO status ++ */ ++typedef enum { ++ WM97XX_GPIO_HIGH, ++ WM97XX_GPIO_LOW ++} wm97xx_gpio_status_t; ++ ++/* Codec GPIO direction ++ */ ++typedef enum { ++ WM97XX_GPIO_IN, ++ WM97XX_GPIO_OUT ++} wm97xx_gpio_dir_t; ++ ++/* Codec GPIO polarity ++ */ ++typedef enum { ++ WM97XX_GPIO_POL_HIGH, ++ WM97XX_GPIO_POL_LOW ++} wm97xx_gpio_pol_t; ++ ++/* Codec GPIO sticky ++ */ ++typedef enum { ++ WM97XX_GPIO_STICKY, ++ WM97XX_GPIO_NOTSTICKY ++} wm97xx_gpio_sticky_t; ++ ++/* Codec GPIO wake ++ */ ++typedef enum { ++ WM97XX_GPIO_WAKE, ++ WM97XX_GPIO_NOWAKE ++} wm97xx_gpio_wake_t; ++ ++ ++/* ++ * Digitiser ioctl commands ++ */ ++#define WM97XX_DIG_START 0x1 ++#define WM97XX_DIG_STOP 0x2 ++#define WM97XX_PHY_INIT 0x3 ++#define WM97XX_AUX_PREPARE 0x4 ++#define WM97XX_DIG_RESTORE 0x5 ++ ++struct wm97xx; ++extern struct wm97xx_codec_drv wm97xx_codec; ++ ++/* ++ * Codec driver interface - allows mapping to WM9705/12/13 and newer codecs ++ */ ++struct wm97xx_codec_drv { ++ u16 id; ++ char *name; ++ int (*poll_sample) (struct wm97xx *, int adcsel, int *sample); /* read 1 sample */ ++ int (*poll_touch) (struct wm97xx *, struct wm97xx_data *); /* read X,Y,[P] in poll */ ++ int (*digitiser_ioctl) (struct wm97xx *, int cmd); ++ int (*acc_enable) (struct wm97xx *, int enable); ++}; ++ ++ ++/* Machine specific and accelerated touch operations */ ++struct wm97xx_mach_ops { ++ ++ /* accelerated touch readback - coords are transmited on AC97 link */ ++ int acc_enabled; ++ void (*acc_pen_up) (struct wm97xx *); ++ int (*acc_pen_down) (struct wm97xx *); ++ int (*acc_startup) (struct wm97xx *); ++ void (*acc_shutdown) (struct wm97xx *); ++ ++ /* pre and post sample - can be used to minimise any analog noise */ ++ void (*pre_sample) (int); /* function to run before sampling */ ++ void (*post_sample) (int); /* function to run after sampling */ ++}; ++ ++struct wm97xx { ++ u16 dig[3], id, gpio[6], misc; /* Cached codec registers */ ++ u16 dig_save[3]; /* saved during aux reading */ ++ struct wm97xx_codec_drv *codec; /* attached codec driver*/ ++ struct input_dev* input_dev; /* touchscreen input device */ ++ struct snd_ac97 *ac97; /* ALSA codec access */ ++ struct device *dev; /* ALSA device */ ++ struct device *battery_dev; ++ struct device *touch_dev; ++ struct wm97xx_mach_ops *mach_ops; ++ struct mutex codec_mutex; ++ struct completion ts_init; ++ struct completion ts_exit; ++ struct task_struct *ts_task; ++ unsigned int pen_irq; /* Pen IRQ number in use */ ++ wait_queue_head_t pen_irq_wait; /* Pen IRQ wait queue */ ++ struct workqueue_struct *pen_irq_workq; ++ struct work_struct pen_event_work; ++ u16 acc_slot; /* AC97 slot used for acc touch data */ ++ u16 acc_rate; /* acc touch data rate */ ++ unsigned int ts_use_count; ++ unsigned pen_is_down:1; /* Pen is down */ ++ unsigned aux_waiting:1; /* aux measurement waiting */ ++ unsigned pen_probably_down:1; /* used in polling mode */ ++}; ++ ++/* Codec GPIO access (not supported on WM9705) ++ * This can be used to set/get codec GPIO and Virtual GPIO status. ++ */ ++wm97xx_gpio_status_t wm97xx_get_gpio(struct wm97xx *wm, u32 gpio); ++void wm97xx_set_gpio(struct wm97xx *wm, u32 gpio, ++ wm97xx_gpio_status_t status); ++void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio, ++ wm97xx_gpio_dir_t dir, ++ wm97xx_gpio_pol_t pol, ++ wm97xx_gpio_sticky_t sticky, ++ wm97xx_gpio_wake_t wake); ++ ++/* codec AC97 IO access */ ++int wm97xx_reg_read(struct wm97xx *wm, u16 reg); ++void wm97xx_reg_write(struct wm97xx *wm, u16 reg, u16 val); ++ ++/* aux adc readback */ ++int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel); ++ ++/* machine ops */ ++int wm97xx_register_mach_ops(struct wm97xx *, struct wm97xx_mach_ops *); ++void wm97xx_unregister_mach_ops(struct wm97xx *); ++ ++extern struct bus_type wm97xx_bus_type; ++#endif +diff --git a/sound/soc/pxa/Kconfig b/sound/soc/pxa/Kconfig +index a83e229..89b5730 100644 +--- a/sound/soc/pxa/Kconfig ++++ b/sound/soc/pxa/Kconfig +@@ -53,3 +53,12 @@ config SND_PXA2XX_SOC_TOSA + help + Say Y if you want to add support for SoC audio on Sharp + Zaurus SL-C6000x models (Tosa). ++ ++config SND_PXA2XX_SOC_EM_X270 ++ tristate "SoC Audio support for CompuLab EM-x270" ++ depends on SND_PXA2XX_SOC && MACH_EM_X270 ++ select SND_PXA2XX_SOC_AC97 ++ select SND_SOC_WM9712 ++ help ++ Say Y if you want to add support for SoC audio on ++ CompuLab EM-x270. +diff --git a/sound/soc/pxa/Makefile b/sound/soc/pxa/Makefile +index 78e0d6b..32375ac 100644 +--- a/sound/soc/pxa/Makefile ++++ b/sound/soc/pxa/Makefile +@@ -12,9 +12,11 @@ snd-soc-corgi-objs := corgi.o + snd-soc-poodle-objs := poodle.o + snd-soc-tosa-objs := tosa.o + snd-soc-spitz-objs := spitz.o ++snd-soc-em-x270-objs := em-x270.o + + obj-$(CONFIG_SND_PXA2XX_SOC_CORGI) += snd-soc-corgi.o + obj-$(CONFIG_SND_PXA2XX_SOC_POODLE) += snd-soc-poodle.o + obj-$(CONFIG_SND_PXA2XX_SOC_TOSA) += snd-soc-tosa.o + obj-$(CONFIG_SND_PXA2XX_SOC_SPITZ) += snd-soc-spitz.o ++obj-$(CONFIG_SND_PXA2XX_SOC_EM_X270) += snd-soc-em-x270.o + +diff --git a/sound/soc/pxa/em-x270.c b/sound/soc/pxa/em-x270.c +new file mode 100644 +index 0000000..9e891d0 +--- /dev/null ++++ b/sound/soc/pxa/em-x270.c +@@ -0,0 +1,137 @@ ++/* ++ * em-x270.c -- SoC audio for EM-X270 ++ * ++ * Copyright 2007 CompuLab, Ltd. ++ * ++ * Author: Mike Rapoport <mike@compulab.co.il> ++ * ++ * Copied from tosa.c: ++ * Copyright 2005 Wolfson Microelectronics PLC. ++ * Copyright 2005 Openedhand Ltd. ++ * ++ * Authors: Liam Girdwood <liam.girdwood@wolfsonmicro.com> ++ * Richard Purdie <richard@openedhand.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. ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/device.h> ++ ++#include <sound/driver.h> ++#include <sound/core.h> ++#include <sound/pcm.h> ++#include <sound/soc.h> ++#include <sound/soc-dapm.h> ++ ++#include <asm/mach-types.h> ++#include <asm/arch/pxa-regs.h> ++#include <asm/arch/hardware.h> ++#include <asm/arch/audio.h> ++ ++#include "../codecs/wm9712.h" ++#include "pxa2xx-pcm.h" ++#include "pxa2xx-ac97.h" ++ ++static struct snd_soc_machine em_x270; ++ ++#define EM_X270_HP 0 ++#define EM_X270_MIC_INT 1 ++#define EM_X270_HEADSET 2 ++#define EM_X270_HP_OFF 3 ++#define EM_X270_SPK_ON 0 ++#define EM_X270_SPK_OFF 1 ++ ++ ++static struct snd_soc_ops em_x270_ops = { ++}; ++ ++static const struct snd_kcontrol_new em_x270_controls[] = { ++}; ++ ++static int em_x270_ac97_init(struct snd_soc_codec *codec) ++{ ++ int i, err; ++ ++ /* add em_x270 specific controls */ ++ for (i = 0; i < ARRAY_SIZE(em_x270_controls); i++) { ++ err = snd_ctl_add(codec->card, ++ snd_soc_cnew(&em_x270_controls[i],codec, NULL)); ++ if (err < 0) ++ return err; ++ } ++ ++ snd_soc_dapm_sync_endpoints(codec); ++ return 0; ++} ++ ++static struct snd_soc_dai_link em_x270_dai[] = { ++ { ++ .name = "AC97", ++ .stream_name = "AC97 HiFi", ++ .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_HIFI], ++ .codec_dai = &wm9712_dai[WM9712_DAI_AC97_HIFI], ++ .init = em_x270_ac97_init, ++ .ops = &em_x270_ops, ++ }, ++ { ++ .name = "AC97 Aux", ++ .stream_name = "AC97 Aux", ++ .cpu_dai = &pxa_ac97_dai[PXA2XX_DAI_AC97_AUX], ++ .codec_dai = &wm9712_dai[WM9712_DAI_AC97_AUX], ++ .ops = &em_x270_ops, ++ }, ++}; ++ ++static struct snd_soc_machine em_x270 = { ++ .name = "EM-X270", ++ .dai_link = em_x270_dai, ++ .num_links = ARRAY_SIZE(em_x270_dai), ++}; ++ ++static struct snd_soc_device em_x270_snd_devdata = { ++ .machine = &em_x270, ++ .platform = &pxa2xx_soc_platform, ++ .codec_dev = &soc_codec_dev_wm9712, ++}; ++ ++static struct platform_device *em_x270_snd_device; ++ ++static int __init em_x270_init(void) ++{ ++ int ret; ++ ++ if (!machine_is_em_x270()) ++ return -ENODEV; ++ ++ em_x270_snd_device = platform_device_alloc("soc-audio", -1); ++ if (!em_x270_snd_device) ++ return -ENOMEM; ++ ++ platform_set_drvdata(em_x270_snd_device, &em_x270_snd_devdata); ++ em_x270_snd_devdata.dev = &em_x270_snd_device->dev; ++ ret = platform_device_add(em_x270_snd_device); ++ ++ if (ret) ++ platform_device_put(em_x270_snd_device); ++ ++ return ret; ++} ++ ++static void __exit em_x270_exit(void) ++{ ++ platform_device_unregister(em_x270_snd_device); ++} ++ ++module_init(em_x270_init); ++module_exit(em_x270_exit); ++ ++/* Module information */ ++MODULE_AUTHOR("Mike Rapoport"); ++MODULE_DESCRIPTION("ALSA SoC EM-X270"); ++MODULE_LICENSE("GPL"); |