diff -urN linux-2.6.21/arch/arm/boot/compressed/head-xscale.S linux-2.6.21-vpac1/arch/arm/boot/compressed/head-xscale.S --- linux-2.6.21/arch/arm/boot/compressed/head-xscale.S 2007-04-26 05:08:32.000000000 +0200 +++ linux-2.6.21-vpac1/arch/arm/boot/compressed/head-xscale.S 2007-06-25 16:02:38.000000000 +0200 @@ -41,6 +41,11 @@ mov r7, #MACH_TYPE_COTULLA_IDP #endif +#ifdef CONFIG_MACH_VPAC270 + mov r7, #(MACH_TYPE_VPAC270 & 0xFF00) + add r7, r7, #(MACH_TYPE_VPAC270 & 0xFF) +#endif + #ifdef CONFIG_MACH_GTWX5715 mov r7, #(MACH_TYPE_GTWX5715 & 0xff) orr r7, r7, #(MACH_TYPE_GTWX5715 & 0xff00) diff -urN linux-2.6.21/arch/arm/configs/vpac270_defconfig linux-2.6.21-vpac1/arch/arm/configs/vpac270_defconfig --- linux-2.6.21/arch/arm/configs/vpac270_defconfig 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.21-vpac1/arch/arm/configs/vpac270_defconfig 2007-06-25 16:02:38.000000000 +0200 @@ -0,0 +1,1109 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.19-vpac1 +# Wed Jan 3 14:12:26 2007 +# +CONFIG_ARM=y +# CONFIG_GENERIC_TIME is not set +CONFIG_MMU=y +CONFIG_GENERIC_HARDIRQS=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_ARCH_MTD_XIP=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_LOCK_KERNEL=y +CONFIG_INIT_ENV_ARG_LIMIT=32 + +# +# General setup +# +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +# CONFIG_SWAP is not set +CONFIG_SYSVIPC=y +# CONFIG_IPC_NS is not set +# 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_AUDIT is not set +# CONFIG_IKCONFIG is not set +# CONFIG_RELAY is not set +CONFIG_INITRAMFS_SOURCE="" +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_EMBEDDED=y +# CONFIG_UID16 is not set +# CONFIG_SYSCTL_SYSCALL is not set +# CONFIG_KALLSYMS is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +# CONFIG_ELF_CORE is not set +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +# CONFIG_EPOLL is not set +# CONFIG_SHMEM is not set +CONFIG_SLAB=y +# CONFIG_VM_EVENT_COUNTERS is not set +CONFIG_RT_MUTEXES=y +CONFIG_TINY_SHMEM=y +CONFIG_BASE_SMALL=0 +# CONFIG_SLOB is not set + +# +# Loadable module support +# +# CONFIG_MODULES is not set + +# +# Block layer +# +CONFIG_BLOCK=y +# CONFIG_BLK_DEV_IO_TRACE is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_AS is not set +CONFIG_IOSCHED_DEADLINE=y +# CONFIG_IOSCHED_CFQ is not set +# CONFIG_DEFAULT_AS is not set +CONFIG_DEFAULT_DEADLINE=y +# CONFIG_DEFAULT_CFQ is not set +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="deadline" + +# +# 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_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_L7200 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_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_VPAC270=y +CONFIG_PXA27x=y +CONFIG_IWMMXT=y + +# +# 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 is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +CONFIG_XSCALE_PMU=y + +# +# Bus support +# + +# +# PCCARD (PCMCIA/CardBus) support +# +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +CONFIG_PREEMPT=y +# CONFIG_NO_IDLE_HZ is not set +CONFIG_HZ=100 +# CONFIG_AEABI is not set +# 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_ALIGNMENT_TRAP=y + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x20000 +CONFIG_ZBOOT_ROM_BSS=0xa0800000 +CONFIG_ZBOOT_ROM=y +CONFIG_CMDLINE="mem=32M root=/dev/nfs ip=dhcp console=ttyS0,38400" + +# +# CPU Frequency scaling +# +CONFIG_CPU_FREQ=y +CONFIG_CPU_FREQ_TABLE=y +# CONFIG_CPU_FREQ_DEBUG is not set +# CONFIG_CPU_FREQ_STAT is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE=y +# CONFIG_CPU_FREQ_GOV_PERFORMANCE is not set +# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set +CONFIG_CPU_FREQ_GOV_USERSPACE=y +# CONFIG_CPU_FREQ_GOV_ONDEMAND is not set +# CONFIG_CPU_FREQ_GOV_CONSERVATIVE is not set +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 is not set +# CONFIG_BINFMT_MISC is not set +# CONFIG_ARTHUR is not set + +# +# Power management options +# +# CONFIG_PM is not set +# CONFIG_APM is not set + +# +# Networking +# +CONFIG_NET=y + +# +# Networking options +# +# CONFIG_NETDEBUG is not set +# CONFIG_PACKET is not set +CONFIG_UNIX=y +# 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 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 is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_DIAG is not set +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# 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 + +# +# DCCP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_DCCP is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP is not set + +# +# TIPC Configuration (EXPERIMENTAL) +# +# 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 + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_IEEE80211 is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +# CONFIG_FW_LOADER is not set +# CONFIG_SYS_HYPERVISOR is not set + +# +# Connector - unified userspace <-> kernelspace linker +# +# CONFIG_CONNECTOR is not set + +# +# Memory Technology Devices (MTD) +# +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_CONCAT is not set +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_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 is not set +CONFIG_MTD_GEN_PROBE=y +CONFIG_MTD_CFI_ADV_OPTIONS=y +CONFIG_MTD_CFI_NOSWAP=y +# CONFIG_MTD_CFI_BE_BYTE_SWAP is not set +# CONFIG_MTD_CFI_LE_BYTE_SWAP is not set +CONFIG_MTD_CFI_GEOMETRY=y +# CONFIG_MTD_MAP_BANK_WIDTH_1 is not set +CONFIG_MTD_MAP_BANK_WIDTH_2=y +# CONFIG_MTD_MAP_BANK_WIDTH_4 is not set +# 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 is not set +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_OTP is not set +CONFIG_MTD_CFI_INTELEXT=y +# CONFIG_MTD_CFI_AMDSTD is not set +# CONFIG_MTD_CFI_STAA is not set +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_OBSOLETE_CHIPS is not set +# CONFIG_MTD_XIP is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PHYSMAP is not set +# CONFIG_MTD_ARM_INTEGRATOR is not set +CONFIG_MTD_VPAC270=y +# 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 + +# +# NAND Flash Device Drivers +# +# CONFIG_MTD_NAND is not set + +# +# OneNAND Flash Device Drivers +# +# CONFIG_MTD_ONENAND is not set + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Plug and Play support +# + +# +# Block devices +# +# CONFIG_BLK_DEV_COW_COMMON is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_BLK_DEV_INITRD is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set + +# +# ATA/ATAPI/MFM/RLL support +# +# CONFIG_IDE is not set + +# +# SCSI device support +# +# CONFIG_RAID_ATTRS is not set +CONFIG_SCSI=y +# 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 + +# +# 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_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set + +# +# SCSI low-level drivers +# +# CONFIG_ISCSI_TCP is not set +# CONFIG_SCSI_DEBUG is not set + +# +# Serial ATA (prod) and Parallel ATA (experimental) drivers +# +# CONFIG_ATA is not set + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Fusion MPT device support +# +# CONFIG_FUSION is not set + +# +# IEEE 1394 (FireWire) support +# + +# +# I2O device support +# + +# +# Network device support +# +CONFIG_NETDEVICES=y +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set + +# +# PHY device support +# +# CONFIG_PHYLIB is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +CONFIG_MII=y +# CONFIG_SMC91X is not set +CONFIG_DM9000=y +# CONFIG_SMC911X is not set + +# +# Ethernet (1000 Mbit) +# + +# +# Ethernet (10000 Mbit) +# + +# +# Token Ring devices +# + +# +# Wireless LAN (non-hamradio) +# +# CONFIG_NET_RADIO is not set + +# +# Wan interfaces +# +# CONFIG_WAN is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# 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 + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +CONFIG_KEYBOARD_ATKBD=y +# 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_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +CONFIG_SERIO=y +# CONFIG_SERIO_SERPORT is not set +CONFIG_SERIO_VPAC270=y +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 + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +CONFIG_HW_RANDOM=y +# CONFIG_NVRAM is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_RAW_DRIVER is not set + +# +# TPM devices +# +# CONFIG_TCG_TPM is not set + +# +# I2C support +# +CONFIG_I2C=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_PXA=y +# CONFIG_I2C_PXA_SLAVE is not set +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_PCA_ISA is not set + +# +# Miscellaneous I2C Chip support +# +# CONFIG_SENSORS_DS1337 is not set +# CONFIG_SENSORS_DS1374 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_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 + +# +# Dallas's 1-wire bus +# +# CONFIG_W1 is not set + +# +# Hardware Monitoring support +# +# CONFIG_HWMON is not set +# CONFIG_HWMON_VID is not set + +# +# Misc devices +# +# CONFIG_TIFM_CORE is not set + +# +# LED devices +# +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +CONFIG_LEDS_VPAC270=y + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGERS=y +# CONFIG_LEDS_TRIGGER_TIMER is not set +CONFIG_LEDS_TRIGGER_HEARTBEAT=y + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set +# CONFIG_USB_DABUSB is not set + +# +# Graphics support +# +# CONFIG_FIRMWARE_EDID is not set +CONFIG_FB=y +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# 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 +# CONFIG_FB_S1D13XXX is not set +CONFIG_FB_PXA=y +# CONFIG_FB_PXA_VGA is not set +CONFIG_FB_PXA_SVGA=y +# CONFIG_FB_PXA_XGA is not set +# CONFIG_FB_PXA_BPP8 is not set +CONFIG_FB_PXA_BPP16=y +# CONFIG_FB_PXA_PARAMETERS is not set +# 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_ROTATION is not set +CONFIG_FONTS=y +# CONFIG_FONT_8x8 is not set +CONFIG_FONT_8x16=y +# CONFIG_FONT_6x11 is not set +# CONFIG_FONT_7x14 is not set +# CONFIG_FONT_PEARL_8x8 is not set +# CONFIG_FONT_ACORN_8x8 is not set +# CONFIG_FONT_MINI_4x6 is not set +# CONFIG_FONT_SUN8x16 is not set +# CONFIG_FONT_SUN12x22 is not set +# CONFIG_FONT_10x18 is not set + +# +# Logo configuration +# +CONFIG_LOGO=y +# CONFIG_LOGO_LINUX_MONO is not set +# CONFIG_LOGO_LINUX_VGA16 is not set +CONFIG_LOGO_LINUX_CLUT224=y +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Sound +# +CONFIG_SOUND=y + +# +# Advanced Linux Sound Architecture +# +# CONFIG_SND is not set + +# +# Open Sound System +# +CONFIG_SOUND_PRIME=y +# CONFIG_OSS_OBSOLETE_DRIVER is not set +CONFIG_SOUND_PXA_AC97=y + +# +# USB support +# +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_BANDWIDTH is not set +# CONFIG_USB_DYNAMIC_MINORS 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 is not set +CONFIG_USB_OHCI_LITTLE_ENDIAN=y +# CONFIG_USB_SL811_HCD is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_ACM is not set +# CONFIG_USB_PRINTER is not set + +# +# 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 Input Devices +# +# CONFIG_USB_HID is not set + +# +# USB HID Boot Protocol drivers +# +# CONFIG_USB_KBD is not set +# CONFIG_USB_MOUSE is not set +# CONFIG_USB_AIPTEK is not set +# CONFIG_USB_WACOM is not set +# CONFIG_USB_ACECAD is not set +# CONFIG_USB_KBTAB is not set +# CONFIG_USB_POWERMATE is not set +# CONFIG_USB_TOUCHSCREEN is not set +# CONFIG_USB_YEALINK is not set +# CONFIG_USB_XPAD is not set +# CONFIG_USB_ATI_REMOTE is not set +# CONFIG_USB_ATI_REMOTE2 is not set +# CONFIG_USB_KEYSPAN_REMOTE is not set +# CONFIG_USB_APPLETOUCH is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK 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_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_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_TEST is not set + +# +# USB DSL modem support +# + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# MMC/SD Card support +# +# CONFIG_MMC is not set + +# +# Real Time Clock +# +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +# CONFIG_RTC_INTF_PROC is not set +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set + +# +# RTC drivers +# +# CONFIG_RTC_DRV_X1205 is not set +CONFIG_RTC_DRV_DS1307=y +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_SA1100 is not set +# CONFIG_RTC_DRV_TEST is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# File systems +# +# CONFIG_EXT2_FS is not set +# CONFIG_EXT3_FS is not set +# CONFIG_EXT4DEV_FS is not set +# 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 is not set +# CONFIG_QUOTA is not set +# CONFIG_DNOTIFY is not set +# 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_MSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_SYSFS=y +# CONFIG_TMPFS 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_JFFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_WRITEBUFFER=y +# CONFIG_JFFS2_SUMMARY is not set +# 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 is not set +# CONFIG_NFSD is not set +# CONFIG_SMB_FS 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 +# CONFIG_9P_FS is not set + +# +# Partition Types +# +# CONFIG_PARTITION_ADVANCED is not set +CONFIG_MSDOS_PARTITION=y + +# +# Native Language Support +# +# CONFIG_NLS is not set + +# +# Profiling support +# +# CONFIG_PROFILING is not set + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +# CONFIG_ENABLE_MUST_CHECK is not set +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_KERNEL is not set +CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_DEBUG_BUGVERBOSE is not set +# CONFIG_DEBUG_FS is not set +CONFIG_FRAME_POINTER=y +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_USER is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +# CONFIG_CRYPTO is not set + +# +# Library routines +# +# CONFIG_CRC_CCITT is not set +# CONFIG_CRC16 is not set +CONFIG_CRC32=y +# CONFIG_LIBCRC32C is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_PLIST=y diff -urN linux-2.6.21/arch/arm/Kconfig linux-2.6.21-vpac1/arch/arm/Kconfig --- linux-2.6.21/arch/arm/Kconfig 2007-04-26 05:08:32.000000000 +0200 +++ linux-2.6.21-vpac1/arch/arm/Kconfig 2007-06-25 16:02:38.000000000 +0200 @@ -800,7 +800,7 @@ endmenu -if (ARCH_SA1100 || ARCH_INTEGRATOR || ARCH_OMAP || ARCH_IMX ) +if (ARCH_SA1100 || ARCH_PXA || ARCH_INTEGRATOR || ARCH_OMAP || ARCH_IMX ) menu "CPU Frequency scaling" @@ -816,6 +816,11 @@ depends on CPU_FREQ && (SA1100_ASSABET || SA1100_CERF || SA1100_PT_SYSTEM3) default y +config CPU_FREQ_PXA + bool + depends on CPU_FREQ && ARCH_PXA + default y + config CPU_FREQ_INTEGRATOR tristate "CPUfreq driver for ARM Integrator CPUs" depends on ARCH_INTEGRATOR && CPU_FREQ diff -urN linux-2.6.21/arch/arm/mach-pxa/cpu-pxa.c linux-2.6.21-vpac1/arch/arm/mach-pxa/cpu-pxa.c --- linux-2.6.21/arch/arm/mach-pxa/cpu-pxa.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.21-vpac1/arch/arm/mach-pxa/cpu-pxa.c 2007-06-25 16:02:38.000000000 +0200 @@ -0,0 +1,417 @@ +/* + * 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 +#include +#include +#include +#include + +#include + +#include + +/* + * This comes from generic.h in this directory. + */ +extern unsigned int get_clk_frequency_khz(int info); + +//#define DEBUG 7 + +#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) +#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 +#define MDREFR_DB2_MASK (MDREFR_K2DB2 | MDREFR_K1DB2) +#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) + +/* + * 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, 0x121, 1, PXA25x_CCLKCFG}, /* run=99, turbo= 99, PXbus=50, SDRAM=50 */ + {199100, 99500, 0x221, 0, PXA25x_CCLKCFG}, /* run=99, turbo=199, PXbus=50, SDRAM=99 */ + {298500, 99500, 0x321, 0, PXA25x_CCLKCFG}, /* run=99, turbo=287, PXbus=50, SDRAM=99 */ + {298600, 99500, 0x1c1, 0, PXA25x_CCLKCFG}, /* run=199, turbo=287, PXbus=99, SDRAM=99 */ + {398100, 99500, 0x241, 0, PXA25x_CCLKCFG} /* run=199, turbo=398, PXbus=99, SDRAM=99 */ +#else + { 99500, 99500, 0x121, 1, PXA25x_CCLKCFG}, /* run= 99, turbo= 99, PXbus=50, SDRAM=50 */ + {132700, 132700, 0x123, 1, PXA25x_CCLKCFG}, /* run=133, turbo=133, PXbus=66, SDRAM=66 */ + {199100, 99500, 0x141, 0, PXA25x_CCLKCFG}, /* run=199, turbo=199, PXbus=99, SDRAM=99 */ + {265400, 132700, 0x143, 1, PXA25x_CCLKCFG}, /* run=265, turbo=265, PXbus=133, SDRAM=66 */ + {331800, 165900, 0x145, 1, PXA25x_CCLKCFG}, /* run=331, turbo=331, PXbus=166, SDRAM=83 */ + {398100, 99500, 0x161, 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]; + +/* 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;icpus_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 -urN linux-2.6.21/arch/arm/mach-pxa/Kconfig linux-2.6.21-vpac1/arch/arm/mach-pxa/Kconfig --- linux-2.6.21/arch/arm/mach-pxa/Kconfig 2007-04-26 05:08:32.000000000 +0200 +++ linux-2.6.21-vpac1/arch/arm/mach-pxa/Kconfig 2007-06-25 16:02:38.000000000 +0200 @@ -37,6 +37,11 @@ bool "Keith und Koep Trizeps4 DIMM-Module" select PXA27x +config MACH_VPAC270 + bool "Voipac PXA270 Module" + select PXA27x + select IWMMXT + endchoice if PXA_SHARPSL diff -urN linux-2.6.21/arch/arm/mach-pxa/Makefile linux-2.6.21-vpac1/arch/arm/mach-pxa/Makefile --- linux-2.6.21/arch/arm/mach-pxa/Makefile 2007-04-26 05:08:32.000000000 +0200 +++ linux-2.6.21-vpac1/arch/arm/mach-pxa/Makefile 2007-06-25 16:02:38.000000000 +0200 @@ -18,6 +18,7 @@ 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_VPAC270) += vpac270.o # Support for blinky lights led-y := leds.o @@ -28,6 +29,9 @@ 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 diff -urN linux-2.6.21/arch/arm/mach-pxa/vpac270.c linux-2.6.21-vpac1/arch/arm/mach-pxa/vpac270.c --- linux-2.6.21/arch/arm/mach-pxa/vpac270.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.21-vpac1/arch/arm/mach-pxa/vpac270.c 2007-06-29 13:32:04.000000000 +0200 @@ -0,0 +1,366 @@ + /* + * linux/arch/arm/mach-pxa/vpac270.c + * + * Support for Voipac PXA270 module + * + * Copyright (c) 2006 Voipac Technologies + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "generic.h" + +#include + + +static void __init vpac270_init_irq(void) +{ + pxa_init_irq(); + + /* setup extra vpac270 irqs */ + set_irq_type(VPAC270_ETH_IRQ, IRQT_RISING); + set_irq_type(VPAC270_IDE_IRQ, IRQT_RISING); +} + +static struct resource dm9000_resources[] = { + [0] = { + .start = (VPAC270_ETH_PHYS + 0x300), + .end = (VPAC270_ETH_PHYS + 0x300 + VPAC270_ETH_SIZE - 1), + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = (VPAC270_ETH_IRQ), + .end = (VPAC270_ETH_IRQ), + .flags = IORESOURCE_IRQ, + }, +}; + +static struct dm9000_plat_data dm9000_setup = { + .flags = 0 //DM9000_PLATF_16BITONLY +}; + +static struct platform_device dm9000_device = { + .name = "dm9000", + .id = 0, + .num_resources = ARRAY_SIZE(dm9000_resources), + .resource = dm9000_resources, + .dev = { + .platform_data = &dm9000_setup, + } +}; +#ifdef CONFIG_SND_PXA2XX_AC97 +static struct platform_device pxa_audio_device = { + .name = "pxa2xx-ac97", + .id = -1, +}; +#else +static struct platform_device pxa_audio_device = { + .name = "pxa-audio", + .id = 0, +}; +#endif +#ifdef CONFIG_SERIO_VPAC270 +static struct platform_device vpac270kb_device = { + .name = "vpac270ps2", + .id = 0, +}; + +static struct platform_device vpac270ms_device = { + .name = "vpac270ps2", + .id = 1, +}; +#endif +static struct platform_device vpac270led_device = { + .name = "vpac270-led", + .id = -1, +}; + +static struct platform_device *devices[] __initdata = { + &dm9000_device, + &pxa_audio_device, +#ifdef CONFIG_SERIO_VPAC270 + &vpac270ms_device, + &vpac270kb_device, +#endif + &vpac270led_device, +}; + +/* + * MMC/SD Device + * + */ +static struct pxamci_platform_data vpac270_mci_platform_data; + +static int vpac270_mci_init(struct device *dev, irqreturn_t (*detect_int)(int, void *), void *data) +{ + int err; + + printk("Voipac PXA270 MMC/SD setup "); + /* setup GPIO for PXA2xx MMC controller */ + pxa_gpio_mode(GPIO32_MMCCLK_MD); + pxa_gpio_mode(GPIO112_MMCCMD_MD); + pxa_gpio_mode(GPIO92_MMCDAT0_MD); + pxa_gpio_mode(GPIO109_MMCDAT1_MD); + pxa_gpio_mode(GPIO110_MMCDAT2_MD); + pxa_gpio_mode(GPIO111_MMCDAT3_MD); + + pxa_gpio_mode(GPIO_MMC_CD_IRQ | GPIO_IN); + + vpac270_mci_platform_data.detect_delay = msecs_to_jiffies(250); + + err = request_irq(VPAC270_MMC_CD_IRQ, detect_int, SA_INTERRUPT, + "MMC card detect", data); + if (err) { + printk(KERN_ERR "vpac270_mci_init: MMC/SD: can't request MMC card detect IRQ\n"); + return -1; + } + + set_irq_type(VPAC270_MMC_CD_IRQ, IRQT_BOTHEDGE); + + printk("done.\n"); + return 0; +} + +static struct pxamci_platform_data vpac270_mci_platform_data = { + .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34, + .init = vpac270_mci_init, +}; + +#ifdef CONFIG_FB_PXA + +#ifdef CONFIG_FB_PXA_BPP8 +#define FB_PXA_BPP 8 +#else +#define FB_PXA_BPP 16 +#endif + +static struct pxafb_mode_info vpac270_fb_mode_info[] __initdata = { +#if defined(CONFIG_FB_PXA_VGA) +/* VGA 640x480 16bit, pclk=26MHz */ +{ + .pixclock = 40000, + .xres = 640, + .yres = 480, + .bpp = FB_PXA_BPP, + .hsync_len = 64, + .left_margin = 96, + .right_margin = 48, + .vsync_len = 2, + .upper_margin = 33, + .lower_margin = 10, + .sync = FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, +}, +#elif defined(CONFIG_FB_PXA_SVGA) +/* SVGA 800x600 16bit, pclk=34.6MHz */ +{ + .pixclock = 28846, + .xres = 800, + .yres = 600, + .bpp = FB_PXA_BPP, + .hsync_len = 8, + .left_margin = 128, + .right_margin = 48, + .vsync_len = 1, + .upper_margin = 33, + .lower_margin = 1, + .sync = FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, +}, +#elif defined(CONFIG_FB_PXA_XGA) +/* XGA 1024x768 16bit, pclk=52.0MHz */ +{ + .pixclock = 19230, + .xres = 1024, + .yres = 768, + .bpp = FB_PXA_BPP, + .hsync_len = 63, + .left_margin = 220, + .right_margin = 8, + .vsync_len = 1, + .upper_margin = 33, + .lower_margin = 2, + .sync = FB_SYNC_HOR_HIGH_ACT|FB_SYNC_VERT_HIGH_ACT, +}, +#endif +}; + +static struct pxafb_mach_info vpac270_fb_mach_info __initdata = { + .modes = vpac270_fb_mode_info, + .num_modes = ARRAY_SIZE(vpac270_fb_mode_info), + .lccr0 = 0x043008f8, + .lccr3 = 0x0040ff00, +}; +#endif // CONFIG_FB_PXA + +static int vpac270_ohci_init(struct device *dev) +{ + /* setup Port1 GPIO pin. */ + pxa_gpio_mode( 88 | GPIO_ALT_FN_1_IN); /* USBHPWR1 */ + pxa_gpio_mode( 89 | GPIO_ALT_FN_2_OUT); /* USBHPEN1 */ + pxa_gpio_mode(119 | GPIO_ALT_FN_1_IN); + pxa_gpio_mode(120 | GPIO_ALT_FN_2_OUT); + + /* Set the Power Control Polarity Low and Power Sense + Polarity Low to active low. */ + UHCHR = (UHCHR | UHCHR_PCPL | UHCHR_PSPL) & + ~(UHCHR_SSEP1 | UHCHR_SSEP2 | UHCHR_SSEP3 | UHCHR_SSE); + + UP2OCR = UP2OCR_HXS | UP2OCR_HXOE | UP2OCR_DPPDE | UP2OCR_DMPDE; + + return 0; +} + +static struct pxaohci_platform_data vpac270_ohci_platform_data = { + .port_mode = PMM_GLOBAL_MODE, //PMM_PERPORT_MODE, PMM_NPS_MODE, + .init = vpac270_ohci_init, +}; + +/* USB Device Controller */ + +static int +udc_detect(void) +{ + printk ("UDC detect\n"); +// if (core_funcs.udc_detect != NULL) +// return core_funcs.udc_detect(); +// else + return 0; +} + +static void +udc_enable(int cmd) +{ + switch (cmd) + { + case PXA2XX_UDC_CMD_DISCONNECT: + printk (KERN_NOTICE "UDC cmd disconnect\n"); + + UP2OCR = (UP2OCR | UP2OCR_HXS| UP2OCR_HXOE | UP2OCR_DPPDE | UP2OCR_DMPDE) & + ~(UP2OCR_DPPUE | UP2OCR_DMPUE | UP2OCR_DPPUBE | UP2OCR_DMPUBE); + break; + + case PXA2XX_UDC_CMD_CONNECT: + printk (KERN_NOTICE "UDC cmd connect\n"); + + UP2OCR = (UP2OCR | UP2OCR_HXOE | UP2OCR_DPPUE) & + ~(UP2OCR_HXS | UP2OCR_DMPUE | UP2OCR_DPPUBE | + UP2OCR_DMPUBE | UP2OCR_DPPDE | UP2OCR_DMPDE); + break; + } +} + +static struct pxa2xx_udc_mach_info hx4700_udc_mach_info = { + .udc_is_connected = udc_detect, + .udc_command = udc_enable, +}; + +static void __init vpac270_init(void) +{ + /* reset UCB1400 */ + GPSR2 &= ~(1u << 31); + pxa_gpio_mode(GPIO_AC97_RESET | GPIO_OUT); + udelay(12); + pxa_set_mci_info(&vpac270_mci_platform_data); + platform_add_devices(devices, ARRAY_SIZE(devices)); +#ifdef CONFIG_FB_PXA + set_pxa_fb_info(&vpac270_fb_mach_info); +#endif + pxa_set_ohci_info(&vpac270_ohci_platform_data); + pxa_set_udc_info( &hx4700_udc_mach_info ); + + GPDR(VPAC270_LCD_BLO_GPIO) |= GPIO_bit(VPAC270_LCD_BLO_GPIO); + GPSR(VPAC270_LCD_BLO_GPIO) |= GPIO_bit(VPAC270_LCD_BLO_GPIO); +} + +static struct map_desc vpac270_io_desc[] __initdata = { + /* virtual physical length type */ +/*{ VPAC270_FLASH, VPAC270_FLASH_PHYS, VPAC270_FLASH_SIZE, MT_DEVICE }, + { VPAC270_ETH_BASE, VPAC270_ETH_PHYS, VPAC270_ETH_SIZE, MT_DEVICE },*/ +// { 0xf0000000, __phys_to_pfn(0x0c000000), 0x04000000, MT_DEVICE }, +}; + +static void __init vpac270_map_io(void) +{ + pxa_map_io(); + iotable_init(vpac270_io_desc, ARRAY_SIZE(vpac270_io_desc)); + + /* enabling FFUART */ + CKEN |= CKEN6_FFUART; + pxa_gpio_mode(GPIO34_FFRXD_MD); + pxa_gpio_mode(GPIO100_FFCTS_MD); + pxa_gpio_mode(GPIO10_FFDCD_MD); + pxa_gpio_mode(GPIO33_FFDSR_MD); + pxa_gpio_mode(GPIO38_FFRI_MD); + pxa_gpio_mode(GPIO39_FFTXD_MD); + pxa_gpio_mode(GPIO40_FFDTR_MD); + pxa_gpio_mode(GPIO27_FFRTS_MD); + + /* enabling BTUART */ + CKEN |= CKEN7_BTUART; + pxa_gpio_mode(GPIO42_BTRXD_MD); + pxa_gpio_mode(GPIO43_BTTXD_MD); + pxa_gpio_mode(GPIO44_BTCTS_MD); + pxa_gpio_mode(GPIO45_BTRTS_MD); + + /* This is for the Davicom chip select */ + pxa_gpio_mode(GPIO78_nCS_2_MD); + + /* bring hdd out of reset */ + GPCR(GPIO_PCMCIA1_RESET) |= GPIO_bit(GPIO_PCMCIA1_RESET); // disable reset + + /* setup sleep mode values */ + PWER = 0x00000002; + PFER = 0x00000000; + PRER = 0x00000002; + PGSR0 = 0x00008000; + PGSR1 = 0x003F0202; + PGSR2 = 0x0001C000; + PCFR |= PCFR_OPDE; +} + +MACHINE_START(VPAC270, "Voipac PXA270 Module") + /* Maintainer: Voipac Technologies */ + .phys_io = 0x40000000, + .io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc, + .boot_params = 0xa0000100, + .map_io = vpac270_map_io, + .init_irq = vpac270_init_irq, + .timer = &pxa_timer, + .init_machine = vpac270_init, +MACHINE_END + diff -urN linux-2.6.21/arch/arm/Makefile linux-2.6.21-vpac1/arch/arm/Makefile --- linux-2.6.21/arch/arm/Makefile 2007-04-26 05:08:32.000000000 +0200 +++ linux-2.6.21-vpac1/arch/arm/Makefile 2007-06-25 16:02:38.000000000 +0200 @@ -20,7 +20,7 @@ # Do not use arch/arm/defconfig - it's always outdated. # Select a platform tht is kept up-to-date -KBUILD_DEFCONFIG := versatile_defconfig +KBUILD_DEFCONFIG := vpac270_defconfig # defines filename extension depending memory manement type. ifeq ($(CONFIG_MMU),) diff -urN linux-2.6.21/drivers/cpufreq/Kconfig linux-2.6.21-vpac1/drivers/cpufreq/Kconfig --- linux-2.6.21/drivers/cpufreq/Kconfig 2007-04-26 05:08:32.000000000 +0200 +++ linux-2.6.21-vpac1/drivers/cpufreq/Kconfig 2007-06-25 22:25:38.000000000 +0200 @@ -16,7 +16,7 @@ if CPU_FREQ config CPU_FREQ_TABLE - tristate + def_tristate m config CPU_FREQ_DEBUG bool "Enable CPUfreq debugging" @@ -52,7 +52,7 @@ choice prompt "Default CPUFreq governor" - default CPU_FREQ_DEFAULT_GOV_USERSPACE if CPU_FREQ_SA1100 || CPU_FREQ_SA1110 + default CPU_FREQ_DEFAULT_GOV_USERSPACE if CPU_FREQ_SA1100 || CPU_FREQ_SA1110 || CPU_FREQ_PXA27x default CPU_FREQ_DEFAULT_GOV_PERFORMANCE help This option sets which CPUFreq governor shall be loaded at diff -urN linux-2.6.21/drivers/ide/arm/Makefile linux-2.6.21-vpac1/drivers/ide/arm/Makefile --- linux-2.6.21/drivers/ide/arm/Makefile 2007-04-26 05:08:32.000000000 +0200 +++ linux-2.6.21-vpac1/drivers/ide/arm/Makefile 2007-06-25 16:02:38.000000000 +0200 @@ -2,5 +2,6 @@ obj-$(CONFIG_BLK_DEV_IDE_ICSIDE) += icside.o obj-$(CONFIG_BLK_DEV_IDE_RAPIDE) += rapide.o obj-$(CONFIG_BLK_DEV_IDE_BAST) += bast-ide.o +obj-$(CONFIG_BLK_DEV_IDE_VPAC270) += vpac270-ide.o EXTRA_CFLAGS := -Idrivers/ide diff -urN linux-2.6.21/drivers/ide/arm/vpac270-ide.c linux-2.6.21-vpac1/drivers/ide/arm/vpac270-ide.c --- linux-2.6.21/drivers/ide/arm/vpac270-ide.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.21-vpac1/drivers/ide/arm/vpac270-ide.c 2007-06-27 00:07:55.000000000 +0200 @@ -0,0 +1,334 @@ +/* linux/drivers/ide/arm/vpac270-ide.c + * + * Copyright (c) 2006 Voipac Technologies + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +/* list of registered interfaces */ +static ide_hwif_t *vpac270_hwif; + +#define IDE_BASE_PHYS 0x0c000000 +//#define DMA_IRQ_DETECT IRQ_GPIO(80) +#define GPIO80_DREQ_1_MD (80 | GPIO_ALT_FN_1_IN) + +static int vpac270_set_speed(ide_drive_t *drive, u8 xfer_mode) +{ + int on = 0, cycle_time = 0, use_dma_info = 0; + + if (xfer_mode > XFER_MW_DMA_2) + xfer_mode = XFER_MW_DMA_2; + + switch (xfer_mode) { + case XFER_MW_DMA_2: + cycle_time = 120; + use_dma_info = 1; + break; + + case XFER_MW_DMA_1: + cycle_time = 250; + use_dma_info = 1; + break; + + case XFER_MW_DMA_0: + cycle_time = 480; + break; + + case XFER_SW_DMA_2: + case XFER_SW_DMA_1: + case XFER_SW_DMA_0: + cycle_time = 480; + break; + } + + if (use_dma_info && drive->id->eide_dma_time > cycle_time) + cycle_time = drive->id->eide_dma_time; + + drive->drive_data = cycle_time; + + if (cycle_time && ide_config_drive_speed(drive, xfer_mode) == 0) + on = 1; + else + drive->drive_data = 480; + + printk("%s: %s selected (peak %dMB/s)\n", drive->name, + ide_xfer_verbose(xfer_mode), 2000 / drive->drive_data); + + drive->current_speed = xfer_mode; + + return on; +} + +static void vpac270_dma_host_off(ide_drive_t *drive) +{ +} + +static void vpac270_dma_off_quietly(ide_drive_t *drive) +{ + drive->using_dma = 0; +} + +static void vpac270_dma_host_on(ide_drive_t *drive) +{ +} + +static int vpac270_dma_on(ide_drive_t *drive) +{ + drive->using_dma = 1; + return 0; +} + +static int vpac270_dma_check(ide_drive_t *drive) +{ + struct hd_driveid *id = drive->id; + ide_hwif_t *hwif = HWIF(drive); + int xfer_mode = XFER_PIO_2; + + if (!(id->capability & 1) || !hwif->autodma) + goto out; + + // consult the list of known "bad" drives + if (__ide_dma_bad_drive(drive)) + goto out; + + // enable DMA on any drive that has multiword DMA + if (id->field_valid & 2) { + xfer_mode = ide_dma_speed(drive, 0); + goto out; + } + + // consult the list of known "good" drives + if (__ide_dma_good_drive(drive)) { + if (id->eide_dma_time > 150) + goto out; + xfer_mode = XFER_MW_DMA_1; + } + +out: + if( vpac270_set_speed(drive, xfer_mode)) + return vpac270_dma_on(drive); + + return 0; +} + +static int vpac270_dma_setup(ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + struct request *rq = hwif->hwgroup->rq; + + blk_queue_max_segment_size( drive->queue, (DCMD_LENGTH & 0xfffffe00) ); + ide_map_sg(drive, rq); + + hwif->sg_dma_direction = (rq_data_dir(rq) == READ)? + DMA_FROM_DEVICE : DMA_TO_DEVICE; + + if( hwif->sg_nents > 0) + { + int i = 0; + int dma = hwif->hw.dma; + struct scatterlist *sg = hwif->sg_table; + + pxa_dma_desc *ddadr = (void *) hwif->dmatable_dma; + pxa_dma_desc *ddptr = (void *) hwif->dmatable_cpu; + + DCSR(dma) = 0; + + do + { + u32 length = sg->length; + + sg->dma_address = dma_map_page( NULL, sg->page, sg->offset, + length, hwif->sg_dma_direction); + + ddptr->ddadr = (u32) ++ddadr; // next DDADR + + if(hwif->sg_dma_direction == DMA_FROM_DEVICE) + { + ddptr->dsadr = 0xc000020; // DSADR + ddptr->dtadr = sg->dma_address; // DTADR + ddptr->dcmd = (DCMD_INCTRGADDR | DCMD_BURST32 | DCMD_FLOWSRC | + DCMD_WIDTH2 | (DCMD_LENGTH & length)); + } + else + { + ddptr->dsadr = sg->dma_address; // DSADR + ddptr->dtadr = 0xc000020; // DTADR + ddptr->dcmd = (DCMD_INCSRCADDR | DCMD_BURST32 | DCMD_FLOWTRG | + DCMD_WIDTH2 | (DCMD_LENGTH & length)); + } + sg++; + } + while( ++i < hwif->sg_nents && ddptr++); + + ddptr->ddadr = DDADR_STOP; + + DDADR(dma) = hwif->dmatable_dma; + + DRQSR1 = DRQSR_REQCLR; + DRCMR1 = dma | DRCMR_MAPVLD; + + DCSR(dma) = DCSR_RUN; + drive->waiting_for_dma = 1; + } + return 0; +} + +static void vpac270_dma_exec_cmd(ide_drive_t *drive, u8 cmd) +{ + ide_execute_command(drive, cmd, &ide_dma_intr, 2 * WAIT_CMD, NULL); +} + +static void vpac270_dma_start(ide_drive_t *drive) +{ +} + +static int vpac270_dma_end(ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + int dma = hwif->hw.dma; + + drive->waiting_for_dma = 0; + + while (!(DCSR(dma) & DCSR_STOPSTATE)) + cpu_relax(); + DCSR(dma) = 0; + + dma_unmap_sg( NULL, hwif->sg_table, hwif->sg_nents, + hwif->sg_dma_direction); + return 0; +} + +static int vpac270_dma_test_irq(ide_drive_t *drive) +{ + return (GPLR(GPIO_IDE_IRQ) & GPIO_bit(GPIO_IDE_IRQ)); +} + +static int vpac270_dma_timeout(ide_drive_t *drive) +{ + ide_hwif_t *hwif = HWIF(drive); + int dma = hwif->hw.dma; +printk("%s:%d\n",__FUNCTION__,__LINE__); +printk("%s:%d DCSR(%d)=%08x\n",__FUNCTION__,__LINE__,dma,DCSR(dma)); + printk(KERN_ERR "%s: DMA timeout occurred: ", drive->name); + + if (vpac270_dma_test_irq(drive)) + return 0; + + ide_dump_status(drive, "DMA timeout", + HWIF(drive)->INB(IDE_STATUS_REG)); + + return vpac270_dma_end(drive); +} + +static int vpac270_dma_lostirq(ide_drive_t *drive) +{ +printk("%s:%d\n",__FUNCTION__,__LINE__); + printk(KERN_ERR "%s: IRQ lost\n", drive->name); + return 1; +} + +static void vpac270_pxa_dma_irq(int dma, void *dummy) +{ +printk("%s:%d DCSR(%d)=%08x\n",__FUNCTION__,__LINE__,dma,DCSR(dma)); +printk("%s:%d DRQSR1=0x%08x\n",__FUNCTION__,__LINE__,DRQSR1); + + DCSR(dma) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR; +} + +static void vpac270_dma_init(ide_hwif_t *hwif) +{ + printk(" %s: SG-DMA", hwif->name); + + pxa_gpio_mode(GPIO80_DREQ_1_MD); + + hwif->atapi_dma = 1; + hwif->mwdma_mask = 7; + hwif->swdma_mask = 7; + + hwif->dmatable_cpu = NULL; + hwif->dmatable_dma = 0; + hwif->speedproc = vpac270_set_speed; + hwif->autodma = 1; + + hwif->ide_dma_check = vpac270_dma_check; + hwif->dma_host_off = vpac270_dma_host_off; + hwif->dma_off_quietly = vpac270_dma_off_quietly; + hwif->dma_host_on = vpac270_dma_host_on; + hwif->ide_dma_on = vpac270_dma_on; + hwif->dma_setup = vpac270_dma_setup; + hwif->dma_exec_cmd = vpac270_dma_exec_cmd; + hwif->dma_start = vpac270_dma_start; + hwif->ide_dma_end = vpac270_dma_end; + hwif->ide_dma_test_irq = vpac270_dma_test_irq; + hwif->ide_dma_timeout = vpac270_dma_timeout; + hwif->ide_dma_lostirq = vpac270_dma_lostirq; + + hwif->drives[0].autodma = hwif->autodma; + + hwif->noprobe = 0; + hwif->chipset = ide_unknown; + hwif->channel = 0; + hwif->serialized = 1; + hwif->hw.dma = pxa_request_dma( hwif->name, DMA_PRIO_LOW, + vpac270_pxa_dma_irq, NULL); + + hwif->dmatable_cpu = dma_alloc_coherent( NULL, (PRD_ENTRIES * 4 * sizeof(u32)), + &hwif->dmatable_dma, GFP_ATOMIC); + + printk(" capable, dma=%d%s\n", hwif->hw.dma, hwif->autodma ? ", auto-enable" : ""); +} + +static int __init vpac270_ide_init(void) +{ + u32 base = (u32) ioremap( IDE_BASE_PHYS, 0x200);; + + printk("Voipac PXA270 IDE driver, (c) 2006 Voipac Technologies\n"); + + if( base) + { + int i; + hw_regs_t hw; + + memset(&hw, 0, sizeof(hw)); + + for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) + hw.io_ports[i] = (base + 0x120 + 2*i); + + hw.io_ports[IDE_CONTROL_OFFSET] = (base + 0x15c); + hw.irq = VPAC270_IDE_IRQ; + + ide_register_hw(&hw, &vpac270_hwif); + + vpac270_dma_init( vpac270_hwif); + } + return 0; +} + +module_init(vpac270_ide_init); + +MODULE_AUTHOR("Voipac Technologies "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Voipac PXA270 IDE driver"); + diff -urN linux-2.6.21/drivers/ide/Kconfig linux-2.6.21-vpac1/drivers/ide/Kconfig --- linux-2.6.21/drivers/ide/Kconfig 2007-04-26 05:08:32.000000000 +0200 +++ linux-2.6.21-vpac1/drivers/ide/Kconfig 2007-06-25 16:02:38.000000000 +0200 @@ -852,6 +852,11 @@ Say Y here if you want to support the onboard IDE channels on the Simtec BAST or the Thorcom VR1000 +config BLK_DEV_IDE_VPAC270 + tristate "Voipac PXA270 IDE support" + depends on ARM && (ARCH_PXA || MACH_VPAC270) + select BLK_DEV_IDEDMA + config BLK_DEV_GAYLE bool "Amiga Gayle IDE interface support" depends on AMIGA diff -urN linux-2.6.21/drivers/input/serio/Kconfig linux-2.6.21-vpac1/drivers/input/serio/Kconfig --- linux-2.6.21/drivers/input/serio/Kconfig 2007-04-26 05:08:32.000000000 +0200 +++ linux-2.6.21-vpac1/drivers/input/serio/Kconfig 2007-06-25 16:02:38.000000000 +0200 @@ -88,6 +88,17 @@ To compile this driver as a module, choose M here: the module will be called rpckbd. +config SERIO_VPAC270 + tristate "Voipac PXA270 PS/2 controller" + depends on MACH_VPAC270 + default y + help + Say Y here if you have the Voipac PXA270 board and want to use + an AT keyboard/mouse connected to its PS/2 controller. + + To compile this driver as a module, choose M here: the + module will be called vpac270ps2. + config SERIO_AMBAKMI tristate "AMBA KMI keyboard controller" depends on ARM_AMBA diff -urN linux-2.6.21/drivers/input/serio/Makefile linux-2.6.21-vpac1/drivers/input/serio/Makefile --- linux-2.6.21/drivers/input/serio/Makefile 2007-04-26 05:08:32.000000000 +0200 +++ linux-2.6.21-vpac1/drivers/input/serio/Makefile 2007-06-25 16:02:38.000000000 +0200 @@ -10,6 +10,7 @@ obj-$(CONFIG_SERIO_SERPORT) += serport.o obj-$(CONFIG_SERIO_CT82C710) += ct82c710.o obj-$(CONFIG_SERIO_RPCKBD) += rpckbd.o +obj-$(CONFIG_SERIO_VPAC270) += vpac270ps2.o obj-$(CONFIG_SERIO_SA1111) += sa1111ps2.o obj-$(CONFIG_SERIO_AMBAKMI) += ambakmi.o obj-$(CONFIG_SERIO_Q40KBD) += q40kbd.o diff -urN linux-2.6.21/drivers/input/serio/vpac270ps2.c linux-2.6.21-vpac1/drivers/input/serio/vpac270ps2.c --- linux-2.6.21/drivers/input/serio/vpac270ps2.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.21-vpac1/drivers/input/serio/vpac270ps2.c 2007-06-29 13:37:17.000000000 +0200 @@ -0,0 +1,384 @@ +/* + * 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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define VPAC270_SCL1_GPIO 14 +#define VPAC270_SCL1_IRQ IRQ_GPIO(VPAC270_SCL1_GPIO) +#define VPAC270_SDA1_GPIO 19 + +#define VPAC270_SCL2_GPIO 82 +#define VPAC270_SCL2_IRQ IRQ_GPIO(VPAC270_SCL2_GPIO) +#define VPAC270_SDA2_GPIO 106 + +static int kb_parity; +static int kb_bitcount; +static unsigned char kb_scan; // holds the scan code + +static int ms_parity; +static int ms_bitcount; +static unsigned char ms_scan; // holds the scan code + +static irqreturn_t vpac270kb_interrupt(int irq, void *dev_id) +{ + struct serio *port = dev_id; +//printk("%s:%d\n",__FUNCTION__,__LINE__); + if( kb_bitcount > 0) + { + if(--kb_bitcount > 0) // all bits received + { + // check parity bit + if( kb_bitcount == 1 && (GPLR0 & GPIO_bit(VPAC270_SDA1_GPIO)) ) + kb_parity++; + + // bit 2 to 9 is data + // 10 parity bit, 1 start bit and 11 stop bit are ignored + if(kb_bitcount < 10 && kb_bitcount > 1) + { + kb_scan = (kb_scan >> 1); + // keyboard data is connected to PORT3, bit3 + if(GPLR0 & GPIO_bit(VPAC270_SDA1_GPIO)) + { + kb_parity++; + kb_scan = kb_scan | 0x80; + } + } + } + else + { + GPDR0 |= GPIO_bit(VPAC270_SCL1_GPIO); // inhibit ps/2 device + GPCR0 = GPIO_bit(VPAC270_SCL1_GPIO); + +// printk("%s:%d scan=%x, parity=%d\n",__FUNCTION__,__LINE__,kb_scan,kb_parity); + + kb_bitcount = 11; + + serio_interrupt(port, kb_scan, (kb_parity & 1)? 0 : SERIO_PARITY); + + kb_parity = 0; + + GPDR0 &= ~GPIO_bit(VPAC270_SCL1_GPIO); // enable + } + } + else + { + if( ++kb_bitcount < 0) // all bits received + { + if( kb_bitcount == -2) + kb_scan = ~kb_parity; + + // 8 data bits and parity bit + if( kb_bitcount < -1) + { + // set/reset data line + if( kb_scan & 0x01) + { + kb_parity++; + GPSR0 |= GPIO_bit(VPAC270_SDA1_GPIO); + } + else + GPCR0 |= GPIO_bit(VPAC270_SDA1_GPIO); + kb_scan = (kb_scan >> 1); + } + else + { + // handle stop condition + GPDR0 &= ~GPIO_bit(VPAC270_SDA1_GPIO); + } + } + else + { + // setup rx mode + GPDR0 &= ~GPIO_bit(VPAC270_SDA1_GPIO); + kb_bitcount = 11; + kb_parity = 0; + +// printk("%s:%d tx complete\n",__FUNCTION__,__LINE__); + } + } + return IRQ_HANDLED; +} + +static irqreturn_t vpac270ms_interrupt(int irq, void *dev_id) +{ + struct serio *port = dev_id; +//printk("%s:%d\n",__FUNCTION__,__LINE__); + if( ms_bitcount > 0) + { + if(--ms_bitcount > 0) // all bits received + { + // check parity bit + if( ms_bitcount == 1 && (GPLR3 & GPIO_bit(VPAC270_SDA2_GPIO)) ) + ms_parity++; + + // bit 2 to 9 is data + // 10 parity bit, 1 start bit and 11 stop bit are ignored + if(ms_bitcount < 10 && ms_bitcount > 1) + { + ms_scan = (ms_scan >> 1); + // keyboard data is connected to PORT3, bit3 + if(GPLR3 & GPIO_bit(VPAC270_SDA2_GPIO)) + { + ms_parity++; + ms_scan = ms_scan | 0x80; + } + } + } + else + { + GPDR2 |= GPIO_bit(VPAC270_SCL2_GPIO); // inhibit ps/2 device + GPCR2 = GPIO_bit(VPAC270_SCL2_GPIO); + +// printk("%s:%d scan=%x, parity=%d\n",__FUNCTION__,__LINE__,ms_scan,ms_parity); + + ms_bitcount = 11; + + serio_interrupt(port, ms_scan, (ms_parity & 1)? 0 : SERIO_PARITY); + + ms_parity = 0; + + GPDR2 &= ~GPIO_bit(VPAC270_SCL2_GPIO); // enable + } + } + else + { + if( ++ms_bitcount < 0) // all bits received + { + if( ms_bitcount == -2) + ms_scan = ~ms_parity; + + // 8 data bits and parity bit + if( ms_bitcount < -1) + { + // set/reset data line + if( ms_scan & 0x01) + { + ms_parity++; + GPSR3 |= GPIO_bit(VPAC270_SDA2_GPIO); + } + else + GPCR3 |= GPIO_bit(VPAC270_SDA2_GPIO); + ms_scan = (ms_scan >> 1); + } + else + { + // handle stop condition + GPDR3 &= ~GPIO_bit(VPAC270_SDA2_GPIO); + } + } + else + { + // setup rx mode + GPDR3 &= ~GPIO_bit(VPAC270_SDA2_GPIO); + ms_bitcount = 11; + ms_parity = 0; + +// printk("%s:%d tx complete\n",__FUNCTION__,__LINE__); + } + } + return IRQ_HANDLED; +} + +static int vpac270kb_write(struct serio *port, unsigned char val) +{ +//printk("%s:%d val=%x\n",__FUNCTION__,__LINE__,val); + + // setup clk gpio pin as output and set it to low + GPDR0 |= GPIO_bit(VPAC270_SCL1_GPIO); + GPCR0 = GPIO_bit(VPAC270_SCL1_GPIO); + + // wait min 100us + udelay(120); + + // setup data gpio pin as output and set it to low + GPDR0 |= GPIO_bit(VPAC270_SDA1_GPIO); + GPCR0 = GPIO_bit(VPAC270_SDA1_GPIO); + + // set scan and setup tx mode + kb_parity = 0; + kb_bitcount = -11; + kb_scan = val; + + // setup clk gpio pin as input (clk transitions to high by pullup) + GPDR0 &= ~GPIO_bit(VPAC270_SCL1_GPIO); + + return 0; +} + +static int vpac270ms_write(struct serio *port, unsigned char val) +{ +//printk("%s:%d val=%x\n",__FUNCTION__,__LINE__,val); + + // setup clk gpio pin as output and set it to low + GPDR2 |= GPIO_bit(VPAC270_SCL2_GPIO); + GPCR2 = GPIO_bit(VPAC270_SCL2_GPIO); + + // wait min 100us + udelay(120); + + // setup data gpio pin as output and set it to low + GPDR3 |= GPIO_bit(VPAC270_SDA2_GPIO); + GPCR3 = GPIO_bit(VPAC270_SDA2_GPIO); + + // set scan and setup tx mode + ms_parity = 0; + ms_bitcount = -11; + ms_scan = val; + + // setup clk gpio pin as input (clk transitions to high by pullup) + GPDR2 &= ~GPIO_bit(VPAC270_SCL2_GPIO); + + return 0; +} + +static int vpac270kb_open(struct serio *port) +{ +//printk("%s:%d\n",__FUNCTION__,__LINE__); + pxa_gpio_mode(VPAC270_SCL1_GPIO | GPIO_IN); + pxa_gpio_mode(VPAC270_SDA1_GPIO | GPIO_IN); + + if (request_irq( VPAC270_SCL1_IRQ, vpac270kb_interrupt, + SA_INTERRUPT, "vpac270kb", port)) + { + printk(KERN_WARNING "%s: failed to request IRQ: %d!\n", __FUNCTION__, VPAC270_SCL1_IRQ); + } + + set_irq_type(VPAC270_SCL1_IRQ, IRQT_FALLING); + + kb_parity = 0; + kb_bitcount = 11; + + return 0; +} + +static int vpac270ms_open(struct serio *port) +{ +//printk("%s:%d\n",__FUNCTION__,__LINE__); + pxa_gpio_mode(VPAC270_SCL2_GPIO | GPIO_IN); + pxa_gpio_mode(VPAC270_SDA2_GPIO | GPIO_IN); + + if (request_irq( VPAC270_SCL2_IRQ, vpac270ms_interrupt, + SA_INTERRUPT, "vpac270ms", port)) + { + printk(KERN_WARNING "%s: failed to request IRQ: %d!\n", __FUNCTION__, VPAC270_SCL2_IRQ); + } + + set_irq_type(VPAC270_SCL2_IRQ, IRQT_FALLING); + + ms_parity = 0; + ms_bitcount = 11; + + return 0; +} + +static void vpac270kb_close(struct serio *port) +{ +//printk("%s:%d\n",__FUNCTION__,__LINE__); + free_irq( VPAC270_SCL1_IRQ, port); +} + +static void vpac270ms_close(struct serio *port) +{ +//printk("%s:%d\n",__FUNCTION__,__LINE__); + free_irq( VPAC270_SCL2_IRQ, port); +} + +/* + * Allocate and initialize serio structure for subsequent registration + * with serio core. + */ +static int __devinit vpac270ps2_probe(struct platform_device *dev) +{ + struct serio *serio; +//printk("%s:%d\n",__FUNCTION__,__LINE__); + + serio = kmalloc(sizeof(struct serio), GFP_KERNEL); + if (!serio) + return -ENOMEM; + + memset(serio, 0, sizeof(struct serio)); + serio->id.type = SERIO_8042; + + if( dev->id == 0) + { + serio->write = vpac270kb_write; + serio->open = vpac270kb_open; + serio->close = vpac270kb_close; + serio->dev.parent = &dev->dev; + strlcpy(serio->name, "Voipac PXA270 PS/2 keyboard port", sizeof(serio->name)); + strlcpy(serio->phys, "vpac270/serio0", sizeof(serio->phys)); + } else { + serio->write = vpac270ms_write; + serio->open = vpac270ms_open; + serio->close = vpac270ms_close; + serio->dev.parent = &dev->dev; + strlcpy(serio->name, "Voipac PXA270 PS/2 mouse port", sizeof(serio->name)); + strlcpy(serio->phys, "vpac270/serio1", sizeof(serio->phys)); + } + + platform_set_drvdata(dev, serio); + serio_register_port(serio); + + return 0; +} + +static int __devexit vpac270ps2_remove(struct platform_device *dev) +{ + struct serio *serio = platform_get_drvdata(dev); + + serio_unregister_port(serio); + + return 0; +} + +static struct platform_driver vpac270ps2_driver = { + .probe = vpac270ps2_probe, + .remove = __devexit_p(vpac270ps2_remove), + .driver = { + .name = "vpac270ps2", + }, +}; + +static int __init vpac270ps2_init(void) +{ + return platform_driver_register(&vpac270ps2_driver); +} + +static void __exit vpac270ps2_exit(void) +{ + platform_driver_unregister(&vpac270ps2_driver); +} + +module_init(vpac270ps2_init); +module_exit(vpac270ps2_exit); + +MODULE_AUTHOR("Voipac Technologies "); +MODULE_DESCRIPTION("Voipac PXA270 PS/2 controller driver"); +MODULE_LICENSE("GPL"); + diff -urN linux-2.6.21/drivers/leds/Kconfig linux-2.6.21-vpac1/drivers/leds/Kconfig --- linux-2.6.21/drivers/leds/Kconfig 2007-04-26 05:08:32.000000000 +0200 +++ linux-2.6.21-vpac1/drivers/leds/Kconfig 2007-06-25 16:02:38.000000000 +0200 @@ -19,6 +19,13 @@ comment "LED drivers" +config LEDS_VPAC270 + tristate "LED Support for the Voipac PXA270 baseboard" + depends LEDS_CLASS && MACH_VPAC270 + help + This option enables support for the LED on Voipac + Technologies PXA270 developement baseboard. + config LEDS_CORGI tristate "LED Support for the Sharp SL-C7x0 series" depends on LEDS_CLASS && PXA_SHARP_C7xx diff -urN linux-2.6.21/drivers/leds/leds-vpac270.c linux-2.6.21-vpac1/drivers/leds/leds-vpac270.c --- linux-2.6.21/drivers/leds/leds-vpac270.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.21-vpac1/drivers/leds/leds-vpac270.c 2007-06-25 16:02:38.000000000 +0200 @@ -0,0 +1,90 @@ +/* + * LED Triggers Core + * + * Copyright (c) 2006 Voipac Technologies + * + * 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 +#include +#include +#include +#include +#include +#include +#include +//#include + +#define VPAC270_GPIO_LED_ORANGE GPIO15_nCS_1 + +static void vpac270led_set(struct led_classdev *led_cdev, enum led_brightness value) +{ + if (value) + GPSR0 = GPIO_bit(VPAC270_GPIO_LED_ORANGE); + else + GPCR0 = GPIO_bit(VPAC270_GPIO_LED_ORANGE); +} + +static struct led_classdev vpac270_orange_led = { + .name = "vpac270:orange", + .default_trigger = "heartbeat", + .brightness_set = vpac270led_set, +}; + +#ifdef CONFIG_PM +static int vpac270led_suspend(struct platform_device *dev, pm_message_t state) +{ + led_classdev_suspend(&vpac270_orange_led); + return 0; +} + +static int vpac270led_resume(struct platform_device *dev) +{ + led_classdev_resume(&vpac270_orange_led); + return 0; +} +#endif + +static int vpac270led_probe(struct platform_device *pdev) +{ + return led_classdev_register(&pdev->dev, &vpac270_orange_led); +} + +static int vpac270led_remove(struct platform_device *pdev) +{ + led_classdev_unregister(&vpac270_orange_led); + return 0; +} + +static struct platform_driver vpac270led_driver = { + .probe = vpac270led_probe, + .remove = vpac270led_remove, +#ifdef CONFIG_PM + .suspend = vpac270led_suspend, + .resume = vpac270led_resume, +#endif + .driver = { + .name = "vpac270-led", + }, +}; + +static int __init vpac270led_init(void) +{ + return platform_driver_register(&vpac270led_driver); +} + +static void __exit vpac270led_exit(void) +{ + platform_driver_unregister(&vpac270led_driver); +} + +module_init(vpac270led_init); +module_exit(vpac270led_exit); + +MODULE_AUTHOR("Voipac Technologies "); +MODULE_DESCRIPTION("Voipac PXA270 LED driver"); +MODULE_LICENSE("GPL"); diff -urN linux-2.6.21/drivers/leds/Makefile linux-2.6.21-vpac1/drivers/leds/Makefile --- linux-2.6.21/drivers/leds/Makefile 2007-04-26 05:08:32.000000000 +0200 +++ linux-2.6.21-vpac1/drivers/leds/Makefile 2007-06-25 16:02:38.000000000 +0200 @@ -5,6 +5,7 @@ obj-$(CONFIG_LEDS_TRIGGERS) += led-triggers.o # LED Platform Drivers +obj-$(CONFIG_LEDS_VPAC270) += leds-vpac270.o obj-$(CONFIG_LEDS_CORGI) += leds-corgi.o obj-$(CONFIG_LEDS_LOCOMO) += leds-locomo.o obj-$(CONFIG_LEDS_SPITZ) += leds-spitz.o diff -urN linux-2.6.21/drivers/mtd/maps/Kconfig linux-2.6.21-vpac1/drivers/mtd/maps/Kconfig --- linux-2.6.21/drivers/mtd/maps/Kconfig 2007-04-26 05:08:32.000000000 +0200 +++ linux-2.6.21-vpac1/drivers/mtd/maps/Kconfig 2007-06-25 16:02:38.000000000 +0200 @@ -498,6 +498,13 @@ This enables access to the flash chips on the Hynix evaluation boards. If you have such a board, say 'Y'. +config MTD_VPAC270 + tristate "Voipac PXA270 module flash mapping" + depends on MACH_VPAC270 && MTD_CFI && MTD_PARTITIONS + help + This enables access to the flash memory on the Voipac PXA270 module. + If you have such a module, say 'Y'. + config MTD_MPC1211 tristate "CFI Flash device mapped on Interface MPC-1211" depends on SH_MPC1211 && MTD_CFI diff -urN linux-2.6.21/drivers/mtd/maps/Makefile linux-2.6.21-vpac1/drivers/mtd/maps/Makefile --- linux-2.6.21/drivers/mtd/maps/Makefile 2007-04-26 05:08:32.000000000 +0200 +++ linux-2.6.21-vpac1/drivers/mtd/maps/Makefile 2007-06-25 16:02:38.000000000 +0200 @@ -72,3 +72,4 @@ obj-$(CONFIG_MTD_OMAP_NOR) += omap_nor.o obj-$(CONFIG_MTD_MTX1) += mtx-1_flash.o obj-$(CONFIG_MTD_TQM834x) += tqm834x.o +obj-$(CONFIG_MTD_VPAC270) += vpac270.o diff -urN linux-2.6.21/drivers/mtd/maps/vpac270.c linux-2.6.21-vpac1/drivers/mtd/maps/vpac270.c --- linux-2.6.21/drivers/mtd/maps/vpac270.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.21-vpac1/drivers/mtd/maps/vpac270.c 2007-06-30 02:48:17.000000000 +0200 @@ -0,0 +1,142 @@ +/* + * Map driver for the Voipac PXA270 module + * + * Modified from drivers/mtd/maps/iq80310.c + * Orignal Author: Nicolas Pitre + * + * Copyright: (C) 2001 MontaVista Software Inc. + * Copyright (c) 2006 Voipac Technologies + * + * 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 +#include +#include +#include +#include +#include +#include +#include + + +#define ROM_ADDR 0x00000000 +#define WINDOW_SIZE 32*1024*1024 + +#ifdef CONFIG_PM +struct pm_dev *flash_pmdev; +#endif + +static struct map_info vpac270_map = { + .name = "Voipac PXA270 Flash", + .size = WINDOW_SIZE, + .bankwidth = 2, + .phys = ROM_ADDR +}; + +static struct mtd_partition vpac270_partitions[] = { + { + name: "Bootloader", + size: 0x00020000, /* 128kB u-boot and config params */ + offset: 0, + /*mask_flags: MTD_WRITEABLE force read-only */ + },{ + name: "Kernel", + size: 0x00100000, /* 1MB for kernel */ + offset: 0x00020000, + },{ + name: "Filesystem", /* the rest for filesystem */ + size: MTDPART_SIZ_FULL, + offset: 0x00120000 + } +}; + +static struct mtd_info *mymtd; + +#ifdef VPAC270_PARTS_PARSING +static struct mtd_partition *parsed_parts; +static int nr_parsed_parts; + +static const char* probes[] = { "RedBoot", "cmdlinepart", NULL }; +#endif + +#include + +static int __init init_vpac270(void) +{ + printk( "Probing Voipac PXA270 flash at physical address 0x%08lx (%d-bit buswidth)\n", + vpac270_map.phys, vpac270_map.bankwidth * 8 ); + + vpac270_map.virt = ioremap(vpac270_map.phys, vpac270_map.size); + if (!vpac270_map.virt) { + printk("Failed to ioremap\n"); + return -EIO; + } + + simple_map_init(&vpac270_map); + + mymtd = do_map_probe("cfi_probe", &vpac270_map); + if (!mymtd) { + iounmap((void *)vpac270_map.virt); + return -ENXIO; + } + mymtd->owner = THIS_MODULE; + +#ifdef DO_FLASH_UNLOCK + /* Unlock the flash device. */ + for (i = 0; i < mymtd->numeraseregions; i++) { + int j; + for( j = 0; j < mymtd->eraseregions[i].numblocks; j++) { + mymtd->unlock(mymtd, mymtd->eraseregions[i].offset + + j * mymtd->eraseregions[i].erasesize, + mymtd->eraseregions[i].erasesize); + } + } +#endif + +#ifdef VPAC270_PARTS_PARSING + ret = parse_mtd_partitions(mymtd, probes, &parsed_parts, 0); + + if (ret > 0) + nr_parsed_parts = ret; +#endif + + if (!mymtd) { + printk(KERN_WARNING "%s is absent. Skipping\n", vpac270_map.name); +#ifdef VPAC270_PARTS_PARSING + } else if (nr_parsed_parts) { + add_mtd_partitions(mymtd, parsed_parts, nr_parsed_parts); +#endif + } else { + add_mtd_partitions(mymtd, vpac270_partitions, ARRAY_SIZE(vpac270_partitions)); + } + + return 0; +} + +static void __exit cleanup_vpac270(void) +{ + if (mymtd) { +#ifdef VPAC270_PARTS_PARSING + if (nr_parsed_parts) + del_mtd_partitions(mymtd); + else +#endif + del_mtd_device(mymtd); + + map_destroy(mymtd); +#ifdef VPAC270_PARTS_PARSING + if (parsed_parts) + kfree(parsed_parts); +#endif + } + + if (vpac270_map.virt) + iounmap((void *)vpac270_map.virt); +} + +module_init(init_vpac270); +module_exit(cleanup_vpac270); + diff -urN linux-2.6.21/drivers/net/dm9000.c linux-2.6.21-vpac1/drivers/net/dm9000.c --- linux-2.6.21/drivers/net/dm9000.c 2007-04-26 05:08:32.000000000 +0200 +++ linux-2.6.21-vpac1/drivers/net/dm9000.c 2007-06-25 16:02:38.000000000 +0200 @@ -422,9 +422,10 @@ ret = -ENODEV; goto out; } else if (pdev->num_resources == 2) { - base = pdev->resource[0].start; + iosize = 4; + base = (unsigned long) ioremap(pdev->resource[0].start, 8); - if (!request_mem_region(base, 4, ndev->name)) { + if (!request_mem_region(base, 8, ndev->name)) { ret = -EBUSY; goto out; } @@ -432,7 +433,7 @@ ndev->base_addr = base; ndev->irq = pdev->resource[1].start; db->io_addr = (void __iomem *)base; - db->io_data = (void __iomem *)(base + 4); + db->io_data = (void __iomem *)(base + iosize); } else { db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); @@ -486,11 +487,11 @@ ndev->base_addr = (unsigned long)db->io_addr; ndev->irq = db->irq_res->start; - - /* ensure at least we have a default set of IO routines */ - dm9000_set_io(db, iosize); } + /* ensure at least we have a default set of IO routines */ + dm9000_set_io(db, iosize); + /* check to see if anything is being over-ridden */ if (pdata != NULL) { /* check to see if the driver wants to over-ride the diff -urN linux-2.6.21/drivers/net/wireless/hostap/hostap_cs.c linux-2.6.21-vpac1/drivers/net/wireless/hostap/hostap_cs.c --- linux-2.6.21/drivers/net/wireless/hostap/hostap_cs.c 2007-04-26 05:08:32.000000000 +0200 +++ linux-2.6.21-vpac1/drivers/net/wireless/hostap/hostap_cs.c 2007-06-27 00:13:49.000000000 +0200 @@ -824,6 +824,7 @@ PCMCIA_DEVICE_MANF_CARD(0x0101, 0x0777), PCMCIA_DEVICE_MANF_CARD(0x0126, 0x8000), PCMCIA_DEVICE_MANF_CARD(0x0138, 0x0002), + PCMCIA_DEVICE_MANF_CARD(0x0156, 0x0002), PCMCIA_DEVICE_MANF_CARD(0x0250, 0x0002), PCMCIA_DEVICE_MANF_CARD(0x026f, 0x030b), PCMCIA_DEVICE_MANF_CARD(0x0274, 0x1612), @@ -835,7 +836,9 @@ PCMCIA_DEVICE_MANF_CARD(0x50c2, 0x7300), /* PCMCIA_DEVICE_MANF_CARD(0xc00f, 0x0000), conflict with pcnet_cs */ PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0002), + PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0004), PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0005), + PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0007), PCMCIA_DEVICE_MANF_CARD(0xd601, 0x0010), PCMCIA_DEVICE_MANF_CARD(0x0126, 0x0002), PCMCIA_DEVICE_MANF_CARD_PROD_ID1(0x0156, 0x0002, "INTERSIL", diff -urN linux-2.6.21/drivers/pcmcia/Makefile linux-2.6.21-vpac1/drivers/pcmcia/Makefile --- linux-2.6.21/drivers/pcmcia/Makefile 2007-04-26 05:08:32.000000000 +0200 +++ linux-2.6.21-vpac1/drivers/pcmcia/Makefile 2007-06-25 16:02:38.000000000 +0200 @@ -69,4 +69,5 @@ pxa2xx_cs-$(CONFIG_ARCH_LUBBOCK) += pxa2xx_lubbock.o sa1111_generic.o pxa2xx_cs-$(CONFIG_MACH_MAINSTONE) += pxa2xx_mainstone.o pxa2xx_cs-$(CONFIG_PXA_SHARPSL) += pxa2xx_sharpsl.o +pxa2xx_cs-$(CONFIG_MACH_VPAC270) += pxa2xx_vpac270.o diff -urN linux-2.6.21/drivers/pcmcia/pxa2xx_vpac270.c linux-2.6.21-vpac1/drivers/pcmcia/pxa2xx_vpac270.c --- linux-2.6.21/drivers/pcmcia/pxa2xx_vpac270.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.21-vpac1/drivers/pcmcia/pxa2xx_vpac270.c 2007-06-25 16:02:38.000000000 +0200 @@ -0,0 +1,248 @@ +/* + * linux26/drivers/pcmcia/pxa2xx_vpac270.c + * + * 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. + * + * Copyright (c) 2002 Accelent Systems, Inc. All Rights Reserved + * Copyright (c) 2006 Voipac Technologies + * + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include "soc_common.h" + +//#define DEBUG_COL_PCMCIA + +#ifdef DEBUG_COL_PCMCIA +#define MARK printk(KERN_INFO "%s: %d\n", __FUNCTION__, __LINE__); +#else +//#define MARK (0) +#endif +#ifdef PCMCIA_DEBUG +int pc_debug = PCMCIA_DEBUG; +#endif + +static struct pcmcia_irqs irqs[] = { + { 0, VPAC270_PCMCIA0_CD_IRQ, "CF CD"}, + { 1, VPAC270_PCMCIA1_CD_IRQ, "PCMCIA CD"}, +}; + + +static int vpac270_pcmcia_init_dev(struct soc_pcmcia_socket *skt) +{ + printk("Voipac PXA270 PCMCIA\n"); + /* set power GPIO; switch off */ + GPCR(GPIO_PCMCIA_POW_EN) |= GPIO_bit(GPIO_PCMCIA_POW_EN); + GPDR(GPIO_PCMCIA_POW_EN) |= GPIO_bit(GPIO_PCMCIA_POW_EN); + + /* set PCMCIA AFs */ + GPSR(GPIO_PCMCIA_NPOE) |= GPIO_bit(GPIO_PCMCIA_NPOE); + pxa_gpio_mode(GPIO_PCMCIA_NPOE | GPIO_PCMCIA_NPOE_AF); + + GPSR(GPIO_PCMCIA_NPIOR) |= GPIO_bit(GPIO_PCMCIA_NPIOR); + pxa_gpio_mode(GPIO_PCMCIA_NPIOR | GPIO_PCMCIA_NPIOR_AF); + + GPSR(GPIO_PCMCIA_NPIOW) |= GPIO_bit(GPIO_PCMCIA_NPIOW); + pxa_gpio_mode(GPIO_PCMCIA_NPIOW | GPIO_PCMCIA_NPIOW_AF); + + GPSR(GPIO_PCMCIA_NPCE1) |= GPIO_bit(GPIO_PCMCIA_NPCE1); + pxa_gpio_mode(GPIO_PCMCIA_NPCE1 | GPIO_PCMCIA_NPCE1_AF); + + GPCR(GPIO_PCMCIA_NPCE2) |= GPIO_bit(GPIO_PCMCIA_NPCE2); + pxa_gpio_mode(GPIO_PCMCIA_NPCE2 | GPIO_PCMCIA_NPCE2_AF); + + GPSR(GPIO_PCMCIA_NPREG) |= GPIO_bit(GPIO_PCMCIA_NPREG); + pxa_gpio_mode(GPIO_PCMCIA_NPREG | GPIO_PCMCIA_NPREG_AF); + + pxa_gpio_mode(GPIO_PCMCIA_NPWAIT | GPIO_PCMCIA_NPWAIT_AF); + + pxa_gpio_mode(GPIO_PCMCIA_NPIOIS16 | GPIO_PCMCIA_NPIOIS16_AF); + + GPSR(GPIO_PCMCIA_PSKTSEL) |= GPIO_bit(GPIO_PCMCIA_PSKTSEL); + pxa_gpio_mode(GPIO_PCMCIA_PSKTSEL | GPIO_PCMCIA_PSKTSEL_AF); + + /* set other PCMCIA GPIOs */ + GPCR(GPIO_PCMCIA0_RESET) |= GPIO_bit(GPIO_PCMCIA0_RESET); + GPDR(GPIO_PCMCIA0_RESET) |= GPIO_bit(GPIO_PCMCIA0_RESET); + GPCR(GPIO_PCMCIA1_RESET) |= GPIO_bit(GPIO_PCMCIA1_RESET); + GPDR(GPIO_PCMCIA1_RESET) |= GPIO_bit(GPIO_PCMCIA1_RESET); + +// GPDR(GPIO_PCMCIA0_BVD1) &= ~GPIO_bit(GPIO_PCMCIA0_BVD1); +// GPDR(GPIO_PCMCIA0_BVD2) &= ~GPIO_bit(GPIO_PCMCIA0_BVD2); + + /* switch power on */ + PCC_PWR_ON(); + + /* reset the PCMCIA controller */ + GPSR(GPIO_PCMCIA0_RESET) |= GPIO_bit(GPIO_PCMCIA0_RESET); + GPSR(GPIO_PCMCIA1_RESET) |= GPIO_bit(GPIO_PCMCIA1_RESET); + udelay(500); + /* un-reset it again */ + GPCR(GPIO_PCMCIA0_RESET) |= GPIO_bit(GPIO_PCMCIA0_RESET); + GPCR(GPIO_PCMCIA1_RESET) |= GPIO_bit(GPIO_PCMCIA1_RESET); + + /* set interrupts */ + GPDR(GPIO_PCMCIA0_CD_IRQ) &= ~GPIO_bit(GPIO_PCMCIA0_CD_IRQ); + set_irq_type(GPIO_PCMCIA0_CD_IRQ, VPAC270_PCMCIA_CD_EDGE); + + GPDR(GPIO_PCMCIA0_RDY_IRQ) &= ~GPIO_bit(GPIO_PCMCIA0_RDY_IRQ); + set_irq_type(GPIO_PCMCIA0_RDY_IRQ, VPAC270_PCMCIA_RDY_EDGE); + + GPDR(GPIO_PCMCIA1_CD_IRQ) &= ~GPIO_bit(GPIO_PCMCIA1_CD_IRQ); + set_irq_type(GPIO_PCMCIA1_CD_IRQ, VPAC270_PCMCIA_CD_EDGE); + + GPDR(GPIO_PCMCIA1_RDY_IRQ) &= ~GPIO_bit(GPIO_PCMCIA1_RDY_IRQ); + set_irq_type(GPIO_PCMCIA1_RDY_IRQ, VPAC270_PCMCIA_RDY_EDGE); + + if (skt->irq == NO_IRQ) + skt->irq = skt->nr ? VPAC270_PCMCIA1_RDY_IRQ : VPAC270_PCMCIA0_RDY_IRQ; + + return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs)); +// skt->irq = VPAC270_PCMCIA0_RDY_IRQ; +// return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs)); +} + +static void vpac270_pcmcia_shutdown(struct soc_pcmcia_socket *skt) +{ + soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs)); + + /* switch power off */ + PCC_PWR_OFF(); +} + +static void vpac270_pcmcia_socket_state(struct soc_pcmcia_socket *skt, + struct pcmcia_state *state) +{ + //memset(state, 0, sizeof(*state)); + + switch( skt->nr) + { + case 0: + state->detect = (PCC0_DETECT) ? 0 : 1; + state->ready = (PCC0_READY) ? 1 : 0; + break; + + case 1: + state->detect = (PCC1_DETECT) ? 0 : 1; + state->ready = (PCC1_READY) ? 1 : 0; + break; + + default: + return -1; + } +/* + printk(KERN_INFO "CF status: detect: %u, ready: %u\n", + GPLR(84) & GPIO_bit(84), + GPLR(1) & GPIO_bit(1)); +*/ + + state->bvd1 = 1; //PCC_BVD1() ? 1 : 0; + state->bvd2 = 1; //PCC_BVD2() ? 1 : 0; + state->wrprot = 0; /* r/w all the time */ + state->vs_3v = PCC_VS3V() ? 1 : 0; + state->vs_Xv = PCC_VS5V() ? 1 : 0; +} + +static int vpac270_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, + socket_state_t const *state) +{ + unsigned long flags; + + local_irq_save(flags); + /* configure Vcc and Vpp */ + if (state->Vcc == 0 && state->Vpp == 0) + { + PCC_PWR_OFF(); + } + else if (state->Vcc == 33 && state->Vpp < 50) + { + PCC_PWR_ON(); + } + else + { + printk(KERN_ERR "%s(): unsupported Vcc %u Vpp %u combination\n", + __FUNCTION__, state->Vcc, state->Vpp); + return -1; + } + + /* reset PCMCIA if requested */ +// if (state->flags & SS_RESET) +// { +// GPSR(GPIO_PCMCIA0_RESET) |= GPIO_bit(GPIO_PCMCIA0_RESET); +// } + + local_irq_restore(flags); + udelay(200); + + return 0; +} + +static void vpac270_pcmcia_socket_init(struct soc_pcmcia_socket *skt) +{ +} + +static void vpac270_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) +{ +} + +struct pcmcia_low_level vpac270_pcmcia_ops = { + .owner = THIS_MODULE, + .hw_init = vpac270_pcmcia_init_dev, + .hw_shutdown = vpac270_pcmcia_shutdown, + .socket_state = vpac270_pcmcia_socket_state, + .configure_socket = vpac270_pcmcia_configure_socket, + .socket_init = vpac270_pcmcia_socket_init, + .socket_suspend = vpac270_pcmcia_socket_suspend, + .first = 0, + .nr = 2 +}; + +static struct platform_device *vpac270_pcmcia_device; + +static int __init vpac270_pcmcia_init(void) +{ + int ret; + + vpac270_pcmcia_device = kmalloc(sizeof(*vpac270_pcmcia_device), GFP_KERNEL); + if (!vpac270_pcmcia_device) + return -ENOMEM; + memset(vpac270_pcmcia_device, 0, sizeof(*vpac270_pcmcia_device)); + vpac270_pcmcia_device->name = "pxa2xx-pcmcia"; + vpac270_pcmcia_device->dev.platform_data = &vpac270_pcmcia_ops; + + ret = platform_device_register(vpac270_pcmcia_device); + if (ret) + kfree(vpac270_pcmcia_device); + + return ret; +} + +static void __exit vpac270_pcmcia_exit(void) +{ + /* Are there still references to vpac270_pcmcia_device? + * I don't know, so I'd better not free it. + * Actually, I don't really care, as I don't really support + * this driver as module anyway... + */ + platform_device_unregister(vpac270_pcmcia_device); +} + +module_init(vpac270_pcmcia_init); +module_exit(vpac270_pcmcia_exit); + +MODULE_DESCRIPTION("Voipac PXA270 CF Support"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Voipac Technologies "); diff -urN linux-2.6.21/drivers/usb/gadget/char.c linux-2.6.21-vpac1/drivers/usb/gadget/char.c --- linux-2.6.21/drivers/usb/gadget/char.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.21-vpac1/drivers/usb/gadget/char.c 2007-06-25 16:38:57.000000000 +0200 @@ -0,0 +1,911 @@ +/* + * char.c -- Character Gadget + * + * Copyright (C) 2003-2005 Joshua Wise. + * Portions copyright (C) 2003-2004 David Brownell. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions, and the following disclaimer, + * without modification. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The names of the above-listed copyright holders may not be used + * to endorse or promote products derived from this software without + * specific prior written permission. + * + * ALTERNATIVELY, this software may be distributed under the terms of the + * GNU General Public License ("GPL") as published by the Free Software + * Foundation, either version 2 of that License or (at your option) any + * later version. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#define DEBUG 1 +#define VERBOSE +#define DBUFMAX 8192 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +struct gchar_dev { + spinlock_t lock; + struct usb_gadget *gadget; + struct usb_request *req; /* for control responses */ + struct usb_ep *in_ep, *out_ep; + + int configured; + + unsigned char databuf[DBUFMAX]; + int databuflen; + spinlock_t buflock; + wait_queue_head_t wait; +}; + +/*-------------------------------------------------------------------------*/ + +static int gchar_bind_misc(struct gchar_dev *dev); +static int gchar_unbind_misc(struct gchar_dev *dev); + +/*-------------------------------------------------------------------------*/ + +static const char shortname [] = "char"; +static const char longname [] = "Character Gadget"; + +static const char charmode [] = "Character mode"; + +/*-------------------------------------------------------------------------*/ + +#define EP0_MAXPACKET 16 +static const char *EP_OUT_NAME; +static const char *EP_IN_NAME; + +/*-------------------------------------------------------------------------*/ + +/* any hub supports this steady state bus power consumption */ +#define MAX_USB_POWER 100 /* mA */ + +/* big enough to hold our biggest descriptor */ +#define USB_BUFSIZ 256 + +/*-------------------------------------------------------------------------*/ + +#define xprintk(d,level,fmt,args...) dev_printk(level , &(d)->gadget->dev , fmt , ## args) + +#ifdef DEBUG +# undef DEBUG +# define DEBUG(dev,fmt,args...) printk(KERN_DEBUG "gchar: " fmt , ## args) +#else +# define DEBUG(dev,fmt,args...) do { } while (0) +#endif /* DEBUG */ + +#ifdef VERBOSE +# define VDEBUG DEBUG +#else +# define VDEBUG(dev,fmt,args...) do { } while (0) +#endif /* VERBOSE */ + +#define ERROR(dev,fmt,args...) printk(KERN_ERR "gchar: " fmt , ## args) +#define WARN(dev,fmt,args...) printk(KERN_WARNING "gchar: " fmt , ## args) +#define INFO(dev,fmt,args...) printk(KERN_INFO "gchar: " fmt , ## args) + +/*-------------------------------------------------------------------------*/ + +static unsigned buflen = 1536; +static unsigned qlen = 4; + +module_param (buflen, uint, S_IRUGO|S_IWUSR); +module_param (qlen, uint, S_IRUGO|S_IWUSR); + +/*-------------------------------------------------------------------------*/ + +#define DRIVER_VENDOR_NUM 0x6666 /* Experimental */ +#define DRIVER_PRODUCT_NUM 0xB007 /* Bootloader */ + +/*-------------------------------------------------------------------------*/ + +/* + * DESCRIPTORS ... most are static, but strings and (full) + * configuration descriptors are built on demand. + */ + +#define STRING_MANUFACTURER 25 +#define STRING_PRODUCT 42 +#define STRING_SERIAL 101 +#define STRING_CHAR 250 + +/* + * This device advertises one configuration. + */ +#define CONFIG_CHAR 1 + +static struct usb_device_descriptor +device_desc = { + .bLength = sizeof device_desc, + .bDescriptorType = USB_DT_DEVICE, + + .bcdUSB = __constant_cpu_to_le16 (0x0200), + .bDeviceClass = USB_CLASS_VENDOR_SPEC, + + .idVendor = __constant_cpu_to_le16 (DRIVER_VENDOR_NUM), + .idProduct = __constant_cpu_to_le16 (DRIVER_PRODUCT_NUM), + .bcdDevice = __constant_cpu_to_le16 (0x0000), + .iManufacturer = STRING_MANUFACTURER, + .iProduct = STRING_PRODUCT, + .iSerialNumber = STRING_SERIAL, + .bNumConfigurations = 1, +}; + +static const struct usb_config_descriptor +char_config = { + .bLength = sizeof char_config, + .bDescriptorType = USB_DT_CONFIG, + + /* compute wTotalLength on the fly */ + .bNumInterfaces = 1, + .bConfigurationValue = CONFIG_CHAR, + .iConfiguration = STRING_CHAR, + .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER | 0 /* no wakeup support */, + .bMaxPower = (MAX_USB_POWER + 1) / 2, +}; + +/* one interface in each configuration */ + +static const struct usb_interface_descriptor +char_intf = { + .bLength = sizeof char_intf, + .bDescriptorType = USB_DT_INTERFACE, + + .bNumEndpoints = 2, + .bInterfaceClass = USB_CLASS_VENDOR_SPEC, + .iInterface = STRING_CHAR, +}; + +/* two full speed bulk endpoints; their use is config-dependent */ + +static struct usb_endpoint_descriptor +fs_char_in_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_IN, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +static struct usb_endpoint_descriptor +fs_char_out_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + + .bEndpointAddress = USB_DIR_OUT, + .bmAttributes = USB_ENDPOINT_XFER_BULK, +}; + +/* if there's no high speed support, maxpacket doesn't change. */ +static char manufacturer [50]; +static char serial [40]; + +/* static strings, in iso 8859/1 */ +static struct usb_string strings [] = { + { STRING_MANUFACTURER, manufacturer, }, + { STRING_PRODUCT, longname, }, + { STRING_SERIAL, serial, }, + { STRING_CHAR, charmode, }, + { } /* end of list */ +}; + +static struct usb_gadget_strings stringtab = { + .language = 0x0409, /* en-us */ + .strings = strings, +}; + +static int +config_buf (enum usb_device_speed speed, + u8 *buf, u8 type, unsigned index) +{ + const unsigned config_len = USB_DT_CONFIG_SIZE + + USB_DT_INTERFACE_SIZE + + 2 * USB_DT_ENDPOINT_SIZE; + + /* two configurations will always be index 0 and index 1 */ + if (index > 1) + return -EINVAL; + if (config_len > USB_BUFSIZ) + return -EDOM; + + /* config (or other speed config) */ + memcpy (buf, &char_config, USB_DT_CONFIG_SIZE); + buf [1] = type; + ((struct usb_config_descriptor *) buf)->wTotalLength + = __constant_cpu_to_le16 (config_len); + buf += USB_DT_CONFIG_SIZE; + + /* one interface */ + memcpy (buf, &char_intf, USB_DT_INTERFACE_SIZE); + buf += USB_DT_INTERFACE_SIZE; + + /* the endpoints in that interface (at that speed) */ + memcpy (buf, &fs_char_in_desc, USB_DT_ENDPOINT_SIZE); + buf += USB_DT_ENDPOINT_SIZE; + memcpy (buf, &fs_char_out_desc, USB_DT_ENDPOINT_SIZE); + buf += USB_DT_ENDPOINT_SIZE; + + return config_len; +} + +/*-------------------------------------------------------------------------*/ + +static struct usb_request * +alloc_ep_req (struct usb_ep *ep, unsigned length) +{ + struct usb_request *req; + + req = usb_ep_alloc_request (ep, GFP_ATOMIC); + if (req) { + req->length = length; + req->buf = usb_ep_alloc_buffer (ep, length, + &req->dma, GFP_ATOMIC); + if (!req->buf) { + usb_ep_free_request (ep, req); + req = NULL; + } + } + return req; +} + +static void free_ep_req (struct usb_ep *ep, struct usb_request *req) +{ + if (req->buf) + usb_ep_free_buffer (ep, req->buf, req->dma, req->length); + usb_ep_free_request (ep, req); +} + +/*-------------------------------------------------------------------------*/ + +static void gchar_complete (struct usb_ep *ep, struct usb_request *req) +{ + struct gchar_dev *dev = ep->driver_data; + int status = req->status; + + switch (status) { + + case 0: /* normal completion? */ + if (ep != dev->out_ep) { + ERROR (dev, "complete: invalid endpoint --> %s (%d/%d)\n", ep->name, req->actual, req->length); + free_ep_req (ep, req); + return; + } + + /* it looks like you've got a packet! */ + req->length = req->actual; + + spin_lock(&dev->buflock); + { + int cpylen; + + cpylen = ((dev->databuflen+req->actual) < DBUFMAX) ? req->actual : (DBUFMAX - dev->databuflen); + memcpy(&dev->databuf[dev->databuflen], req->buf, cpylen); + dev->databuflen += cpylen; + } + spin_unlock(&dev->buflock); + wake_up_interruptible(&dev->wait); + + /* queue the buffer for some later OUT packet */ + req->length = buflen; + status = usb_ep_queue (dev->out_ep, req, GFP_ATOMIC); + if (status == 0) + return; + + /* FALLTHROUGH */ + default: + ERROR (dev, "%s gchar recv complete --> %d, %d/%d\n", ep->name, + status, req->actual, req->length); + /* FALLTHROUGH */ + + /* NOTE: since this driver doesn't maintain an explicit record + * of requests it submitted (just maintains qlen count), we + * rely on the hardware driver to clean up on disconnect or + * endpoint disable. + */ + case -ECONNABORTED: /* hardware forced ep reset */ + case -ECONNRESET: /* request dequeued */ + case -ESHUTDOWN: /* disconnect from host */ + + return; + } +} + +static int +set_char_config (struct gchar_dev *dev, int gfp_flags) +{ + int result = 0; + struct usb_ep *ep; + struct usb_gadget *gadget = dev->gadget; + + gadget_for_each_ep (ep, gadget) { + /* one endpoint writes data back IN to the host */ + if (strcmp (ep->name, EP_IN_NAME) == 0) { + result = usb_ep_enable (ep, &fs_char_in_desc); + if (result == 0) { + ep->driver_data = dev; + dev->in_ep = ep; + continue; + } + + /* one endpoint just reads OUT packets */ + } else if (strcmp (ep->name, EP_OUT_NAME) == 0) { + result = usb_ep_enable (ep, &fs_char_out_desc); + if (result == 0) { + ep->driver_data = dev; + dev->out_ep = ep; + continue; + } + + /* ignore any other endpoints */ + } else + continue; + + /* stop on error */ + ERROR (dev, "can't enable %s, result %d\n", ep->name, result); + break; + } + + /* allocate a bunch of read buffers and queue them all at once. + * we buffer at most 'qlen' transfers; fewer if any need more + * than 'buflen' bytes each. + */ + if (result == 0) { + struct usb_request *req; + unsigned i; + + ep = dev->out_ep; + for (i = 0; i < qlen && result == 0; i++) { + DEBUG (dev, "%s alloc req of size %d (%d/%d)\n", ep->name, buflen, i, qlen); + req = alloc_ep_req (ep, buflen); + if (req) { + req->complete = gchar_complete; + result = usb_ep_queue (ep, req, GFP_ATOMIC); + if (result) + DEBUG (dev, "%s queue req --> %d\n", + ep->name, result); + } else + result = -ENOMEM; + } + + } + + /* caller is responsible for cleanup on error */ + return result; +} + +/*-------------------------------------------------------------------------*/ + +static void gchar_reset_config (struct gchar_dev *dev) +{ + if (dev->configured == 0) + return; + + DEBUG (dev, "reset config\n"); + + /* just disable endpoints, forcing completion of pending i/o. + * all our completion handlers free their requests in this case. + */ + if (dev->in_ep) { + usb_ep_disable (dev->in_ep); + dev->in_ep = NULL; + } + if (dev->out_ep) { + usb_ep_disable (dev->out_ep); + dev->out_ep = NULL; + } + dev->configured = 0; +} + +/* change our operational config. this code must agree with the code + * that returns config descriptors, and altsetting code. + * + * it's also responsible for power management interactions. some + * configurations might not work with our current power sources. + * + * note that some device controller hardware will constrain what this + * code can do, perhaps by disallowing more than one configuration or + * by limiting configuration choices (like the pxa2xx). + */ +static int +gchar_set_config (struct gchar_dev *dev, unsigned number, int gfp_flags) +{ + int result = 0; + struct usb_gadget *gadget = dev->gadget; + +#if 0 + if (dev->configured) + return 0; +#endif + +#ifdef CONFIG_USB_CHAR_SA1100 + if (dev->configured) { + /* tx fifo is full, but we can't clear it...*/ + INFO (dev, "can't change configurations\n"); + return -ESPIPE; + } +#endif + gchar_reset_config (dev); + + switch (number) { + case CONFIG_CHAR: + result = set_char_config (dev, gfp_flags); + break; + default: + result = -EINVAL; + /* FALL THROUGH */ + case 0: + return result; + } + + if (!result && (!dev->in_ep || !dev->out_ep)) + result = -ENODEV; + if (result) + gchar_reset_config (dev); + else { + char *speed; + + switch (gadget->speed) { + case USB_SPEED_LOW: speed = "low"; break; + case USB_SPEED_FULL: speed = "full"; break; + case USB_SPEED_HIGH: speed = "high"; break; + default: speed = "?"; break; + } + + dev->configured = 1; + INFO (dev, "%s speed\n", speed); + } + return result; +} + +/*-------------------------------------------------------------------------*/ + +static void gchar_setup_complete (struct usb_ep *ep, struct usb_request *req) +{ + if (req->status || req->actual != req->length) + DEBUG ((struct gchar_dev *) ep->driver_data, + "setup complete --> %d, %d/%d\n", + req->status, req->actual, req->length); +} + +/* + * The setup() callback implements all the ep0 functionality that's + * not handled lower down, in hardware or the hardware driver (like + * device and endpoint feature flags, and their status). It's all + * housekeeping for the gadget function we're implementing. Most of + * the work is in config-specific setup. + */ +static int +gchar_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) +{ + struct gchar_dev *dev = get_gadget_data (gadget); + struct usb_request *req = dev->req; + int value = -EOPNOTSUPP; + + /* usually this stores reply data in the pre-allocated ep0 buffer, + * but config change events will reconfigure hardware. + */ + switch (ctrl->bRequest) { + + case USB_REQ_GET_DESCRIPTOR: + if (ctrl->bRequestType != USB_DIR_IN) + break; + switch (ctrl->wValue >> 8) { + + case USB_DT_DEVICE: + value = min (ctrl->wLength, (u16) sizeof device_desc); + memcpy (req->buf, &device_desc, value); + break; + case USB_DT_CONFIG: + value = config_buf (gadget->speed, req->buf, + ctrl->wValue >> 8, + ctrl->wValue & 0xff); + if (value >= 0) + value = min (ctrl->wLength, (u16) value); + break; + + case USB_DT_STRING: + /* wIndex == language code. + * this driver only handles one language, you can + * add others even if they don't use iso8859/1 + */ + value = usb_gadget_get_string (&stringtab, + ctrl->wValue & 0xff, req->buf); + if (value >= 0) + value = min (ctrl->wLength, (u16) value); + break; + } + break; + + /* currently two configs, two speeds */ + case USB_REQ_SET_CONFIGURATION: + if (ctrl->bRequestType != 0) + break; + spin_lock (&dev->lock); + value = gchar_set_config (dev, ctrl->wValue, GFP_ATOMIC); + spin_unlock (&dev->lock); + break; + case USB_REQ_GET_CONFIGURATION: + if (ctrl->bRequestType != USB_DIR_IN) + break; + *(u8 *)req->buf = dev->configured ? CONFIG_CHAR : 0; + value = min (ctrl->wLength, (u16) 1); + break; + case USB_REQ_SET_INTERFACE: + if (ctrl->bRequestType != USB_RECIP_INTERFACE) + break; + spin_lock (&dev->lock); + if (dev->configured && ctrl->wIndex == 0 && ctrl->wValue == 0) { + u8 config = dev->configured ? CONFIG_CHAR : 0; + + gchar_reset_config (dev); + gchar_set_config (dev, config, GFP_ATOMIC); + value = 0; + } + spin_unlock (&dev->lock); + break; + case USB_REQ_GET_INTERFACE: + if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) + break; + if (!dev->configured) + break; + if (ctrl->wIndex != 0) { + value = -EDOM; + break; + } + *(u8 *)req->buf = 0; + value = min (ctrl->wLength, (u16) 1); + break; + + default: + VDEBUG (dev, + "unknown control req%02x.%02x v%04x i%04x l%d\n", + ctrl->bRequestType, ctrl->bRequest, + ctrl->wValue, ctrl->wIndex, ctrl->wLength); + } + + /* respond with data transfer before status phase? */ + if (value >= 0) { + req->length = value; + value = usb_ep_queue (gadget->ep0, req, GFP_ATOMIC); + if (value < 0) { + DEBUG (dev, "ep_queue --> %d\n", value); + req->status = 0; + gchar_setup_complete (gadget->ep0, req); + } + } + + /* device either stalls (value < 0) or reports success */ + return value; +} + +static void +gchar_disconnect (struct usb_gadget *gadget) +{ + struct gchar_dev *dev = get_gadget_data (gadget); + unsigned long flags; + + spin_lock_irqsave (&dev->lock, flags); + gchar_reset_config (dev); + + /* a more significant application might have some non-usb + * activities to quiesce here, saving resources like power + * or pushing the notification up a network stack. + */ + spin_unlock_irqrestore (&dev->lock, flags); + + /* next we may get setup() calls to enumerate new connections; + * or an unbind() during shutdown (including removing module). + */ +} + +/*-------------------------------------------------------------------------*/ + +static void +gchar_unbind (struct usb_gadget *gadget) +{ + struct gchar_dev *dev = get_gadget_data (gadget); + + DEBUG (dev, "unbind\n"); + + /* we've already been disconnected ... no i/o is active */ + if (dev->req) + free_ep_req (gadget->ep0, dev->req); + kfree (dev); + set_gadget_data (gadget, NULL); + + gchar_unbind_misc(dev); + +} + +static int +gchar_bind (struct usb_gadget *gadget) +{ + struct gchar_dev *dev; + struct usb_ep *ep; + int err; + + usb_ep_autoconfig_reset (gadget); + + ep = usb_ep_autoconfig (gadget, &fs_char_in_desc); + if (!ep) { +autoconf_fail: + printk(KERN_ERR "%s: can't autoconfigure on %s\n", shortname, gadget->name); + return -ENODEV; + } + EP_IN_NAME = ep->name; + ep->driver_data = ep; /* claim */ + + ep = usb_ep_autoconfig (gadget, &fs_char_out_desc); + if (!ep) + goto autoconf_fail; + EP_OUT_NAME = ep->name; + ep->driver_data = ep; /* claim */ + + dev = kmalloc (sizeof *dev, SLAB_KERNEL); + if (!dev) + return -ENOMEM; + + memset (dev, 0, sizeof *dev); + spin_lock_init (&dev->lock); + spin_lock_init (&dev->buflock); + dev->gadget = gadget; + init_waitqueue_head (&dev->wait); + + set_gadget_data (gadget, dev); + + /* preallocate control response and buffer */ + dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL); + if (!dev->req) + goto enomem; + dev->req->buf = usb_ep_alloc_buffer (gadget->ep0, USB_BUFSIZ, + &dev->req->dma, GFP_KERNEL); + if (!dev->req->buf) + goto enomem; + + dev->req->complete = gchar_setup_complete; + device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; + gadget->ep0->driver_data = dev; + + snprintf (manufacturer, sizeof manufacturer, "%s %s with %s", + system_utsname.sysname, system_utsname.release, + gadget->name); + + /* grab a misc device */ + if ((err = gchar_bind_misc(dev))) { + gchar_unbind(gadget); + return err; + } + + return 0; + +enomem: + gchar_unbind (gadget); + return -ENOMEM; +} + +/*-------------------------------------------------------------------------*/ + +static struct usb_gadget_driver gchar_driver = { + .speed = USB_SPEED_FULL, + .function = (char *) longname, + .bind = gchar_bind, + .unbind = gchar_unbind, + + .setup = gchar_setup, + .disconnect = gchar_disconnect, + + .driver = { + .name = (char *) shortname, + // .shutdown = ... + // .suspend = ... + // .resume = ... + }, +}; + +MODULE_AUTHOR ("Joshua Wise/David Brownell"); +MODULE_LICENSE ("Dual BSD/GPL"); + +static ssize_t read_gchar(struct file* file, char __user *buf, size_t count, loff_t *ppos) +{ + struct gchar_dev *dev = (struct gchar_dev *)file->private_data; + ssize_t readcnt; + DECLARE_WAITQUEUE(wait, current); + + add_wait_queue(&dev->wait, &wait); + current->state = TASK_INTERRUPTIBLE; + + do { + if (dev->databuflen != 0) + break; + + if (file->f_flags & O_NONBLOCK) + { + readcnt = -EAGAIN; + goto out; + } + + schedule(); + if (signal_pending(current)) + { + readcnt = -ERESTARTSYS; + goto out; + } + } while (1); + + spin_lock_irq(&dev->buflock); + readcnt = (count > dev->databuflen) ? dev->databuflen : count; + copy_to_user(buf, dev->databuf, readcnt); + memmove(dev->databuf, dev->databuf+readcnt, dev->databuflen-readcnt); + dev->databuflen -= readcnt; + spin_unlock_irq(&dev->buflock); + +out: + current->state = TASK_RUNNING; + remove_wait_queue(&dev->wait, &wait); + + return readcnt; +} + +static void gchar_send_complete (struct usb_ep *ep, struct usb_request *req) +{ + free_ep_req(ep, req); +} + +/*-------------------------------------------------------------------------*/ + +#define USBC_MINOR 240 +#define USBC_MAX_DEVICES 1 + +static ssize_t write_gchar(struct file* file, const char __user *buf, size_t count, loff_t* ppos) +{ + struct gchar_dev *dev = (struct gchar_dev *)file->private_data; + struct usb_request *req; + int result; + + if (!dev->configured) + return count; + + req = alloc_ep_req (dev->in_ep, count); + if (!req) + return -ENOMEM; + + req->complete = gchar_send_complete; + req->length = req->actual = count; + req->zero = 0; + + copy_from_user(req->buf, buf, count); + + result = usb_ep_queue (dev->in_ep, req, GFP_ATOMIC); + if (result) + DEBUG (dev, "%s queue req --> %d\n", + dev->in_ep->name, result); + + return count; +} + +static unsigned int poll_gchar( struct file * filp, poll_table *wait ) +{ + struct gchar_dev *dev = (struct gchar_dev *)filp->private_data; + + poll_wait(filp, &dev->wait, wait); + + if (dev->databuflen) + return POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM; + return POLLOUT | POLLWRNORM; +} + +/*-------------------------------------------------------------------------*/ + +struct gchar_dev *gc_devs[USBC_MAX_DEVICES]; + +static int open_gchar(struct inode *inode, struct file *file) +{ + struct gchar_dev *dev = gc_devs[iminor(inode)-USBC_MINOR]; + + if (!dev->configured && !dev->databuflen) + return -ENXIO; + + file->private_data = (void*)dev; + nonseekable_open(inode, file); + + return 0; +} + +static struct file_operations gchar_fops = { + .llseek = no_llseek, + .read = read_gchar, + .write = write_gchar, + .poll = poll_gchar, + .open = open_gchar +}; + +static struct miscdevice gchar_misc_device = { + USBC_MINOR, "usbchar", &gchar_fops +}; + +static int gchar_bind_misc(struct gchar_dev *dev) +{ + int error; + + if ((error = misc_register(&gchar_misc_device))) + { + printk("gchar: Unable to register device 10, %d. Errno: %d\n", USBC_MINOR, error); + return error; + } + + gc_devs[gchar_misc_device.minor-USBC_MINOR] = dev; + return 0; +} + +static int gchar_unbind_misc(struct gchar_dev *dev) +{ + misc_deregister (&gchar_misc_device); + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static int __init gchar_init (void) +{ + /* a real value would likely come through some id prom + * or module option. this one takes at least two packets. + */ + strlcpy (serial, "hp iPAQ: Linux As Bootloader ", sizeof serial); + + return usb_gadget_register_driver (&gchar_driver); +} +module_init (gchar_init); + +static void __exit cleanup (void) +{ + usb_gadget_unregister_driver (&gchar_driver); + +} +module_exit (cleanup); diff -urN linux-2.6.21/drivers/usb/gadget/epautoconf.c linux-2.6.21-vpac1/drivers/usb/gadget/epautoconf.c --- linux-2.6.21/drivers/usb/gadget/epautoconf.c 2007-04-26 05:08:32.000000000 +0200 +++ linux-2.6.21-vpac1/drivers/usb/gadget/epautoconf.c 2007-06-25 16:38:57.000000000 +0200 @@ -230,12 +230,17 @@ */ struct usb_ep * __devinit 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 -urN linux-2.6.21/drivers/usb/gadget/ether.c linux-2.6.21-vpac1/drivers/usb/gadget/ether.c --- linux-2.6.21/drivers/usb/gadget/ether.c 2007-04-26 05:08:32.000000000 +0200 +++ linux-2.6.21-vpac1/drivers/usb/gadget/ether.c 2007-06-25 16:38:57.000000000 +0200 @@ -259,7 +259,8 @@ #endif #ifdef CONFIG_USB_GADGET_PXA27X -#define DEV_CONFIG_CDC +//#define DEV_CONFIG_CDC +#define DEV_CONFIG_SUBSET #endif #ifdef CONFIG_USB_GADGET_S3C2410 @@ -1340,6 +1341,10 @@ /* 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; @@ -1350,6 +1355,10 @@ 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); } @@ -2276,7 +2285,8 @@ 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; @@ -2375,7 +2385,30 @@ /* 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, @@ -2385,7 +2418,12 @@ } 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 */ @@ -2395,7 +2433,25 @@ * 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 -urN linux-2.6.21/drivers/usb/gadget/file_storage.c linux-2.6.21-vpac1/drivers/usb/gadget/file_storage.c --- linux-2.6.21/drivers/usb/gadget/file_storage.c 2007-04-26 05:08:32.000000000 +0200 +++ linux-2.6.21-vpac1/drivers/usb/gadget/file_storage.c 2007-06-25 16:38:57.000000000 +0200 @@ -3849,6 +3849,7 @@ 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); @@ -3919,21 +3920,25 @@ } /* 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 -urN linux-2.6.21/drivers/usb/gadget/gadget_chips.h linux-2.6.21-vpac1/drivers/usb/gadget/gadget_chips.h --- linux-2.6.21/drivers/usb/gadget/gadget_chips.h 2007-04-26 05:08:32.000000000 +0200 +++ linux-2.6.21-vpac1/drivers/usb/gadget/gadget_chips.h 2007-06-25 16:38:57.000000000 +0200 @@ -24,7 +24,11 @@ #ifdef CONFIG_USB_GADGET_PXA2XX #define gadget_is_pxa(g) !strcmp("pxa2xx_udc", (g)->name) #else -#define gadget_is_pxa(g) 0 +# ifdef CONFIG_USB_GADGET_PXA27X +# define gadget_is_pxa(g) !strcmp("pxa27x_udc", (g)->name) +# else +# define gadget_is_pxa(g) 0 +# endif #endif #ifdef CONFIG_USB_GADGET_GOKU diff -urN linux-2.6.21/drivers/usb/gadget/Kconfig linux-2.6.21-vpac1/drivers/usb/gadget/Kconfig --- linux-2.6.21/drivers/usb/gadget/Kconfig 2007-04-26 05:08:32.000000000 +0200 +++ linux-2.6.21-vpac1/drivers/usb/gadget/Kconfig 2007-06-29 16:36:09.000000000 +0200 @@ -115,11 +115,38 @@ # don't waste memory for the other endpoints config USB_PXA2XX_SMALL depends on USB_GADGET_PXA2XX - bool + bool "Handle only three endpoints (driver smaller by 1K)" default n if USB_ETH_RNDIS default y if USB_ZERO default y if USB_ETH default y if USB_G_SERIAL + help + Use this option if you're really really short on memory + and your gadget driver needs only one IN and one OUT bulk + endpoints. This is enough for most simple gadgets (gadget + zero, serial, ethernet and such). + +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. + + 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_PXA27X_DMA + bool # "Use DMA support" + depends on USB_GADGET_PXA27X + default n config USB_GADGET_GOKU boolean "Toshiba TC86C001 'Goku-S'" @@ -355,6 +382,8 @@ XP, you'll need to download drivers from Microsoft's website; a URL is given in comments found in that info file. + This breaks ETH support on PXA hosts. + config USB_GADGETFS tristate "Gadget Filesystem (EXPERIMENTAL)" depends on EXPERIMENTAL @@ -418,6 +447,11 @@ Say "y" to link the driver statically, or "m" to build a dynamically linked module called "g_midi". +config USB_G_CHAR + tristate "Character Gadget" + help + Character Gadget is what usb-char used to be. Have fun. + # put drivers that need isochronous transfer support (for audio # or video class gadget drivers), or specific hardware, here. @@ -426,4 +460,14 @@ endchoice +config USB_PXA2XX_GPIO + boolean "Use GPIOs to control pxa2xx_udc driver" + depends on (USB_GADGET_PXA2XX || USB_GADGET_PXA27X) + help + pxa2xx_udc requires machine-specific methods to handle + connection detection and pull up control. Many machines + use GPIOs for these. This driver allows to just specify + these GPIOs, without writing duplicate functions to + get/set these GPIOs. + endmenu diff -urN linux-2.6.21/drivers/usb/gadget/Makefile linux-2.6.21-vpac1/drivers/usb/gadget/Makefile --- linux-2.6.21/drivers/usb/gadget/Makefile 2007-04-26 05:08:32.000000000 +0200 +++ linux-2.6.21-vpac1/drivers/usb/gadget/Makefile 2007-06-29 16:38:09.000000000 +0200 @@ -8,6 +8,7 @@ obj-$(CONFIG_USB_OMAP) += omap_udc.o obj-$(CONFIG_USB_LH7A40X) += lh7a40x_udc.o obj-$(CONFIG_USB_AT91) += at91_udc.o +obj-$(CONFIG_USB_PXA27X) += pxa27x_udc.o # # USB gadget drivers @@ -19,6 +20,7 @@ gadgetfs-objs := inode.o g_file_storage-objs := file_storage.o usbstring.o config.o \ epautoconf.o +g_char-objs := usbstring.o char.o epautoconf.o ifeq ($(CONFIG_USB_ETH_RNDIS),y) g_ether-objs += rndis.o @@ -30,4 +32,6 @@ obj-$(CONFIG_USB_FILE_STORAGE) += g_file_storage.o obj-$(CONFIG_USB_G_SERIAL) += g_serial.o obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o +obj-$(CONFIG_USB_G_CHAR) += g_char.o +obj-$(CONFIG_USB_PXA2XX_GPIO) += pxa2xx_udc_gpio.o diff -urN linux-2.6.21/drivers/usb/gadget/pxa27x_udc.c linux-2.6.21-vpac1/drivers/usb/gadget/pxa27x_udc.c --- linux-2.6.21/drivers/usb/gadget/pxa27x_udc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.21-vpac1/drivers/usb/gadget/pxa27x_udc.c 2007-06-25 16:38:57.000000000 +0200 @@ -0,0 +1,2434 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +/* + * 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 +//#define DISABLE_TEST_MODE + +#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 + +#ifdef CONFIG_USB_PXA27X_SMALL +#define SIZE_STR " (small)" +#else +#define SIZE_STR "" +#endif + +#ifdef DISABLE_TEST_MODE +/* (mode == 0) == no undocumented chip tweaks + * (mode & 1) == double buffer bulk IN + * (mode & 2) == double buffer bulk OUT + * ... so mode = 3 (or 7, 15, etc) does it for both + */ +static ushort fifo_mode = 0; +module_param(fifo_mode, ushort, 0); +MODULE_PARM_DESC (fifo_mode, "pxa27x udc fifo mode"); +#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); +} + + +/* PXA cache needs flushing with DMA I/O (it's dma-incoherent), but there's + * no device-affinity and the heap works perfectly well for i/o buffers. + * It wastes much less memory than dma_alloc_coherent() would, and even + * prevents cacheline (32 bytes wide) sharing problems. + */ +static void * +pxa27x_ep_alloc_buffer(struct usb_ep *_ep, unsigned bytes, dma_addr_t *dma, unsigned gfp_flags) +{ + char *retval; + + retval = kmalloc(bytes, gfp_flags & ~(__GFP_DMA|__GFP_HIGHMEM)); + if (retval) + *dma = virt_to_bus(retval); + return retval; +} + +static void +pxa27x_ep_free_buffer(struct usb_ep *_ep, void *buf, dma_addr_t dma, unsigned bytes) +{ + kfree(buf); +} + +/*-------------------------------------------------------------------------*/ + +/* + * 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. + */ + *ep->reg_udccsr = UDCCSR_PC; + 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, + + .alloc_buffer = pxa27x_ep_alloc_buffer, + .free_buffer = pxa27x_ep_free_buffer, + + .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 SIZE_STR 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(CKEN11_USB, 0); + + ep0_idle(dev); + dev->gadget.speed = USB_SPEED_UNKNOWN; + if (dev->mach->udc_command) + 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(CKEN11_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); + if (dev->mach->udc_command) + 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, + }, +}; + +#define CP15R0_VENDOR_MASK 0xffffe000 +#define CP15R0_XSCALE_VALUE 0x69054000 /* intel/arm/xscale */ + +static int __init pxa27x_udc_probe(struct platform_device *_dev) +{ + struct pxa27x_udc *dev = &memory; + int retval; + u32 chiprev; + + /* insist on Intel/ARM/XScale */ + asm("mrc%? p15, 0, %0, c0, c0" : "=r" (chiprev)); + if ((chiprev & CP15R0_VENDOR_MASK) != CP15R0_XSCALE_VALUE) { + printk(KERN_ERR "%s: not XScale!\n", driver_name); + return -ENODEV; + } + /* other non-static parts of init */ + dev->dev = &_dev->dev; + dev->mach = _dev->dev.platform_data; + + 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 __exit 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; (iep[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(CKEN11_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 = __exit_p(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 -urN linux-2.6.21/drivers/usb/gadget/pxa27x_udc.h linux-2.6.21-vpac1/drivers/usb/gadget/pxa27x_udc.h --- linux-2.6.21/drivers/usb/gadget/pxa27x_udc.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.21-vpac1/drivers/usb/gadget/pxa27x_udc.h 2007-06-25 16:38:57.000000000 +0200 @@ -0,0 +1,342 @@ +/* + * linux/drivers/usb/gadget/pxa27x_udc.h + * Intel PXA27x on-chip full speed USB device controller + * + * Copyright (C) 2003 Robert Schwebel , 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 + +struct pxa27x_udc; + +struct pxa27x_ep { + struct usb_ep 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; + + int dma; + unsigned fifo_size; + unsigned ep_num; + unsigned pxa_ep_num; + unsigned ep_type; + + unsigned stopped : 1; + unsigned dma_con : 1; + unsigned dir_in : 1; + unsigned assigned : 1; + + 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; +}; + +#ifdef CONFIG_USB_PXA27X_SMALL +/* when memory's tight, SMALL config saves code+data. */ +//#undef USE_DMA +//#define UDC_EP_NUM 3 +#endif + +#ifndef UDC_EP_NUM +#define UDC_EP_NUM 24 +#endif + +struct pxa27x_udc { + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + + enum ep0_state ep0state; + struct udc_stats stats; + unsigned got_irq : 1, + got_disc : 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 +}; + +/*-------------------------------------------------------------------------*/ +#if 0 +#ifdef DEBUG +#define HEX_DISPLAY(n) do { \ + if (machine_is_mainstone())\ + { MST_LEDDAT1 = (n); } \ + } while(0) + +#define HEX_DISPLAY1(n) HEX_DISPLAY(n) + +#define HEX_DISPLAY2(n) do { \ + if (machine_is_mainstone()) \ + { MST_LEDDAT2 = (n); } \ + } while(0) + +#endif /* DEBUG */ +#endif +/*-------------------------------------------------------------------------*/ + +/* LEDs are only for debug */ +#ifndef HEX_DISPLAY +#define HEX_DISPLAY(n) do {} while(0) +#endif + +#ifndef LED_CONNECTED_ON +#define LED_CONNECTED_ON do {} while(0) +#define LED_CONNECTED_OFF do {} while(0) +#endif +#ifndef LED_EP0_ON +#define LED_EP0_ON do {} while (0) +#define LED_EP0_OFF do {} while (0) +#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].desc == 0) + continue; + 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)0) + +#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 -urN linux-2.6.21/drivers/usb/gadget/pxa2xx_udc.c linux-2.6.21-vpac1/drivers/usb/gadget/pxa2xx_udc.c --- linux-2.6.21/drivers/usb/gadget/pxa2xx_udc.c 2007-04-26 05:08:32.000000000 +0200 +++ linux-2.6.21-vpac1/drivers/usb/gadget/pxa2xx_udc.c 2007-06-25 16:38:57.000000000 +0200 @@ -104,6 +104,7 @@ #include "pxa2xx_udc.h" +static void udc_init_eps(struct pxa2xx_udc *dev); #ifdef USE_DMA static int use_dma = 1; module_param(use_dma, bool, 0); @@ -2473,6 +2474,53 @@ #define IXP425_B0 0x000001f1 #define IXP465_AD 0x00000200 +static void udc_init_eps(struct pxa2xx_udc *dev) { +#ifdef USE_DMA + /* pxa 250 erratum 130 prevents using OUT dma (fixed C0) */ + if (!dev->out_dma) { + DMSG("disabled OUT dma\n"); + dev->ep[ 2].reg_drcmr = dev->ep[ 4].reg_drcmr = 0; + dev->ep[ 7].reg_drcmr = dev->ep[ 9].reg_drcmr = 0; + dev->ep[12].reg_drcmr = dev->ep[14].reg_drcmr = 0; + } +#endif + + /* Reset UDCCS register to be able to recover from whatever + * state UDC was previously in. */ + *dev->ep[ 2].reg_udccs = UDCCS_BO_RPC | UDCCS_BO_SST; +#ifndef CONFIG_USB_PXA2XX_SMALL + *dev->ep[ 7].reg_udccs = UDCCS_BO_RPC | UDCCS_BO_SST; + *dev->ep[12].reg_udccs = UDCCS_BO_RPC | UDCCS_BO_SST; +#endif + + *dev->ep[ 1].reg_udccs = UDCCS_BI_TPC | UDCCS_BI_FTF | + UDCCS_BI_TUR | UDCCS_BI_SST | UDCCS_BI_TSP; +#ifndef CONFIG_USB_PXA2XX_SMALL + *dev->ep[ 6].reg_udccs = UDCCS_BI_TPC | UDCCS_BI_FTF | + UDCCS_BI_TUR | UDCCS_BI_SST | UDCCS_BI_TSP; + *dev->ep[11].reg_udccs = UDCCS_BI_TPC | UDCCS_BI_FTF | + UDCCS_BI_TUR | UDCCS_BI_SST | UDCCS_BI_TSP; + + *dev->ep[ 3].reg_udccs = UDCCS_II_TPC | UDCCS_II_FTF | + UDCCS_II_TUR | UDCCS_II_TSP; + *dev->ep[ 8].reg_udccs = UDCCS_II_TPC | UDCCS_II_FTF | + UDCCS_II_TUR | UDCCS_II_TSP; + *dev->ep[13].reg_udccs = UDCCS_II_TPC | UDCCS_II_FTF | + UDCCS_II_TUR | UDCCS_II_TSP; + + *dev->ep[ 4].reg_udccs = UDCCS_IO_RPC | UDCCS_IO_ROF; + *dev->ep[ 9].reg_udccs = UDCCS_IO_RPC | UDCCS_IO_ROF; + *dev->ep[11].reg_udccs = UDCCS_IO_RPC | UDCCS_IO_ROF; + + *dev->ep[ 5].reg_udccs = UDCCS_INT_TPC | UDCCS_INT_FTF | + UDCCS_INT_TUR | UDCCS_INT_SST; + *dev->ep[10].reg_udccs = UDCCS_INT_TPC | UDCCS_INT_FTF | + UDCCS_INT_TUR | UDCCS_INT_SST; + *dev->ep[15].reg_udccs = UDCCS_INT_TPC | UDCCS_INT_FTF | + UDCCS_INT_TUR | UDCCS_INT_SST; +#endif +} + /* * probe - binds to the platform device */ @@ -2489,6 +2537,7 @@ return -ENODEV; } + dev->out_dma = 1; /* trigger chiprev-specific logic */ switch (chiprev & CP15R0_PRODREV_MASK) { #if defined(CONFIG_ARCH_PXA) @@ -2502,7 +2551,7 @@ case PXA250_B2: case PXA210_B2: case PXA250_B1: case PXA210_B1: case PXA250_B0: case PXA210_B0: - out_dma = 0; + dev->out_dma = 0; /* fall through */ case PXA250_C0: case PXA210_C0: break; @@ -2511,11 +2560,11 @@ case IXP425_B0: case IXP465_AD: dev->has_cfr = 1; - out_dma = 0; + dev->out_dma = 0; break; #endif default: - out_dma = 0; + dev->out_dma = 0; printk(KERN_ERR "%s: unrecognized processor: %08x\n", driver_name, chiprev); /* iop3xx, ixp4xx, ... */ @@ -2524,22 +2573,16 @@ pr_debug("%s: IRQ %d%s%s%s\n", driver_name, IRQ_USB, dev->has_cfr ? "" : " (!cfr)", - out_dma ? "" : " (broken dma-out)", + dev->out_dma ? "" : " (broken dma-out)", SIZE_STR DMASTR ); #ifdef USE_DMA #ifndef USE_OUT_DMA - out_dma = 0; + dev->out_dma = 0; #endif - /* pxa 250 erratum 130 prevents using OUT dma (fixed C0) */ - if (!out_dma) { - DMSG("disabled OUT dma\n"); - dev->ep[ 2].reg_drcmr = dev->ep[ 4].reg_drcmr = 0; - dev->ep[ 7].reg_drcmr = dev->ep[ 9].reg_drcmr = 0; - dev->ep[12].reg_drcmr = dev->ep[14].reg_drcmr = 0; - } #endif + udc_init_eps(dev); /* other non-static parts of init */ dev->dev = &pdev->dev; @@ -2567,7 +2610,8 @@ udc_disable(dev); udc_reinit(dev); - dev->vbus = is_vbus_present(); + //dev->vbus = is_vbus_present(); + dev->vbus = 1; /* irq setup after old hardware state is cleaned up */ retval = request_irq(IRQ_USB, pxa2xx_udc_irq, diff -urN linux-2.6.21/drivers/usb/gadget/pxa2xx_udc_gpio.c linux-2.6.21-vpac1/drivers/usb/gadget/pxa2xx_udc_gpio.c --- linux-2.6.21/drivers/usb/gadget/pxa2xx_udc_gpio.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.21-vpac1/drivers/usb/gadget/pxa2xx_udc_gpio.c 2007-06-25 16:38:57.000000000 +0200 @@ -0,0 +1,72 @@ +/* + * pxa2xx_udc_gpio.c: Generic driver for GPIO-controlled PXA2xx UDC. + * + * */ + +#include +#include +#include +#include +#include +#include + +static struct pxa2xx_udc_gpio_info *pdata; + +static void pda_udc_command(int cmd) +{ + switch (cmd) { + case PXA2XX_UDC_CMD_DISCONNECT: + DPM_DEBUG("pda_udc: Turning off port\n"); + dpm_power(&pdata->power_ctrl, 0); + break; + case PXA2XX_UDC_CMD_CONNECT: + DPM_DEBUG("pda_udc: Turning on port\n"); + dpm_power(&pdata->power_ctrl, 1); + break; + default: + printk("pda_udc: unknown command!\n"); + break; + } +} + +static int pda_udc_is_connected(void) +{ + int status = !!gpiodev_get_value(&pdata->detect_gpio) ^ pdata->detect_gpio_negative; + return status; +} + +static struct pxa2xx_udc_mach_info pda_udc_info __initdata = { + .udc_is_connected = pda_udc_is_connected, + .udc_command = pda_udc_command, +}; + +static int pda_udc_probe(struct platform_device * pdev) +{ + printk("pxa2xx-udc-gpio: Generic driver for GPIO-controlled PXA2xx UDC\n"); + pdata = pdev->dev.platform_data; + + pxa_set_udc_info(&pda_udc_info); + return 0; +} + +static struct platform_driver pda_udc_driver = { + .driver = { + .name = "pxa2xx-udc-gpio", + }, + .probe = pda_udc_probe, +}; + +static int __init pda_udc_init(void) +{ + return platform_driver_register(&pda_udc_driver); +} + +#ifdef MODULE +module_init(pda_udc_init); +#else /* start early for dependencies */ +fs_initcall(pda_udc_init); +#endif + +MODULE_AUTHOR("Paul Sokolovsky "); +MODULE_DESCRIPTION("Generic driver for GPIO-controlled PXA2xx UDC"); +MODULE_LICENSE("GPL"); diff -urN linux-2.6.21/drivers/usb/gadget/pxa2xx_udc.h linux-2.6.21-vpac1/drivers/usb/gadget/pxa2xx_udc.h --- linux-2.6.21/drivers/usb/gadget/pxa2xx_udc.h 2007-04-26 05:08:32.000000000 +0200 +++ linux-2.6.21-vpac1/drivers/usb/gadget/pxa2xx_udc.h 2007-06-25 16:38:57.000000000 +0200 @@ -128,7 +128,8 @@ has_cfr : 1, req_pending : 1, req_std : 1, - req_config : 1; + req_config : 1, + out_dma : 1; #define start_watchdog(dev) mod_timer(&dev->timer, jiffies + (HZ/200)) struct timer_list timer; diff -urN linux-2.6.21/drivers/usb/gadget/serial.c linux-2.6.21-vpac1/drivers/usb/gadget/serial.c --- linux-2.6.21/drivers/usb/gadget/serial.c 2007-04-26 05:08:32.000000000 +0200 +++ linux-2.6.21-vpac1/drivers/usb/gadget/serial.c 2007-06-25 16:38:57.000000000 +0200 @@ -1356,6 +1356,7 @@ 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 @@ __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 -urN linux-2.6.21/drivers/usb/gadget/zero.c linux-2.6.21-vpac1/drivers/usb/gadget/zero.c --- linux-2.6.21/drivers/usb/gadget/zero.c 2007-04-26 05:08:32.000000000 +0200 +++ linux-2.6.21-vpac1/drivers/usb/gadget/zero.c 2007-06-25 16:38:57.000000000 +0200 @@ -1143,6 +1143,7 @@ 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. @@ -1155,7 +1156,15 @@ * 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", @@ -1165,7 +1174,7 @@ 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 -urN linux-2.6.21/drivers/video/Kconfig linux-2.6.21-vpac1/drivers/video/Kconfig --- linux-2.6.21/drivers/video/Kconfig 2007-04-26 05:08:32.000000000 +0200 +++ linux-2.6.21-vpac1/drivers/video/Kconfig 2007-06-25 16:02:38.000000000 +0200 @@ -1495,6 +1495,37 @@ If unsure, say N. +choice + + depends on FB_PXA + prompt "PXA framebuffer resolution" + default FB_PXA_VGA + +config FB_PXA_VGA + bool "VGA 640x480" + +config FB_PXA_SVGA + bool "SVGA 800x600" + +config FB_PXA_XGA + bool "XGA 1024x768" + +endchoice + +choice + + depends on FB_PXA + prompt "PXA framebuffer bits per pixel" + default FB_PXA_BPP16 + +config FB_PXA_BPP8 + bool "8 bpp" + +config FB_PXA_BPP16 + bool "16 bpp" + +endchoice + config FB_PXA_PARAMETERS bool "PXA LCD command line parameters" default n diff -urN linux-2.6.21/drivers/video/pxafb.c linux-2.6.21-vpac1/drivers/video/pxafb.c --- linux-2.6.21/drivers/video/pxafb.c 2007-04-26 05:08:32.000000000 +0200 +++ linux-2.6.21-vpac1/drivers/video/pxafb.c 2007-06-25 16:02:38.000000000 +0200 @@ -517,7 +517,7 @@ pcd = (unsigned long long)get_lcdclk_frequency_10khz() * pixclock; do_div(pcd, 100000000 * 2); /* no need for this, since we should subtract 1 anyway. they cancel */ - /* pcd += 1; */ /* make up for integer math truncations */ + pcd += 1; /* make up for integer math truncations */ return (unsigned int)pcd; } @@ -806,6 +806,7 @@ pxa_set_cken(CKEN16_LCD, 1); /* Sequence from 11.7.10 */ + LCCR4 = (fbi->fb.var.bits_per_pixel < 16)? 0x800b0000 : 0x800a0000; LCCR3 = fbi->reg_lccr3; LCCR2 = fbi->reg_lccr2; LCCR1 = fbi->reg_lccr1; @@ -821,6 +822,7 @@ pr_debug("LCCR1 0x%08x\n", (unsigned int) LCCR1); pr_debug("LCCR2 0x%08x\n", (unsigned int) LCCR2); pr_debug("LCCR3 0x%08x\n", (unsigned int) LCCR3); + pr_debug("LCCR4 0x%08x\n", (unsigned int) LCCR4); } static void pxafb_disable_controller(struct pxafb_info *fbi) @@ -1338,7 +1340,7 @@ goto failed; #endif -#ifdef DEBUG_VAR +#if DEBUG_VAR /* Check for various illegal bit-combinations. Currently only * a warning is given. */ diff -urN linux-2.6.21/include/asm-arm/arch-pxa/hardware.h linux-2.6.21-vpac1/include/asm-arm/arch-pxa/hardware.h --- linux-2.6.21/include/asm-arm/arch-pxa/hardware.h 2007-04-26 05:08:32.000000000 +0200 +++ linux-2.6.21-vpac1/include/asm-arm/arch-pxa/hardware.h 2007-06-25 16:02:38.000000000 +0200 @@ -44,12 +44,24 @@ #ifndef __ASSEMBLY__ +#if 0 # define __REG(x) (*((volatile u32 *)io_p2v(x))) +#else +/* + * This __REG() version gives the same results as the one above, except + * that we are fooling gcc somehow so it generates far better and smaller + * assembly code for access to contigous registers. It's a shame that gcc + * doesn't guess this by itself. + */ +#include +typedef struct { volatile u32 offset[4096]; } __regbase; +# define __REGP(x) ((__regbase *)((x)&~4095))->offset[((x)&4095)>>2] +# define __REG(x) __REGP(io_p2v(x)) +#endif /* With indexed regs we don't want to feed the index through io_p2v() especially if it is a variable, otherwise horrible code will result. */ -# define __REG2(x,y) \ - (*(volatile u32 *)((u32)&__REG(x) + (y))) +# define __REG2(x,y) (*(volatile u32 *)((u32)&__REG(x) + (y))) # define __PREG(x) (io_v2p((u32)&(x))) @@ -90,4 +102,8 @@ #endif +#ifdef CONFIG_MACH_VPAC270 +#include "vpac270.h" +#endif + #endif /* _ASM_ARCH_HARDWARE_H */ diff -urN linux-2.6.21/include/asm-arm/arch-pxa/pxa-regs.h linux-2.6.21-vpac1/include/asm-arm/arch-pxa/pxa-regs.h --- linux-2.6.21/include/asm-arm/arch-pxa/pxa-regs.h 2007-04-26 05:08:32.000000000 +0200 +++ linux-2.6.21-vpac1/include/asm-arm/arch-pxa/pxa-regs.h 2007-06-25 16:02:38.000000000 +0200 @@ -108,6 +108,14 @@ #define DCSR_BUSERR (1 << 0) /* Bus Error Interrupt (read / write) */ #define DALGN __REG(0x400000a0) /* DMA Alignment Register */ + +#define DRQSR0 __REG(0x400000e0) /* DMA Request Status Register 0 */ +#define DRQSR1 __REG(0x400000e4) /* DMA Request Status Register 1 */ +#define DRQSR2 __REG(0x400000e8) /* DMA Request Status Register 2 */ + +#define DRQSR_REQCLR (1 << 8) /* Clear Pending Requests */ +#define DRQSR_REQPEND 0x0000001f + #define DINT __REG(0x400000f0) /* DMA Interrupt Register */ #define DRCMR(n) __REG2(0x40000100, (n)<<2) @@ -273,6 +281,10 @@ #define DCMD_WIDTH4 (3 << 14) /* 4 byte width (Word) */ #define DCMD_LENGTH 0x01fff /* length mask (max = 8K - 1) */ +/* default combinations */ +#define DCMD_RXPCDR (DCMD_INCTRGADDR|DCMD_FLOWSRC|DCMD_BURST32|DCMD_WIDTH4) +#define DCMD_RXMCDR (DCMD_INCTRGADDR|DCMD_FLOWSRC|DCMD_BURST32|DCMD_WIDTH4) +#define DCMD_TXPCDR (DCMD_INCSRCADDR|DCMD_FLOWTRG|DCMD_BURST32|DCMD_WIDTH4) /* * UARTs @@ -1123,7 +1135,6 @@ #define ICPR __REG(0x40D00010) /* Interrupt Controller Pending Register */ #define ICCR __REG(0x40D00014) /* Interrupt Controller Control Register */ - /* * General Purpose I/O */ @@ -1316,6 +1327,7 @@ #define GPIO77_LCD_ACBIAS 77 /* LCD AC Bias */ #define GPIO78_nCS_2 78 /* chip select 2 */ #define GPIO79_nCS_3 79 /* chip select 3 */ +#define GPIO79_pSKTSEL 79 /* pSKTSEL - PC Card Socket select (PXA27xx)*/ #define GPIO80_nCS_4 80 /* chip select 4 */ #define GPIO81_NSCLK 81 /* NSSP clock */ #define GPIO82_NSFRM 82 /* NSSP Frame */ @@ -1355,6 +1367,7 @@ #define GPIO8_MMCCS0_MD ( 8 | GPIO_ALT_FN_1_OUT) #define GPIO9_MMCCS1_MD ( 9 | GPIO_ALT_FN_1_OUT) #define GPIO10_RTCCLK_MD (10 | GPIO_ALT_FN_1_OUT) +#define GPIO10_FFDCD_MD (10 | GPIO_ALT_FN_1_IN) #define GPIO11_3_6MHz_MD (11 | GPIO_ALT_FN_1_OUT) #define GPIO12_32KHz_MD (12 | GPIO_ALT_FN_1_OUT) #define GPIO13_MBGNT_MD (13 | GPIO_ALT_FN_2_OUT) @@ -1370,6 +1383,7 @@ #define GPIO25_STXD_MD (25 | GPIO_ALT_FN_2_OUT) #define GPIO26_SRXD_MD (26 | GPIO_ALT_FN_1_IN) #define GPIO27_SEXTCLK_MD (27 | GPIO_ALT_FN_1_IN) +#define GPIO27_FFRTS_MD (27 | GPIO_ALT_FN_3_OUT) #define GPIO28_BITCLK_AC97_MD (28 | GPIO_ALT_FN_1_IN) #define GPIO28_BITCLK_IN_I2S_MD (28 | GPIO_ALT_FN_2_IN) #define GPIO28_BITCLK_OUT_I2S_MD (28 | GPIO_ALT_FN_1_OUT) @@ -1383,6 +1397,7 @@ #define GPIO32_SYSCLK_I2S_MD (32 | GPIO_ALT_FN_1_OUT) #define GPIO32_MMCCLK_MD ( 32 | GPIO_ALT_FN_2_OUT) #define GPIO33_nCS_5_MD (33 | GPIO_ALT_FN_2_OUT) +#define GPIO33_FFDSR_MD (33 | GPIO_ALT_FN_2_IN) #define GPIO34_FFRXD_MD (34 | GPIO_ALT_FN_1_IN) #define GPIO34_MMCCS0_MD (34 | GPIO_ALT_FN_2_OUT) #define GPIO35_FFCTS_MD (35 | GPIO_ALT_FN_1_IN) @@ -1466,6 +1481,7 @@ #define GPIO84_NSSP_RX (84 | GPIO_ALT_FN_2_IN) #define GPIO85_nPCE_1_MD (85 | GPIO_ALT_FN_1_OUT) #define GPIO92_MMCDAT0_MD (92 | GPIO_ALT_FN_1_OUT) +#define GPIO100_FFCTS_MD (100 | GPIO_ALT_FN_2_IN) #define GPIO102_nPCE_1_MD (102 | GPIO_ALT_FN_1_OUT) #define GPIO104_pSKTSEL_MD (104 | GPIO_ALT_FN_1_OUT) #define GPIO109_MMCDAT1_MD (109 | GPIO_ALT_FN_1_OUT) @@ -1479,6 +1495,7 @@ #define GPIO117_I2CSCL_MD (117 | GPIO_ALT_FN_1_IN) #define GPIO118_I2CSDA_MD (118 | GPIO_ALT_FN_1_IN) + /* * Power Manager */ @@ -1843,6 +1860,7 @@ #define LCCR1 __REG(0x44000004) /* LCD Controller Control Register 1 */ #define LCCR2 __REG(0x44000008) /* LCD Controller Control Register 2 */ #define LCCR3 __REG(0x4400000C) /* LCD Controller Control Register 3 */ +#define LCCR4 __REG(0x44000010) /* LCD Controller Control Register 4 */ #define DFBR0 __REG(0x44000020) /* DMA Channel 0 Frame Branch Register */ #define DFBR1 __REG(0x44000024) /* DMA Channel 1 Frame Branch Register */ #define LCSR __REG(0x44000038) /* LCD Controller Status Register */ @@ -1855,6 +1873,7 @@ #define LCCR3_4BPP (2 << 24) #define LCCR3_8BPP (3 << 24) #define LCCR3_16BPP (4 << 24) +#define LCCR3_18BPP (5 << 24) #define FDADR0 __REG(0x44000200) /* DMA Channel 0 Frame Descriptor Address Register */ #define FSADR0 __REG(0x44000204) /* DMA Channel 0 Frame Source Address Register */ diff -urN linux-2.6.21/include/asm-arm/arch-pxa/vpac270.h linux-2.6.21-vpac1/include/asm-arm/arch-pxa/vpac270.h --- linux-2.6.21/include/asm-arm/arch-pxa/vpac270.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.21-vpac1/include/asm-arm/arch-pxa/vpac270.h 2007-06-25 16:02:38.000000000 +0200 @@ -0,0 +1,253 @@ +/* + * linux/include/asm-arm/arch-pxa/vpac270.h + * + * 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. + * + * Copyright (c) 2006 Voipac Technologies + * + */ + +#include +#include + + +/* + * Note: include file for assembler and C + */ + +/* + * Video settings + */ + +#define VPAC270_ANALOG_VGA +//#define VPAC270_LCD_PHILIPSLB + +#ifdef VPAC270_ANALOG_VGA +/* set to 1 if you want these settings to be active */ +#define VPAC270_LCD_SETTINGS 1 + +/* set to GPIO that switches on the backlight */ +#define VPAC270_LCD_BLO_GPIO 81 + +/* set to 1 if the LCD is an active panel (TFT) or for VGA */ +#define VPAC270_LCD_ACTIVE 1 + +/* set to 1 if the LCD panel is a dual-scan panel */ +#define VPAC270_LCD_DUAL 0 + +/* set to 1 if the LCD BIAS pin should be active low (OEP) */ +#define VPAC270_BIAS_ACTIVE_LOW 0 + +/* set to 1 if the data is sampled on the falling edge of the pixel clock PCP*/ +#define VPAC270_PIX_CLK_FALLING 0 + +/* set to 3 if more than 16 bpp (PDFOR) */ +#define VPAC270_PIXEL_DATA_FORMAT 3 + +/* the following settings can also be set using fbset */ +/* these are settings are for analog VGA */ +#define LCD_PIXCLOCK 40000 +#define LCD_XRES 640 +#define LCD_YRES 480 +#define LCD_HORIZONTAL_SYNC_PULSE_WIDTH 64 +#define LCD_VERTICAL_SYNC_PULSE_WIDTH 2 +#define LCD_BEGIN_OF_LINE_WAIT_COUNT 96 +#define LCD_BEGIN_FRAME_WAIT_COUNT 33 +#define LCD_END_OF_LINE_WAIT_COUNT 48 +#define LCD_END_OF_FRAME_WAIT_COUNT 10 +#define LCD_SYNC 0 + + +/* VGA timings: +pixclk 0x1801050 Hz (25MHz) = 40000ps +640x480 18BPP +LCD_HORIZONTAL_SYNC_PULSE_WIDTH HSPW 0x40 +LCD_BEGIN_OF_LINE_WAIT_COUNT BLW 0x60 +LCD_END_OF_LINE_WAIT_COUNT ELW 0x30 +LCD_VERTICAL_SYNC_PULSE_WIDTH VSW 0x2 +LCD_BEGIN_FRAME_WAIT_COUNT BFW 0x21 +LCD_END_OF_FRAME_WAIT_COUNT EFW 0xa +VPAC270_PIX_CLK_FALLING PCP 0 +FB_SYNC_HOR_HIGH_ACT_LOW HSP 1 +FB_SYNC_VERT_HIGH_ACT_LOW VSP 1 +VPAC270_LCD_DUAL SDS 0 +VPAC270_LCD_ACTIVE PAS 1 +VPAC270_BIAS_ACTIVE_LOW OEP 0 +*/ + +#elif defined VPAC270_LCD_PHILIPSLB + +/* set to 1 if you want these settings to be active */ +#define VPAC270_LCD_SETTINGS 1 + +/* set to GPIO that switches on the backlight */ +#define VPAC270_LCD_BLO_GPIO 81 + +/* set to 1 if the LCD is an active panel (TFT) or for VGA */ +#define VPAC270_LCD_ACTIVE 1 + +/* set to 1 if the LCD panel is a dual-scan panel */ +#define VPAC270_LCD_DUAL 0 + +/* set to 1 if the LCD BIAS pin should be active low (OEP) */ +#define VPAC270_BIAS_ACTIVE_LOW 0 + +/* set to 1 if the data is sampled on the falling edge of the pixel clock PCP*/ +#define VPAC270_PIX_CLK_FALLING 1 + +/* set to 3 if more than 16 bpp (PDFOR) */ +#define VPAC270_PIXEL_DATA_FORMAT 3 + +/* the following settings can also be set using fbset */ +/* these are settings are for analog VGA */ +#define LCD_PIXCLOCK 40000 +#define LCD_XRES 640 +#define LCD_YRES 480 +#define LCD_HORIZONTAL_SYNC_PULSE_WIDTH 2 +#define LCD_VERTICAL_SYNC_PULSE_WIDTH 45 +#define LCD_BEGIN_OF_LINE_WAIT_COUNT 160 +#define LCD_BEGIN_FRAME_WAIT_COUNT 0 +#define LCD_END_OF_LINE_WAIT_COUNT 2 +#define LCD_END_OF_FRAME_WAIT_COUNT 0 +#define LCD_SYNC (FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT) + +/* Philips LB064V02-A1 timings: +pixclk 0x1801050 Hz (25MHz) = 40000ps +640x480 18BPP +LCD_HORIZONTAL_SYNC_PULSE_WIDTH HSPW 2 +LCD_BEGIN_OF_LINE_WAIT_COUNT BLW 160 +LCD_END_OF_LINE_WAIT_COUNT ELW 2 +LCD_VERTICAL_SYNC_PULSE_WIDTH VSW 45 +LCD_BEGIN_FRAME_WAIT_COUNT BFW 0 +LCD_END_OF_FRAME_WAIT_COUNT EFW 0 +VPAC270_PIX_CLK_FALLING PCP 1 +FB_SYNC_HOR_HIGH_ACT_LOW HSP 0 +FB_SYNC_VERT_HIGH_ACT_LOW VSP 0 +VPAC270_LCD_DUAL SDS 0 +VPAC270_LCD_ACTIVE PAS 1 +VPAC270_BIAS_ACTIVE_LOW OEP 0 +*/ +#endif + + +/* + * physical memory map + */ + +#define VPAC270_FLASH_PHYS PXA_CS0_PHYS /* 0x00000000 */ +#define VPAC270_FLASH_SIZE (0x02000000) + +#define VPAC270_ETH_PHYS PXA_CS2_PHYS /* 0x08000000 */ +//#define VPAC270_CPLD_PHYS PXA_CS3_PHYS /* 0x0c000000 */ +#define VPAC270_BCR_PHYS 0x0e000000 + +/* + * virtual memory map + */ + +#define VPAC270_FLASH 0x00000000 +//#define VPAC270_ETH_BASE 0xf4000000 +#define VPAC270_ETH_SIZE 8 + +//#define VPAC270_CPLD_BASE 0xf0000000 +//#define VPAC270_CPLD_SIZE 0x04000000 +#define VPAC270_CF_OFFSET 0x00000000 +#define VPAC270_BCR_OFFSET 0x02000000 +#define VPAC270_IRDA_OFFSET 0x02400000 +#define VPAC270_UPS_OFFSET 0x02800000 +#define VPAC270_DCR_OFFSET 0x03800000 + +//#ifndef __ASSEMBLY__ +//# define CPLD_REG(x) (*((volatile unsigned short *)(VPAC270_CPLD_BASE + (x)))) +//#else +//# define CPLD_REG(x) (VPAC270_CPLD_BASE + (x)) +//#endif + + +//#define DM9000_BASE VPAC270_ETH_BASE +//#define DM9000_MMIO 1 +//#define DM9000_ADDRFROMTAG 1 + +#define VPAC270_CF0_STATUS CPLD_REG(VPAC270_CF_OFFSET) +#define VPAC270_BCR_CONTROL CPLD_REG(VPAC270_BCR_OFFSET) +#define VPAC270_DCR CPLD_REG(VPAC270_DCR_OFFSET) + +/* + * interrupts + */ +/* Ethernet */ +#define GPIO_ETH_IRQ 114 +#define VPAC270_ETH_IRQ IRQ_GPIO(GPIO_ETH_IRQ) +#define DM9000_IRQ VPAC270_ETH_IRQ + +/* IDE */ +#define GPIO_IDE_IRQ 36 +#define VPAC270_IDE_IRQ IRQ_GPIO(GPIO_IDE_IRQ) + +/* Compact Flash/PCMCIA */ +#define GPIO_PCMCIA0_CD_IRQ 84 +#define VPAC270_PCMCIA0_CD_IRQ IRQ_GPIO(GPIO_PCMCIA0_CD_IRQ) +#define GPIO_PCMCIA1_CD_IRQ 17 +#define VPAC270_PCMCIA1_CD_IRQ IRQ_GPIO(GPIO_PCMCIA1_CD_IRQ) +#define VPAC270_PCMCIA_CD_EDGE IRQT_BOTHEDGE +#define GPIO_PCMCIA0_RDY_IRQ 35 +#define VPAC270_PCMCIA0_RDY_IRQ IRQ_GPIO(GPIO_PCMCIA0_RDY_IRQ) +#define GPIO_PCMCIA1_RDY_IRQ 12 +#define VPAC270_PCMCIA1_RDY_IRQ IRQ_GPIO(GPIO_PCMCIA1_RDY_IRQ) +#define VPAC270_PCMCIA_RDY_EDGE IRQT_FALLING +#define GPIO_PCMCIA_POW_EN 107 +#define GPIO_PCMCIA_NPOE 48 +#define GPIO_PCMCIA_NPOE_AF GPIO_ALT_FN_2_OUT +#define GPIO_PCMCIA_NPIOR 50 +#define GPIO_PCMCIA_NPIOR_AF GPIO_ALT_FN_2_OUT +#define GPIO_PCMCIA_NPIOW 51 +#define GPIO_PCMCIA_NPIOW_AF GPIO_ALT_FN_2_OUT +#define GPIO_PCMCIA_NPCE1 85 +#define GPIO_PCMCIA_NPCE1_AF GPIO_ALT_FN_1_OUT +#define GPIO_PCMCIA_NPCE2 54 +#define GPIO_PCMCIA_NPCE2_AF GPIO_ALT_FN_2_OUT +#define GPIO_PCMCIA_NPREG 55 +#define GPIO_PCMCIA_NPREG_AF GPIO_ALT_FN_2_OUT +#define GPIO_PCMCIA_NPWAIT 56 +#define GPIO_PCMCIA_NPWAIT_AF GPIO_ALT_FN_1_IN +#define GPIO_PCMCIA_NPIOIS16 57 +#define GPIO_PCMCIA_NPIOIS16_AF GPIO_ALT_FN_1_IN +#define GPIO_PCMCIA_PSKTSEL 104 +#define GPIO_PCMCIA_PSKTSEL_AF GPIO_ALT_FN_1_OUT +#define GPIO_PCMCIA0_RESET 11 +#define GPIO_PCMCIA1_RESET 16 +//#define GPIO_PCMCIA0_BVD1 83 +//#define GPIO_PCMCIA0_BVD2 82 + +#define PCC0_DETECT (GPLR(GPIO_PCMCIA0_CD_IRQ) & GPIO_bit(GPIO_PCMCIA0_CD_IRQ)) +#define PCC0_READY (GPLR(GPIO_PCMCIA0_RDY_IRQ) & GPIO_bit(GPIO_PCMCIA0_RDY_IRQ)) +#define PCC1_DETECT (GPLR(GPIO_PCMCIA1_CD_IRQ) & GPIO_bit(GPIO_PCMCIA1_CD_IRQ)) +#define PCC1_READY (GPLR(GPIO_PCMCIA1_RDY_IRQ) & GPIO_bit(GPIO_PCMCIA1_RDY_IRQ)) +#define PCC_BVD1() (GPLR(GPIO_PCMCIA0_BVD1) & GPIO_bit(GPIO_PCMCIA0_BVD1)) +#define PCC_BVD2() (GPLR(GPIO_PCMCIA0_BVD2) & GPIO_bit(GPIO_PCMCIA0_BVD2)) +#define PCC_VS3V() 1 /* only 3.3V supported */ +#define PCC_VS5V() 0 /* only 3.3V supported */ +#define PCC_PWR_ON() (GPCR(GPIO_PCMCIA_POW_EN) |= GPIO_bit(GPIO_PCMCIA_POW_EN)) +#define PCC_PWR_OFF() (GPSR(GPIO_PCMCIA_POW_EN) |= GPIO_bit(GPIO_PCMCIA_POW_EN)) + +/* MMC/SD */ +#define GPIO_MMC_CD_IRQ 53 +#define VPAC270_MMC_CD_IRQ IRQ_GPIO(GPIO_MMC_CD_IRQ) +#define GPIO_MMCCLK_AF GPIO32_MMCCLK_MD +#define GPIO_MMCDAT0_AF GPIO92_MMCDAT0 +#define GPIO_MMCDAT1_AF GPIO109_MMCDAT1 +#define GPIO_MMCDAT2_AF GPIO110_MMCDAT2 +#define GPIO_MMCDAT3_AF GPIO111_MMCDAT3 +#define GPIO_MMCCMD_AF GPIO112_MMCCMD_MD +#define GPIO_MMCCS0_AF GPIO110_MMCCS0_MD + +/* TouchScreen and Sound */ +#define GPIO_TOUCH_IRQ 113 +#define VPAC270_TOUCH_IRQ IRQ_GPIO(GPIO_TOUCH_IRQ) + +#define GPIO_AC97_RESET 95 +#define GPIO_AC97_RST_AF GPIO_ALT_FN_1_OUT +#define GPIO_AC97_SYSCLK 98 +#define GPIO_AC97_SYSCLK_AF GPIO_ALT_FN_1_OUT diff -urN linux-2.6.21/include/linux/fs.h linux-2.6.21-vpac1/include/linux/fs.h --- linux-2.6.21/include/linux/fs.h 2007-04-26 05:08:32.000000000 +0200 +++ linux-2.6.21-vpac1/include/linux/fs.h 2007-06-25 16:02:38.000000000 +0200 @@ -718,7 +718,7 @@ struct path f_path; #define f_dentry f_path.dentry #define f_vfsmnt f_path.mnt - const struct file_operations *f_op; + struct file_operations *f_op; atomic_t f_count; unsigned int f_flags; mode_t f_mode; diff -urN linux-2.6.21/include/linux/usb_gadget.h linux-2.6.21-vpac1/include/linux/usb_gadget.h --- linux-2.6.21/include/linux/usb_gadget.h 2007-04-26 05:08:32.000000000 +0200 +++ linux-2.6.21-vpac1/include/linux/usb_gadget.h 2007-06-25 16:51:27.000000000 +0200 @@ -445,10 +445,28 @@ 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); @@ -872,7 +890,10 @@ /* 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 +) __devinit; extern void usb_ep_autoconfig_reset (struct usb_gadget *) __devinit; diff -urN linux-2.6.21/Makefile linux-2.6.21-vpac1/Makefile --- linux-2.6.21/Makefile 2007-04-26 05:08:32.000000000 +0200 +++ linux-2.6.21-vpac1/Makefile 2007-06-25 16:02:38.000000000 +0200 @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 6 SUBLEVEL = 21 -EXTRAVERSION = +EXTRAVERSION = -vpac1 NAME = Nocturnal Monster Puppy # *DOCUMENTATION* @@ -182,8 +182,9 @@ # Default value for CROSS_COMPILE is not to prefix executables # Note: Some architectures assign CROSS_COMPILE in their arch/*/Makefile -ARCH ?= $(SUBARCH) -CROSS_COMPILE ?= +ARCH = arm +CROSS_COMPILE = arm-linux- +#CROSS_COMPILE = arm-none-linux-gnueabi- # Architecture as present in compile.h UTS_MACHINE := $(ARCH) diff -urN linux-2.6.21/sound/arm/pxa2xx-ac97.c linux-2.6.21-vpac1/sound/arm/pxa2xx-ac97.c --- linux-2.6.21/sound/arm/pxa2xx-ac97.c 2007-04-26 05:08:32.000000000 +0200 +++ linux-2.6.21-vpac1/sound/arm/pxa2xx-ac97.c 2007-06-25 16:02:38.000000000 +0200 @@ -133,10 +133,12 @@ #ifdef CONFIG_PXA27x /* warm reset broken on Bulverde, so manually keep AC97 reset high */ - pxa_gpio_mode(113 | GPIO_OUT | GPIO_DFLT_HIGH); +// pxa_gpio_mode(113 | GPIO_OUT | GPIO_DFLT_HIGH); + pxa_gpio_mode( 95 | GPIO_OUT | GPIO_DFLT_HIGH); udelay(10); GCR |= GCR_WARM_RST; - pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT); +// pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT); + pxa_gpio_mode(GPIO_AC97_RESET | GPIO_AC97_RST_AF); udelay(500); #else GCR |= GCR_WARM_RST|GCR_PRIRDY_IEN|GCR_SECRDY_IEN; @@ -148,6 +150,9 @@ __FUNCTION__, gsr_bits); } + pxa2xx_ac97_write(ac97, 0x6a, 0x0050); + pxa2xx_ac97_write(ac97, 0x6c, 0x0030); + GCR &= ~(GCR_PRIRDY_IEN|GCR_SECRDY_IEN); GCR |= GCR_SDONE_IE|GCR_CDONE_IE; } @@ -335,7 +340,9 @@ pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD); #ifdef CONFIG_PXA27x /* Use GPIO 113 as AC97 Reset on Bulverde */ - pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT); +// pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT); + pxa_gpio_mode(GPIO_AC97_RESET | GPIO_AC97_RST_AF); + pxa_gpio_mode(GPIO_AC97_SYSCLK | GPIO_AC97_SYSCLK_AF); #endif pxa_set_cken(CKEN2_AC97, 1); diff -urN linux-2.6.21/sound/oss/ac97_codec.c linux-2.6.21-vpac1/sound/oss/ac97_codec.c --- linux-2.6.21/sound/oss/ac97_codec.c 2007-04-26 05:08:32.000000000 +0200 +++ linux-2.6.21-vpac1/sound/oss/ac97_codec.c 2007-06-25 16:02:38.000000000 +0200 @@ -162,6 +162,7 @@ {0x45838308, "ESS Allegro ES1988", &null_ops}, {0x49434511, "ICE1232", &null_ops}, /* I hope --jk */ {0x4e534331, "National Semiconductor LM4549", &null_ops}, + {0x50534304, "Philips UCB1400", &default_ops}, {0x53494c22, "Silicon Laboratory Si3036", &null_ops}, {0x53494c23, "Silicon Laboratory Si3038", &null_ops}, {0x545200FF, "TriTech TR?????", &tritech_m_ops}, diff -urN linux-2.6.21/sound/oss/Kconfig linux-2.6.21-vpac1/sound/oss/Kconfig --- linux-2.6.21/sound/oss/Kconfig 2007-04-26 05:08:32.000000000 +0200 +++ linux-2.6.21-vpac1/sound/oss/Kconfig 2007-06-25 16:02:38.000000000 +0200 @@ -19,6 +19,10 @@ If unsure, say N. +config SOUND_PXA_AC97 + tristate "Intel PXA2xx AC97 audio" + depends on SOUND_PRIME && ARCH_PXA && SOUND + config SOUND_BT878 tristate "BT878 audio dma" depends on SOUND_PRIME && PCI diff -urN linux-2.6.21/sound/oss/Makefile linux-2.6.21-vpac1/sound/oss/Makefile --- linux-2.6.21/sound/oss/Makefile 2007-04-26 05:08:32.000000000 +0200 +++ linux-2.6.21-vpac1/sound/oss/Makefile 2007-06-25 16:02:38.000000000 +0200 @@ -7,6 +7,7 @@ obj-$(CONFIG_SOUND_OSS) += sound.o obj-$(CONFIG_SOUND_CS4232) += cs4232.o ad1848.o +obj-$(CONFIG_SOUND_PXA_AC97) += pxa-ac97.o pxa-audio.o ac97_codec.o # Please leave it as is, cause the link order is significant ! diff -urN linux-2.6.21/sound/oss/pxa-ac97.c linux-2.6.21-vpac1/sound/oss/pxa-ac97.c --- linux-2.6.21/sound/oss/pxa-ac97.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.21-vpac1/sound/oss/pxa-ac97.c 2007-06-25 16:02:38.000000000 +0200 @@ -0,0 +1,363 @@ +/* + * linux/drivers/sound/pxa-ac97.c -- AC97 interface for the Cotula chip + * + * Author: Nicolas Pitre + * Created: Aug 15, 2001 + * Copyright: MontaVista Software Inc. + * + * Forward ported to 2.6 by Ian Molton 15/09/2003 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "pxa-audio.h" + +static struct completion CAR_completion; +static int waitingForMask; +static DECLARE_MUTEX(CAR_mutex); + +static u16 pxa_ac97_read(struct ac97_codec *codec, u8 reg) +{ + u16 val = -1; + + down(&CAR_mutex); + if (!(CAR & CAR_CAIP)) { + volatile u32 *reg_addr = (u32 *)&PAC_REG_BASE + (reg >> 1); + + waitingForMask=GSR_SDONE; + + init_completion(&CAR_completion); + (void)*reg_addr; //start read access across the ac97 link + wait_for_completion(&CAR_completion); + + if (GSR & GSR_RDCS) { + GSR = GSR_RDCS; //write a 1 to clear + printk(KERN_CRIT "%s: read codec register timeout.\n", __FUNCTION__); + } + + init_completion(&CAR_completion); + val = *reg_addr; //valid data now but we've just started another cycle... + wait_for_completion(&CAR_completion); + + } else { + printk(KERN_CRIT"%s: CAR_CAIP already set\n", __FUNCTION__); + } + up(&CAR_mutex); + //printk("%s(0x%02x) = 0x%04x\n", __FUNCTION__, reg, val); + return val; +} + +static void pxa_ac97_write(struct ac97_codec *codec, u8 reg, u16 val) +{ + down(&CAR_mutex); + if (!(CAR & CAR_CAIP)) { + volatile u32 *reg_addr = (u32 *)&PAC_REG_BASE + (reg >> 1); + + waitingForMask=GSR_CDONE; + init_completion(&CAR_completion); + *reg_addr = val; + wait_for_completion(&CAR_completion); + } else { + printk(KERN_CRIT "%s: CAR_CAIP already set\n", __FUNCTION__); + } + up(&CAR_mutex); + //printk("%s(0x%02x, 0x%04x)\n", __FUNCTION__, reg, val); +} + +static irqreturn_t pxa_ac97_irq(int irq, void *dev_id) +{ + int gsr = GSR; + GSR = gsr & (GSR_SDONE|GSR_CDONE); //write a 1 to clear + if (gsr & waitingForMask) + complete(&CAR_completion); + + return IRQ_HANDLED; +} + +static struct ac97_codec pxa_ac97_codec = { + codec_read: pxa_ac97_read, + codec_write: pxa_ac97_write, +}; + +static DECLARE_MUTEX(pxa_ac97_mutex); +static int pxa_ac97_refcount; + +int pxa_ac97_get(struct ac97_codec **codec) +{ + int ret; + + *codec = NULL; + down(&pxa_ac97_mutex); + + if (!pxa_ac97_refcount) { + ret = request_irq(IRQ_AC97, pxa_ac97_irq, 0, "AC97", NULL); + if (ret) + return ret; + + CKEN |= CKEN2_AC97; + + pxa_gpio_mode(GPIO31_SYNC_AC97_MD); + pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD); + pxa_gpio_mode(GPIO28_BITCLK_AC97_MD); + pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD); + + GCR = 0; + udelay(10); + GCR = GCR_COLD_RST|GCR_CDONE_IE|GCR_SDONE_IE; + while (!(GSR & GSR_PCR)) { + schedule(); + } + + ret = ac97_probe_codec(&pxa_ac97_codec); + if (ret != 1) { + free_irq(IRQ_AC97, NULL); + GCR = GCR_ACLINK_OFF; + CKEN &= ~CKEN2_AC97; + return ret; + } + + // need little hack for UCB1400 (should be moved elsewhere) +// pxa_ac97_write(&pxa_ac97_codec,AC97_EXTENDED_STATUS,1); + //pxa_ac97_write(&pxa_ac97_codec, 0x6a, 0x1ff7); + pxa_ac97_write(&pxa_ac97_codec, 0x6a, 0x0050); + pxa_ac97_write(&pxa_ac97_codec, 0x6c, 0x0030); + } + + pxa_ac97_refcount++; + up(&pxa_ac97_mutex); + *codec = &pxa_ac97_codec; + return 0; +} + +void pxa_ac97_put(void) +{ + down(&pxa_ac97_mutex); + pxa_ac97_refcount--; + if (!pxa_ac97_refcount) { + GCR = GCR_ACLINK_OFF; + CKEN &= ~CKEN2_AC97; + free_irq(IRQ_AC97, NULL); + } + up(&pxa_ac97_mutex); +} + +EXPORT_SYMBOL(pxa_ac97_get); +EXPORT_SYMBOL(pxa_ac97_put); + + +/* + * Audio Mixer stuff + */ + +static audio_state_t ac97_audio_state; +static audio_stream_t ac97_audio_in; + +/* + * According to the PXA250 spec, mic-in should use different + * DRCMR and different AC97 FIFO. + * Unfortunately current UCB1400 versions (up to ver 2A) don't + * produce slot 6 for the audio input frame, therefore the PXA + * AC97 mic-in FIFO is always starved. + * But since UCB1400 is not the only audio CODEC out there, + * this is still enabled by default. + */ +static void update_audio_in (void) +{ +#if 1 + long val; + + /* Use the value stuffed by ac97_recmask_io() + * into recording select register + */ + val = pxa_ac97_codec.codec_read(&pxa_ac97_codec, AC97_RECORD_SELECT); + pxa_audio_clear_buf(&ac97_audio_in); + *ac97_audio_in.drcmr = 0; + if (val == 0) { + ac97_audio_in.dcmd = DCMD_RXMCDR; + ac97_audio_in.drcmr = &DRCMRRXMCDR; + ac97_audio_in.dev_addr = __PREG(MCDR); + } else { + ac97_audio_in.dcmd = DCMD_RXPCDR; + ac97_audio_in.drcmr = &DRCMRRXPCDR; + ac97_audio_in.dev_addr = __PREG(PCDR); + } + if (ac97_audio_state.rd_ref) + *ac97_audio_in.drcmr = + ac97_audio_in.dma_ch | DRCMR_MAPVLD; +#endif +} + +static int mixer_ioctl( struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int ret; + + ret = pxa_ac97_codec.mixer_ioctl(&pxa_ac97_codec, cmd, arg); + if (ret) + return ret; + + /* We must snoop for some commands to provide our own extra processing */ + switch (cmd) { + case SOUND_MIXER_WRITE_RECSRC: + update_audio_in (); + break; + } + return 0; +} + +static struct file_operations mixer_fops = { + ioctl: mixer_ioctl, + llseek: no_llseek, + owner: THIS_MODULE +}; + +/* + * AC97 codec ioctls + */ + +static int codec_adc_rate = 48000; +static int codec_dac_rate = 48000; + +static int ac97_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int ret; + long val = 0; + + switch(cmd) { + case SNDCTL_DSP_STEREO: + ret = get_user(val, (int *) arg); + if (ret) + return ret; + /* FIXME: do we support mono? */ + ret = (val == 0) ? -EINVAL : 1; + return put_user(ret, (int *) arg); + + case SNDCTL_DSP_CHANNELS: + case SOUND_PCM_READ_CHANNELS: + /* FIXME: do we support mono? */ + return put_user(2, (long *) arg); + + case SNDCTL_DSP_SPEED: + ret = get_user(val, (long *) arg); + if (ret) + return ret; + if (file->f_mode & FMODE_READ) + codec_adc_rate = ac97_set_adc_rate(&pxa_ac97_codec, val); + if (file->f_mode & FMODE_WRITE) + codec_dac_rate = ac97_set_dac_rate(&pxa_ac97_codec, val); + /* fall through */ + case SOUND_PCM_READ_RATE: + if (file->f_mode & FMODE_READ) + val = codec_adc_rate; + if (file->f_mode & FMODE_WRITE) + val = codec_dac_rate; + return put_user(val, (long *) arg); + + case SNDCTL_DSP_SETFMT: + case SNDCTL_DSP_GETFMTS: + /* FIXME: can we do other fmts? */ + return put_user(AFMT_S16_LE, (long *) arg); + + default: + /* Maybe this is meant for the mixer (As per OSS Docs) */ + return mixer_ioctl(inode, file, cmd, arg); + } + return 0; +} + + +/* + * Audio stuff + */ + +static audio_stream_t ac97_audio_out = { + name: "AC97 audio out", + dcmd: DCMD_TXPCDR, + drcmr: &DRCMRTXPCDR, + dev_addr: __PREG(PCDR), +}; + +static audio_stream_t ac97_audio_in = { + name: "AC97 audio in", + dcmd: DCMD_RXPCDR, + drcmr: &DRCMRRXPCDR, + dev_addr: __PREG(PCDR), +}; + +static audio_state_t ac97_audio_state = { + output_stream: &ac97_audio_out, + input_stream: &ac97_audio_in, + client_ioctl: ac97_ioctl, + sem: __SEMAPHORE_INIT(ac97_audio_state.sem,1), +}; + +static int ac97_audio_open(struct inode *inode, struct file *file) +{ + return pxa_audio_attach(inode, file, &ac97_audio_state); +} + +/* + * Missing fields of this structure will be patched with the call + * to pxa_audio_attach(). + */ + +static struct file_operations ac97_audio_fops = { + open: ac97_audio_open, + owner: THIS_MODULE +}; + + +static int __init pxa_ac97_init(void) +{ + int ret; + struct ac97_codec *dummy; + + ret = pxa_ac97_get(&dummy); + if (ret) + return ret; + + update_audio_in (); + + ac97_audio_state.dev_dsp = register_sound_dsp(&ac97_audio_fops, -1); + pxa_ac97_codec.dev_mixer = register_sound_mixer(&mixer_fops, -1); + + return 0; +} + +static void __exit pxa_ac97_exit(void) +{ + unregister_sound_dsp(ac97_audio_state.dev_dsp); + unregister_sound_mixer(pxa_ac97_codec.dev_mixer); + pxa_ac97_put(); +} + + +module_init(pxa_ac97_init); +module_exit(pxa_ac97_exit); + +MODULE_AUTHOR("Nicolas Pitre"); +MODULE_DESCRIPTION("AC97 interface for the Cotula chip"); +MODULE_LICENSE("GPL"); diff -urN linux-2.6.21/sound/oss/pxa-audio.c linux-2.6.21-vpac1/sound/oss/pxa-audio.c --- linux-2.6.21/sound/oss/pxa-audio.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.21-vpac1/sound/oss/pxa-audio.c 2007-06-25 16:02:38.000000000 +0200 @@ -0,0 +1,856 @@ +/* + * linux/drivers/sound/pxa-audio.c -- audio interface for the Cotula chip + * + * Author: Nicolas Pitre + * Created: Aug 15, 2001 + * Copyright: MontaVista Software Inc. + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "pxa-audio.h" +#define io_remap_page_range(vma, vaddr, paddr, size, prot) \ + remap_pfn_range(vma, vaddr, (paddr) >> PAGE_SHIFT, size, prot) + + +#define AUDIO_NBFRAGS_DEFAULT 8 +#define AUDIO_FRAGSIZE_DEFAULT 8192 + +#define MAX_DMA_SIZE 4096 +#define DMA_DESC_SIZE sizeof(pxa_dma_desc) + + +/* + * This function frees all buffers + */ +#define audio_clear_buf pxa_audio_clear_buf + +void pxa_audio_clear_buf(audio_stream_t * s) +{ + DECLARE_WAITQUEUE(wait, current); + int frag; + + if (!s->buffers) + return; + + /* Ensure DMA isn't running */ + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&s->stop_wq, &wait); + DCSR(s->dma_ch) = DCSR_STOPIRQEN; + schedule(); + remove_wait_queue(&s->stop_wq, &wait); + + /* free DMA buffers */ + for (frag = 0; frag < s->nbfrags; frag++) { + audio_buf_t *b = &s->buffers[frag]; + if (!b->master) + continue; + dma_free_writecombine(NULL, b->master, b->data, b->dma_desc->dsadr); + } + + /* free descriptor ring */ + if (s->buffers->dma_desc) + dma_free_writecombine(NULL, s->nbfrags * s->descs_per_frag * DMA_DESC_SIZE, + s->buffers->dma_desc, s->dma_desc_phys); + + /* free buffer structure array */ + kfree(s->buffers); + s->buffers = NULL; +} + +/* + * This function allocates the DMA descriptor array and buffer data space + * according to the current number of fragments and fragment size. + */ +static int audio_setup_buf(audio_stream_t * s) +{ + pxa_dma_desc *dma_desc; + dma_addr_t dma_desc_phys; + int nb_desc, frag, i, buf_size = 0; + char *dma_buf = NULL; + dma_addr_t dma_buf_phys = 0; + + if (s->buffers) + return -EBUSY; + + /* Our buffer structure array */ + s->buffers = kmalloc(sizeof(audio_buf_t) * s->nbfrags, GFP_KERNEL); + if (!s->buffers) + goto err; + memzero(s->buffers, sizeof(audio_buf_t) * s->nbfrags); + + /* + * Our DMA descriptor array: + * for Each fragment we have one checkpoint descriptor plus one + * descriptor per MAX_DMA_SIZE byte data blocks. + */ + nb_desc = (1 + (s->fragsize + MAX_DMA_SIZE - 1)/MAX_DMA_SIZE) * s->nbfrags; + dma_desc = dma_alloc_writecombine(NULL, nb_desc * DMA_DESC_SIZE, + &dma_desc_phys, GFP_KERNEL); + + if (!dma_desc) + goto err; + s->descs_per_frag = nb_desc / s->nbfrags; + s->buffers->dma_desc = dma_desc; + s->dma_desc_phys = dma_desc_phys; + for (i = 0; i < nb_desc - 1; i++) + dma_desc[i].ddadr = dma_desc_phys + (i + 1) * DMA_DESC_SIZE; + dma_desc[i].ddadr = dma_desc_phys; + + /* Our actual DMA buffers */ + for (frag = 0; frag < s->nbfrags; frag++) { + audio_buf_t *b = &s->buffers[frag]; + + /* + * Let's allocate non-cached memory for DMA buffers. + * We try to allocate all memory at once. + * If this fails (a common reason is memory fragmentation), + * then we'll try allocating smaller buffers. + */ + if (!buf_size) { + buf_size = (s->nbfrags - frag) * s->fragsize; + do { + dma_buf = dma_alloc_writecombine(NULL, buf_size, + &dma_buf_phys, + GFP_KERNEL); + if (!dma_buf) + buf_size -= s->fragsize; + } while (!dma_buf && buf_size); + if (!dma_buf) + goto err; + b->master = buf_size; + memzero(dma_buf, buf_size); + } + + /* + * Set up our checkpoint descriptor. Since the count + * is always zero, we'll abuse the dsadr and dtadr fields + * just in case this one is picked up by the hardware + * while processing SOUND_DSP_GETPTR. + */ + dma_desc->dsadr = dma_buf_phys; + dma_desc->dtadr = dma_buf_phys; + dma_desc->dcmd = DCMD_ENDIRQEN; + if (s->output && !s->mapped) + dma_desc->ddadr |= DDADR_STOP; + b->dma_desc = dma_desc++; + + /* set up the actual data descriptors */ + for (i = 0; (i * MAX_DMA_SIZE) < s->fragsize; i++) { + dma_desc[i].dsadr = (s->output) ? + (dma_buf_phys + i*MAX_DMA_SIZE) : s->dev_addr; + dma_desc[i].dtadr = (s->output) ? + s->dev_addr : (dma_buf_phys + i*MAX_DMA_SIZE); + dma_desc[i].dcmd = s->dcmd | + ((s->fragsize < MAX_DMA_SIZE) ? + s->fragsize : MAX_DMA_SIZE); + } + dma_desc += i; + + /* handle buffer pointers */ + b->data = dma_buf; + dma_buf += s->fragsize; + dma_buf_phys += s->fragsize; + buf_size -= s->fragsize; + } + + s->usr_frag = s->dma_frag = 0; + s->bytecount = 0; + s->fragcount = 0; + sema_init(&s->sem, (s->output) ? s->nbfrags : 0); + return 0; + +err: + printk("pxa-audio: unable to allocate audio memory\n "); + audio_clear_buf(s); + return -ENOMEM; +} + +/* + * Our DMA interrupt handler + */ +static void audio_dma_irq(int ch, void *dev_id) +{ + audio_stream_t *s = dev_id; + u_int dcsr; + + dcsr = DCSR(ch); + DCSR(ch) = dcsr & ~DCSR_STOPIRQEN; + + if (!s->buffers) { + printk("AC97 DMA: wow... received IRQ for channel %d but no buffer exists\n", ch); + return; + } + + if (dcsr & DCSR_BUSERR) + printk("AC97 DMA: bus error interrupt on channel %d\n", ch); + + if (dcsr & DCSR_ENDINTR) { + u_long cur_dma_desc; + u_int cur_dma_frag; + + /* + * Find out which DMA desc is current. Note that DDADR + * points to the next desc, not the current one. + */ + cur_dma_desc = DDADR(ch) - s->dma_desc_phys - DMA_DESC_SIZE; + + /* + * Let the compiler nicely optimize constant divisors into + * multiplications for the common cases which is much faster. + * Common cases: x = 1 + (1 << y) for y = [0..3] + */ + switch (s->descs_per_frag) { + case 2: cur_dma_frag = cur_dma_desc / (2*DMA_DESC_SIZE); break; + case 3: cur_dma_frag = cur_dma_desc / (3*DMA_DESC_SIZE); break; + case 5: cur_dma_frag = cur_dma_desc / (5*DMA_DESC_SIZE); break; + case 9: cur_dma_frag = cur_dma_desc / (9*DMA_DESC_SIZE); break; + default: cur_dma_frag = + cur_dma_desc / (s->descs_per_frag * DMA_DESC_SIZE); + } + + /* Account for possible wrap back of cur_dma_desc above */ + if (cur_dma_frag >= s->nbfrags) + cur_dma_frag = s->nbfrags - 1; + + while (s->dma_frag != cur_dma_frag) { + if (!s->mapped) { + /* + * This fragment is done - set the checkpoint + * descriptor to STOP until it is gets + * processed by the read or write function. + */ + s->buffers[s->dma_frag].dma_desc->ddadr |= DDADR_STOP; + up(&s->sem); + } + if (++s->dma_frag >= s->nbfrags) + s->dma_frag = 0; + + /* Accounting */ + s->bytecount += s->fragsize; + s->fragcount++; + } + + /* ... and for polling processes */ + wake_up(&s->frag_wq); + } + + if ((dcsr & DCSR_STOPIRQEN) && (dcsr & DCSR_STOPSTATE)) + wake_up(&s->stop_wq); +} + +/* + * Validate and sets up buffer fragments, etc. + */ +static int audio_set_fragments(audio_stream_t *s, int val) +{ + if (s->mapped || DCSR(s->dma_ch) & DCSR_RUN) + return -EBUSY; + if (s->buffers) + audio_clear_buf(s); + s->nbfrags = (val >> 16) & 0x7FFF; + val &= 0xffff; + if (val < 5) + val = 5; + if (val > 15) + val = 15; + s->fragsize = 1 << val; + if (s->nbfrags < 2) + s->nbfrags = 2; + if (s->nbfrags * s->fragsize > 256 * 1024) + s->nbfrags = 256 * 1024 / s->fragsize; + if (audio_setup_buf(s)) + return -ENOMEM; + return val|(s->nbfrags << 16); +} + + +/* + * The fops functions + */ + +static int audio_write(struct file *file, const char *buffer, + size_t count, loff_t * ppos) +{ + const char *buffer0 = buffer; + audio_state_t *state = (audio_state_t *)file->private_data; + audio_stream_t *s = state->output_stream; + int chunksize, ret = 0; + + if (s->mapped) + return -ENXIO; + if (!s->buffers && audio_setup_buf(s)) + return -ENOMEM; + + while (count > 0) { + audio_buf_t *b = &s->buffers[s->usr_frag]; + + /* Grab a fragment */ + if (file->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + if (down_trylock(&s->sem)) + break; + } else { + ret = -ERESTARTSYS; + if (down_interruptible(&s->sem)) + break; + } + + /* Feed the current buffer */ + chunksize = s->fragsize - b->offset; + if (chunksize > count) + chunksize = count; + if (copy_from_user(b->data + b->offset, buffer, chunksize)) { + up(&s->sem); + return -EFAULT; + } + + b->offset += chunksize; + buffer += chunksize; + count -= chunksize; + if (b->offset < s->fragsize) { + ret = 0; + up(&s->sem); + break; + } + + /* + * Activate DMA on current buffer. + * We unlock this fragment's checkpoint descriptor and + * kick DMA if it is idle. Using checkpoint descriptors + * allows for control operations without the need for + * stopping the DMA channel if it is already running. + */ + b->offset = 0; + b->dma_desc->ddadr &= ~DDADR_STOP; + if (DCSR(s->dma_ch) & DCSR_STOPSTATE) { + DDADR(s->dma_ch) = b->dma_desc->ddadr; + DCSR(s->dma_ch) = DCSR_RUN; + } + + /* move the index to the next fragment */ + if (++s->usr_frag >= s->nbfrags) + s->usr_frag = 0; + } + + if ((buffer - buffer0)) + ret = buffer - buffer0; + return ret; +} + + +static int audio_read(struct file *file, char *buffer, + size_t count, loff_t * ppos) +{ + char *buffer0 = buffer; + audio_state_t *state = file->private_data; + audio_stream_t *s = state->input_stream; + int chunksize, ret = 0; + + if (s->mapped) + return -ENXIO; + if (!s->buffers && audio_setup_buf(s)) + return -ENOMEM; + + while (count > 0) { + audio_buf_t *b = &s->buffers[s->usr_frag]; + + /* prime DMA */ + if (DCSR(s->dma_ch) & DCSR_STOPSTATE) { + DDADR(s->dma_ch) = + s->buffers[s->dma_frag].dma_desc->ddadr; + DCSR(s->dma_ch) = DCSR_RUN; + } + + /* Wait for a buffer to become full */ + if (file->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + if (down_trylock(&s->sem)) + break; + } else { + ret = -ERESTARTSYS; + if (down_interruptible(&s->sem)) + break; + } + + /* Grab data from current buffer */ + chunksize = s->fragsize - b->offset; + if (chunksize > count) + chunksize = count; + if (copy_to_user(buffer, b->data + b->offset, chunksize)) { + up(&s->sem); + return -EFAULT; + } + b->offset += chunksize; + buffer += chunksize; + count -= chunksize; + if (b->offset < s->fragsize) { + ret = 0; + up(&s->sem); + break; + } + + /* + * Make this buffer available for DMA again. + * We unlock this fragment's checkpoint descriptor and + * kick DMA if it is idle. Using checkpoint descriptors + * allows for control operations without the need for + * stopping the DMA channel if it is already running. + */ + b->offset = 0; + b->dma_desc->ddadr &= ~DDADR_STOP; + + /* move the index to the next fragment */ + if (++s->usr_frag >= s->nbfrags) + s->usr_frag = 0; + } + + if ((buffer - buffer0)) + ret = buffer - buffer0; + return ret; +} + + +static int audio_sync(struct file *file) +{ + audio_state_t *state = file->private_data; + audio_stream_t *s = state->output_stream; + audio_buf_t *b; + pxa_dma_desc *final_desc; + u_long dcmd_save = 0; + DECLARE_WAITQUEUE(wait, current); + + if (!(file->f_mode & FMODE_WRITE) || !s->buffers || s->mapped) + return 0; + + /* + * Send current buffer if it contains data. Be sure to send + * a full sample count. + */ + final_desc = NULL; + b = &s->buffers[s->usr_frag]; + if (b->offset &= ~3) { + final_desc = &b->dma_desc[1 + b->offset/MAX_DMA_SIZE]; + b->offset &= (MAX_DMA_SIZE-1); + dcmd_save = final_desc->dcmd; + final_desc->dcmd = b->offset | s->dcmd | DCMD_ENDIRQEN; + final_desc->ddadr |= DDADR_STOP; + b->offset = 0; + b->dma_desc->ddadr &= ~DDADR_STOP; + if (DCSR(s->dma_ch) & DCSR_STOPSTATE) { + DDADR(s->dma_ch) = b->dma_desc->ddadr; + DCSR(s->dma_ch) = DCSR_RUN; + } + } + + /* Wait for DMA to complete. */ + set_current_state(TASK_INTERRUPTIBLE); +#if 0 + /* + * The STOPSTATE IRQ never seem to occur if DCSR_STOPIRQEN is set + * along wotj DCSR_RUN. Silicon bug? + */ + add_wait_queue(&s->stop_wq, &wait); + DCSR(s->dma_ch) |= DCSR_STOPIRQEN; + schedule(); +#else + add_wait_queue(&s->frag_wq, &wait); + while ((DCSR(s->dma_ch) & DCSR_RUN) && !signal_pending(current)) { + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + } +#endif + set_current_state(TASK_RUNNING); + remove_wait_queue(&s->frag_wq, &wait); + + /* Restore the descriptor chain. */ + if (final_desc) { + final_desc->dcmd = dcmd_save; + final_desc->ddadr &= ~DDADR_STOP; + b->dma_desc->ddadr |= DDADR_STOP; + } + return 0; +} + + +static unsigned int audio_poll(struct file *file, + struct poll_table_struct *wait) +{ + audio_state_t *state = file->private_data; + audio_stream_t *is = state->input_stream; + audio_stream_t *os = state->output_stream; + unsigned int mask = 0; + + if (file->f_mode & FMODE_READ) { + /* Start audio input if not already active */ + if (!is->buffers && audio_setup_buf(is)) + return -ENOMEM; + if (DCSR(is->dma_ch) & DCSR_STOPSTATE) { + DDADR(is->dma_ch) = + is->buffers[is->dma_frag].dma_desc->ddadr; + DCSR(is->dma_ch) = DCSR_RUN; + } + poll_wait(file, &is->frag_wq, wait); + } + + if (file->f_mode & FMODE_WRITE) { + if (!os->buffers && audio_setup_buf(os)) + return -ENOMEM; + poll_wait(file, &os->frag_wq, wait); + } + + if (file->f_mode & FMODE_READ) + if (( is->mapped && is->bytecount > 0) || + (!is->mapped && atomic_read(&is->sem.count) > 0)) + mask |= POLLIN | POLLRDNORM; + + if (file->f_mode & FMODE_WRITE) + if (( os->mapped && os->bytecount > 0) || + (!os->mapped && atomic_read(&os->sem.count) > 0)) + mask |= POLLOUT | POLLWRNORM; + + return mask; +} + + +static int audio_ioctl( struct inode *inode, struct file *file, + uint cmd, ulong arg) +{ + audio_state_t *state = file->private_data; + audio_stream_t *os = state->output_stream; + audio_stream_t *is = state->input_stream; + long val; + + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) + return put_user(os->fragsize, (int *)arg); + else + return put_user(is->fragsize, (int *)arg); + + case SNDCTL_DSP_GETCAPS: + val = DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP; + if (is && os) + val |= DSP_CAP_DUPLEX; + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (long *) arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + int ret = audio_set_fragments(is, val); + if (ret < 0) + return ret; + ret = put_user(ret, (int *)arg); + if (ret) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + int ret = audio_set_fragments(os, val); + if (ret < 0) + return ret; + ret = put_user(ret, (int *)arg); + if (ret) + return ret; + } + return 0; + + case SNDCTL_DSP_SYNC: + return audio_sync(file); + + case SNDCTL_DSP_SETDUPLEX: + return 0; + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (file->f_mode & FMODE_READ && DCSR(is->dma_ch) & DCSR_RUN) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && DCSR(os->dma_ch) & DCSR_RUN) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + if (!is->buffers && audio_setup_buf(is)) + return -ENOMEM; + if (!(DCSR(is->dma_ch) & DCSR_RUN)) { + audio_buf_t *b = &is->buffers[is->dma_frag]; + DDADR(is->dma_ch) = b->dma_desc->ddadr; + DCSR(is->dma_ch) = DCSR_RUN; + } + } else { + DCSR(is->dma_ch) = 0; + } + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!os->buffers && audio_setup_buf(os)) + return -ENOMEM; + if (!(DCSR(os->dma_ch) & DCSR_RUN)) { + audio_buf_t *b = &os->buffers[os->dma_frag]; + DDADR(os->dma_ch) = b->dma_desc->ddadr; + DCSR(os->dma_ch) = DCSR_RUN; + } + } else { + DCSR(os->dma_ch) = 0; + } + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + case SNDCTL_DSP_GETISPACE: + { + audio_buf_info inf = { 0, }; + audio_stream_t *s = (cmd == SNDCTL_DSP_GETOSPACE) ? os : is; + + if ((s == is && !(file->f_mode & FMODE_READ)) || + (s == os && !(file->f_mode & FMODE_WRITE))) + return -EINVAL; + if (!s->buffers && audio_setup_buf(s)) + return -ENOMEM; + inf.bytes = atomic_read(&s->sem.count) * s->fragsize; + inf.bytes -= s->buffers[s->usr_frag].offset; + inf.fragments = inf.bytes / s->fragsize; + inf.fragsize = s->fragsize; + inf.fragstotal = s->nbfrags; + return copy_to_user((void *)arg, &inf, sizeof(inf)); + } + + case SNDCTL_DSP_GETOPTR: + case SNDCTL_DSP_GETIPTR: + { + count_info inf = { 0, }; + audio_stream_t *s = (cmd == SNDCTL_DSP_GETOPTR) ? os : is; + dma_addr_t ptr; + int bytecount, offset; + unsigned long flags; + + if ((s == is && !(file->f_mode & FMODE_READ)) || + (s == os && !(file->f_mode & FMODE_WRITE))) + return -EINVAL; + local_irq_save(flags); + if (DCSR(s->dma_ch) & DCSR_RUN) { + audio_buf_t *b; + ptr = (s->output) ? DSADR(s->dma_ch) : DTADR(s->dma_ch); + b = &s->buffers[s->dma_frag]; + offset = ptr - b->dma_desc->dsadr; + if (offset >= s->fragsize) + offset = s->fragsize - 4; + } else { + offset = 0; + } + inf.ptr = s->dma_frag * s->fragsize + offset; + bytecount = s->bytecount + offset; + s->bytecount = -offset; + inf.blocks = s->fragcount; + s->fragcount = 0; + local_irq_restore(flags); + if (bytecount < 0) + bytecount = 0; + inf.bytes = bytecount; + return copy_to_user((void *)arg, &inf, sizeof(inf)); + } + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_RESET: + if (file->f_mode & FMODE_WRITE) + audio_clear_buf(os); + if (file->f_mode & FMODE_READ) + audio_clear_buf(is); + return 0; + + default: + return state->client_ioctl ? + state->client_ioctl(inode, file, cmd, arg) : -EINVAL; + } + + return 0; +} + + +static int audio_mmap(struct file *file, struct vm_area_struct *vma) +{ + audio_state_t *state = file->private_data; + audio_stream_t *s; + unsigned long size, vma_addr; + int i, ret; + + if (vma->vm_pgoff != 0) + return -EINVAL; + + if (vma->vm_flags & VM_WRITE) { + if (!state->wr_ref) + return -EINVAL;; + s = state->output_stream; + } else if (vma->vm_flags & VM_READ) { + if (!state->rd_ref) + return -EINVAL; + s = state->input_stream; + } else return -EINVAL; + + if (s->mapped) + return -EINVAL; + size = vma->vm_end - vma->vm_start; + if (size != s->fragsize * s->nbfrags) + return -EINVAL; + if (!s->buffers && audio_setup_buf(s)) + return -ENOMEM; + vma_addr = vma->vm_start; + for (i = 0; i < s->nbfrags; i++) { + audio_buf_t *buf = &s->buffers[i]; + if (!buf->master) + continue; + ret = io_remap_page_range(vma, vma->vm_start, buf->dma_desc->dsadr, + buf->master, vma->vm_page_prot); + if (ret) + return ret; + vma_addr += buf->master; + } + for (i = 0; i < s->nbfrags; i++) + s->buffers[i].dma_desc->ddadr &= ~DDADR_STOP; + s->mapped = 1; + return 0; +} + + +static int audio_release(struct inode *inode, struct file *file) +{ + audio_state_t *state = file->private_data; + + down(&state->sem); + + if (file->f_mode & FMODE_READ) { + audio_clear_buf(state->input_stream); + *state->input_stream->drcmr = 0; + pxa_free_dma(state->input_stream->dma_ch); + state->rd_ref = 0; + } + + if (file->f_mode & FMODE_WRITE) { + audio_sync(file); + audio_clear_buf(state->output_stream); + *state->output_stream->drcmr = 0; + pxa_free_dma(state->output_stream->dma_ch); + state->wr_ref = 0; + } + + up(&state->sem); + return 0; +} + + +int pxa_audio_attach(struct inode *inode, struct file *file, + audio_state_t *state) +{ + audio_stream_t *is = state->input_stream; + audio_stream_t *os = state->output_stream; + int err; + + down(&state->sem); + + /* access control */ + err = -ENODEV; + if ((file->f_mode & FMODE_WRITE) && !os) + goto out; + if ((file->f_mode & FMODE_READ) && !is) + goto out; + err = -EBUSY; + if ((file->f_mode & FMODE_WRITE) && state->wr_ref) + goto out; + if ((file->f_mode & FMODE_READ) && state->rd_ref) + goto out; + + /* request DMA channels */ + if (file->f_mode & FMODE_WRITE) { + err = pxa_request_dma(os->name, DMA_PRIO_LOW, + audio_dma_irq, os); + if (err < 0) + goto out; + os->dma_ch = err; + } + if (file->f_mode & FMODE_READ) { + err = pxa_request_dma(is->name, DMA_PRIO_LOW, + audio_dma_irq, is); + if (err < 0) { + if (file->f_mode & FMODE_WRITE) { + *os->drcmr = 0; + pxa_free_dma(os->dma_ch); + } + goto out; + } + is->dma_ch = err; + } + + file->private_data = state; + file->f_op->release = audio_release; + file->f_op->write = audio_write; + file->f_op->read = audio_read; + file->f_op->mmap = audio_mmap; + file->f_op->poll = audio_poll; + file->f_op->ioctl = audio_ioctl; + file->f_op->llseek = no_llseek; + + if ((file->f_mode & FMODE_WRITE)) { + state->wr_ref = 1; + os->fragsize = AUDIO_FRAGSIZE_DEFAULT; + os->nbfrags = AUDIO_NBFRAGS_DEFAULT; + os->output = 1; + os->mapped = 0; + init_waitqueue_head(&os->frag_wq); + init_waitqueue_head(&os->stop_wq); + *os->drcmr = os->dma_ch | DRCMR_MAPVLD; + } + if (file->f_mode & FMODE_READ) { + state->rd_ref = 1; + is->fragsize = AUDIO_FRAGSIZE_DEFAULT; + is->nbfrags = AUDIO_NBFRAGS_DEFAULT; + is->output = 0; + is->mapped = 0; + init_waitqueue_head(&is->frag_wq); + init_waitqueue_head(&is->stop_wq); + *is->drcmr = is->dma_ch | DRCMR_MAPVLD; + } + + err = 0; + +out: + up(&state->sem); + return err; +} + +EXPORT_SYMBOL(pxa_audio_attach); +EXPORT_SYMBOL(pxa_audio_clear_buf); + +MODULE_AUTHOR("Nicolas Pitre, MontaVista Software Inc."); +MODULE_DESCRIPTION("audio interface for the Cotula chip"); +MODULE_LICENSE("GPL"); diff -urN linux-2.6.21/sound/oss/pxa-audio.h linux-2.6.21-vpac1/sound/oss/pxa-audio.h --- linux-2.6.21/sound/oss/pxa-audio.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.21-vpac1/sound/oss/pxa-audio.h 2007-06-25 16:02:38.000000000 +0200 @@ -0,0 +1,54 @@ +/* + * linux/drivers/sound/pxa-audio.h -- audio interface for the Cotula chip + * + * Author: Nicolas Pitre + * Created: Aug 15, 2001 + * Copyright: MontaVista Software Inc. + * + * 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. + */ + +typedef struct { + int offset; /* current buffer position */ + char *data; /* actual buffer */ + pxa_dma_desc *dma_desc; /* pointer to the starting desc */ + int master; /* owner for buffer allocation, contain size whn true */ +} audio_buf_t; + +typedef struct { + char *name; /* stream identifier */ + audio_buf_t *buffers; /* pointer to audio buffer array */ + u_int usr_frag; /* user fragment index */ + u_int dma_frag; /* DMA fragment index */ + u_int fragsize; /* fragment size */ + u_int nbfrags; /* number of fragments */ + u_int dma_ch; /* DMA channel number */ + dma_addr_t dma_desc_phys; /* phys addr of descriptor ring */ + u_int descs_per_frag; /* nbr descriptors per fragment */ + int bytecount; /* nbr of processed bytes */ + int fragcount; /* nbr of fragment transitions */ + struct semaphore sem; /* account for fragment usage */ + wait_queue_head_t frag_wq; /* for poll(), etc. */ + wait_queue_head_t stop_wq; /* for users of DCSR_STOPIRQEN */ + u_long dcmd; /* DMA descriptor dcmd field */ + volatile u32 *drcmr; /* the DMA request channel to use */ + u_long dev_addr; /* device physical address for DMA */ + int mapped:1; /* mmap()'ed buffers */ + int output:1; /* 0 for input, 1 for output */ +} audio_stream_t; + +typedef struct { + audio_stream_t *output_stream; + audio_stream_t *input_stream; + int dev_dsp; /* audio device handle */ + int rd_ref:1; /* open reference for recording */ + int wr_ref:1; /* open reference for playback */ + int (*client_ioctl)(struct inode *, struct file *, uint, ulong); + struct semaphore sem; /* prevent races in attach/release */ +} audio_state_t; + +extern int pxa_audio_attach(struct inode *inode, struct file *file, + audio_state_t *state); +extern void pxa_audio_clear_buf(audio_stream_t *s);