diff -urN linux-2.6.20.4-0rig/arch/arm/configs/at91sam9263ek_defconfig linux-2.6.20.4-atmel/arch/arm/configs/at91sam9263ek_defconfig --- linux-2.6.20.4-0rig/arch/arm/configs/at91sam9263ek_defconfig 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/arm/configs/at91sam9263ek_defconfig 2007-03-24 16:39:15.000000000 +0100 @@ -0,0 +1,1184 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.20-rc1 +# Mon Jan 8 16:06:54 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_ARCH_HAS_ILOG2_U32 is not set +# CONFIG_ARCH_HAS_ILOG2_U64 is not set +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=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_SYSFS_DEPRECATED=y +# CONFIG_RELAY is not set +CONFIG_INITRAMFS_SOURCE="" +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +# CONFIG_EMBEDDED is not set +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_SHMEM=y +CONFIG_SLAB=y +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_RT_MUTEXES=y +# CONFIG_TINY_SHMEM is not set +CONFIG_BASE_SMALL=0 +# CONFIG_SLOB is not set + +# +# Loadable module support +# +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +# CONFIG_MODULE_FORCE_UNLOAD is not set +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_KMOD=y + +# +# Block layer +# +CONFIG_BLOCK=y +# CONFIG_LBD is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_LSF is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +# CONFIG_IOSCHED_DEADLINE is not set +# CONFIG_IOSCHED_CFQ is not set +CONFIG_DEFAULT_AS=y +# CONFIG_DEFAULT_DEADLINE is not set +# CONFIG_DEFAULT_CFQ is not set +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="anticipatory" + +# +# System Type +# +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +CONFIG_ARCH_AT91=y +# 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_IOP13XX 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 is not set +# 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 + +# +# Atmel AT91 System-on-Chip +# +# CONFIG_ARCH_AT91RM9200 is not set +# CONFIG_ARCH_AT91SAM9260 is not set +# CONFIG_ARCH_AT91SAM9261 is not set +CONFIG_ARCH_AT91SAM9263=y + +# +# AT91SAM9263 Board Type +# +CONFIG_MACH_AT91SAM9263EK=y + +# +# AT91 Board Options +# +CONFIG_MTD_AT91_DATAFLASH_CARD=y +# CONFIG_MTD_NAND_AT91_BUSWIDTH_16 is not set + +# +# AT91 Feature Selections +# +# CONFIG_AT91_PROGRAMMABLE_CLOCKS is not set + +# +# Processor Type +# +CONFIG_CPU_32=y +CONFIG_CPU_ARM926T=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5TJ=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_COPY_V4WB=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_ICACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_CPU_DCACHE_WRITETHROUGH is not set +# CONFIG_CPU_CACHE_ROUND_ROBIN is not set + +# +# Bus support +# + +# +# PCCARD (PCMCIA/CardBus) support +# +# CONFIG_PCCARD is not set + +# +# Kernel Features +# +# CONFIG_PREEMPT is not set +# 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_LEDS is not set +CONFIG_ALIGNMENT_TRAP=y + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="mem=64M console=ttyS0,115200 initrd=0x21100000,3145728 root=/dev/ram0 rw" +# CONFIG_XIP_KERNEL is not set + +# +# 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 +# CONFIG_VFP 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=y +# CONFIG_PACKET_MMAP 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=y +# CONFIG_IP_PNP_DHCP is not set +CONFIG_IP_PNP_BOOTP=y +CONFIG_IP_PNP_RARP=y +# 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_TCP_MD5SIG is not set +# CONFIG_IPV6 is not set +# CONFIG_INET6_XFRM_TUNNEL is not set +# CONFIG_INET6_TUNNEL is not set +# CONFIG_NETWORK_SECMARK is not set +# CONFIG_NETFILTER is not set + +# +# 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_DEBUG_DRIVER 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=y +# CONFIG_MTD_AFS_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set + +# +# RAM/ROM/Flash chip drivers +# +# CONFIG_MTD_CFI is not set +# CONFIG_MTD_JEDECPROBE is not set +CONFIG_MTD_MAP_BANK_WIDTH_1=y +CONFIG_MTD_MAP_BANK_WIDTH_2=y +CONFIG_MTD_MAP_BANK_WIDTH_4=y +# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set +# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set +CONFIG_MTD_CFI_I1=y +CONFIG_MTD_CFI_I2=y +# CONFIG_MTD_CFI_I4 is not set +# CONFIG_MTD_CFI_I8 is not set +# CONFIG_MTD_RAM is not set +# CONFIG_MTD_ROM is not set +# CONFIG_MTD_ABSENT is not set +# CONFIG_MTD_OBSOLETE_CHIPS is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +CONFIG_MTD_DATAFLASH=y +# CONFIG_MTD_M25P80 is not set +# 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=y +# CONFIG_MTD_NAND_VERIFY_WRITE is not set +# CONFIG_MTD_NAND_ECC_SMC is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND_DISKONCHIP is not set +CONFIG_MTD_NAND_AT91=y +# CONFIG_MTD_NAND_NANDSIM 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=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_UB is not set +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_COUNT=16 +CONFIG_BLK_DEV_RAM_SIZE=8192 +CONFIG_BLK_DEV_RAM_BLOCKSIZE=1024 +CONFIG_BLK_DEV_INITRD=y +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set + +# +# SCSI device support +# +# CONFIG_RAID_ATTRS is not set +CONFIG_SCSI=y +# CONFIG_SCSI_TGT is not set +# CONFIG_SCSI_NETLINK is not set +CONFIG_SCSI_PROC_FS=y + +# +# 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=y +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set +# CONFIG_SCSI_SCAN_ASYNC 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 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=y +# CONFIG_INPUT_MOUSEDEV_PSAUX is not set +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_TSDEV=y +CONFIG_INPUT_TSDEV_SCREEN_X=240 +CONFIG_INPUT_TSDEV_SCREEN_Y=320 +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set + +# +# Input Device Drivers +# +# CONFIG_INPUT_KEYBOARD is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_ADS7846=y +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_UCB1400 is not set +# CONFIG_INPUT_MISC is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO 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_ATMEL=y +CONFIG_SERIAL_ATMEL_CONSOLE=y +# CONFIG_SERIAL_ATMEL_TTYAT is not set +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +CONFIG_WATCHDOG=y +CONFIG_WATCHDOG_NOWAYOUT=y + +# +# Watchdog Device Drivers +# +# CONFIG_SOFT_WATCHDOG is not set + +# +# USB-based Watchdog Cards +# +# CONFIG_USBPCWATCHDOG is not set +CONFIG_HW_RANDOM=y +# CONFIG_NVRAM is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_RAW_DRIVER is not set + +# +# TPM devices +# +# CONFIG_TCG_TPM is not set + +# +# I2C support +# +CONFIG_I2C=y +CONFIG_I2C_CHARDEV=y + +# +# 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_AT91=y +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_PCA 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=y +# CONFIG_SPI_DEBUG is not set +CONFIG_SPI_MASTER=y + +# +# SPI Master Controller Drivers +# +CONFIG_SPI_ATMEL=y +# CONFIG_SPI_BITBANG is not set + +# +# SPI Protocol Masters +# + +# +# 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 is not set + +# +# LED drivers +# + +# +# LED Triggers +# + +# +# 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 is not set +# CONFIG_FB_CFB_COPYAREA is not set +# CONFIG_FB_CFB_IMAGEBLIT is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set +# CONFIG_FB_S1D13XXX 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 is not set + +# +# Logo configuration +# +# CONFIG_LOGO is not set +# CONFIG_BACKLIGHT_LCD_SUPPORT is not set + +# +# Sound +# +# CONFIG_SOUND is not set + +# +# HID Devices +# +CONFIG_HID=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_MULTITHREAD_PROBE 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_ONETOUCH 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=y + +# +# 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=y +# CONFIG_USB_GADGET_DEBUG_FILES is not set +CONFIG_USB_GADGET_SELECTED=y +# CONFIG_USB_GADGET_NET2280 is not set +# CONFIG_USB_GADGET_PXA2XX is not set +# CONFIG_USB_GADGET_GOKU is not set +# CONFIG_USB_GADGET_LH7A40X is not set +# CONFIG_USB_GADGET_OMAP is not set +CONFIG_USB_GADGET_AT91=y +CONFIG_USB_AT91=y +# CONFIG_USB_GADGET_DUMMY_HCD is not set +# CONFIG_USB_GADGET_DUALSPEED is not set +CONFIG_USB_ZERO=m +# CONFIG_USB_ETH is not set +CONFIG_USB_GADGETFS=m +CONFIG_USB_FILE_STORAGE=m +# CONFIG_USB_FILE_STORAGE_TEST is not set +CONFIG_USB_G_SERIAL=m +# CONFIG_USB_MIDI_GADGET is not set + +# +# MMC/SD Card support +# +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_BLOCK=y +CONFIG_MMC_AT91=m +# CONFIG_MMC_TIFM_SD is not set + +# +# Real Time Clock +# +CONFIG_RTC_LIB=y +# CONFIG_RTC_CLASS is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XIP 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=y +CONFIG_INOTIFY_USER=y +# CONFIG_QUOTA is not set +CONFIG_DNOTIFY=y +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +# CONFIG_MSDOS_FS is not set +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y +# CONFIG_CONFIGFS_FS is not set + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_WRITEBUFFER=y +# CONFIG_JFFS2_SUMMARY 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=y +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 is not set +# CONFIG_NFS_V4 is not set +# CONFIG_NFS_DIRECTIO is not set +# CONFIG_NFSD is not set +CONFIG_ROOT_NFS=y +CONFIG_LOCKD=y +CONFIG_NFS_COMMON=y +CONFIG_SUNRPC=y +# CONFIG_RPCSEC_GSS_KRB5 is not set +# CONFIG_RPCSEC_GSS_SPKM3 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=y +CONFIG_NLS_DEFAULT="iso8859-1" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +CONFIG_NLS_CODEPAGE_850=y +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ASCII is not set +CONFIG_NLS_ISO8859_1=y +# CONFIG_NLS_ISO8859_2 is not set +# CONFIG_NLS_ISO8859_3 is not set +# CONFIG_NLS_ISO8859_4 is not set +# CONFIG_NLS_ISO8859_5 is not set +# CONFIG_NLS_ISO8859_6 is not set +# CONFIG_NLS_ISO8859_7 is not set +# CONFIG_NLS_ISO8859_9 is not set +# CONFIG_NLS_ISO8859_13 is not set +# CONFIG_NLS_ISO8859_14 is not set +# CONFIG_NLS_ISO8859_15 is not set +# CONFIG_NLS_KOI8_R is not set +# CONFIG_NLS_KOI8_U is not set +# CONFIG_NLS_UTF8 is not set + +# +# Distributed Lock Manager +# +# CONFIG_DLM is not set + +# +# Profiling support +# +# CONFIG_PROFILING is not set + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_ENABLE_MUST_CHECK=y +# CONFIG_MAGIC_SYSRQ is not set +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +CONFIG_DEBUG_KERNEL=y +CONFIG_LOG_BUF_SHIFT=14 +CONFIG_DETECT_SOFTLOCKUP=y +# CONFIG_SCHEDSTATS is not set +# CONFIG_DEBUG_SLAB is not set +# CONFIG_DEBUG_RT_MUTEXES is not set +# CONFIG_RT_MUTEX_TESTER is not set +# CONFIG_DEBUG_SPINLOCK is not set +# CONFIG_DEBUG_MUTEXES is not set +# CONFIG_DEBUG_RWSEMS is not set +# CONFIG_DEBUG_SPINLOCK_SLEEP is not set +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +# CONFIG_DEBUG_KOBJECT is not set +CONFIG_DEBUG_BUGVERBOSE=y +# CONFIG_DEBUG_INFO is not set +# CONFIG_DEBUG_VM is not set +# CONFIG_DEBUG_LIST is not set +CONFIG_FRAME_POINTER=y +CONFIG_FORCED_INLINING=y +# CONFIG_RCU_TORTURE_TEST is not set +CONFIG_DEBUG_USER=y +# CONFIG_DEBUG_ERRORS is not set +CONFIG_DEBUG_LL=y +# CONFIG_DEBUG_ICEDCC 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_BITREVERSE=y +# CONFIG_CRC_CCITT is not set +# CONFIG_CRC16 is not set +CONFIG_CRC32=y +# CONFIG_LIBCRC32C is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_PLIST=y +CONFIG_IOMAP_COPY=y diff -urN linux-2.6.20.4-0rig/arch/arm/configs/csb337_defconfig linux-2.6.20.4-atmel/arch/arm/configs/csb337_defconfig --- linux-2.6.20.4-0rig/arch/arm/configs/csb337_defconfig 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/arm/configs/csb337_defconfig 2007-03-24 16:39:15.000000000 +0100 @@ -355,10 +355,12 @@ # Mapping drivers for chip access # # CONFIG_MTD_COMPLEX_MAPPINGS is not set -# CONFIG_MTD_PHYSMAP is not set +CONFIG_MTD_PHYSMAP=y +CONFIG_MTD_PHYSMAP_START=0 +CONFIG_MTD_PHYSMAP_LEN=0 +CONFIG_MTD_PHYSMAP_BANKWIDTH=0 # CONFIG_MTD_ARM_INTEGRATOR is not set # CONFIG_MTD_PLATRAM is not set -CONFIG_MTD_CSB337=y # # Self-contained MTD device drivers diff -urN linux-2.6.20.4-0rig/arch/arm/configs/csb637_defconfig linux-2.6.20.4-atmel/arch/arm/configs/csb637_defconfig --- linux-2.6.20.4-0rig/arch/arm/configs/csb637_defconfig 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/arm/configs/csb637_defconfig 2007-03-24 16:39:15.000000000 +0100 @@ -355,10 +355,12 @@ # Mapping drivers for chip access # # CONFIG_MTD_COMPLEX_MAPPINGS is not set -# CONFIG_MTD_PHYSMAP is not set +CONFIG_MTD_PHYSMAP=y +CONFIG_MTD_PHYSMAP_START=0 +CONFIG_MTD_PHYSMAP_LEN=0 +CONFIG_MTD_PHYSMAP_BANKWIDTH=0 # CONFIG_MTD_ARM_INTEGRATOR is not set # CONFIG_MTD_PLATRAM is not set -CONFIG_MTD_CSB637=y # # Self-contained MTD device drivers diff -urN linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/at91rm9200.c linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/at91rm9200.c --- linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/at91rm9200.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/at91rm9200.c 2007-03-24 16:39:15.000000000 +0100 @@ -117,6 +117,36 @@ .pmc_mask = 1 << AT91RM9200_ID_PIOD, .type = CLK_TYPE_PERIPHERAL, }; +static struct clk tc0_clk = { + .name = "tc0_clk", + .pmc_mask = 1 << AT91RM9200_ID_TC0, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk tc1_clk = { + .name = "tc1_clk", + .pmc_mask = 1 << AT91RM9200_ID_TC1, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk tc2_clk = { + .name = "tc2_clk", + .pmc_mask = 1 << AT91RM9200_ID_TC2, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk tc3_clk = { + .name = "tc3_clk", + .pmc_mask = 1 << AT91RM9200_ID_TC3, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk tc4_clk = { + .name = "tc4_clk", + .pmc_mask = 1 << AT91RM9200_ID_TC4, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk tc5_clk = { + .name = "tc5_clk", + .pmc_mask = 1 << AT91RM9200_ID_TC5, + .type = CLK_TYPE_PERIPHERAL, +}; static struct clk *periph_clocks[] __initdata = { &pioA_clk, @@ -132,7 +162,12 @@ &twi_clk, &spi_clk, // ssc 0 .. ssc2 - // tc0 .. tc5 + &tc0_clk, + &tc1_clk, + &tc2_clk, + &tc3_clk, + &tc4_clk, + &tc5_clk, &ohci_clk, ðer_clk, // irq0 .. irq6 diff -urN linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/at91rm9200_devices.c linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/at91rm9200_devices.c --- linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/at91rm9200_devices.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/at91rm9200_devices.c 2007-03-24 16:39:15.000000000 +0100 @@ -315,7 +315,7 @@ .num_resources = ARRAY_SIZE(mmc_resources), }; -void __init at91_add_device_mmc(struct at91_mmc_data *data) +void __init at91_add_device_mmc(short mmc_id, struct at91_mmc_data *data) { if (!data) return; @@ -361,7 +361,7 @@ platform_device_register(&at91rm9200_mmc_device); } #else -void __init at91_add_device_mmc(struct at91_mmc_data *data) {} +void __init at91_add_device_mmc(short mmc_id, struct at91_mmc_data *data) {} #endif @@ -480,7 +480,18 @@ * SPI * -------------------------------------------------------------------- */ -#if defined(CONFIG_SPI_AT91) || defined(CONFIG_SPI_AT91_MODULE) || defined(CONFIG_AT91_SPI) || defined(CONFIG_AT91_SPI_MODULE) +#if defined(CONFIG_AT91_SPI) || defined(CONFIG_AT91_SPI_MODULE) /* legacy SPI driver */ +#define SPI_DEVNAME "at91_spi" + +#elif defined(CONFIG_SPI_AT91) || defined(CONFIG_SPI_AT91_MODULE) /* SPI bitbanging driver */ +#define SPI_DEVNAME "at91_spi" + +#elif defined(CONFIG_SPI_ATMEL) || defined(CONFIG_SPI_ATMEL_MODULE) /* new SPI driver */ +#define SPI_DEVNAME "atmel_spi" + +#endif + +#ifdef SPI_DEVNAME static u64 spi_dmamask = 0xffffffffUL; static struct resource spi_resources[] = { @@ -497,7 +508,7 @@ }; static struct platform_device at91rm9200_spi_device = { - .name = "at91_spi", + .name = SPI_DEVNAME, .id = 0, .dev = { .dma_mask = &spi_dmamask, @@ -594,6 +605,10 @@ void __init at91_init_leds(u8 cpu_led, u8 timer_led) { + /* Enable GPIO to access the LEDs */ + at91_set_gpio_output(cpu_led, 1); + at91_set_gpio_output(timer_led, 1); + at91_leds_cpu = cpu_led; at91_leds_timer = timer_led; } @@ -602,6 +617,32 @@ #endif +#if defined(CONFIG_NEW_LEDS) + +static struct platform_device at91_leds = { + .name = "at91_leds", + .id = -1, +}; + +void __init at91_gpio_leds(struct at91_gpio_led *leds, int nr) +{ + if (!nr) + return; + + at91_leds.dev.platform_data = leds; + + for ( ; nr; nr--, leds++) { + leds->index = nr; /* first record stores number of leds */ + at91_set_gpio_output(leds->gpio, (leds->flags & 1) == 0); + } + + platform_device_register(&at91_leds); +} +#else +void __init at91_gpio_leds(struct at91_gpio_led *leds, int nr) {} +#endif + + /* -------------------------------------------------------------------- * UART * -------------------------------------------------------------------- */ diff -urN linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/at91rm9200_time.c linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/at91rm9200_time.c --- linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/at91rm9200_time.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/at91rm9200_time.c 2007-03-24 16:39:15.000000000 +0100 @@ -38,7 +38,8 @@ * The ST_CRTR is updated asynchronously to the master clock. It is therefore * necessary to read it twice (with the same value) to ensure accuracy. */ -static inline unsigned long read_CRTR(void) { +static inline unsigned long read_CRTR(void) +{ unsigned long x1, x2; do { diff -urN linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/at91sam9260.c linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/at91sam9260.c --- linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/at91sam9260.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/at91sam9260.c 2007-03-24 16:39:15.000000000 +0100 @@ -14,6 +14,7 @@ #include <asm/mach/arch.h> #include <asm/mach/map.h> +#include <asm/arch/cpu.h> #include <asm/arch/at91sam9260.h> #include <asm/arch/at91_pmc.h> #include <asm/arch/at91_rstc.h> @@ -27,7 +28,11 @@ .pfn = __phys_to_pfn(AT91_BASE_SYS), .length = SZ_16K, .type = MT_DEVICE, - }, { + } +}; + +static struct map_desc at91sam9260_sram_desc[] __initdata = { + { .virtual = AT91_IO_VIRT_BASE - AT91SAM9260_SRAM0_SIZE, .pfn = __phys_to_pfn(AT91SAM9260_SRAM0_BASE), .length = AT91SAM9260_SRAM0_SIZE, @@ -37,7 +42,14 @@ .pfn = __phys_to_pfn(AT91SAM9260_SRAM1_BASE), .length = AT91SAM9260_SRAM1_SIZE, .type = MT_DEVICE, - }, + } +}; + +static struct map_desc at91sam9xe_sram_desc[] __initdata = { + { + .pfn = __phys_to_pfn(AT91SAM9XE_SRAM_BASE), + .type = MT_DEVICE, + } }; /* -------------------------------------------------------------------- @@ -107,13 +119,28 @@ .pmc_mask = 1 << AT91SAM9260_ID_SPI1, .type = CLK_TYPE_PERIPHERAL, }; +static struct clk tc0_clk = { + .name = "tc0_clk", + .pmc_mask = 1 << AT91SAM9260_ID_TC0, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk tc1_clk = { + .name = "tc1_clk", + .pmc_mask = 1 << AT91SAM9260_ID_TC1, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk tc2_clk = { + .name = "tc2_clk", + .pmc_mask = 1 << AT91SAM9260_ID_TC2, + .type = CLK_TYPE_PERIPHERAL, +}; static struct clk ohci_clk = { .name = "ohci_clk", .pmc_mask = 1 << AT91SAM9260_ID_UHP, .type = CLK_TYPE_PERIPHERAL, }; -static struct clk ether_clk = { - .name = "ether_clk", +static struct clk macb_clk = { + .name = "macb_clk", .pmc_mask = 1 << AT91SAM9260_ID_EMAC, .type = CLK_TYPE_PERIPHERAL, }; @@ -137,6 +164,21 @@ .pmc_mask = 1 << AT91SAM9260_ID_US5, .type = CLK_TYPE_PERIPHERAL, }; +static struct clk tc3_clk = { + .name = "tc3_clk", + .pmc_mask = 1 << AT91SAM9260_ID_TC3, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk tc4_clk = { + .name = "tc4_clk", + .pmc_mask = 1 << AT91SAM9260_ID_TC4, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk tc5_clk = { + .name = "tc5_clk", + .pmc_mask = 1 << AT91SAM9260_ID_TC5, + .type = CLK_TYPE_PERIPHERAL, +}; static struct clk *periph_clocks[] __initdata = { &pioA_clk, @@ -152,14 +194,18 @@ &spi0_clk, &spi1_clk, // ssc - // tc0 .. tc2 + &tc0_clk, + &tc1_clk, + &tc2_clk, &ohci_clk, - ðer_clk, + &macb_clk, &isi_clk, &usart3_clk, &usart4_clk, &usart5_clk, - // tc3 .. tc5 + &tc3_clk, + &tc4_clk, + &tc5_clk, // irq0 .. irq2 }; @@ -213,7 +259,7 @@ static void at91sam9260_reset(void) { - at91_sys_write(AT91_RSTC_CR, (0xA5 << 24) | AT91_RSTC_PROCRST | AT91_RSTC_PERRST); + at91_sys_write(AT91_RSTC_CR, AT91_RSTC_KEY | AT91_RSTC_PROCRST | AT91_RSTC_PERRST); } @@ -221,10 +267,36 @@ * AT91SAM9260 processor initialization * -------------------------------------------------------------------- */ +static void __init at91sam9xe_initialize(void) +{ + unsigned long cidr, sram_size; + + cidr = at91_sys_read(AT91_DBGU_CIDR); + + switch (cidr & AT91_CIDR_SRAMSIZ) { + case AT91_CIDR_SRAMSIZ_32K: + sram_size = 2 * SZ_16K; + break; + case AT91_CIDR_SRAMSIZ_16K: + default: + sram_size = SZ_16K; + } + + at91sam9xe_sram_desc->virtual = AT91_IO_VIRT_BASE - sram_size; + at91sam9xe_sram_desc->length = sram_size; + + iotable_init(at91sam9xe_sram_desc, ARRAY_SIZE(at91sam9xe_sram_desc)); +} + void __init at91sam9260_initialize(unsigned long main_clock) { /* Map peripherals */ iotable_init(at91sam9260_io_desc, ARRAY_SIZE(at91sam9260_io_desc)); + + if (cpu_is_at91sam9xe()) + at91sam9xe_initialize(); + else + iotable_init(at91sam9260_sram_desc, ARRAY_SIZE(at91sam9260_sram_desc)); at91_arch_reset = at91sam9260_reset; at91_extern_irq = (1 << AT91SAM9260_ID_IRQ0) | (1 << AT91SAM9260_ID_IRQ1) diff -urN linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/at91sam9260_devices.c linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/at91sam9260_devices.c --- linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/at91sam9260_devices.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/at91sam9260_devices.c 2007-03-24 16:39:15.000000000 +0100 @@ -128,7 +128,7 @@ #if defined(CONFIG_MACB) || defined(CONFIG_MACB_MODULE) static u64 eth_dmamask = 0xffffffffUL; -static struct eth_platform_data eth_data; +static struct at91_eth_data eth_data; static struct resource eth_resources[] = { [0] = { @@ -155,7 +155,7 @@ .num_resources = ARRAY_SIZE(eth_resources), }; -void __init at91_add_device_eth(struct eth_platform_data *data) +void __init at91_add_device_eth(struct at91_eth_data *data) { if (!data) return; @@ -192,7 +192,7 @@ platform_device_register(&at91sam9260_eth_device); } #else -void __init at91_add_device_eth(struct eth_platform_data *data) {} +void __init at91_add_device_eth(struct at91_eth_data *data) {} #endif @@ -229,7 +229,7 @@ .num_resources = ARRAY_SIZE(mmc_resources), }; -void __init at91_add_device_mmc(struct at91_mmc_data *data) +void __init at91_add_device_mmc(short mmc_id, struct at91_mmc_data *data) { if (!data) return; @@ -275,7 +275,7 @@ platform_device_register(&at91sam9260_mmc_device); } #else -void __init at91_add_device_mmc(struct at91_mmc_data *data) {} +void __init at91_add_device_mmc(short mmc_id, struct at91_mmc_data *data) {} #endif @@ -515,6 +515,10 @@ void __init at91_init_leds(u8 cpu_led, u8 timer_led) { + /* Enable GPIO to access the LEDs */ + at91_set_gpio_output(cpu_led, 1); + at91_set_gpio_output(timer_led, 1); + at91_leds_cpu = cpu_led; at91_leds_timer = timer_led; } @@ -523,6 +527,32 @@ #endif +#if defined(CONFIG_NEW_LEDS) + +static struct platform_device at91_leds = { + .name = "at91_leds", + .id = -1, +}; + +void __init at91_gpio_leds(struct at91_gpio_led *leds, int nr) +{ + if (!nr) + return; + + at91_leds.dev.platform_data = leds; + + for ( ; nr; nr--, leds++) { + leds->index = nr; /* first record stores number of leds */ + at91_set_gpio_output(leds->gpio, (leds->flags & 1) == 0); + } + + platform_device_register(&at91_leds); +} +#else +void __init at91_gpio_leds(struct at91_gpio_led *leds, int nr) {} +#endif + + /* -------------------------------------------------------------------- * UART * -------------------------------------------------------------------- */ diff -urN linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/at91sam9261.c linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/at91sam9261.c --- linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/at91sam9261.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/at91sam9261.c 2007-03-24 16:39:15.000000000 +0100 @@ -97,6 +97,21 @@ .pmc_mask = 1 << AT91SAM9261_ID_SPI1, .type = CLK_TYPE_PERIPHERAL, }; +static struct clk tc0_clk = { + .name = "tc0_clk", + .pmc_mask = 1 << AT91SAM9261_ID_TC0, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk tc1_clk = { + .name = "tc1_clk", + .pmc_mask = 1 << AT91SAM9261_ID_TC1, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk tc2_clk = { + .name = "tc2_clk", + .pmc_mask = 1 << AT91SAM9261_ID_TC2, + .type = CLK_TYPE_PERIPHERAL, +}; static struct clk ohci_clk = { .name = "ohci_clk", .pmc_mask = 1 << AT91SAM9261_ID_UHP, @@ -121,7 +136,9 @@ &spi0_clk, &spi1_clk, // ssc 0 .. ssc2 - // tc0 .. tc2 + &tc0_clk, + &tc1_clk, + &tc2_clk, &ohci_clk, &lcdc_clk, // irq0 .. irq2 @@ -208,7 +225,7 @@ static void at91sam9261_reset(void) { - at91_sys_write(AT91_RSTC_CR, (0xA5 << 24) | AT91_RSTC_PROCRST | AT91_RSTC_PERRST); + at91_sys_write(AT91_RSTC_CR, AT91_RSTC_KEY | AT91_RSTC_PROCRST | AT91_RSTC_PERRST); } diff -urN linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/at91sam9261_devices.c linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/at91sam9261_devices.c --- linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/at91sam9261_devices.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/at91sam9261_devices.c 2007-03-24 16:39:15.000000000 +0100 @@ -14,6 +14,9 @@ #include <asm/mach/map.h> #include <linux/platform_device.h> +#include <linux/fb.h> + +#include <video/atmel_lcdc.h> #include <asm/arch/board.h> #include <asm/arch/gpio.h> @@ -159,7 +162,7 @@ .num_resources = ARRAY_SIZE(mmc_resources), }; -void __init at91_add_device_mmc(struct at91_mmc_data *data) +void __init at91_add_device_mmc(short mmc_id, struct at91_mmc_data *data) { if (!data) return; @@ -192,7 +195,7 @@ platform_device_register(&at91sam9261_mmc_device); } #else -void __init at91_add_device_mmc(struct at91_mmc_data *data) {} +void __init at91_add_device_mmc(short mmc_id, struct at91_mmc_data *data) {} #endif @@ -345,7 +348,7 @@ .num_resources = ARRAY_SIZE(spi0_resources), }; -static const unsigned spi0_standard_cs[4] = { AT91_PIN_PA3, AT91_PIN_PA4, AT91_PIN_PA5, AT91_PIN_PA6 }; +static const unsigned spi0_standard_cs[4] = { AT91_PIN_PA3, AT91_PIN_PA4, AT91_PIN_PA28, AT91_PIN_PA6 }; static struct resource spi1_resources[] = { [0] = { @@ -430,9 +433,9 @@ * LCD Controller * -------------------------------------------------------------------- */ -#if defined(CONFIG_FB_AT91) || defined(CONFIG_FB_AT91_MODULE) +#if defined(CONFIG_FB_ATMEL) || defined(CONFIG_FB_ATMEL_MODULE) static u64 lcdc_dmamask = 0xffffffffUL; -static struct at91fb_info lcdc_data; +static struct atmel_lcdfb_info lcdc_data; static struct resource lcdc_resources[] = { [0] = { @@ -455,18 +458,18 @@ }; static struct platform_device at91_lcdc_device = { - .name = "at91-fb", - .id = 0, - .dev = { - .dma_mask = &lcdc_dmamask, - .coherent_dma_mask = 0xffffffff, - .platform_data = &lcdc_data, + .name = "atmel_lcdfb", + .id = 0, + .dev = { + .dma_mask = &lcdc_dmamask, + .coherent_dma_mask = 0xffffffff, + .platform_data = &lcdc_data, }, .resource = lcdc_resources, .num_resources = ARRAY_SIZE(lcdc_resources), }; -void __init at91_add_device_lcdc(struct at91fb_info *data) +void __init at91_add_device_lcdc(struct atmel_lcdfb_info *data) { if (!data) { return; @@ -499,7 +502,7 @@ platform_device_register(&at91_lcdc_device); } #else -void __init at91_add_device_lcdc(struct at91fb_info *data) {} +void __init at91_add_device_lcdc(struct atmel_lcdfb_info *data) {} #endif @@ -513,6 +516,10 @@ void __init at91_init_leds(u8 cpu_led, u8 timer_led) { + /* Enable GPIO to access the LEDs */ + at91_set_gpio_output(cpu_led, 1); + at91_set_gpio_output(timer_led, 1); + at91_leds_cpu = cpu_led; at91_leds_timer = timer_led; } @@ -521,6 +528,32 @@ #endif +#if defined(CONFIG_NEW_LEDS) + +static struct platform_device at91_leds = { + .name = "at91_leds", + .id = -1, +}; + +void __init at91_gpio_leds(struct at91_gpio_led *leds, int nr) +{ + if (!nr) + return; + + at91_leds.dev.platform_data = leds; + + for ( ; nr; nr--, leds++) { + leds->index = nr; /* first record stores number of leds */ + at91_set_gpio_output(leds->gpio, (leds->flags & 1) == 0); + } + + platform_device_register(&at91_leds); +} +#else +void __init at91_gpio_leds(struct at91_gpio_led *leds, int nr) {} +#endif + + /* -------------------------------------------------------------------- * UART * -------------------------------------------------------------------- */ diff -urN linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/at91sam9263.c linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/at91sam9263.c --- linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/at91sam9263.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/at91sam9263.c 2007-03-24 16:39:15.000000000 +0100 @@ -0,0 +1,313 @@ +/* + * arch/arm/mach-at91rm9200/at91sam9263.c + * + * Copyright (C) 2007 Atmel 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. + * + */ + +#include <linux/module.h> + +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/arch/at91sam9263.h> +#include <asm/arch/at91_pmc.h> +#include <asm/arch/at91_rstc.h> + +#include "generic.h" +#include "clock.h" + +static struct map_desc at91sam9263_io_desc[] __initdata = { + { + .virtual = AT91_VA_BASE_SYS, + .pfn = __phys_to_pfn(AT91_BASE_SYS), + .length = SZ_16K, + .type = MT_DEVICE, + }, { + .virtual = AT91_IO_VIRT_BASE - AT91SAM9263_SRAM0_SIZE, + .pfn = __phys_to_pfn(AT91SAM9263_SRAM0_BASE), + .length = AT91SAM9263_SRAM0_SIZE, + .type = MT_DEVICE, + }, { + .virtual = AT91_IO_VIRT_BASE - AT91SAM9263_SRAM0_SIZE - AT91SAM9263_SRAM1_SIZE, + .pfn = __phys_to_pfn(AT91SAM9263_SRAM1_BASE), + .length = AT91SAM9263_SRAM1_SIZE, + .type = MT_DEVICE, + }, +}; + +/* -------------------------------------------------------------------- + * Clocks + * -------------------------------------------------------------------- */ + +/* + * The peripheral clocks. + */ +static struct clk pioA_clk = { + .name = "pioA_clk", + .pmc_mask = 1 << AT91SAM9263_ID_PIOA, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk pioB_clk = { + .name = "pioB_clk", + .pmc_mask = 1 << AT91SAM9263_ID_PIOB, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk pioCDE_clk = { + .name = "pioCDE_clk", + .pmc_mask = 1 << AT91SAM9263_ID_PIOCDE, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk usart0_clk = { + .name = "usart0_clk", + .pmc_mask = 1 << AT91SAM9263_ID_US0, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk usart1_clk = { + .name = "usart1_clk", + .pmc_mask = 1 << AT91SAM9263_ID_US1, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk usart2_clk = { + .name = "usart2_clk", + .pmc_mask = 1 << AT91SAM9263_ID_US2, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk mmc0_clk = { + .name = "mci0_clk", + .pmc_mask = 1 << AT91SAM9263_ID_MCI0, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk mmc1_clk = { + .name = "mci1_clk", + .pmc_mask = 1 << AT91SAM9263_ID_MCI1, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk twi_clk = { + .name = "twi_clk", + .pmc_mask = 1 << AT91SAM9263_ID_TWI, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk spi0_clk = { + .name = "spi0_clk", + .pmc_mask = 1 << AT91SAM9263_ID_SPI0, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk spi1_clk = { + .name = "spi1_clk", + .pmc_mask = 1 << AT91SAM9263_ID_SPI1, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk tcb_clk = { + .name = "tcb_clk", + .pmc_mask = 1 << AT91SAM9263_ID_TCB, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk macb_clk = { + .name = "macb_clk", + .pmc_mask = 1 << AT91SAM9263_ID_EMAC, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk udc_clk = { + .name = "udc_clk", + .pmc_mask = 1 << AT91SAM9263_ID_UDP, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk isi_clk = { + .name = "isi_clk", + .pmc_mask = 1 << AT91SAM9263_ID_ISI, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk lcdc_clk = { + .name = "lcdc_clk", + .pmc_mask = 1 << AT91SAM9263_ID_LCDC, + .type = CLK_TYPE_PERIPHERAL, +}; +static struct clk ohci_clk = { + .name = "ohci_clk", + .pmc_mask = 1 << AT91SAM9263_ID_UHP, + .type = CLK_TYPE_PERIPHERAL, +}; + +static struct clk *periph_clocks[] __initdata = { + &pioA_clk, + &pioB_clk, + &pioCDE_clk, + &usart0_clk, + &usart1_clk, + &usart2_clk, + &mmc0_clk, + &mmc1_clk, + // can + &twi_clk, + &spi0_clk, + &spi1_clk, + // ssc0 .. ssc1 + // ac97 + &tcb_clk, + // pwmc + &macb_clk, + // 2dge + &udc_clk, + &isi_clk, + &lcdc_clk, + // dma + &ohci_clk, + // irq0 .. irq1 +}; + +/* + * The four programmable clocks. + * You must configure pin multiplexing to bring these signals out. + */ +static struct clk pck0 = { + .name = "pck0", + .pmc_mask = AT91_PMC_PCK0, + .type = CLK_TYPE_PROGRAMMABLE, + .id = 0, +}; +static struct clk pck1 = { + .name = "pck1", + .pmc_mask = AT91_PMC_PCK1, + .type = CLK_TYPE_PROGRAMMABLE, + .id = 1, +}; +static struct clk pck2 = { + .name = "pck2", + .pmc_mask = AT91_PMC_PCK2, + .type = CLK_TYPE_PROGRAMMABLE, + .id = 2, +}; +static struct clk pck3 = { + .name = "pck3", + .pmc_mask = AT91_PMC_PCK3, + .type = CLK_TYPE_PROGRAMMABLE, + .id = 3, +}; + +static void __init at91sam9263_register_clocks(void) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(periph_clocks); i++) + clk_register(periph_clocks[i]); + + clk_register(&pck0); + clk_register(&pck1); + clk_register(&pck2); + clk_register(&pck3); +} + +/* -------------------------------------------------------------------- + * GPIO + * -------------------------------------------------------------------- */ + +static struct at91_gpio_bank at91sam9263_gpio[] = { + { + .id = AT91SAM9263_ID_PIOA, + .offset = AT91_PIOA, + .clock = &pioA_clk, + }, { + .id = AT91SAM9263_ID_PIOB, + .offset = AT91_PIOB, + .clock = &pioB_clk, + }, { + .id = AT91SAM9263_ID_PIOCDE, + .offset = AT91_PIOC, + .clock = &pioCDE_clk, + }, { + .id = AT91SAM9263_ID_PIOCDE, + .offset = AT91_PIOD, + .clock = &pioCDE_clk, + }, { + .id = AT91SAM9263_ID_PIOCDE, + .offset = AT91_PIOE, + .clock = &pioCDE_clk, + } +}; + +static void at91sam9263_reset(void) +{ + at91_sys_write(AT91_RSTC_CR, AT91_RSTC_KEY | AT91_RSTC_PROCRST | AT91_RSTC_PERRST); +} + + +/* -------------------------------------------------------------------- + * AT91SAM9263 processor initialization + * -------------------------------------------------------------------- */ + +void __init at91sam9263_initialize(unsigned long main_clock) +{ + /* Map peripherals */ + iotable_init(at91sam9263_io_desc, ARRAY_SIZE(at91sam9263_io_desc)); + + at91_arch_reset = at91sam9263_reset; + at91_extern_irq = (1 << AT91SAM9263_ID_IRQ0) | (1 << AT91SAM9263_ID_IRQ1); + + /* Init clock subsystem */ + at91_clock_init(main_clock); + + /* Register the processor-specific clocks */ + at91sam9263_register_clocks(); + + /* Register GPIO subsystem */ + at91_gpio_init(at91sam9263_gpio, 5); +} + +/* -------------------------------------------------------------------- + * Interrupt initialization + * -------------------------------------------------------------------- */ + +/* + * The default interrupt priority levels (0 = lowest, 7 = highest). + */ +static unsigned int at91sam9263_default_irq_priority[NR_AIC_IRQS] __initdata = { + 7, /* Advanced Interrupt Controller (FIQ) */ + 7, /* System Peripherals */ + 0, /* Parallel IO Controller A */ + 0, /* Parallel IO Controller B */ + 0, /* Parallel IO Controller C, D and E */ + 0, + 0, + 6, /* USART 0 */ + 6, /* USART 1 */ + 6, /* USART 2 */ + 0, /* Multimedia Card Interface 0 */ + 0, /* Multimedia Card Interface 1 */ + 4, /* CAN */ + 0, /* Two-Wire Interface */ + 6, /* Serial Peripheral Interface 0 */ + 6, /* Serial Peripheral Interface 1 */ + 5, /* Serial Synchronous Controller 0 */ + 5, /* Serial Synchronous Controller 1 */ + 6, /* AC97 Controller */ + 0, /* Timer Counter 0, 1 and 2 */ + 0, /* Pulse Width Modulation Controller */ + 3, /* Ethernet */ + 0, + 0, /* 2D Graphic Engine */ + 3, /* USB Device Port */ + 0, /* Image Sensor Interface */ + 3, /* LDC Controller */ + 0, /* DMA Controller */ + 0, + 3, /* USB Host port */ + 0, /* Advanced Interrupt Controller (IRQ0) */ + 0, /* Advanced Interrupt Controller (IRQ1) */ +}; + +void __init at91sam9263_init_interrupts(unsigned int priority[NR_AIC_IRQS]) +{ + if (!priority) + priority = at91sam9263_default_irq_priority; + + /* Initialize the AIC interrupt controller */ + at91_aic_init(priority); + + /* Enable GPIO interrupts */ + at91_gpio_irq_setup(); +} diff -urN linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/at91sam9263_devices.c linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/at91sam9263_devices.c --- linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/at91sam9263_devices.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/at91sam9263_devices.c 2007-03-24 16:39:15.000000000 +0100 @@ -0,0 +1,918 @@ +/* + * arch/arm/mach-at91rm9200/at91sam9263_devices.c + * + * Copyright (C) 2007 Atmel 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. + * + */ +#include <asm/mach/arch.h> +#include <asm/mach/map.h> + +#include <linux/platform_device.h> +#include <linux/fb.h> + +#include <video/atmel_lcdc.h> + +#include <asm/arch/board.h> +#include <asm/arch/gpio.h> +#include <asm/arch/at91sam9263.h> +#include <asm/arch/at91sam926x_mc.h> +#include <asm/arch/at91sam9263_matrix.h> + +#include "generic.h" + +#define SZ_512 0x00000200 +#define SZ_256 0x00000100 +#define SZ_16 0x00000010 + +/* -------------------------------------------------------------------- + * USB Host + * -------------------------------------------------------------------- */ + +#if defined(CONFIG_USB_OHCI_HCD) || defined(CONFIG_USB_OHCI_HCD_MODULE) +static u64 ohci_dmamask = 0xffffffffUL; +static struct at91_usbh_data usbh_data; + +static struct resource usbh_resources[] = { + [0] = { + .start = AT91SAM9263_UHP_BASE, + .end = AT91SAM9263_UHP_BASE + SZ_1M - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = AT91SAM9263_ID_UHP, + .end = AT91SAM9263_ID_UHP, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device at91_usbh_device = { + .name = "at91_ohci", + .id = -1, + .dev = { + .dma_mask = &ohci_dmamask, + .coherent_dma_mask = 0xffffffff, + .platform_data = &usbh_data, + }, + .resource = usbh_resources, + .num_resources = ARRAY_SIZE(usbh_resources), +}; + +void __init at91_add_device_usbh(struct at91_usbh_data *data) +{ + int i; + + if (!data) + return; + + /* Enable VBus control for UHP ports */ + for (i = 0; i < data->ports; i++) { + if (data->vbus_pin[i]) + at91_set_gpio_output(data->vbus_pin[i], 0); + } + + usbh_data = *data; + platform_device_register(&at91_usbh_device); +} +#else +void __init at91_add_device_usbh(struct at91_usbh_data *data) {} +#endif + + +/* -------------------------------------------------------------------- + * USB Device (Gadget) + * -------------------------------------------------------------------- */ + +#ifdef CONFIG_USB_GADGET_AT91 +static struct at91_udc_data udc_data; + +static struct resource udc_resources[] = { + [0] = { + .start = AT91SAM9263_BASE_UDP, + .end = AT91SAM9263_BASE_UDP + SZ_16K - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = AT91SAM9263_ID_UDP, + .end = AT91SAM9263_ID_UDP, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device at91_udc_device = { + .name = "at91_udc", + .id = -1, + .dev = { + .platform_data = &udc_data, + }, + .resource = udc_resources, + .num_resources = ARRAY_SIZE(udc_resources), +}; + +void __init at91_add_device_udc(struct at91_udc_data *data) +{ + if (!data) + return; + + if (data->vbus_pin) { + at91_set_gpio_input(data->vbus_pin, 0); + at91_set_deglitch(data->vbus_pin, 1); + } + + /* Pullup pin is handled internally by USB device peripheral */ + + udc_data = *data; + platform_device_register(&at91_udc_device); +} +#else +void __init at91_add_device_udc(struct at91_udc_data *data) {} +#endif + + +/* -------------------------------------------------------------------- + * Ethernet + * -------------------------------------------------------------------- */ + +#if defined(CONFIG_MACB) || defined(CONFIG_MACB_MODULE) +static u64 eth_dmamask = 0xffffffffUL; +static struct at91_eth_data eth_data; + +static struct resource eth_resources[] = { + [0] = { + .start = AT91SAM9263_BASE_EMAC, + .end = AT91SAM9263_BASE_EMAC + SZ_16K - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = AT91SAM9263_ID_EMAC, + .end = AT91SAM9263_ID_EMAC, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device at91sam9263_eth_device = { + .name = "macb", + .id = -1, + .dev = { + .dma_mask = ð_dmamask, + .coherent_dma_mask = 0xffffffff, + .platform_data = ð_data, + }, + .resource = eth_resources, + .num_resources = ARRAY_SIZE(eth_resources), +}; + +void __init at91_add_device_eth(struct at91_eth_data *data) +{ + if (!data) + return; + + if (data->phy_irq_pin) { + at91_set_gpio_input(data->phy_irq_pin, 0); + at91_set_deglitch(data->phy_irq_pin, 1); + } + + /* Pins used for MII and RMII */ + at91_set_A_periph(AT91_PIN_PE21, 0); /* ETXCK_EREFCK */ + at91_set_B_periph(AT91_PIN_PC25, 0); /* ERXDV */ + at91_set_A_periph(AT91_PIN_PE25, 0); /* ERX0 */ + at91_set_A_periph(AT91_PIN_PE26, 0); /* ERX1 */ + at91_set_A_periph(AT91_PIN_PE27, 0); /* ERXER */ + at91_set_A_periph(AT91_PIN_PE28, 0); /* ETXEN */ + at91_set_A_periph(AT91_PIN_PE23, 0); /* ETX0 */ + at91_set_A_periph(AT91_PIN_PE24, 0); /* ETX1 */ + at91_set_A_periph(AT91_PIN_PE30, 0); /* EMDIO */ + at91_set_A_periph(AT91_PIN_PE29, 0); /* EMDC */ + + if (!data->is_rmii) { + at91_set_A_periph(AT91_PIN_PE22, 0); /* ECRS */ + at91_set_B_periph(AT91_PIN_PC26, 0); /* ECOL */ + at91_set_B_periph(AT91_PIN_PC22, 0); /* ERX2 */ + at91_set_B_periph(AT91_PIN_PC23, 0); /* ERX3 */ + at91_set_B_periph(AT91_PIN_PC27, 0); /* ERXCK */ + at91_set_B_periph(AT91_PIN_PC20, 0); /* ETX2 */ + at91_set_B_periph(AT91_PIN_PC21, 0); /* ETX3 */ + at91_set_B_periph(AT91_PIN_PC24, 0); /* ETXER */ + } + + eth_data = *data; + platform_device_register(&at91sam9263_eth_device); +} +#else +void __init at91_add_device_eth(struct at91_eth_data *data) {} +#endif + + +/* -------------------------------------------------------------------- + * MMC / SD + * -------------------------------------------------------------------- */ + +#if defined(CONFIG_MMC_AT91) || defined(CONFIG_MMC_AT91_MODULE) +static u64 mmc_dmamask = 0xffffffffUL; +static struct at91_mmc_data mmc0_data, mmc1_data; + +static struct resource mmc0_resources[] = { + [0] = { + .start = AT91SAM9263_BASE_MCI0, + .end = AT91SAM9263_BASE_MCI0 + SZ_16K - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = AT91SAM9263_ID_MCI0, + .end = AT91SAM9263_ID_MCI0, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device at91sam9263_mmc0_device = { + .name = "at91_mci", + .id = 0, + .dev = { + .dma_mask = &mmc_dmamask, + .coherent_dma_mask = 0xffffffff, + .platform_data = &mmc0_data, + }, + .resource = mmc0_resources, + .num_resources = ARRAY_SIZE(mmc0_resources), +}; + +static struct resource mmc1_resources[] = { + [0] = { + .start = AT91SAM9263_BASE_MCI1, + .end = AT91SAM9263_BASE_MCI1 + SZ_16K - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = AT91SAM9263_ID_MCI1, + .end = AT91SAM9263_ID_MCI1, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device at91sam9263_mmc1_device = { + .name = "at91_mci", + .id = 1, + .dev = { + .dma_mask = &mmc_dmamask, + .coherent_dma_mask = 0xffffffff, + .platform_data = &mmc1_data, + }, + .resource = mmc1_resources, + .num_resources = ARRAY_SIZE(mmc1_resources), +}; + +void __init at91_add_device_mmc(short mmc_id, struct at91_mmc_data *data) +{ + if (!data) + return; + + /* input/irq */ + if (data->det_pin) { + at91_set_gpio_input(data->det_pin, 1); + at91_set_deglitch(data->det_pin, 1); + } + if (data->wp_pin) + at91_set_gpio_input(data->wp_pin, 1); + if (data->vcc_pin) + at91_set_gpio_output(data->vcc_pin, 0); + + if (mmc_id == 0) { /* MCI0 */ + /* CLK */ + at91_set_A_periph(AT91_PIN_PA12, 0); + + if (data->slot_b) { + /* CMD */ + at91_set_A_periph(AT91_PIN_PA16, 1); + + /* DAT0, maybe DAT1..DAT3 */ + at91_set_A_periph(AT91_PIN_PA17, 1); + if (data->wire4) { + at91_set_A_periph(AT91_PIN_PA18, 1); + at91_set_A_periph(AT91_PIN_PA19, 1); + at91_set_A_periph(AT91_PIN_PA20, 1); + } + } else { + /* CMD */ + at91_set_A_periph(AT91_PIN_PA1, 1); + + /* DAT0, maybe DAT1..DAT3 */ + at91_set_A_periph(AT91_PIN_PA0, 1); + if (data->wire4) { + at91_set_A_periph(AT91_PIN_PA3, 1); + at91_set_A_periph(AT91_PIN_PA4, 1); + at91_set_A_periph(AT91_PIN_PA5, 1); + } + } + + mmc0_data = *data; + at91_clock_associate("mci0_clk", &at91sam9263_mmc1_device.dev, "mci_clk"); + platform_device_register(&at91sam9263_mmc0_device); + } else { /* MCI1 */ + /* CLK */ + at91_set_A_periph(AT91_PIN_PA6, 0); + + if (data->slot_b) { + /* CMD */ + at91_set_A_periph(AT91_PIN_PA21, 1); + + /* DAT0, maybe DAT1..DAT3 */ + at91_set_A_periph(AT91_PIN_PA22, 1); + if (data->wire4) { + at91_set_A_periph(AT91_PIN_PA23, 1); + at91_set_A_periph(AT91_PIN_PA24, 1); + at91_set_A_periph(AT91_PIN_PA25, 1); + } + } else { + /* CMD */ + at91_set_A_periph(AT91_PIN_PA7, 1); + + /* DAT0, maybe DAT1..DAT3 */ + at91_set_A_periph(AT91_PIN_PA8, 1); + if (data->wire4) { + at91_set_A_periph(AT91_PIN_PA9, 1); + at91_set_A_periph(AT91_PIN_PA10, 1); + at91_set_A_periph(AT91_PIN_PA11, 1); + } + } + + mmc1_data = *data; + at91_clock_associate("mci1_clk", &at91sam9263_mmc1_device.dev, "mci_clk"); + platform_device_register(&at91sam9263_mmc1_device); + } +} +#else +void __init at91_add_device_mmc(short mmc_id, struct at91_mmc_data *data) {} +#endif + + +/* -------------------------------------------------------------------- + * NAND / SmartMedia + * -------------------------------------------------------------------- */ + +#if defined(CONFIG_MTD_NAND_AT91) || defined(CONFIG_MTD_NAND_AT91_MODULE) +static struct at91_nand_data nand_data; + +#define NAND_BASE AT91_CHIPSELECT_3 + +static struct resource nand_resources[] = { + { + .start = NAND_BASE, + .end = NAND_BASE + SZ_256M - 1, + .flags = IORESOURCE_MEM, + } +}; + +static struct platform_device at91sam9263_nand_device = { + .name = "at91_nand", + .id = -1, + .dev = { + .platform_data = &nand_data, + }, + .resource = nand_resources, + .num_resources = ARRAY_SIZE(nand_resources), +}; + +void __init at91_add_device_nand(struct at91_nand_data *data) +{ + unsigned long csa, mode; + + if (!data) + return; + + csa = at91_sys_read(AT91_MATRIX_EBI0CSA); + at91_sys_write(AT91_MATRIX_EBI0CSA, csa | AT91_MATRIX_EBI0_CS3A_SMC); + + /* set the bus interface characteristics */ + at91_sys_write(AT91_SMC_SETUP(3), AT91_SMC_NWESETUP_(0) | AT91_SMC_NCS_WRSETUP_(0) + | AT91_SMC_NRDSETUP_(0) | AT91_SMC_NCS_RDSETUP_(0)); + + at91_sys_write(AT91_SMC_PULSE(3), AT91_SMC_NWEPULSE_(3) | AT91_SMC_NCS_WRPULSE_(3) + | AT91_SMC_NRDPULSE_(3) | AT91_SMC_NCS_RDPULSE_(3)); + + at91_sys_write(AT91_SMC_CYCLE(3), AT91_SMC_NWECYCLE_(5) | AT91_SMC_NRDCYCLE_(5)); + + if (data->bus_width_16) + mode = AT91_SMC_DBW_16; + else + mode = AT91_SMC_DBW_8; + at91_sys_write(AT91_SMC_MODE(3), mode | AT91_SMC_READMODE | AT91_SMC_WRITEMODE | AT91_SMC_EXNWMODE_DISABLE | AT91_SMC_TDF_(2)); + + /* enable pin */ + if (data->enable_pin) + at91_set_gpio_output(data->enable_pin, 1); + + /* ready/busy pin */ + if (data->rdy_pin) + at91_set_gpio_input(data->rdy_pin, 1); + + /* card detect pin */ + if (data->det_pin) + at91_set_gpio_input(data->det_pin, 1); + + nand_data = *data; + platform_device_register(&at91sam9263_nand_device); +} +#else +void __init at91_add_device_nand(struct at91_nand_data *data) {} +#endif + + +/* -------------------------------------------------------------------- + * TWI (i2c) + * -------------------------------------------------------------------- */ + +#if defined(CONFIG_I2C_AT91) || defined(CONFIG_I2C_AT91_MODULE) + +static struct resource twi_resources[] = { + [0] = { + .start = AT91SAM9263_BASE_TWI, + .end = AT91SAM9263_BASE_TWI + SZ_16K - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = AT91SAM9263_ID_TWI, + .end = AT91SAM9263_ID_TWI, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device at91sam9263_twi_device = { + .name = "at91_i2c", + .id = -1, + .resource = twi_resources, + .num_resources = ARRAY_SIZE(twi_resources), +}; + +void __init at91_add_device_i2c(void) +{ + /* pins used for TWI interface */ + at91_set_A_periph(AT91_PIN_PB4, 0); /* TWD */ + at91_set_multi_drive(AT91_PIN_PB4, 1); + + at91_set_A_periph(AT91_PIN_PB5, 0); /* TWCK */ + at91_set_multi_drive(AT91_PIN_PB5, 1); + + platform_device_register(&at91sam9263_twi_device); +} +#else +void __init at91_add_device_i2c(void) {} +#endif + + +/* -------------------------------------------------------------------- + * SPI + * -------------------------------------------------------------------- */ + +#if defined(CONFIG_SPI_ATMEL) || defined(CONFIG_SPI_ATMEL_MODULE) +static u64 spi_dmamask = 0xffffffffUL; + +static struct resource spi0_resources[] = { + [0] = { + .start = AT91SAM9263_BASE_SPI0, + .end = AT91SAM9263_BASE_SPI0 + SZ_16K - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = AT91SAM9263_ID_SPI0, + .end = AT91SAM9263_ID_SPI0, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device at91sam9263_spi0_device = { + .name = "atmel_spi", + .id = 0, + .dev = { + .dma_mask = &spi_dmamask, + .coherent_dma_mask = 0xffffffff, + }, + .resource = spi0_resources, + .num_resources = ARRAY_SIZE(spi0_resources), +}; + +static const unsigned spi0_standard_cs[4] = { AT91_PIN_PA5, AT91_PIN_PA3, AT91_PIN_PA4, AT91_PIN_PB11 }; + +static struct resource spi1_resources[] = { + [0] = { + .start = AT91SAM9263_BASE_SPI1, + .end = AT91SAM9263_BASE_SPI1 + SZ_16K - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = AT91SAM9263_ID_SPI1, + .end = AT91SAM9263_ID_SPI1, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device at91sam9263_spi1_device = { + .name = "atmel_spi", + .id = 1, + .dev = { + .dma_mask = &spi_dmamask, + .coherent_dma_mask = 0xffffffff, + }, + .resource = spi1_resources, + .num_resources = ARRAY_SIZE(spi1_resources), +}; + +static const unsigned spi1_standard_cs[4] = { AT91_PIN_PB15, AT91_PIN_PB16, AT91_PIN_PB17, AT91_PIN_PB18 }; + +void __init at91_add_device_spi(struct spi_board_info *devices, int nr_devices) +{ + int i; + unsigned long cs_pin; + short enable_spi0 = 0; + short enable_spi1 = 0; + + /* Choose SPI chip-selects */ + for (i = 0; i < nr_devices; i++) { + if (devices[i].controller_data) + cs_pin = (unsigned long) devices[i].controller_data; + else if (devices[i].bus_num == 0) + cs_pin = spi0_standard_cs[devices[i].chip_select]; + else + cs_pin = spi1_standard_cs[devices[i].chip_select]; + + if (devices[i].bus_num == 0) + enable_spi0 = 1; + else + enable_spi1 = 1; + + /* enable chip-select pin */ + at91_set_gpio_output(cs_pin, 1); + + /* pass chip-select pin to driver */ + devices[i].controller_data = (void *) cs_pin; + } + + spi_register_board_info(devices, nr_devices); + + /* Configure SPI bus(es) */ + if (enable_spi0) { + at91_set_B_periph(AT91_PIN_PA0, 0); /* SPI0_MISO */ + at91_set_B_periph(AT91_PIN_PA1, 0); /* SPI0_MOSI */ + at91_set_B_periph(AT91_PIN_PA2, 0); /* SPI0_SPCK */ + + at91_clock_associate("spi0_clk", &at91sam9263_spi0_device.dev, "spi_clk"); + platform_device_register(&at91sam9263_spi0_device); + } + if (enable_spi1) { + at91_set_A_periph(AT91_PIN_PB12, 0); /* SPI1_MISO */ + at91_set_A_periph(AT91_PIN_PB13, 0); /* SPI1_MOSI */ + at91_set_A_periph(AT91_PIN_PB14, 0); /* SPI1_SPCK */ + + at91_clock_associate("spi1_clk", &at91sam9263_spi1_device.dev, "spi_clk"); + platform_device_register(&at91sam9263_spi1_device); + } +} +#else +void __init at91_add_device_spi(struct spi_board_info *devices, int nr_devices) {} +#endif + + +/* -------------------------------------------------------------------- + * LCD Controller + * -------------------------------------------------------------------- */ + +#if defined(CONFIG_FB_ATMEL) || defined(CONFIG_FB_ATMEL_MODULE) +static u64 lcdc_dmamask = 0xffffffffUL; +static struct atmel_lcdfb_info lcdc_data; + +static struct resource lcdc_resources[] = { + [0] = { + .start = AT91SAM9263_LCDC_BASE, + .end = AT91SAM9263_LCDC_BASE + SZ_4K - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = AT91SAM9263_ID_LCDC, + .end = AT91SAM9263_ID_LCDC, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct platform_device at91_lcdc_device = { + .name = "atmel_lcdfb", + .id = 0, + .dev = { + .dma_mask = &lcdc_dmamask, + .coherent_dma_mask = 0xffffffff, + .platform_data = &lcdc_data, + }, + .resource = lcdc_resources, + .num_resources = ARRAY_SIZE(lcdc_resources), +}; + +void __init at91_add_device_lcdc(struct atmel_lcdfb_info *data) +{ + if (!data) { + return; + } + + /* configure PIO for LCDC */ + at91_set_A_periph(AT91_PIN_PC1, 0); /* LCDHSYNC */ + at91_set_A_periph(AT91_PIN_PC2, 0); /* LCDDOTCK */ + at91_set_A_periph(AT91_PIN_PC3, 0); /* LCDDEN */ + at91_set_B_periph(AT91_PIN_PB9, 0); /* LCDCC */ + at91_set_A_periph(AT91_PIN_PC6, 0); /* LCDD2 */ + at91_set_A_periph(AT91_PIN_PC7, 0); /* LCDD3 */ + at91_set_A_periph(AT91_PIN_PC8, 0); /* LCDD4 */ + at91_set_A_periph(AT91_PIN_PC9, 0); /* LCDD5 */ + at91_set_A_periph(AT91_PIN_PC10, 0); /* LCDD6 */ + at91_set_A_periph(AT91_PIN_PC11, 0); /* LCDD7 */ + at91_set_A_periph(AT91_PIN_PC14, 0); /* LCDD10 */ + at91_set_A_periph(AT91_PIN_PC15, 0); /* LCDD11 */ + at91_set_A_periph(AT91_PIN_PC16, 0); /* LCDD12 */ + at91_set_B_periph(AT91_PIN_PC12, 0); /* LCDD13 */ + at91_set_A_periph(AT91_PIN_PC18, 0); /* LCDD14 */ + at91_set_A_periph(AT91_PIN_PC19, 0); /* LCDD15 */ + at91_set_A_periph(AT91_PIN_PC22, 0); /* LCDD18 */ + at91_set_A_periph(AT91_PIN_PC23, 0); /* LCDD19 */ + at91_set_A_periph(AT91_PIN_PC24, 0); /* LCDD20 */ + at91_set_B_periph(AT91_PIN_PC17, 0); /* LCDD21 */ + at91_set_A_periph(AT91_PIN_PC26, 0); /* LCDD22 */ + at91_set_A_periph(AT91_PIN_PC27, 0); /* LCDD23 */ + + lcdc_data = *data; + platform_device_register(&at91_lcdc_device); +} +#else +void __init at91_add_device_lcdc(struct atmel_lcdfb_info *data) {} +#endif + + +/* -------------------------------------------------------------------- + * LEDs + * -------------------------------------------------------------------- */ + +#if defined(CONFIG_LEDS) +u8 at91_leds_cpu; +u8 at91_leds_timer; + +void __init at91_init_leds(u8 cpu_led, u8 timer_led) +{ + /* Enable GPIO to access the LEDs */ + at91_set_gpio_output(cpu_led, 1); + at91_set_gpio_output(timer_led, 1); + + at91_leds_cpu = cpu_led; + at91_leds_timer = timer_led; +} +#else +void __init at91_init_leds(u8 cpu_led, u8 timer_led) {} +#endif + + +#if defined(CONFIG_NEW_LEDS) + +static struct platform_device at91_leds = { + .name = "at91_leds", + .id = -1, +}; + +void __init at91_gpio_leds(struct at91_gpio_led *leds, int nr) +{ + if (!nr) + return; + + at91_leds.dev.platform_data = leds; + + for ( ; nr; nr--, leds++) { + leds->index = nr; /* first record stores number of leds */ + at91_set_gpio_output(leds->gpio, (leds->flags & 1) == 0); + } + + platform_device_register(&at91_leds); +} +#else +void __init at91_gpio_leds(struct at91_gpio_led *leds, int nr) {} +#endif + + +/* -------------------------------------------------------------------- + * UART + * -------------------------------------------------------------------- */ + +#if defined(CONFIG_SERIAL_ATMEL) + +static struct resource dbgu_resources[] = { + [0] = { + .start = AT91_VA_BASE_SYS + AT91_DBGU, + .end = AT91_VA_BASE_SYS + AT91_DBGU + SZ_512 - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = AT91_ID_SYS, + .end = AT91_ID_SYS, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct atmel_uart_data dbgu_data = { + .use_dma_tx = 0, + .use_dma_rx = 0, /* DBGU not capable of receive DMA */ + .regs = (void __iomem *)(AT91_VA_BASE_SYS + AT91_DBGU), +}; + +static struct platform_device at91sam9263_dbgu_device = { + .name = "atmel_usart", + .id = 0, + .dev = { + .platform_data = &dbgu_data, + .coherent_dma_mask = 0xffffffff, + }, + .resource = dbgu_resources, + .num_resources = ARRAY_SIZE(dbgu_resources), +}; + +static inline void configure_dbgu_pins(void) +{ + at91_set_A_periph(AT91_PIN_PC30, 0); /* DRXD */ + at91_set_A_periph(AT91_PIN_PC31, 1); /* DTXD */ +} + +static struct resource uart0_resources[] = { + [0] = { + .start = AT91SAM9263_BASE_US0, + .end = AT91SAM9263_BASE_US0 + SZ_16K - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = AT91SAM9263_ID_US0, + .end = AT91SAM9263_ID_US0, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct atmel_uart_data uart0_data = { + .use_dma_tx = 1, + .use_dma_rx = 1, +}; + +static struct platform_device at91sam9263_uart0_device = { + .name = "atmel_usart", + .id = 1, + .dev = { + .platform_data = &uart0_data, + .coherent_dma_mask = 0xffffffff, + }, + .resource = uart0_resources, + .num_resources = ARRAY_SIZE(uart0_resources), +}; + +static inline void configure_usart0_pins(void) +{ + at91_set_A_periph(AT91_PIN_PA26, 1); /* TXD0 */ + at91_set_A_periph(AT91_PIN_PA27, 0); /* RXD0 */ + at91_set_A_periph(AT91_PIN_PA28, 0); /* RTS0 */ + at91_set_A_periph(AT91_PIN_PA29, 0); /* CTS0 */ +} + +static struct resource uart1_resources[] = { + [0] = { + .start = AT91SAM9263_BASE_US1, + .end = AT91SAM9263_BASE_US1 + SZ_16K - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = AT91SAM9263_ID_US1, + .end = AT91SAM9263_ID_US1, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct atmel_uart_data uart1_data = { + .use_dma_tx = 1, + .use_dma_rx = 1, +}; + +static struct platform_device at91sam9263_uart1_device = { + .name = "atmel_usart", + .id = 2, + .dev = { + .platform_data = &uart1_data, + .coherent_dma_mask = 0xffffffff, + }, + .resource = uart1_resources, + .num_resources = ARRAY_SIZE(uart1_resources), +}; + +static inline void configure_usart1_pins(void) +{ + at91_set_A_periph(AT91_PIN_PD0, 1); /* TXD1 */ + at91_set_A_periph(AT91_PIN_PD1, 0); /* RXD1 */ + at91_set_B_periph(AT91_PIN_PD7, 0); /* RTS1 */ + at91_set_B_periph(AT91_PIN_PD8, 0); /* CTS1 */ +} + +static struct resource uart2_resources[] = { + [0] = { + .start = AT91SAM9263_BASE_US2, + .end = AT91SAM9263_BASE_US2 + SZ_16K - 1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = AT91SAM9263_ID_US2, + .end = AT91SAM9263_ID_US2, + .flags = IORESOURCE_IRQ, + }, +}; + +static struct atmel_uart_data uart2_data = { + .use_dma_tx = 1, + .use_dma_rx = 1, +}; + +static struct platform_device at91sam9263_uart2_device = { + .name = "atmel_usart", + .id = 3, + .dev = { + .platform_data = &uart2_data, + .coherent_dma_mask = 0xffffffff, + }, + .resource = uart2_resources, + .num_resources = ARRAY_SIZE(uart2_resources), +}; + +static inline void configure_usart2_pins(void) +{ + at91_set_A_periph(AT91_PIN_PD2, 1); /* TXD2 */ + at91_set_A_periph(AT91_PIN_PD3, 0); /* RXD2 */ + at91_set_B_periph(AT91_PIN_PD5, 0); /* RTS2 */ + at91_set_B_periph(AT91_PIN_PD6, 0); /* CTS2 */ +} + +struct platform_device *at91_uarts[ATMEL_MAX_UART]; /* the UARTs to use */ +struct platform_device *atmel_default_console_device; /* the serial console device */ + +void __init at91_init_serial(struct at91_uart_config *config) +{ + int i; + + /* Fill in list of supported UARTs */ + for (i = 0; i < config->nr_tty; i++) { + switch (config->tty_map[i]) { + case 0: + configure_usart0_pins(); + at91_uarts[i] = &at91sam9263_uart0_device; + at91_clock_associate("usart0_clk", &at91sam9263_uart0_device.dev, "usart"); + break; + case 1: + configure_usart1_pins(); + at91_uarts[i] = &at91sam9263_uart1_device; + at91_clock_associate("usart1_clk", &at91sam9263_uart1_device.dev, "usart"); + break; + case 2: + configure_usart2_pins(); + at91_uarts[i] = &at91sam9263_uart2_device; + at91_clock_associate("usart2_clk", &at91sam9263_uart2_device.dev, "usart"); + break; + case 3: + configure_dbgu_pins(); + at91_uarts[i] = &at91sam9263_dbgu_device; + at91_clock_associate("mck", &at91sam9263_dbgu_device.dev, "usart"); + break; + default: + continue; + } + at91_uarts[i]->id = i; /* update ID number to mapped ID */ + } + + /* Set serial console device */ + if (config->console_tty < ATMEL_MAX_UART) + atmel_default_console_device = at91_uarts[config->console_tty]; + if (!atmel_default_console_device) + printk(KERN_INFO "AT91: No default serial console defined.\n"); +} + +void __init at91_add_device_serial(void) +{ + int i; + + for (i = 0; i < ATMEL_MAX_UART; i++) { + if (at91_uarts[i]) + platform_device_register(at91_uarts[i]); + } +} +#else +void __init at91_init_serial(struct at91_uart_config *config) {} +void __init at91_add_device_serial(void) {} +#endif + + +/* -------------------------------------------------------------------- */ +/* + * These devices are always present and don't need any board-specific + * setup. + */ +static int __init at91_add_standard_devices(void) +{ + return 0; +} + +arch_initcall(at91_add_standard_devices); diff -urN linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/at91sam926x_time.c linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/at91sam926x_time.c --- linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/at91sam926x_time.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/at91sam926x_time.c 2007-03-24 16:39:15.000000000 +0100 @@ -30,7 +30,6 @@ * Returns number of microseconds since last timer interrupt. Note that interrupts * will have been disabled by do_gettimeofday() * 'LATCH' is hwclock ticks (see CLOCK_TICK_RATE in timex.h) per jiffy. - * 'tick' is usecs per jiffy (linux/timex.h). */ static unsigned long at91sam926x_gettimeoffset(void) { @@ -39,7 +38,7 @@ elapsed = (PIT_PICNT(t) * LATCH) + PIT_CPIV(t); /* hardware clock cycles */ - return (unsigned long)(elapsed * 1000000) / LATCH; + return (unsigned long)(elapsed * jiffies_to_usecs(1)) / LATCH; } /* diff -urN linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/board-carmeva.c linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/board-carmeva.c --- linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/board-carmeva.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/board-carmeva.c 2007-03-24 16:39:15.000000000 +0100 @@ -134,7 +134,7 @@ /* Compact Flash */ // at91_add_device_cf(&carmeva_cf_data); /* MMC */ - at91_add_device_mmc(&carmeva_mmc_data); + at91_add_device_mmc(0, &carmeva_mmc_data); } MACHINE_START(CARMEVA, "Carmeva") diff -urN linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/board-csb337.c linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/board-csb337.c --- linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/board-csb337.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/board-csb337.c 2007-03-24 16:39:15.000000000 +0100 @@ -24,6 +24,8 @@ #include <linux/module.h> #include <linux/platform_device.h> #include <linux/spi/spi.h> +#include <linux/interrupt.h> +#include <linux/mtd/physmap.h> #include <asm/hardware.h> #include <asm/setup.h> @@ -58,6 +60,7 @@ /* Setup the LEDs */ at91_init_leds(AT91_PIN_PB0, AT91_PIN_PB1); + at91_set_gpio_output(AT91_PIN_PB2, 1); /* third (unused) LED */ /* Setup the serial ports and console */ at91_init_serial(&csb337_uart_config); @@ -112,6 +115,91 @@ }, }; +#define CSB_FLASH_BASE AT91_CHIPSELECT_0 +#define CSB_FLASH_SIZE 0x800000 + +static struct mtd_partition csb_flash_partitions[] = { + { + .name = "uMON flash", + .offset = 0, + .size = MTDPART_SIZ_FULL, + .mask_flags = MTD_WRITEABLE, /* read only */ + } +}; + +static struct physmap_flash_data csb_flash_data = { + .width = 2, + .parts = csb_flash_partitions, + .nr_parts = ARRAY_SIZE(csb_flash_partitions), +}; + +static struct resource csb_flash_resources[] = { + { + .start = CSB_FLASH_BASE, + .end = CSB_FLASH_BASE + CSB_FLASH_SIZE - 1, + .flags = IORESOURCE_MEM, + } +}; + +static struct platform_device csb_flash = { + .name = "physmap-flash", + .id = 0, + .dev = { + .platform_data = &csb_flash_data, + }, + .resource = csb_flash_resources, + .num_resources = ARRAY_SIZE(csb_flash_resources), +}; + +static struct at91_gpio_led csb337_leds[] = { + { + .name = "led0", + .gpio = AT91_PIN_PB0, + .trigger = "heartbeat", + }, + { + .name = "led1", + .gpio = AT91_PIN_PB1, + .trigger = "timer", + }, + { + .name = "led2", + .gpio = AT91_PIN_PB2, + } +}; + +#if defined(CONFIG_CSB300_WAKE_SW0) || defined(CONFIG_CSB300_WAKE_SW1) +static irqreturn_t switch_irq_handler(int irq, void *context) +{ + return IRQ_HANDLED; +} + +static inline void __init switch_irq_setup(int irq, char *name, unsigned long mode) +{ + int res; + + res = request_irq(irq, switch_irq_handler, IRQF_SAMPLE_RANDOM | mode, name, NULL); + if (res == 0) + enable_irq_wake(irq); +} + +static void __init csb300_switches(void) +{ +#ifdef CONFIG_CSB300_WAKE_SW0 + at91_set_A_periph(AT91_PIN_PB29, 1); /* IRQ0 */ + switch_irq_setup(AT91RM9200_ID_IRQ0, "csb300_sw0", IRQF_TRIGGER_FALLING); +#endif +#ifdef CONFIG_CSB300_WAKE_SW1 + at91_set_gpio_input(AT91_PIN_PB28, 1); + at91_set_deglitch(AT91_PIN_PB28, 1); + switch_irq_setup(AT91_PIN_PB28, "csb300_sw1", IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING); +#endif + /* there's also SW2 at PA21, GPIO or TIOA2 */ +} +#else +static void __init csb300_switches(void) {} +#endif + static void __init csb337_board_init(void) { /* Serial */ @@ -130,7 +218,13 @@ /* SPI */ at91_add_device_spi(csb337_spi_devices, ARRAY_SIZE(csb337_spi_devices)); /* MMC */ - at91_add_device_mmc(&csb337_mmc_data); + at91_add_device_mmc(0, &csb337_mmc_data); + /* LEDS */ + at91_gpio_leds(csb337_leds, ARRAY_SIZE(csb337_leds)); + /* NOR flash */ + platform_device_register(&csb_flash); + /* Switches on CSB300 */ + csb300_switches(); } MACHINE_START(CSB337, "Cogent CSB337") diff -urN linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/board-csb637.c linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/board-csb637.c --- linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/board-csb637.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/board-csb637.c 2007-03-24 16:39:15.000000000 +0100 @@ -23,6 +23,7 @@ #include <linux/mm.h> #include <linux/module.h> #include <linux/platform_device.h> +#include <linux/mtd/physmap.h> #include <asm/hardware.h> #include <asm/setup.h> @@ -81,6 +82,42 @@ .pullup_pin = AT91_PIN_PB1, }; +#define CSB_FLASH_BASE AT91_CHIPSELECT_0 +#define CSB_FLASH_SIZE 0x1000000 + +static struct mtd_partition csb_flash_partitions[] = { + { + .name = "uMON flash", + .offset = 0, + .size = MTDPART_SIZ_FULL, + .mask_flags = MTD_WRITEABLE, /* read only */ + } +}; + +static struct physmap_flash_data csb_flash_data = { + .width = 2, + .parts = csb_flash_partitions, + .nr_parts = ARRAY_SIZE(csb_flash_partitions), +}; + +static struct resource csb_flash_resources[] = { + { + .start = CSB_FLASH_BASE, + .end = CSB_FLASH_BASE + CSB_FLASH_SIZE - 1, + .flags = IORESOURCE_MEM, + } +}; + +static struct platform_device csb_flash = { + .name = "physmap-flash", + .id = 0, + .dev = { + .platform_data = &csb_flash_data, + }, + .resource = csb_flash_resources, + .num_resources = ARRAY_SIZE(csb_flash_resources), +}; + static void __init csb637_board_init(void) { /* Serial */ @@ -95,6 +132,8 @@ at91_add_device_i2c(); /* SPI */ at91_add_device_spi(NULL, 0); + /* NOR flash */ + platform_device_register(&csb_flash); } MACHINE_START(CSB637, "Cogent CSB637") diff -urN linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/board-dk.c linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/board-dk.c --- linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/board-dk.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/board-dk.c 2007-03-24 16:39:15.000000000 +0100 @@ -73,6 +73,185 @@ at91rm9200_init_interrupts(NULL); } +#if defined(CONFIG_FB_S1D13XXX) || defined(CONFIG_FB_S1D13XXX_MODULE) +#include <video/s1d13xxxfb.h> +#include <asm/arch/ics1523.h> + +/* EPSON S1D13806 FB */ +#define AT91_FB_REG_BASE 0x30000000L +#define AT91_FB_REG_SIZE 0x200 +#define AT91_FB_VMEM_BASE 0x30200000L +#define AT91_FB_VMEM_SIZE 0x140000L + +static void __init dk_init_video(void) +{ + /* NWAIT Signal */ + at91_set_A_periph(AT91_PIN_PC6, 0); + + /* Initialization of the Static Memory Controller for Chip Select 2 */ + at91_sys_write(AT91_SMC_CSR(2), AT91_SMC_DBW_16 /* 16 bit */ + | AT91_SMC_WSEN | AT91_SMC_NWS_(4) /* wait states */ + | AT91_SMC_TDF_(1) /* float time */ + ); + + at91_ics1523_init(); +} + +/* CRT: (active) 640x480 60Hz (PCLK=CLKI=25.175MHz) + Memory: Embedded SDRAM (MCLK=CLKI3=50.000MHz) (BUSCLK=60.000MHz) */ +static const struct s1d13xxxfb_regval dk_s1dfb_initregs[] = { + {S1DREG_MISC, 0x00}, /* Enable Memory/Register select bit */ + {S1DREG_COM_DISP_MODE, 0x00}, /* disable display output */ + {S1DREG_GPIO_CNF0, 0x00}, + {S1DREG_GPIO_CNF1, 0x00}, + {S1DREG_GPIO_CTL0, 0x08}, + {S1DREG_GPIO_CTL1, 0x00}, + {S1DREG_CLK_CNF, 0x01}, /* no divide, MCLK source is CLKI3 0x02*/ + {S1DREG_LCD_CLK_CNF, 0x00}, + {S1DREG_CRT_CLK_CNF, 0x00}, + {S1DREG_MPLUG_CLK_CNF, 0x00}, + {S1DREG_CPU2MEM_WST_SEL, 0x01}, /* 2*period(MCLK) - 4ns > period(BCLK) */ + {S1DREG_SDRAM_REF_RATE, 0x03}, /* 32768 <= MCLK <= 50000 (MHz) */ + {S1DREG_SDRAM_TC0, 0x00}, /* MCLK source freq (MHz): */ + {S1DREG_SDRAM_TC1, 0x01}, /* 42 <= MCLK <= 50 */ + {S1DREG_MEM_CNF, 0x80}, /* SDRAM Initialization - needed before mem access */ + {S1DREG_PANEL_TYPE, 0x25}, /* std TFT 16bit, 8bit SCP format 2, single passive LCD */ + {S1DREG_MOD_RATE, 0x00}, /* toggle every FPFRAME */ + {S1DREG_LCD_DISP_HWIDTH, 0x4F}, /* 680 pix */ + {S1DREG_LCD_NDISP_HPER, 0x12}, /* 152 pix */ + {S1DREG_TFT_FPLINE_START, 0x01}, /* 13 pix */ + {S1DREG_TFT_FPLINE_PWIDTH, 0x0B}, /* 96 pix */ + {S1DREG_LCD_DISP_VHEIGHT0, 0xDF}, + {S1DREG_LCD_DISP_VHEIGHT1, 0x01}, /* 480 lines */ + {S1DREG_LCD_NDISP_VPER, 0x2C}, /* 44 lines */ + {S1DREG_TFT_FPFRAME_START, 0x0A}, /* 10 lines */ + {S1DREG_TFT_FPFRAME_PWIDTH, 0x01}, /* 2 lines */ + {S1DREG_LCD_DISP_MODE, 0x05}, /* 16 bpp */ + {S1DREG_LCD_MISC, 0x00}, /* dithering enabled, dual panel buffer enabled */ + {S1DREG_LCD_DISP_START0, 0x00}, + {S1DREG_LCD_DISP_START1, 0xC8}, + {S1DREG_LCD_DISP_START2, 0x00}, + {S1DREG_LCD_MEM_OFF0, 0x80}, + {S1DREG_LCD_MEM_OFF1, 0x02}, + {S1DREG_LCD_PIX_PAN, 0x00}, + {S1DREG_LCD_DISP_FIFO_HTC, 0x3B}, + {S1DREG_LCD_DISP_FIFO_LTC, 0x3C}, + {S1DREG_CRT_DISP_HWIDTH, 0x4F}, /* 680 pix */ + {S1DREG_CRT_NDISP_HPER, 0x13}, /* 160 pix */ + {S1DREG_CRT_HRTC_START, 0x01}, /* 13 pix */ + {S1DREG_CRT_HRTC_PWIDTH, 0x0B}, /* 96 pix */ + {S1DREG_CRT_DISP_VHEIGHT0, 0xDF}, + {S1DREG_CRT_DISP_VHEIGHT1, 0x01}, /* 480 lines */ + {S1DREG_CRT_NDISP_VPER, 0x2B}, /* 44 lines */ + {S1DREG_CRT_VRTC_START, 0x09}, /* 10 lines */ + {S1DREG_CRT_VRTC_PWIDTH, 0x01}, /* 2 lines */ + {S1DREG_TV_OUT_CTL, 0x10}, + {S1DREG_CRT_DISP_MODE, 0x05}, /* 16 bpp */ + {S1DREG_CRT_DISP_START0, 0x00}, + {S1DREG_CRT_DISP_START1, 0x00}, + {S1DREG_CRT_DISP_START2, 0x00}, + {S1DREG_CRT_MEM_OFF0, 0x80}, + {S1DREG_CRT_MEM_OFF1, 0x02}, + {S1DREG_CRT_PIX_PAN, 0x00}, + {S1DREG_CRT_DISP_FIFO_HTC, 0x3B}, + {S1DREG_CRT_DISP_FIFO_LTC, 0x3C}, + {S1DREG_LCD_CUR_CTL, 0x00}, /* inactive */ + {S1DREG_LCD_CUR_START, 0x01}, + {S1DREG_LCD_CUR_XPOS0, 0x00}, + {S1DREG_LCD_CUR_XPOS1, 0x00}, + {S1DREG_LCD_CUR_YPOS0, 0x00}, + {S1DREG_LCD_CUR_YPOS1, 0x00}, + {S1DREG_LCD_CUR_BCTL0, 0x00}, + {S1DREG_LCD_CUR_GCTL0, 0x00}, + {S1DREG_LCD_CUR_RCTL0, 0x00}, + {S1DREG_LCD_CUR_BCTL1, 0x1F}, + {S1DREG_LCD_CUR_GCTL1, 0x3F}, + {S1DREG_LCD_CUR_RCTL1, 0x1F}, + {S1DREG_LCD_CUR_FIFO_HTC, 0x00}, + {S1DREG_CRT_CUR_CTL, 0x00}, /* inactive */ + {S1DREG_CRT_CUR_START, 0x01}, + {S1DREG_CRT_CUR_XPOS0, 0x00}, + {S1DREG_CRT_CUR_XPOS1, 0x00}, + {S1DREG_CRT_CUR_YPOS0, 0x00}, + {S1DREG_CRT_CUR_YPOS1, 0x00}, + {S1DREG_CRT_CUR_BCTL0, 0x00}, + {S1DREG_CRT_CUR_GCTL0, 0x00}, + {S1DREG_CRT_CUR_RCTL0, 0x00}, + {S1DREG_CRT_CUR_BCTL1, 0x1F}, + {S1DREG_CRT_CUR_GCTL1, 0x3F}, + {S1DREG_CRT_CUR_RCTL1, 0x1F}, + {S1DREG_CRT_CUR_FIFO_HTC, 0x00}, + {S1DREG_BBLT_CTL0, 0x00}, + {S1DREG_BBLT_CTL0, 0x00}, + {S1DREG_BBLT_CC_EXP, 0x00}, + {S1DREG_BBLT_OP, 0x00}, + {S1DREG_BBLT_SRC_START0, 0x00}, + {S1DREG_BBLT_SRC_START1, 0x00}, + {S1DREG_BBLT_SRC_START2, 0x00}, + {S1DREG_BBLT_DST_START0, 0x00}, + {S1DREG_BBLT_DST_START1, 0x00}, + {S1DREG_BBLT_DST_START2, 0x00}, + {S1DREG_BBLT_MEM_OFF0, 0x00}, + {S1DREG_BBLT_MEM_OFF1, 0x00}, + {S1DREG_BBLT_WIDTH0, 0x00}, + {S1DREG_BBLT_WIDTH1, 0x00}, + {S1DREG_BBLT_HEIGHT0, 0x00}, + {S1DREG_BBLT_HEIGHT1, 0x00}, + {S1DREG_BBLT_BGC0, 0x00}, + {S1DREG_BBLT_BGC1, 0x00}, + {S1DREG_BBLT_FGC0, 0x00}, + {S1DREG_BBLT_FGC1, 0x00}, + {S1DREG_LKUP_MODE, 0x00}, /* LCD LUT r | LCD and CRT/TV LUT w */ + {S1DREG_LKUP_ADDR, 0x00}, + {S1DREG_PS_CNF, 0x00}, /* Power Save disable */ + {S1DREG_PS_STATUS, 0x02}, /* LCD Panel down, mem up */ + {S1DREG_CPU2MEM_WDOGT, 0x00}, + {S1DREG_COM_DISP_MODE, 0x02}, /* enable CRT display output */ +}; + +static struct s1d13xxxfb_pdata dk_s1dfb_pdata = { + .initregs = dk_s1dfb_initregs, + .initregssize = ARRAY_SIZE(dk_s1dfb_initregs), + .platform_init_video = dk_init_video, +}; + +static u64 s1dfb_dmamask = 0xffffffffUL; + +static struct resource dk_s1dfb_resource[] = { + [0] = { /* video mem */ + .name = "s1d13806 memory", + .start = AT91_FB_VMEM_BASE, + .end = AT91_FB_VMEM_BASE + AT91_FB_VMEM_SIZE -1, + .flags = IORESOURCE_MEM, + }, + [1] = { /* video registers */ + .name = "s1d13806 registers", + .start = AT91_FB_REG_BASE, + .end = AT91_FB_REG_BASE + AT91_FB_REG_SIZE -1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device dk_s1dfb_device = { + .name = "s1d13806fb", + .id = -1, + .dev = { + .dma_mask = &s1dfb_dmamask, + .coherent_dma_mask = 0xffffffff, + .platform_data = &dk_s1dfb_pdata, + }, + .resource = dk_s1dfb_resource, + .num_resources = ARRAY_SIZE(dk_s1dfb_resource), +}; + +static void __init dk_add_device_video(void) +{ + platform_device_register(&dk_s1dfb_device); +} +#else +static void __init dk_add_device_video(void) {} +#endif + static struct at91_eth_data __initdata dk_eth_data = { .phy_irq_pin = AT91_PIN_PC4, .is_rmii = 1, @@ -151,7 +330,7 @@ #define DK_FLASH_SIZE 0x200000 static struct physmap_flash_data dk_flash_data = { - .width = 2, + .width = 2, }; static struct resource dk_flash_resource = { @@ -170,6 +349,13 @@ .num_resources = 1, }; +static struct at91_gpio_led dk_leds[] = { + { + .name = "led0", + .gpio = AT91_PIN_PB2, + .trigger = "timer", + } +}; static void __init dk_board_init(void) { @@ -194,14 +380,16 @@ #else /* MMC */ at91_set_gpio_output(AT91_PIN_PB7, 1); /* this MMC card slot can optionally use SPI signaling (CS3). */ - at91_add_device_mmc(&dk_mmc_data); + at91_add_device_mmc(0, &dk_mmc_data); #endif /* NAND */ at91_add_device_nand(&dk_nand_data); /* NOR Flash */ platform_device_register(&dk_flash); + /* LEDs */ + at91_gpio_leds(dk_leds, ARRAY_SIZE(dk_leds)); /* VGA */ -// dk_add_device_video(); + dk_add_device_video(); } MACHINE_START(AT91RM9200DK, "Atmel AT91RM9200-DK") diff -urN linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/board-eb9200.c linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/board-eb9200.c --- linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/board-eb9200.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/board-eb9200.c 2007-03-24 16:39:15.000000000 +0100 @@ -109,7 +109,7 @@ at91_add_device_spi(NULL, 0); /* MMC */ /* only supports 1 or 4 bit interface, not wired through to SPI */ - at91_add_device_mmc(&eb9200_mmc_data); + at91_add_device_mmc(0, &eb9200_mmc_data); } MACHINE_START(ATEB9200, "Embest ATEB9200") diff -urN linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/board-ek.c linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/board-ek.c --- linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/board-ek.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/board-ek.c 2007-03-24 16:39:15.000000000 +0100 @@ -73,6 +73,187 @@ at91rm9200_init_interrupts(NULL); } +#if defined(CONFIG_FB_S1D13XXX) || defined(CONFIG_FB_S1D13XXX_MODULE) +#include <video/s1d13xxxfb.h> +#include <asm/arch/ics1523.h> + +/* EPSON S1D13806 FB */ +#define AT91_FB_REG_BASE 0x40000000L +#define AT91_FB_REG_SIZE 0x200 +#define AT91_FB_VMEM_BASE 0x40200000L +#define AT91_FB_VMEM_SIZE 0x140000L + +static void __init ek_init_video(void) +{ + /* NWAIT Signal */ + at91_set_A_periph(AT91_PIN_PC6, 0); + + /* Initialization of the Static Memory Controller for Chip Select 3 */ + at91_sys_write(AT91_SMC_CSR(3), AT91_SMC_DBW_16 /* 16 bit */ + | AT91_SMC_WSEN | AT91_SMC_NWS_(5) /* wait states */ + | AT91_SMC_TDF_(1) /* float time */ + ); + + at91_ics1523_init(); +} + +/* CRT: (active) 640x480 60Hz (PCLK=CLKI=25.175MHz) + Memory: Embedded SDRAM (MCLK=CLKI3=50.000MHz) (BUSCLK=60.000MHz) */ +static const struct s1d13xxxfb_regval ek_s1dfb_initregs[] = { + {S1DREG_MISC, 0x00}, /* Enable Memory/Register select bit */ + {S1DREG_COM_DISP_MODE, 0x00}, /* disable display output */ + {S1DREG_GPIO_CNF0, 0xFF}, // 0x00 + {S1DREG_GPIO_CNF1, 0x1F}, // 0x08 + {S1DREG_GPIO_CTL0, 0x00}, + {S1DREG_GPIO_CTL1, 0x00}, + {S1DREG_CLK_CNF, 0x01}, /* no divide, MCLK source is CLKI3 0x02*/ + {S1DREG_LCD_CLK_CNF, 0x00}, + {S1DREG_CRT_CLK_CNF, 0x00}, + {S1DREG_MPLUG_CLK_CNF, 0x00}, + {S1DREG_CPU2MEM_WST_SEL, 0x01}, /* 2*period(MCLK) - 4ns > period(BCLK) */ + {S1DREG_SDRAM_REF_RATE, 0x03}, /* 32768 <= MCLK <= 50000 (MHz) */ + {S1DREG_SDRAM_TC0, 0x00}, /* MCLK source freq (MHz): */ + {S1DREG_SDRAM_TC1, 0x01}, /* 42 <= MCLK <= 50 */ + {S1DREG_MEM_CNF, 0x80}, /* SDRAM Initialization - needed before mem access */ + {S1DREG_PANEL_TYPE, 0x25}, /* std TFT 16bit, 8bit SCP format 2, single passive LCD */ + {S1DREG_MOD_RATE, 0x00}, /* toggle every FPFRAME */ + {S1DREG_LCD_DISP_HWIDTH, 0x4F}, /* 680 pix */ + {S1DREG_LCD_NDISP_HPER, 0x12}, /* 152 pix */ + {S1DREG_TFT_FPLINE_START, 0x01}, /* 13 pix */ + {S1DREG_TFT_FPLINE_PWIDTH, 0x0B}, /* 96 pix */ + {S1DREG_LCD_DISP_VHEIGHT0, 0xDF}, + {S1DREG_LCD_DISP_VHEIGHT1, 0x01}, /* 480 lines */ + {S1DREG_LCD_NDISP_VPER, 0x2C}, /* 44 lines */ + {S1DREG_TFT_FPFRAME_START, 0x0A}, /* 10 lines */ + {S1DREG_TFT_FPFRAME_PWIDTH, 0x01}, /* 2 lines */ + {S1DREG_LCD_DISP_MODE, 0x05}, /* 16 bpp */ + {S1DREG_LCD_MISC, 0x00}, /* dithering enabled, dual panel buffer enabled */ + {S1DREG_LCD_DISP_START0, 0x00}, + {S1DREG_LCD_DISP_START1, 0xC8}, + {S1DREG_LCD_DISP_START2, 0x00}, + {S1DREG_LCD_MEM_OFF0, 0x80}, + {S1DREG_LCD_MEM_OFF1, 0x02}, + {S1DREG_LCD_PIX_PAN, 0x00}, + {S1DREG_LCD_DISP_FIFO_HTC, 0x3B}, + {S1DREG_LCD_DISP_FIFO_LTC, 0x3C}, + {S1DREG_CRT_DISP_HWIDTH, 0x4F}, /* 680 pix */ + {S1DREG_CRT_NDISP_HPER, 0x13}, /* 160 pix */ + {S1DREG_CRT_HRTC_START, 0x01}, /* 13 pix */ + {S1DREG_CRT_HRTC_PWIDTH, 0x0B}, /* 96 pix */ + {S1DREG_CRT_DISP_VHEIGHT0, 0xDF}, + {S1DREG_CRT_DISP_VHEIGHT1, 0x01}, /* 480 lines */ + {S1DREG_CRT_NDISP_VPER, 0x2B}, /* 44 lines */ + {S1DREG_CRT_VRTC_START, 0x09}, /* 10 lines */ + {S1DREG_CRT_VRTC_PWIDTH, 0x01}, /* 2 lines */ + {S1DREG_TV_OUT_CTL, 0x10}, + {0x005E, 0x9F}, + {0x005F, 0x00}, + {S1DREG_CRT_DISP_MODE, 0x05}, /* 16 bpp */ + {S1DREG_CRT_DISP_START0, 0x00}, + {S1DREG_CRT_DISP_START1, 0x00}, + {S1DREG_CRT_DISP_START2, 0x00}, + {S1DREG_CRT_MEM_OFF0, 0x80}, + {S1DREG_CRT_MEM_OFF1, 0x02}, + {S1DREG_CRT_PIX_PAN, 0x00}, + {S1DREG_CRT_DISP_FIFO_HTC, 0x3B}, + {S1DREG_CRT_DISP_FIFO_LTC, 0x3C}, + {S1DREG_LCD_CUR_CTL, 0x00}, /* inactive */ + {S1DREG_LCD_CUR_START, 0x01}, + {S1DREG_LCD_CUR_XPOS0, 0x00}, + {S1DREG_LCD_CUR_XPOS1, 0x00}, + {S1DREG_LCD_CUR_YPOS0, 0x00}, + {S1DREG_LCD_CUR_YPOS1, 0x00}, + {S1DREG_LCD_CUR_BCTL0, 0x00}, + {S1DREG_LCD_CUR_GCTL0, 0x00}, + {S1DREG_LCD_CUR_RCTL0, 0x00}, + {S1DREG_LCD_CUR_BCTL1, 0x1F}, + {S1DREG_LCD_CUR_GCTL1, 0x3F}, + {S1DREG_LCD_CUR_RCTL1, 0x1F}, + {S1DREG_LCD_CUR_FIFO_HTC, 0x00}, + {S1DREG_CRT_CUR_CTL, 0x00}, /* inactive */ + {S1DREG_CRT_CUR_START, 0x01}, + {S1DREG_CRT_CUR_XPOS0, 0x00}, + {S1DREG_CRT_CUR_XPOS1, 0x00}, + {S1DREG_CRT_CUR_YPOS0, 0x00}, + {S1DREG_CRT_CUR_YPOS1, 0x00}, + {S1DREG_CRT_CUR_BCTL0, 0x00}, + {S1DREG_CRT_CUR_GCTL0, 0x00}, + {S1DREG_CRT_CUR_RCTL0, 0x00}, + {S1DREG_CRT_CUR_BCTL1, 0x1F}, + {S1DREG_CRT_CUR_GCTL1, 0x3F}, + {S1DREG_CRT_CUR_RCTL1, 0x1F}, + {S1DREG_CRT_CUR_FIFO_HTC, 0x00}, + {S1DREG_BBLT_CTL0, 0x00}, + {S1DREG_BBLT_CTL0, 0x00}, + {S1DREG_BBLT_CC_EXP, 0x00}, + {S1DREG_BBLT_OP, 0x00}, + {S1DREG_BBLT_SRC_START0, 0x00}, + {S1DREG_BBLT_SRC_START1, 0x00}, + {S1DREG_BBLT_SRC_START2, 0x00}, + {S1DREG_BBLT_DST_START0, 0x00}, + {S1DREG_BBLT_DST_START1, 0x00}, + {S1DREG_BBLT_DST_START2, 0x00}, + {S1DREG_BBLT_MEM_OFF0, 0x00}, + {S1DREG_BBLT_MEM_OFF1, 0x00}, + {S1DREG_BBLT_WIDTH0, 0x00}, + {S1DREG_BBLT_WIDTH1, 0x00}, + {S1DREG_BBLT_HEIGHT0, 0x00}, + {S1DREG_BBLT_HEIGHT1, 0x00}, + {S1DREG_BBLT_BGC0, 0x00}, + {S1DREG_BBLT_BGC1, 0x00}, + {S1DREG_BBLT_FGC0, 0x00}, + {S1DREG_BBLT_FGC1, 0x00}, + {S1DREG_LKUP_MODE, 0x00}, /* LCD LUT r | LCD and CRT/TV LUT w */ + {S1DREG_LKUP_ADDR, 0x00}, + {S1DREG_PS_CNF, 0x10}, /* Power Save disable */ + {S1DREG_PS_STATUS, 0x02}, /* LCD Panel down, mem up */ + {S1DREG_CPU2MEM_WDOGT, 0x00}, + {S1DREG_COM_DISP_MODE, 0x02}, /* enable CRT display output */ +}; + +static struct s1d13xxxfb_pdata ek_s1dfb_pdata = { + .initregs = ek_s1dfb_initregs, + .initregssize = ARRAY_SIZE(ek_s1dfb_initregs), + .platform_init_video = ek_init_video, +}; + +static u64 s1dfb_dmamask = 0xffffffffUL; + +static struct resource ek_s1dfb_resource[] = { + [0] = { /* video mem */ + .name = "s1d13806 memory", + .start = AT91_FB_VMEM_BASE, + .end = AT91_FB_VMEM_BASE + AT91_FB_VMEM_SIZE -1, + .flags = IORESOURCE_MEM, + }, + [1] = { /* video registers */ + .name = "s1d13806 registers", + .start = AT91_FB_REG_BASE, + .end = AT91_FB_REG_BASE + AT91_FB_REG_SIZE -1, + .flags = IORESOURCE_MEM, + }, +}; + +static struct platform_device ek_s1dfb_device = { + .name = "s1d13806fb", + .id = -1, + .dev = { + .dma_mask = &s1dfb_dmamask, + .coherent_dma_mask = 0xffffffff, + .platform_data = &ek_s1dfb_pdata, + }, + .resource = ek_s1dfb_resource, + .num_resources = ARRAY_SIZE(ek_s1dfb_resource), +}; + +static void __init ek_add_device_video(void) +{ + platform_device_register(&ek_s1dfb_device); +} +#else +static void __init ek_add_device_video(void) {} +#endif + static struct at91_eth_data __initdata ek_eth_data = { .phy_irq_pin = AT91_PIN_PC4, .is_rmii = 1, @@ -113,7 +294,7 @@ #define EK_FLASH_SIZE 0x200000 static struct physmap_flash_data ek_flash_data = { - .width = 2, + .width = 2, }; static struct resource ek_flash_resource = { @@ -132,6 +313,18 @@ .num_resources = 1, }; +static struct at91_gpio_led ek_leds[] = { + { + .name = "led0", + .gpio = AT91_PIN_PB1, + .trigger = "heartbeat", + }, + { + .name = "led1", + .gpio = AT91_PIN_PB2, + .trigger = "timer", + } +}; static void __init ek_board_init(void) { @@ -154,12 +347,14 @@ #else /* MMC */ at91_set_gpio_output(AT91_PIN_PB22, 1); /* this MMC card slot can optionally use SPI signaling (CS3). */ - at91_add_device_mmc(&ek_mmc_data); + at91_add_device_mmc(0, &ek_mmc_data); #endif /* NOR Flash */ platform_device_register(&ek_flash); + /* LEDs */ + at91_gpio_leds(ek_leds, ARRAY_SIZE(ek_leds)); /* VGA */ -// ek_add_device_video(); + ek_add_device_video(); } MACHINE_START(AT91RM9200EK, "Atmel AT91RM9200-EK") diff -urN linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/board-kb9202.c linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/board-kb9202.c --- linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/board-kb9202.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/board-kb9202.c 2007-03-24 16:39:15.000000000 +0100 @@ -122,7 +122,7 @@ /* USB Device */ at91_add_device_udc(&kb9202_udc_data); /* MMC */ - at91_add_device_mmc(&kb9202_mmc_data); + at91_add_device_mmc(0, &kb9202_mmc_data); /* I2C */ at91_add_device_i2c(); /* SPI */ diff -urN linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/board-sam9260ek.c linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/board-sam9260ek.c --- linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/board-sam9260ek.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/board-sam9260ek.c 2007-03-24 16:39:15.000000000 +0100 @@ -1,5 +1,5 @@ /* - * linux/arch/arm/mach-at91rm9200/board-ek.c + * linux/arch/arm/mach-at91rm9200/board-sam9260ek.c * * Copyright (C) 2005 SAN People * Copyright (C) 2006 Atmel @@ -118,7 +118,7 @@ /* * MACB Ethernet device */ -static struct __initdata eth_platform_data ek_macb_data = { +static struct __initdata at91_eth_data ek_macb_data = { .phy_irq_pin = AT91_PIN_PA7, .is_rmii = 1, }; @@ -187,7 +187,7 @@ /* Ethernet */ at91_add_device_eth(&ek_macb_data); /* MMC */ - at91_add_device_mmc(&ek_mmc_data); + at91_add_device_mmc(0, &ek_mmc_data); } MACHINE_START(AT91SAM9260EK, "Atmel AT91SAM9260-EK") diff -urN linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/board-sam9261ek.c linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/board-sam9261ek.c --- linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/board-sam9261ek.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/board-sam9261ek.c 2007-03-24 16:39:15.000000000 +0100 @@ -1,5 +1,5 @@ /* - * linux/arch/arm/mach-at91rm9200/board-ek.c + * linux/arch/arm/mach-at91rm9200/board-sam9261ek.c * * Copyright (C) 2005 SAN People * Copyright (C) 2006 Atmel @@ -26,6 +26,11 @@ #include <linux/platform_device.h> #include <linux/spi/spi.h> #include <linux/dm9000.h> +#include <linux/spi/ads7846.h> +#include <linux/fb.h> +#include <linux/clk.h> + +#include <video/atmel_lcdc.h> #include <asm/hardware.h> #include <asm/setup.h> @@ -148,6 +153,42 @@ /* + * Touchscreen ads7843 + */ +#if defined(CONFIG_TOUCHSCREEN_ADS7846) || defined(CONFIG_TOUCHSCREEN_ADS7846_MODULE) + +int ads7843_pendown_state(void) +{ + return !at91_get_gpio_value(AT91_PIN_PC2); +} + +static struct ads7846_platform_data ads_info = { + .model = 7843, + .x_min = 150, .x_max = 3830, + .y_min = 190, .y_max = 3830, + .vref_delay_usecs = 100, + .x_plate_ohms = 450, + .y_plate_ohms = 250, + .pressure_max = 15000, + .debounce_max = 1, + .debounce_rep = 0, + .debounce_tol = (~0), + .get_pendown_state = ads7843_pendown_state, +}; + +void __init at91_add_device_ts(void) +{ + /* Configure Interrupt 1 as external IRQ, with pullup */ + at91_set_B_periph(AT91_PIN_PC2, 1); /* IRQ0 */ + /* ts busy */ + at91_set_gpio_input(AT91_PIN_PA11, 1); +} +#else +void __init at91_add_device_ts(void) {} +#endif + + +/* * MCI (SD/MMC) */ static struct at91_mmc_data __initdata ek_mmc_data = { @@ -204,6 +245,17 @@ .max_speed_hz = 15 * 1000 * 1000, .bus_num = 0, }, +#if defined(CONFIG_TOUCHSCREEN_ADS7846) || defined(CONFIG_TOUCHSCREEN_ADS7846_MODULE) + { + .modalias = "ads7846", + .chip_select = 2, + .max_speed_hz = 125000 /* max sample rate at 3V */ + * 26, /* command + data + overhead */ + .bus_num = 0, + .platform_data = &ads_info, + .irq = AT91SAM9261_ID_IRQ0, + }, +#endif #if defined(CONFIG_MTD_AT91_DATAFLASH_CARD) { /* DataFlash card - jumper (J12) configurable to CS3 or CS0 */ .modalias = "mtd_dataflash", @@ -222,6 +274,64 @@ }; +/* + * LCD Controller + */ +#if defined(CONFIG_FB_ATMEL) || defined(CONFIG_FB_ATMEL_MODULE) +static struct fb_videomode at91_tft_vga_modes[] = { + { + .name = "TX09D50VM1CCA @ 60", + .refresh = 60, + .xres = 240, .yres = 320, + .pixclock = KHZ2PICOS(4965), + + .left_margin = 1, .right_margin = 33, + .upper_margin = 1, .lower_margin = 0, + .hsync_len = 5, .vsync_len = 1, + + .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + .vmode = FB_VMODE_NONINTERLACED, + }, +}; + +static struct fb_monspecs at91fb_default_monspecs = { + .manufacturer = "HIT", + .monitor = "TX09D50VM1CCA", + + .modedb = at91_tft_vga_modes, + .modedb_len = ARRAY_SIZE(at91_tft_vga_modes), + .hfmin = 15000, + .hfmax = 64000, + .vfmin = 50, + .vfmax = 150, +}; + +/* Driver defaults */ +#define AT91SAM9261_DEFAULT_LCDCON2 (ATMEL_LCDC_MEMOR_LITTLE \ + | ATMEL_LCDC_DISTYPE_TFT \ + | ATMEL_LCDC_CLKMOD_ALWAYSACTIVE) + +#define AT91SAM9261_DEFAULT_FB_FLAGS (FBINFO_DEFAULT \ + | FBINFO_PARTIAL_PAN_OK \ + | FBINFO_HWACCEL_XPAN \ + | FBINFO_HWACCEL_YPAN) + +/* Driver datas */ +static struct atmel_lcdfb_info __initdata ek_lcdc_data = { + .default_bpp = 16, + .default_dmacon = ATMEL_LCDC_DMAEN, + .default_lcdcon2 = AT91SAM9261_DEFAULT_LCDCON2, + .default_monspecs = &at91fb_default_monspecs, + .default_flags = AT91SAM9261_DEFAULT_FB_FLAGS, + .power_control_pin = AT91_PIN_PA12, + .guard_time = 1, +}; + +#else +static struct atmel_lcdfb_info __initdata ek_lcdc_data; +#endif + + static void __init ek_board_init(void) { /* Serial */ @@ -243,8 +353,12 @@ at91_add_device_spi(ek_spi_devices, ARRAY_SIZE(ek_spi_devices)); #else /* MMC */ - at91_add_device_mmc(&ek_mmc_data); + at91_add_device_mmc(0, &ek_mmc_data); #endif + /* LCD Controller */ + at91_add_device_lcdc(&ek_lcdc_data); + /* Touchscreen */ + at91_add_device_ts(); } MACHINE_START(AT91SAM9261EK, "Atmel AT91SAM9261-EK") diff -urN linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/board-sam9263ek.c linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/board-sam9263ek.c --- linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/board-sam9263ek.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/board-sam9263ek.c 2007-03-24 16:39:15.000000000 +0100 @@ -0,0 +1,297 @@ +/* + * linux/arch/arm/mach-at91rm9200/board-sam9263ek.c + * + * Copyright (C) 2005 SAN People + * Copyright (C) 2007 Atmel 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 + */ + +#include <linux/types.h> +#include <linux/init.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/spi/spi.h> +#include <linux/spi/ads7846.h> +#include <linux/fb.h> +#include <linux/clk.h> + +#include <video/atmel_lcdc.h> + +#include <asm/hardware.h> +#include <asm/setup.h> +#include <asm/mach-types.h> +#include <asm/irq.h> + +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/mach/irq.h> + +#include <asm/arch/board.h> +#include <asm/arch/gpio.h> +#include <asm/arch/at91sam926x_mc.h> + +#include "generic.h" + + +/* + * Serial port configuration. + * 0 .. 2 = USART0 .. USART2 + * 3 = DBGU + */ +static struct at91_uart_config __initdata ek_uart_config = { + .console_tty = 0, /* ttyS0 */ + .nr_tty = 2, + .tty_map = { 3, 0, -1, -1, } /* ttyS0, ..., ttyS3 */ +}; + +static void __init ek_map_io(void) +{ + /* Initialize processor: 16.367 MHz crystal */ + at91sam9263_initialize(16367660); + + /* Setup the serial ports and console */ + at91_init_serial(&ek_uart_config); +} + +static void __init ek_init_irq(void) +{ + at91sam9263_init_interrupts(NULL); +} + + +/* + * USB Host port + */ +static struct at91_usbh_data __initdata ek_usbh_data = { + .ports = 2, + .vbus_pin = { AT91_PIN_PA24, AT91_PIN_PA21 }, +}; + +/* + * USB Device port + */ +static struct at91_udc_data __initdata ek_udc_data = { + .vbus_pin = AT91_PIN_PA25, + .pullup_pin = 0, /* pull-up driven by UDC */ +}; + +/* + * Touchscreen ads7843 + */ +#if defined(CONFIG_TOUCHSCREEN_ADS7846) || defined(CONFIG_TOUCHSCREEN_ADS7846_MODULE) + +int ads7843_pendown_state(void) +{ + return !at91_get_gpio_value(AT91_PIN_PA15); +} + +static struct ads7846_platform_data ads_info = { + .model = 7843, + .x_min = 150, .x_max = 3830, + .y_min = 190, .y_max = 3830, + .vref_delay_usecs = 100, + .x_plate_ohms = 450, + .y_plate_ohms = 250, + .pressure_max = 15000, + .debounce_max = 1, + .debounce_rep = 0, + .debounce_tol = (~0), + .get_pendown_state = ads7843_pendown_state, +}; + +void __init at91_add_device_ts(void) +{ + /* Configure Interrupt 1 as external IRQ, with pullup */ + at91_set_B_periph(AT91_PIN_PA15, 1); /* IRQ1 */ + /* ts busy */ + at91_set_gpio_input(AT91_PIN_PA31, 1); +} +#else +void __init at91_add_device_ts(void) {} +#endif + +/* + * SPI devices. + */ +static struct spi_board_info ek_spi_devices[] = { +#if defined(CONFIG_MTD_AT91_DATAFLASH_CARD) + { /* DataFlash card */ + .modalias = "mtd_dataflash", + .chip_select = 0, + .max_speed_hz = 15 * 1000 * 1000, + .bus_num = 0, + }, +#endif +#if defined(CONFIG_TOUCHSCREEN_ADS7846) || defined(CONFIG_TOUCHSCREEN_ADS7846_MODULE) + { + .modalias = "ads7846", + .chip_select = 3, + .max_speed_hz = 125000 /* max sample rate at 3V */ + * 26, /* command + data + overhead */ + .bus_num = 0, + .platform_data = &ads_info, + .irq = AT91SAM9263_ID_IRQ1, + }, +#endif +}; + +/* + * MACB device + */ +static struct __initdata at91_eth_data ek_macb_data = { + .is_rmii = 1, +}; + +/* + * MCI (SD/MMC) + */ +static struct at91_mmc_data __initdata ek_mmc_data = { + .wire4 = 1, + .det_pin = AT91_PIN_PE18, + .wp_pin = AT91_PIN_PE19, +// .vcc_pin = ... not connected +}; + + +/* + * NAND flash + */ +static struct mtd_partition __initdata ek_nand_partition[] = { + { + .name = "Partition 1", + .offset = 0, + .size = 64 * 1024 * 1024, + }, + { + .name = "Partition 2", + .offset = 64 * 1024 * 1024, + .size = MTDPART_SIZ_FULL, + }, +}; + +static struct mtd_partition *nand_partitions(int size, int *num_partitions) +{ + *num_partitions = ARRAY_SIZE(ek_nand_partition); + return ek_nand_partition; +} + +static struct at91_nand_data __initdata ek_nand_data = { + .ale = 21, + .cle = 22, +// .det_pin = ... not connected + .rdy_pin = AT91_PIN_PA22, + .enable_pin = AT91_PIN_PD15, + .partition_info = nand_partitions, +#if defined(CONFIG_MTD_NAND_AT91_BUSWIDTH_16) + .bus_width_16 = 1, +#else + .bus_width_16 = 0, +#endif +}; + +/* + * LCD Controller + */ +#if defined(CONFIG_FB_ATMEL) || defined(CONFIG_FB_ATMEL_MODULE) +static struct fb_videomode at91_tft_vga_modes[] = { + { + .name = "TX09D50VM1CCA @ 60", + .refresh = 60, + .xres = 240, .yres = 320, + .pixclock = KHZ2PICOS(4965), + + .left_margin = 1, .right_margin = 33, + .upper_margin = 1, .lower_margin = 0, + .hsync_len = 5, .vsync_len = 1, + + .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, + .vmode = FB_VMODE_NONINTERLACED, + }, +}; + +static struct fb_monspecs at91fb_default_monspecs = { + .manufacturer = "HIT", + .monitor = "TX09D70VM1CCA", + + .modedb = at91_tft_vga_modes, + .modedb_len = ARRAY_SIZE(at91_tft_vga_modes), + .hfmin = 15000, + .hfmax = 64000, + .vfmin = 50, + .vfmax = 150, +}; + +/* Driver defaults */ +#define AT91SAM9261_DEFAULT_LCDCON2 (ATMEL_LCDC_MEMOR_LITTLE \ + | ATMEL_LCDC_DISTYPE_TFT \ + | ATMEL_LCDC_CLKMOD_ALWAYSACTIVE) + +#define AT91SAM9261_DEFAULT_FB_FLAGS (FBINFO_DEFAULT \ + | FBINFO_PARTIAL_PAN_OK \ + | FBINFO_HWACCEL_XPAN \ + | FBINFO_HWACCEL_YPAN) + +/* Driver datas */ +static struct atmel_lcdfb_info __initdata ek_lcdc_data = { + .default_bpp = 16, + .default_dmacon = ATMEL_LCDC_DMAEN, + .default_lcdcon2 = AT91SAM9261_DEFAULT_LCDCON2, + .default_monspecs = &at91fb_default_monspecs, + .default_flags = AT91SAM9261_DEFAULT_FB_FLAGS, + .power_control_pin = AT91_PIN_PD12, + .guard_time = 1, +}; + +#else +static struct atmel_lcdfb_info __initdata ek_lcdc_data; +#endif + + +static void __init ek_board_init(void) +{ + /* Serial */ + at91_add_device_serial(); + /* USB Host */ + at91_add_device_usbh(&ek_usbh_data); + /* USB Device */ + at91_add_device_udc(&ek_udc_data); + /* select SPI clk for Dataflash card slot */ + at91_set_gpio_output(AT91_PIN_PE20, 1); + /* SPI */ + at91_add_device_spi(ek_spi_devices, ARRAY_SIZE(ek_spi_devices)); + /* MMC */ + at91_add_device_mmc(1, &ek_mmc_data); + /* MACB */ + at91_add_device_eth(&ek_macb_data); + /* NAND */ + at91_add_device_nand(&ek_nand_data); + /* LCD Controller */ + at91_add_device_lcdc(&ek_lcdc_data); + /* Touchscreen */ + at91_add_device_ts(); +} + +MACHINE_START(AT91SAM9263EK, "Atmel AT91SAM9263-EK") + /* Maintainer: Atmel */ + .phys_io = AT91_BASE_SYS, + .io_pg_offst = (AT91_VA_BASE_SYS >> 18) & 0xfffc, + .boot_params = AT91_SDRAM_BASE + 0x100, + .timer = &at91sam926x_timer, + .map_io = ek_map_io, + .init_irq = ek_init_irq, + .init_machine = ek_board_init, +MACHINE_END diff -urN linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/clock.c linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/clock.c --- linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/clock.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/clock.c 2007-03-24 16:39:15.000000000 +0100 @@ -375,6 +375,7 @@ seq_printf(s, "PLLB = %8x\n", at91_sys_read(AT91_CKGR_PLLBR)); seq_printf(s, "MCKR = %8x\n", at91_sys_read(AT91_PMC_MCKR)); +#warning "Hard-coded PCK" for (i = 0; i < 4; i++) seq_printf(s, "PCK%d = %8x\n", i, at91_sys_read(AT91_PMC_PCKR(i))); seq_printf(s, "SR = %8x\n", sr = at91_sys_read(AT91_PMC_SR)); @@ -525,27 +526,6 @@ return 0; } -/* - * Several unused clocks may be active. Turn them off. - */ -static void __init at91_periphclk_reset(void) -{ - unsigned long reg; - struct clk *clk; - - reg = at91_sys_read(AT91_PMC_PCSR); - - list_for_each_entry(clk, &clocks, node) { - if (clk->mode != pmc_periph_mode) - continue; - - if (clk->users > 0) - reg &= ~clk->pmc_mask; - } - - at91_sys_write(AT91_PMC_PCDR, reg); -} - static struct clk *const standard_pmc_clocks[] __initdata = { /* four primary clocks */ &clk32k, @@ -586,7 +566,7 @@ pr_info("Clocks: PLLA overclocked, %ld MHz\n", plla.rate_hz / 1000000); /* - * USB clock init: choose 48 MHz PLLB value, turn all clocks off, + * USB clock init: choose 48 MHz PLLB value, * disable 48MHz clock during usb peripheral suspend. * * REVISIT: assumes MCK doesn't derive from PLLB! @@ -596,16 +576,10 @@ if (cpu_is_at91rm9200()) { uhpck.pmc_mask = AT91RM9200_PMC_UHP; udpck.pmc_mask = AT91RM9200_PMC_UDP; - at91_sys_write(AT91_PMC_SCDR, AT91RM9200_PMC_UHP | AT91RM9200_PMC_UDP); at91_sys_write(AT91_PMC_SCER, AT91RM9200_PMC_MCKUDP); - } else if (cpu_is_at91sam9260()) { + } else if (cpu_is_at91sam9260() || cpu_is_at91sam9261() || cpu_is_at91sam9263()) { uhpck.pmc_mask = AT91SAM926x_PMC_UHP; udpck.pmc_mask = AT91SAM926x_PMC_UDP; - at91_sys_write(AT91_PMC_SCDR, AT91SAM926x_PMC_UHP | AT91SAM926x_PMC_UDP); - } else if (cpu_is_at91sam9261()) { - uhpck.pmc_mask = (AT91SAM926x_PMC_UHP | AT91_PMC_HCK0); - udpck.pmc_mask = AT91SAM926x_PMC_UDP; - at91_sys_write(AT91_PMC_SCDR, AT91SAM926x_PMC_UHP | AT91_PMC_HCK0 | AT91SAM926x_PMC_UDP); } at91_sys_write(AT91_CKGR_PLLBR, 0); @@ -634,11 +608,34 @@ (unsigned) main_clock / 1000000, ((unsigned) main_clock % 1000000) / 1000); - /* disable all programmable clocks */ - at91_sys_write(AT91_PMC_SCDR, AT91_PMC_PCK0 | AT91_PMC_PCK1 | AT91_PMC_PCK2 | AT91_PMC_PCK3); + return 0; +} + +/* + * Several unused clocks may be active. Turn them off. + */ +static int __init at91_clock_reset(void) +{ + unsigned long pcdr = 0; + unsigned long scdr = 0; + struct clk *clk; + + list_for_each_entry(clk, &clocks, node) { + if (clk->users > 0) + continue; + + if (clk->mode == pmc_periph_mode) + pcdr |= clk->pmc_mask; + + if (clk->mode == pmc_sys_mode) + scdr |= clk->pmc_mask; + + pr_debug("Clocks: disable unused %s\n", clk->name); + } - /* disable all other unused peripheral clocks */ - at91_periphclk_reset(); + at91_sys_write(AT91_PMC_PCDR, pcdr); + at91_sys_write(AT91_PMC_SCDR, scdr); return 0; } +late_initcall(at91_clock_reset); diff -urN linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/generic.h linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/generic.h --- linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/generic.h 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/generic.h 2007-03-24 16:39:15.000000000 +0100 @@ -12,11 +12,13 @@ extern void __init at91rm9200_initialize(unsigned long main_clock, unsigned short banks); extern void __init at91sam9260_initialize(unsigned long main_clock); extern void __init at91sam9261_initialize(unsigned long main_clock); +extern void __init at91sam9263_initialize(unsigned long main_clock); /* Interrupts */ extern void __init at91rm9200_init_interrupts(unsigned int priority[]); extern void __init at91sam9260_init_interrupts(unsigned int priority[]); extern void __init at91sam9261_init_interrupts(unsigned int priority[]); +extern void __init at91sam9263_init_interrupts(unsigned int priority[]); extern void __init at91_aic_init(unsigned int priority[]); /* Timer */ diff -urN linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/ics1523.c linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/ics1523.c --- linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/ics1523.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/ics1523.c 2007-03-24 16:39:15.000000000 +0100 @@ -0,0 +1,207 @@ +/* + * arch/arm/mach-at91rm9200/ics1523.c + * + * Copyright (C) 2003 ATMEL Rousset + * + * 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 <asm/hardware.h> +#include <asm/io.h> + +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/init.h> + +#include <asm/arch/ics1523.h> +#include <asm/arch/at91_twi.h> +#include <asm/arch/gpio.h> + +/* TWI Errors */ +#define AT91_TWI_ERROR (AT91_TWI_NACK | AT91_TWI_UNRE | AT91_TWI_OVRE) + + +static void __iomem *twi_base; + +#define at91_twi_read(reg) __raw_readl(twi_base + (reg)) +#define at91_twi_write(reg, val) __raw_writel((val), twi_base + (reg)) + + +/* ----------------------------------------------------------------------------- + * Initialization of TWI CLOCK + * ----------------------------------------------------------------------------- */ + +static void at91_ics1523_SetTwiClock(unsigned int mck_khz) +{ + int sclock; + + /* Here, CKDIV = 1 and CHDIV = CLDIV ==> CLDIV = CHDIV = 1/4*((Fmclk/FTWI) -6) */ + sclock = (10*mck_khz / ICS_TRANSFER_RATE); + if (sclock % 10 >= 5) + sclock = (sclock /10) - 5; + else + sclock = (sclock /10)- 6; + sclock = (sclock + (4 - sclock %4)) >> 2; /* div 4 */ + + at91_twi_write(AT91_TWI_CWGR, 0x00010000 | sclock | (sclock << 8)); +} + +/* ----------------------------------------------------------------------------- + * Read a byte with TWI Interface from the Clock Generator ICS1523 + * ----------------------------------------------------------------------------- */ + +static int at91_ics1523_ReadByte(unsigned char reg_address, unsigned char *data_in) +{ + int Status, nb_trial; + + at91_twi_write(AT91_TWI_MMR, AT91_TWI_MREAD | AT91_TWI_IADRSZ_1 | ((ICS_ADDR << 16) & AT91_TWI_DADR)); + at91_twi_write(AT91_TWI_IADR, reg_address); + at91_twi_write(AT91_TWI_CR, AT91_TWI_START | AT91_TWI_STOP); + + /* Program temporizing period (300us) */ + udelay(300); + + /* Wait TXcomplete ... */ + nb_trial = 0; + Status = at91_twi_read(AT91_TWI_SR); + while (!(Status & AT91_TWI_TXCOMP) && (nb_trial < 10)) { + nb_trial++; + Status = at91_twi_read(AT91_TWI_SR); + } + + if (Status & AT91_TWI_TXCOMP) { + *data_in = (unsigned char) at91_twi_read(AT91_TWI_RHR); + return ICS1523_ACCESS_OK; + } + else + return ICS1523_ACCESS_ERROR; +} + +/* ----------------------------------------------------------------------------- + * Write a byte with TWI Interface to the Clock Generator ICS1523 + * ----------------------------------------------------------------------------- */ + +static int at91_ics1523_WriteByte(unsigned char reg_address, unsigned char data_out) +{ + int Status, nb_trial; + + at91_twi_write(AT91_TWI_MMR, AT91_TWI_IADRSZ_1 | ((ICS_ADDR << 16) & AT91_TWI_DADR)); + at91_twi_write(AT91_TWI_IADR, reg_address); + at91_twi_write(AT91_TWI_THR, data_out); + at91_twi_write(AT91_TWI_CR, AT91_TWI_START | AT91_TWI_STOP); + + /* Program temporizing period (300us) */ + udelay(300); + + nb_trial = 0; + Status = at91_twi_read(AT91_TWI_SR); + while (!(Status & AT91_TWI_TXCOMP) && (nb_trial < 10)) { + nb_trial++; + if (Status & AT91_TWI_ERROR) { + /* If Underrun OR NACK - Start again */ + at91_twi_write(AT91_TWI_CR, AT91_TWI_START | AT91_TWI_STOP); + + /* Program temporizing period (300us) */ + udelay(300); + } + Status = at91_twi_read(AT91_TWI_SR); + }; + + if (Status & AT91_TWI_TXCOMP) + return ICS1523_ACCESS_OK; + else + return ICS1523_ACCESS_ERROR; +} + +/* ----------------------------------------------------------------------------- + * Initialization of the Clock Generator ICS1523 + * ----------------------------------------------------------------------------- */ + +int at91_ics1523_init(void) +{ + int nb_trial; + int ack = ICS1523_ACCESS_OK; + unsigned int status = 0xffffffff; + struct clk *twi_clk; + + /* Map in TWI peripheral */ + twi_base = ioremap(AT91RM9200_BASE_TWI, SZ_16K); + if (!twi_base) + return -ENOMEM; + + /* pins used for TWI interface */ + at91_set_A_periph(AT91_PIN_PA25, 0); /* TWD */ + at91_set_multi_drive(AT91_PIN_PA25, 1); + at91_set_A_periph(AT91_PIN_PA26, 0); /* TWCK */ + at91_set_multi_drive(AT91_PIN_PA26, 1); + + /* Enable the TWI clock */ + twi_clk = clk_get(NULL, "twi_clk"); + if (IS_ERR(twi_clk)) + return ICS1523_ACCESS_ERROR; + clk_enable(twi_clk); + + /* Disable interrupts */ + at91_twi_write(AT91_TWI_IDR, -1); + + /* Reset peripheral */ + at91_twi_write(AT91_TWI_CR, AT91_TWI_SWRST); + + /* Set Master mode */ + at91_twi_write(AT91_TWI_CR, AT91_TWI_MSEN); + + /* Set TWI Clock Waveform Generator Register */ + at91_ics1523_SetTwiClock(60000); /* MCK in KHz = 60000 KHz */ + + /* ICS1523 Initialisation */ + ack |= at91_ics1523_WriteByte ((unsigned char) ICS_ICR, (unsigned char) 0); + ack |= at91_ics1523_WriteByte ((unsigned char) ICS_OE, (unsigned char) (ICS_OEF | ICS_OET2 | ICS_OETCK)); + ack |= at91_ics1523_WriteByte ((unsigned char) ICS_OD, (unsigned char) (ICS_INSEL | 0x7F)); + ack |= at91_ics1523_WriteByte ((unsigned char) ICS_DPAO, (unsigned char) 0); + + nb_trial = 0; + do { + nb_trial++; + ack |= at91_ics1523_WriteByte ((unsigned char) ICS_ICR, (unsigned char) (ICS_ENDLS | ICS_ENPLS | ICS_PDEN /*| ICS_FUNCSEL*/)); + ack |= at91_ics1523_WriteByte ((unsigned char) ICS_LCR, (unsigned char) (ICS_PSD | ICS_PFD)); + ack |= at91_ics1523_WriteByte ((unsigned char) ICS_FD0, (unsigned char) 0x39) ; /* 0x7A */ + ack |= at91_ics1523_WriteByte ((unsigned char) ICS_FD1, (unsigned char) 0x00); + ack |= at91_ics1523_WriteByte ((unsigned char) ICS_SWRST, (unsigned char) (ICS_PLLR)); + + /* Program 1ms temporizing period */ + mdelay(1); + + at91_ics1523_ReadByte ((unsigned char) ICS_SR, (char *)&status); + } while (!((unsigned int) status & (unsigned int) ICS_PLLLOCK) && (nb_trial < 10)); + + ack |= at91_ics1523_WriteByte ((unsigned char) ICS_DPAC, (unsigned char) 0x03) ; /* 0x01 */ + ack |= at91_ics1523_WriteByte ((unsigned char) ICS_SWRST, (unsigned char) (ICS_DPAR)); + + /* Program 1ms temporizing period */ + mdelay(1); + + ack |= at91_ics1523_WriteByte ((unsigned char) ICS_DPAO, (unsigned char) 0x00); + + /* Program 1ms temporizing period */ + mdelay(1); + + /* All done - cleanup */ + iounmap(twi_base); + clk_disable(twi_clk); + clk_put(twi_clk); + + return ack; +} diff -urN linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/Kconfig linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/Kconfig --- linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/Kconfig 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/Kconfig 2007-03-24 16:39:15.000000000 +0100 @@ -9,11 +9,14 @@ bool "AT91RM9200" config ARCH_AT91SAM9260 - bool "AT91SAM9260" + bool "AT91SAM9260 or AT91SAM9XE" config ARCH_AT91SAM9261 bool "AT91SAM9261" +config ARCH_AT91SAM9263 + bool "AT91SAM9263" + endchoice # ---------------------------------------------------------- @@ -90,13 +93,22 @@ if ARCH_AT91SAM9260 -comment "AT91SAM9260 Board Type" +comment "AT91SAM9260 Variants" + +config ARCH_AT91SAM9260_SAM9XE + bool "AT91SAM9XE" + depends on ARCH_AT91SAM9260 + help + Select this if you are using Atmel's AT91SAM9XE System-on-Chip. + They are basicaly AT91SAM9260s with various sizes of embedded Flash. + +comment "AT91SAM9260 / AT91SAM9XE Board Type" config MACH_AT91SAM9260EK - bool "Atmel AT91SAM9260-EK Evaluation Kit" + bool "Atmel AT91SAM9260-EK / AT91SAM9XE Evaluation Kit" depends on ARCH_AT91SAM9260 help - Select this if you are using Atmel's AT91SAM9260-EK Evaluation Kit. + Select this if you are using Atmel's AT91SAM9260-EK or AT91SAM9XE Evaluation Kit <http://www.atmel.com/dyn/products/tools_card.asp?tool_id=3933> endif @@ -118,21 +130,50 @@ # ---------------------------------------------------------- +if ARCH_AT91SAM9263 + +comment "AT91SAM9263 Board Type" + +config MACH_AT91SAM9263EK + bool "Atmel AT91SAM9263-EK Evaluation Kit" + depends on ARCH_AT91SAM9263 + help + Select this if you are using Atmel's AT91SAM9263-EK Evaluation Kit. + <http://www.atmel.com/dyn/products/tools_card.asp?tool_id=4057> + +endif + +# ---------------------------------------------------------- + comment "AT91 Board Options" config MTD_AT91_DATAFLASH_CARD bool "Enable DataFlash Card support" - depends on (ARCH_AT91RM9200DK || MACH_AT91RM9200EK || MACH_AT91SAM9260EK || MACH_AT91SAM9261EK) + depends on (ARCH_AT91RM9200DK || MACH_AT91RM9200EK || MACH_AT91SAM9260EK || MACH_AT91SAM9261EK || MACH_AT91SAM9263EK) help Enable support for the DataFlash card. config MTD_NAND_AT91_BUSWIDTH_16 bool "Enable 16-bit data bus interface to NAND flash" - depends on (MACH_AT91SAM9261EK || MACH_AT91SAM9260EK) + depends on (MACH_AT91SAM9261EK || MACH_AT91SAM9260EK || MACH_AT91SAM9263EK) help On AT91SAM926x boards both types of NAND flash can be present (8 and 16 bit data bus width). +config CSB300_WAKE_SW0 + bool "CSB300 SW0 irq0 wakeup" + depends on MACH_CSB337 && PM + help + If you have a CSB300 connected to your CSB337, this lets + SW0 serve as a wakeup button. It uses IRQ0. + +config CSB300_WAKE_SW1 + bool "CSB300 SW1 gpio wakeup" + depends on MACH_CSB337 && PM + help + If you have a CSB300 connected to your CSB337, this lets + SW1 serve as a wakeup button. It uses GPIO. + # ---------------------------------------------------------- comment "AT91 Feature Selections" @@ -143,6 +184,13 @@ Select this if you need to program one or more of the PCK0..PCK3 programmable clock outputs. +config AT91_SLOW_CLOCK + bool "Suspend-to-RAM uses slow clock mode (EXPERIMENTAL)" + depends on PM && EXPERIMENTAL + help + Select this if you wish to put the CPU into slow clock mode + while in the "Suspend to RAM" state, to save more power. + endmenu endif diff -urN linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/leds.c linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/leds.c --- linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/leds.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/leds.c 2007-03-24 16:39:15.000000000 +0100 @@ -86,10 +86,6 @@ if (!at91_leds_timer || !at91_leds_cpu) return -ENODEV; - /* Enable PIO to access the LEDs */ - at91_set_gpio_output(at91_leds_timer, 1); - at91_set_gpio_output(at91_leds_cpu, 1); - leds_event = at91_leds_event; leds_event(led_start); diff -urN linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/Makefile linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/Makefile --- linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/Makefile 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/Makefile 2007-03-24 16:39:15.000000000 +0100 @@ -8,11 +8,13 @@ obj- := obj-$(CONFIG_PM) += pm.o +obj-$(CONFIG_AT91_SLOW_CLOCK) += pm_slowclock.o # CPU-specific support obj-$(CONFIG_ARCH_AT91RM9200) += at91rm9200.o at91rm9200_time.o at91rm9200_devices.o obj-$(CONFIG_ARCH_AT91SAM9260) += at91sam9260.o at91sam926x_time.o at91sam9260_devices.o obj-$(CONFIG_ARCH_AT91SAM9261) += at91sam9261.o at91sam926x_time.o at91sam9261_devices.o +obj-$(CONFIG_ARCH_AT91SAM9263) += at91sam9263.o at91sam926x_time.o at91sam9263_devices.o # AT91RM9200 board-specific support obj-$(CONFIG_MACH_ONEARM) += board-1arm.o @@ -31,6 +33,9 @@ # AT91SAM9261 board-specific support obj-$(CONFIG_MACH_AT91SAM9261EK) += board-sam9261ek.o +# AT91SAM9263 board-specific support +obj-$(CONFIG_MACH_AT91SAM9263EK) += board-sam9263ek.o + # LEDs support led-$(CONFIG_ARCH_AT91RM9200DK) += leds.o led-$(CONFIG_MACH_AT91RM9200EK) += leds.o @@ -41,7 +46,7 @@ obj-$(CONFIG_LEDS) += $(led-y) # VGA support -#obj-$(CONFIG_FB_S1D13XXX) += ics1523.o +obj-$(CONFIG_FB_S1D13XXX) += ics1523.o ifeq ($(CONFIG_PM_DEBUG),y) diff -urN linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/pm.c linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/pm.c --- linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/pm.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/pm.c 2007-03-24 16:39:15.000000000 +0100 @@ -63,6 +63,7 @@ * Verify that all the clocks are correct before entering * slow-clock mode. */ +#warning "SAM9260 only has 3 programmable clocks." static int at91_pm_verify_clocks(void) { unsigned long scsr; @@ -80,6 +81,8 @@ #warning "Check SAM9260 USB clocks" } else if (cpu_is_at91sam9261()) { #warning "Check SAM9261 USB clocks" + } else if (cpu_is_at91sam9263()) { +#warning "Check SAM9263 USB clocks" } #ifdef CONFIG_AT91_PROGRAMMABLE_CLOCKS @@ -205,16 +208,23 @@ .enter = at91_pm_enter, }; +#ifdef CONFIG_AT91_SLOW_CLOCK +extern void at91rm9200_slow_clock(void); +extern u32 at91rm9200_slow_clock_sz; +#endif + static int __init at91_pm_init(void) { - printk("AT91: Power Management\n"); - -#ifdef CONFIG_AT91_PM_SLOW_CLOCK - /* REVISIT allocations of SRAM should be dynamically managed. +#ifdef CONFIG_AT91_SLOW_CLOCK + /* + * REVISIT allocations of SRAM should be dynamically managed. * FIQ handlers and other components will want SRAM/TCM too... */ - slow_clock = (void *) (AT91_VA_BASE_SRAM + (3 * SZ_4K)); + slow_clock = (void *) (AT91_IO_VIRT_BASE - AT91RM9200_SRAM_SIZE + (3 * SZ_4K)); memcpy(slow_clock, at91rm9200_slow_clock, at91rm9200_slow_clock_sz); + printk("AT91: Power Management (with slow clock mode)\n"); +#else + printk("AT91: Power Management\n"); #endif /* Disable SDRAM low-power mode. Cannot be used with self-refresh. */ diff -urN linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/pm_slowclock.S linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/pm_slowclock.S --- linux-2.6.20.4-0rig/arch/arm/mach-at91rm9200/pm_slowclock.S 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/arm/mach-at91rm9200/pm_slowclock.S 2007-03-24 16:39:15.000000000 +0100 @@ -0,0 +1,172 @@ +/* + * arch/arm/mach-at91rm9200/pm_slow_clock.S + * + * Copyright (C) 2006 Savin Zlobec + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/linkage.h> +#include <asm/hardware.h> +#include <asm/arch/at91_pmc.h> +#include <asm/arch/at91rm9200_mc.h> + +#define MCKRDY_TIMEOUT 1000 +#define MOSCRDY_TIMEOUT 1000 +#define PLLALOCK_TIMEOUT 1000 + + .macro wait_mckrdy + mov r2, #MCKRDY_TIMEOUT +1: sub r2, r2, #1 + cmp r2, #0 + beq 2f + ldr r3, [r1, #AT91_PMC_SR] + tst r3, #AT91_PMC_MCKRDY + beq 1b +2: + .endm + + .macro wait_moscrdy + mov r2, #MOSCRDY_TIMEOUT +1: sub r2, r2, #1 + cmp r2, #0 + beq 2f + ldr r3, [r1, #AT91_PMC_SR] + tst r3, #AT91_PMC_MOSCS + beq 1b +2: + .endm + + .macro wait_pllalock + mov r2, #PLLALOCK_TIMEOUT +1: sub r2, r2, #1 + cmp r2, #0 + beq 2f + ldr r3, [r1, #AT91_PMC_SR] + tst r3, #AT91_PMC_LOCKA + beq 1b +2: + .endm + + .macro wait_plladis + mov r2, #PLLALOCK_TIMEOUT +1: sub r2, r2, #1 + cmp r2, #0 + beq 2f + ldr r3, [r1, #AT91_PMC_SR] + tst r3, #AT91_PMC_LOCKA + bne 1b +2: + .endm + + .text + +ENTRY(at91rm9200_slow_clock) + + ldr r1, .at91_va_base_sys + + /* Put SDRAM in self refresh mode */ + + b 1f + .align 5 +1: mcr p15, 0, r0, c7, c10, 4 + mov r2, #1 + str r2, [r1, #AT91_SDRAMC_SRR] + + /* Save Master clock setting */ + + ldr r2, [r1, #AT91_PMC_MCKR] + str r2, .saved_mckr + + /* + * Set the Master clock source to slow clock + * + * First set the CSS field, wait for MCKRDY + * and than set the PRES and MDIV fields. + * + * See eratta #2[78] for details. + */ + + bic r2, r2, #3 + str r2, [r1, #AT91_PMC_MCKR] + + wait_mckrdy + + mov r2, #0 + str r2, [r1, #AT91_PMC_MCKR] + + /* Save PLLA setting and disable it */ + + ldr r2, [r1, #AT91_CKGR_PLLAR] + str r2, .saved_pllar + + mov r2, #0 + str r2, [r1, #AT91_CKGR_PLLAR] + + wait_plladis + + /* Turn off the main oscillator */ + + ldr r2, [r1, #AT91_CKGR_MOR] + bic r2, r2, #AT91_PMC_MOSCEN + str r2, [r1, #AT91_CKGR_MOR] + + /* Wait for interrupt */ + + mcr p15, 0, r0, c7, c0, 4 + + /* Turn on the main oscillator */ + + ldr r2, [r1, #AT91_CKGR_MOR] + orr r2, r2, #AT91_PMC_MOSCEN + str r2, [r1, #AT91_CKGR_MOR] + + wait_moscrdy + + /* Restore PLLA setting */ + + ldr r2, .saved_pllar + str r2, [r1, #AT91_CKGR_PLLAR] + + wait_pllalock + + /* + * Restore master clock setting + * + * First set PRES if it was not 0, + * than set CSS and MDIV fields. + * After every change wait for + * MCKRDY. + * + * See eratta #2[78] for details. + */ + + ldr r2, .saved_mckr + tst r2, #0x1C + beq 2f + and r2, r2, #0x1C + str r2, [r1, #AT91_PMC_MCKR] + + wait_mckrdy + +2: ldr r2, .saved_mckr + str r2, [r1, #AT91_PMC_MCKR] + + wait_mckrdy + + mov pc, lr + +.saved_mckr: + .word 0 + +.saved_pllar: + .word 0 + +.at91_va_base_sys: + .word AT91_VA_BASE_SYS + +ENTRY(at91rm9200_slow_clock_sz) + .word .-at91rm9200_slow_clock diff -urN linux-2.6.20.4-0rig/arch/arm/mm/Kconfig linux-2.6.20.4-atmel/arch/arm/mm/Kconfig --- linux-2.6.20.4-0rig/arch/arm/mm/Kconfig 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/arm/mm/Kconfig 2007-03-24 16:39:15.000000000 +0100 @@ -171,8 +171,8 @@ # ARM926T config CPU_ARM926T bool "Support ARM926T processor" - depends on ARCH_INTEGRATOR || ARCH_VERSATILE_PB || MACH_VERSATILE_AB || ARCH_OMAP730 || ARCH_OMAP16XX || MACH_REALVIEW_EB || ARCH_PNX4008 || ARCH_NETX || CPU_S3C2412 || ARCH_AT91SAM9260 || ARCH_AT91SAM9261 - default y if ARCH_VERSATILE_PB || MACH_VERSATILE_AB || ARCH_OMAP730 || ARCH_OMAP16XX || ARCH_PNX4008 || ARCH_NETX || CPU_S3C2412 || ARCH_AT91SAM9260 || ARCH_AT91SAM9261 + depends on ARCH_INTEGRATOR || ARCH_VERSATILE_PB || MACH_VERSATILE_AB || ARCH_OMAP730 || ARCH_OMAP16XX || MACH_REALVIEW_EB || ARCH_PNX4008 || ARCH_NETX || CPU_S3C2412 || ARCH_AT91SAM9260 || ARCH_AT91SAM9261 || ARCH_AT91SAM9263 + default y if ARCH_VERSATILE_PB || MACH_VERSATILE_AB || ARCH_OMAP730 || ARCH_OMAP16XX || ARCH_PNX4008 || ARCH_NETX || CPU_S3C2412 || ARCH_AT91SAM9260 || ARCH_AT91SAM9261 || ARCH_AT91SAM9263 select CPU_32v5 select CPU_ABRT_EV5TJ select CPU_CACHE_VIVT diff -urN linux-2.6.20.4-0rig/arch/avr32/boards/atstk1000/atstk1002.c linux-2.6.20.4-atmel/arch/avr32/boards/atstk1000/atstk1002.c --- linux-2.6.20.4-0rig/arch/avr32/boards/atstk1000/atstk1002.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/avr32/boards/atstk1000/atstk1002.c 2007-03-24 16:42:29.000000000 +0100 @@ -14,11 +14,17 @@ #include <linux/platform_device.h> #include <linux/string.h> #include <linux/types.h> +#include <linux/spi/spi.h> #include <asm/io.h> #include <asm/setup.h> +#include <asm/arch/at32ap7000.h> #include <asm/arch/board.h> #include <asm/arch/init.h> +#include <asm/arch/portmux.h> + + +#define SW2_DEFAULT /* MMCI and UART_A available */ struct eth_addr { u8 addr[6]; @@ -29,6 +35,20 @@ static struct eth_platform_data __initdata eth_data[2]; extern struct lcdc_platform_data atstk1000_fb0_data; +static struct spi_board_info spi0_board_info[] __initdata = { + { + /* QVGA display */ + .modalias = "ltv350qv", + .max_speed_hz = 16000000, + .chip_select = 1, + }, +}; + +static struct mci_platform_data __initdata mci0_data = { + .detect_pin = GPIO_PIN_NONE, + .wp_pin = GPIO_PIN_NONE, +}; + /* * The next two functions should go away as the boot loader is * supposed to initialize the macb address registers with a valid @@ -86,25 +106,58 @@ void __init setup_board(void) { - at32_map_usart(1, 0); /* /dev/ttyS0 */ - at32_map_usart(2, 1); /* /dev/ttyS1 */ - at32_map_usart(3, 2); /* /dev/ttyS2 */ +#ifdef SW2_DEFAULT + at32_map_usart(1, 0); /* USART 1/A: /dev/ttyS0, DB9 */ +#else + at32_map_usart(0, 1); /* USART 0/B: /dev/ttyS1, IRDA */ +#endif + /* USART 2/unused: expansion connector */ + at32_map_usart(3, 2); /* USART 3/C: /dev/ttyS2, DB9 */ at32_setup_serial_console(0); } static int __init atstk1002_init(void) { + /* + * ATSTK1000 uses 32-bit SDRAM interface. Reserve the + * SDRAM-specific pins so that nobody messes with them. + */ + at32_reserve_pin(GPIO_PIN_PE(0)); /* DATA[16] */ + at32_reserve_pin(GPIO_PIN_PE(1)); /* DATA[17] */ + at32_reserve_pin(GPIO_PIN_PE(2)); /* DATA[18] */ + at32_reserve_pin(GPIO_PIN_PE(3)); /* DATA[19] */ + at32_reserve_pin(GPIO_PIN_PE(4)); /* DATA[20] */ + at32_reserve_pin(GPIO_PIN_PE(5)); /* DATA[21] */ + at32_reserve_pin(GPIO_PIN_PE(6)); /* DATA[22] */ + at32_reserve_pin(GPIO_PIN_PE(7)); /* DATA[23] */ + at32_reserve_pin(GPIO_PIN_PE(8)); /* DATA[24] */ + at32_reserve_pin(GPIO_PIN_PE(9)); /* DATA[25] */ + at32_reserve_pin(GPIO_PIN_PE(10)); /* DATA[26] */ + at32_reserve_pin(GPIO_PIN_PE(11)); /* DATA[27] */ + at32_reserve_pin(GPIO_PIN_PE(12)); /* DATA[28] */ + at32_reserve_pin(GPIO_PIN_PE(13)); /* DATA[29] */ + at32_reserve_pin(GPIO_PIN_PE(14)); /* DATA[30] */ + at32_reserve_pin(GPIO_PIN_PE(15)); /* DATA[31] */ + at32_reserve_pin(GPIO_PIN_PE(26)); /* SDCS */ + at32_add_system_devices(); +#ifdef SW2_DEFAULT at32_add_device_usart(0); +#else at32_add_device_usart(1); +#endif at32_add_device_usart(2); set_hw_addr(at32_add_device_eth(0, ð_data[0])); - at32_add_device_spi(0); + at32_add_device_spi(0, spi0_board_info, ARRAY_SIZE(spi0_board_info)); + at32_add_device_twi(0); + at32_add_device_mci(0, &mci0_data); at32_add_device_lcdc(0, &atstk1000_fb0_data); + at32_add_device_usba(0); + at32_add_device_abdac(0); return 0; } diff -urN linux-2.6.20.4-0rig/arch/avr32/boards/atstk1000/Makefile linux-2.6.20.4-atmel/arch/avr32/boards/atstk1000/Makefile --- linux-2.6.20.4-0rig/arch/avr32/boards/atstk1000/Makefile 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/avr32/boards/atstk1000/Makefile 2007-03-24 16:42:28.000000000 +0100 @@ -1,2 +1,2 @@ -obj-y += setup.o spi.o flash.o +obj-y += setup.o flash.o obj-$(CONFIG_BOARD_ATSTK1002) += atstk1002.o diff -urN linux-2.6.20.4-0rig/arch/avr32/boards/atstk1000/spi.c linux-2.6.20.4-atmel/arch/avr32/boards/atstk1000/spi.c --- linux-2.6.20.4-0rig/arch/avr32/boards/atstk1000/spi.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/avr32/boards/atstk1000/spi.c 1970-01-01 01:00:00.000000000 +0100 @@ -1,27 +0,0 @@ -/* - * ATSTK1000 SPI devices - * - * Copyright (C) 2005 Atmel Norway - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ -#include <linux/device.h> -#include <linux/spi/spi.h> - -static struct spi_board_info spi_board_info[] __initdata = { - { - .modalias = "ltv350qv", - .max_speed_hz = 16000000, - .bus_num = 0, - .chip_select = 1, - }, -}; - -static int board_init_spi(void) -{ - spi_register_board_info(spi_board_info, ARRAY_SIZE(spi_board_info)); - return 0; -} -arch_initcall(board_init_spi); diff -urN linux-2.6.20.4-0rig/arch/avr32/drivers/dw-dmac.c linux-2.6.20.4-atmel/arch/avr32/drivers/dw-dmac.c --- linux-2.6.20.4-0rig/arch/avr32/drivers/dw-dmac.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/avr32/drivers/dw-dmac.c 2007-03-24 16:42:29.000000000 +0100 @@ -0,0 +1,761 @@ +/* + * Driver for the Synopsys DesignWare DMA Controller + * + * Copyright (C) 2005-2006 Atmel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/dmapool.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include <asm/dma-controller.h> +#include <asm/io.h> + +#include "dw-dmac.h" + +#define DMAC_NR_CHANNELS 3 +#define DMAC_MAX_BLOCKSIZE 4095 + +enum { + CH_STATE_FREE = 0, + CH_STATE_ALLOCATED, + CH_STATE_BUSY, +}; + +struct dw_dma_lli { + dma_addr_t sar; + dma_addr_t dar; + dma_addr_t llp; + u32 ctllo; + u32 ctlhi; + u32 sstat; + u32 dstat; +}; + +struct dw_dma_block { + struct dw_dma_lli *lli_vaddr; + dma_addr_t lli_dma_addr; +}; + +struct dw_dma_channel { + unsigned int state; + int is_cyclic; + struct dma_request_sg *req_sg; + struct dma_request_cyclic *req_cyclic; + unsigned int nr_blocks; + int direction; + struct dw_dma_block *block; +}; + +struct dw_dma_controller { + spinlock_t lock; + void * __iomem regs; + struct dma_pool *lli_pool; + struct clk *hclk; + struct dma_controller dma; + struct dw_dma_channel channel[DMAC_NR_CHANNELS]; +}; +#define to_dw_dmac(dmac) container_of(dmac, struct dw_dma_controller, dma) + +#define dmac_writel_hi(dmac, reg, value) \ + __raw_writel((value), (dmac)->regs + DW_DMAC_##reg + 4) +#define dmac_readl_hi(dmac, reg) \ + __raw_readl((dmac)->regs + DW_DMAC_##reg + 4) +#define dmac_writel_lo(dmac, reg, value) \ + __raw_writel((value), (dmac)->regs + DW_DMAC_##reg) +#define dmac_readl_lo(dmac, reg) \ + __raw_readl((dmac)->regs + DW_DMAC_##reg) +#define dmac_chan_writel_hi(dmac, chan, reg, value) \ + __raw_writel((value), ((dmac)->regs + 0x58 * (chan) \ + + DW_DMAC_CHAN_##reg + 4)) +#define dmac_chan_readl_hi(dmac, chan, reg) \ + __raw_readl((dmac)->regs + 0x58 * (chan) + DW_DMAC_CHAN_##reg + 4) +#define dmac_chan_writel_lo(dmac, chan, reg, value) \ + __raw_writel((value), (dmac)->regs + 0x58 * (chan) + DW_DMAC_CHAN_##reg) +#define dmac_chan_readl_lo(dmac, chan, reg) \ + __raw_readl((dmac)->regs + 0x58 * (chan) + DW_DMAC_CHAN_##reg) +#define set_channel_bit(dmac, reg, chan) \ + dmac_writel_lo(dmac, reg, (1 << (chan)) | (1 << ((chan) + 8))) +#define clear_channel_bit(dmac, reg, chan) \ + dmac_writel_lo(dmac, reg, (0 << (chan)) | (1 << ((chan) + 8))) + +static int dmac_alloc_channel(struct dma_controller *_dmac) +{ + struct dw_dma_controller *dmac = to_dw_dmac(_dmac); + struct dw_dma_channel *chan; + unsigned long flags; + int i; + + spin_lock_irqsave(&dmac->lock, flags); + for (i = 0; i < DMAC_NR_CHANNELS; i++) + if (dmac->channel[i].state == CH_STATE_FREE) + break; + + if (i < DMAC_NR_CHANNELS) { + chan = &dmac->channel[i]; + chan->state = CH_STATE_ALLOCATED; + } else { + i = -EBUSY; + } + + spin_unlock_irqrestore(&dmac->lock, flags); + + return i; +} + +static void dmac_release_channel(struct dma_controller *_dmac, int channel) +{ + struct dw_dma_controller *dmac = to_dw_dmac(_dmac); + + BUG_ON(channel >= DMAC_NR_CHANNELS + || dmac->channel[channel].state != CH_STATE_ALLOCATED); + + dmac->channel[channel].state = CH_STATE_FREE; +} + +static struct dw_dma_block *allocate_blocks(struct dw_dma_controller *dmac, + unsigned int nr_blocks) +{ + struct dw_dma_block *block; + void *p; + unsigned int i; + + block = kmalloc(nr_blocks * sizeof(*block), + GFP_KERNEL); + if (unlikely(!block)) + return NULL; + + for (i = 0; i < nr_blocks; i++) { + p = dma_pool_alloc(dmac->lli_pool, GFP_KERNEL, + &block[i].lli_dma_addr); + block[i].lli_vaddr = p; + if (unlikely(!p)) + goto fail; + } + + return block; + +fail: + for (i = 0; i < nr_blocks; i++) { + if (!block[i].lli_vaddr) + break; + dma_pool_free(dmac->lli_pool, block[i].lli_vaddr, + block[i].lli_dma_addr); + } + kfree(block); + return NULL; +} + +static void cleanup_channel(struct dw_dma_controller *dmac, + struct dw_dma_channel *chan) +{ + unsigned int i; + + if (chan->nr_blocks > 1) { + for (i = 0; i < chan->nr_blocks; i++) + dma_pool_free(dmac->lli_pool, chan->block[i].lli_vaddr, + chan->block[i].lli_dma_addr); + kfree(chan->block); + } + + chan->state = CH_STATE_ALLOCATED; +} + +static int dmac_prepare_request_sg(struct dma_controller *_dmac, + struct dma_request_sg *req) +{ + struct dw_dma_controller *dmac = to_dw_dmac(_dmac); + struct dw_dma_channel *chan; + unsigned long ctlhi, ctllo, cfghi, cfglo; + unsigned long block_size; + unsigned int nr_blocks; + int ret, i, direction; + unsigned long flags; + + spin_lock_irqsave(&dmac->lock, flags); + + ret = -EINVAL; + if (req->req.channel >= DMAC_NR_CHANNELS + || dmac->channel[req->req.channel].state != CH_STATE_ALLOCATED + || req->block_size > DMAC_MAX_BLOCKSIZE) { + spin_unlock_irqrestore(&dmac->lock, flags); + return -EINVAL; + } + + chan = &dmac->channel[req->req.channel]; + chan->state = CH_STATE_BUSY; + chan->req_sg = req; + chan->is_cyclic = 0; + + /* + * We have marked the channel as busy, so no need to keep the + * lock as long as we only touch the channel-specific + * registers + */ + spin_unlock_irqrestore(&dmac->lock, flags); + + /* + * There may be limitations in the driver and/or the DMA + * controller that prevents us from sending a whole + * scatterlist item in one go. Taking this into account, + * calculate the number of block transfers we need to set up. + * + * FIXME: Let the peripheral driver know about the maximum + * block size we support. We really don't want to use a + * different block size than what was suggested by the + * peripheral. + * + * Each block will get its own Linked List Item (LLI) below. + */ + block_size = req->block_size; + nr_blocks = req->nr_blocks; + pr_debug("block_size %lu, nr_blocks %u nr_sg = %u\n", + block_size, nr_blocks, req->nr_sg); + + BUG_ON(nr_blocks == 0); + chan->nr_blocks = nr_blocks; + + ret = -EINVAL; + cfglo = cfghi = 0; + switch (req->direction) { + case DMA_DIR_MEM_TO_PERIPH: + direction = DMA_TO_DEVICE; + cfghi = req->periph_id << (43 - 32); + break; + + case DMA_DIR_PERIPH_TO_MEM: + direction = DMA_FROM_DEVICE; + cfghi = req->periph_id << (39 - 32); + break; + default: + goto out_unclaim_channel; + } + + chan->direction = direction; + + dmac_chan_writel_hi(dmac, req->req.channel, CFG, cfghi); + dmac_chan_writel_lo(dmac, req->req.channel, CFG, cfglo); + + ctlhi = block_size >> req->width; + ctllo = ((req->direction << 20) + // | (1 << 14) | (1 << 11) // source/dest burst trans len + | (req->width << 4) | (req->width << 1) + | (1 << 0)); // interrupt enable + + if (nr_blocks == 1) { + /* Only one block: No need to use block chaining */ + if (direction == DMA_TO_DEVICE) { + dmac_chan_writel_lo(dmac, req->req.channel, SAR, + req->sg->dma_address); + dmac_chan_writel_lo(dmac, req->req.channel, DAR, + req->data_reg); + ctllo |= 2 << 7; // no dst increment + } else { + dmac_chan_writel_lo(dmac, req->req.channel, SAR, + req->data_reg); + dmac_chan_writel_lo(dmac, req->req.channel, DAR, + req->sg->dma_address); + ctllo |= 2 << 9; // no src increment + } + dmac_chan_writel_lo(dmac, req->req.channel, CTL, ctllo); + dmac_chan_writel_hi(dmac, req->req.channel, CTL, ctlhi); + pr_debug("ctl hi:lo 0x%lx:%lx\n", ctlhi, ctllo); + } else { + struct dw_dma_lli *lli, *lli_prev = NULL; + int j = 0, offset = 0; + + ret = -ENOMEM; + chan->block = allocate_blocks(dmac, nr_blocks); + if (!chan->block) + goto out_unclaim_channel; + + if (direction == DMA_TO_DEVICE) + ctllo |= 1 << 28 | 1 << 27 | 2 << 7; + else + ctllo |= 1 << 28 | 1 << 27 | 2 << 9; + + /* + * Map scatterlist items to blocks. One scatterlist + * item may need more than one block for the reasons + * mentioned above. + */ + for (i = 0; i < nr_blocks; i++) { + lli = chan->block[i].lli_vaddr; + if (lli_prev) { + lli_prev->llp = chan->block[i].lli_dma_addr; + pr_debug("lli[%d] (0x%p/0x%x): 0x%x 0x%x 0x%x 0x%x 0x%x\n", + i - 1, chan->block[i - 1].lli_vaddr, + chan->block[i - 1].lli_dma_addr, + lli_prev->sar, lli_prev->dar, lli_prev->llp, + lli_prev->ctllo, lli_prev->ctlhi); + } + lli->llp = 0; + lli->ctllo = ctllo; + lli->ctlhi = ctlhi; + if (direction == DMA_TO_DEVICE) { + lli->sar = req->sg[j].dma_address + offset; + lli->dar = req->data_reg; + } else { + lli->sar = req->data_reg; + lli->dar = req->sg[j].dma_address + offset; + } + lli_prev = lli; + + offset += block_size; + if (offset > req->sg[j].length) { + j++; + offset = 0; + } + } + + pr_debug("lli[%d] (0x%p/0x%x): 0x%x 0x%x 0x%x 0x%x 0x%x\n", + i - 1, chan->block[i - 1].lli_vaddr, + chan->block[i - 1].lli_dma_addr, lli_prev->sar, + lli_prev->dar, lli_prev->llp, + lli_prev->ctllo, lli_prev->ctlhi); + + /* + * SAR, DAR and CTL are initialized from the LLI. We + * only have to enable the LLI bits in CTL. + */ + dmac_chan_writel_hi(dmac, req->req.channel, CTL, 0); + dmac_chan_writel_lo(dmac, req->req.channel, LLP, + chan->block[0].lli_dma_addr); + dmac_chan_writel_lo(dmac, req->req.channel, CTL, 1 << 28 | 1 << 27); + } + + set_channel_bit(dmac, MASK_XFER, req->req.channel); + set_channel_bit(dmac, MASK_ERROR, req->req.channel); + if (req->req.block_complete) + set_channel_bit(dmac, MASK_BLOCK, req->req.channel); + else + clear_channel_bit(dmac, MASK_BLOCK, req->req.channel); + + return 0; + +out_unclaim_channel: + chan->state = CH_STATE_ALLOCATED; + return ret; +} + +static int dmac_prepare_request_cyclic(struct dma_controller *_dmac, + struct dma_request_cyclic *req) +{ + struct dw_dma_controller *dmac = to_dw_dmac(_dmac); + struct dw_dma_channel *chan; + unsigned long ctlhi, ctllo, cfghi, cfglo; + unsigned long block_size; + int ret, i, direction; + unsigned long flags; + + spin_lock_irqsave(&dmac->lock, flags); + + block_size = (req->buffer_size/req->periods) >> req->width; + + ret = -EINVAL; + if (req->req.channel >= DMAC_NR_CHANNELS + || dmac->channel[req->req.channel].state != CH_STATE_ALLOCATED + || (req->periods == 0) + || block_size > DMAC_MAX_BLOCKSIZE) { + spin_unlock_irqrestore(&dmac->lock, flags); + return -EINVAL; + } + + chan = &dmac->channel[req->req.channel]; + chan->state = CH_STATE_BUSY; + chan->is_cyclic = 1; + chan->req_cyclic = req; + + /* + * We have marked the channel as busy, so no need to keep the + * lock as long as we only touch the channel-specific + * registers + */ + spin_unlock_irqrestore(&dmac->lock, flags); + + /* + Setup + */ + BUG_ON(req->buffer_size % req->periods); + /* printk(KERN_INFO "block_size = %lu, periods = %u\n", block_size, req->periods); */ + + chan->nr_blocks = req->periods; + + ret = -EINVAL; + cfglo = cfghi = 0; + switch (req->direction) { + case DMA_DIR_MEM_TO_PERIPH: + direction = DMA_TO_DEVICE; + cfghi = req->periph_id << (43 - 32); + break; + + case DMA_DIR_PERIPH_TO_MEM: + direction = DMA_FROM_DEVICE; + cfghi = req->periph_id << (39 - 32); + break; + default: + goto out_unclaim_channel; + } + + chan->direction = direction; + + dmac_chan_writel_hi(dmac, req->req.channel, CFG, cfghi); + dmac_chan_writel_lo(dmac, req->req.channel, CFG, cfglo); + + ctlhi = block_size; + ctllo = ((req->direction << 20) + | (req->width << 4) | (req->width << 1) + | (1 << 0)); // interrupt enable + + { + struct dw_dma_lli *lli = NULL, *lli_prev = NULL; + + ret = -ENOMEM; + chan->block = allocate_blocks(dmac, req->periods); + if (!chan->block) + goto out_unclaim_channel; + + if (direction == DMA_TO_DEVICE) + ctllo |= 1 << 28 | 1 << 27 | 2 << 7; + else + ctllo |= 1 << 28 | 1 << 27 | 2 << 9; + + /* + * Set up a linked list items where each period gets + * an item. The linked list item for the last period + * points back to the star of the buffer making a + * cyclic buffer. + */ + for (i = 0; i < req->periods; i++) { + lli = chan->block[i].lli_vaddr; + if (lli_prev) { + lli_prev->llp = chan->block[i].lli_dma_addr; + /* printk(KERN_INFO "lli[%d] (0x%p/0x%x): 0x%x 0x%x 0x%x 0x%x 0x%x\n", + i - 1, chan->block[i - 1].lli_vaddr, + chan->block[i - 1].lli_dma_addr, + lli_prev->sar, lli_prev->dar, lli_prev->llp, + lli_prev->ctllo, lli_prev->ctlhi);*/ + } + lli->llp = 0; + lli->ctllo = ctllo; + lli->ctlhi = ctlhi; + if (direction == DMA_TO_DEVICE) { + lli->sar = req->buffer_start + i*(block_size << req->width); + lli->dar = req->data_reg; + } else { + lli->sar = req->data_reg; + lli->dar = req->buffer_start + i*(block_size << req->width); + } + lli_prev = lli; + } + lli->llp = chan->block[0].lli_dma_addr; + + /*printk(KERN_INFO "lli[%d] (0x%p/0x%x): 0x%x 0x%x 0x%x 0x%x 0x%x\n", + i - 1, chan->block[i - 1].lli_vaddr, + chan->block[i - 1].lli_dma_addr, lli_prev->sar, + lli_prev->dar, lli_prev->llp, + lli_prev->ctllo, lli_prev->ctlhi); */ + + /* + * SAR, DAR and CTL are initialized from the LLI. We + * only have to enable the LLI bits in CTL. + */ + dmac_chan_writel_lo(dmac, req->req.channel, LLP, + chan->block[0].lli_dma_addr); + dmac_chan_writel_lo(dmac, req->req.channel, CTL, 1 << 28 | 1 << 27); + } + + clear_channel_bit(dmac, MASK_XFER, req->req.channel); + set_channel_bit(dmac, MASK_ERROR, req->req.channel); + if (req->req.block_complete) + set_channel_bit(dmac, MASK_BLOCK, req->req.channel); + else + clear_channel_bit(dmac, MASK_BLOCK, req->req.channel); + + return 0; + +out_unclaim_channel: + chan->state = CH_STATE_ALLOCATED; + return ret; +} + +static int dmac_start_request(struct dma_controller *_dmac, + unsigned int channel) +{ + struct dw_dma_controller *dmac = to_dw_dmac(_dmac); + + BUG_ON(channel >= DMAC_NR_CHANNELS); + + set_channel_bit(dmac, CH_EN, channel); + + return 0; +} + +static dma_addr_t dmac_get_current_pos(struct dma_controller *_dmac, + unsigned int channel) +{ + struct dw_dma_controller *dmac = to_dw_dmac(_dmac); + struct dw_dma_channel *chan; + dma_addr_t current_pos; + + BUG_ON(channel >= DMAC_NR_CHANNELS); + + chan = &dmac->channel[channel]; + + switch (chan->direction) { + case DMA_TO_DEVICE: + current_pos = dmac_chan_readl_lo(dmac, channel, SAR); + break; + case DMA_FROM_DEVICE: + current_pos = dmac_chan_readl_lo(dmac, channel, DAR); + break; + default: + return 0; + } + + + if (!current_pos) { + if (chan->is_cyclic) { + current_pos = chan->req_cyclic->buffer_start; + } else { + current_pos = chan->req_sg->sg->dma_address; + } + } + + return current_pos; +} + + +static int dmac_stop_request(struct dma_controller *_dmac, + unsigned int channel) +{ + struct dw_dma_controller *dmac = to_dw_dmac(_dmac); + struct dw_dma_channel *chan; + + BUG_ON(channel >= DMAC_NR_CHANNELS); + + chan = &dmac->channel[channel]; + pr_debug("stop: st%u s%08x d%08x l%08x ctl0x%08x:0x%08x\n", + chan->state, dmac_chan_readl_lo(dmac, channel, SAR), + dmac_chan_readl_lo(dmac, channel, DAR), + dmac_chan_readl_lo(dmac, channel, LLP), + dmac_chan_readl_hi(dmac, channel, CTL), + dmac_chan_readl_lo(dmac, channel, CTL)); + + if (chan->state == CH_STATE_BUSY) { + clear_channel_bit(dmac, CH_EN, channel); + cleanup_channel(dmac, &dmac->channel[channel]); + } + + return 0; +} + + +static void dmac_block_complete(struct dw_dma_controller *dmac) +{ + struct dw_dma_channel *chan; + unsigned long status, chanid; + + status = dmac_readl_lo(dmac, STATUS_BLOCK); + + while (status) { + struct dma_request *req; + chanid = __ffs(status); + chan = &dmac->channel[chanid]; + + if (chan->is_cyclic) { + BUG_ON(!chan->req_cyclic + || !chan->req_cyclic->req.block_complete); + req = &chan->req_cyclic->req; + } else { + BUG_ON(!chan->req_sg || !chan->req_sg->req.block_complete); + req = &chan->req_sg->req; + } + dmac_writel_lo(dmac, CLEAR_BLOCK, 1 << chanid); + req->block_complete(req); + status = dmac_readl_lo(dmac, STATUS_BLOCK); + } +} + +static void dmac_xfer_complete(struct dw_dma_controller *dmac) +{ + struct dw_dma_channel *chan; + struct dma_request *req; + unsigned long status, chanid; + + status = dmac_readl_lo(dmac, STATUS_XFER); + + while (status) { + chanid = __ffs(status); + chan = &dmac->channel[chanid]; + + dmac_writel_lo(dmac, CLEAR_XFER, 1 << chanid); + + req = &chan->req_sg->req; + BUG_ON(!req); + cleanup_channel(dmac, chan); + if (req->xfer_complete) + req->xfer_complete(req); + + status = dmac_readl_lo(dmac, STATUS_XFER); + } +} + +static void dmac_error(struct dw_dma_controller *dmac) +{ + struct dw_dma_channel *chan; + unsigned long status, chanid; + + status = dmac_readl_lo(dmac, STATUS_ERROR); + + while (status) { + struct dma_request *req; + + chanid = __ffs(status); + chan = &dmac->channel[chanid]; + + dmac_writel_lo(dmac, CLEAR_ERROR, 1 << chanid); + clear_channel_bit(dmac, CH_EN, chanid); + + if (chan->is_cyclic) { + BUG_ON(!chan->req_cyclic); + req = &chan->req_cyclic->req; + } else { + BUG_ON(!chan->req_sg); + req = &chan->req_sg->req; + } + + cleanup_channel(dmac, chan); + if (req->error) + req->error(req); + + status = dmac_readl_lo(dmac, STATUS_XFER); + } +} + +static irqreturn_t dmac_interrupt(int irq, void *dev_id) +{ + struct dw_dma_controller *dmac = dev_id; + unsigned long status; + int ret = IRQ_NONE; + + spin_lock(&dmac->lock); + + status = dmac_readl_lo(dmac, STATUS_INT); + + while (status) { + ret = IRQ_HANDLED; + if (status & 0x10) + dmac_error(dmac); + if (status & 0x02) + dmac_block_complete(dmac); + if (status & 0x01) + dmac_xfer_complete(dmac); + + status = dmac_readl_lo(dmac, STATUS_INT); + } + + spin_unlock(&dmac->lock); + return ret; +} + +static int __devinit dmac_probe(struct platform_device *pdev) +{ + struct dw_dma_controller *dmac; + struct resource *regs; + int ret; + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) + return -ENXIO; + + dmac = kmalloc(sizeof(*dmac), GFP_KERNEL); + if (!dmac) + return -ENOMEM; + memset(dmac, 0, sizeof(*dmac)); + + dmac->hclk = clk_get(&pdev->dev, "hclk"); + if (IS_ERR(dmac->hclk)) { + ret = PTR_ERR(dmac->hclk); + goto out_free_dmac; + } + clk_enable(dmac->hclk); + + ret = -ENOMEM; + dmac->lli_pool = dma_pool_create("dmac", &pdev->dev, + sizeof(struct dw_dma_lli), 4, 0); + if (!dmac->lli_pool) + goto out_disable_clk; + + spin_lock_init(&dmac->lock); + dmac->dma.dev = &pdev->dev; + dmac->dma.alloc_channel = dmac_alloc_channel; + dmac->dma.release_channel = dmac_release_channel; + dmac->dma.prepare_request_sg = dmac_prepare_request_sg; + dmac->dma.prepare_request_cyclic = dmac_prepare_request_cyclic; + dmac->dma.start_request = dmac_start_request; + dmac->dma.stop_request = dmac_stop_request; + dmac->dma.get_current_pos = dmac_get_current_pos; + + dmac->regs = ioremap(regs->start, regs->end - regs->start + 1); + if (!dmac->regs) + goto out_free_pool; + + ret = request_irq(platform_get_irq(pdev, 0), dmac_interrupt, + SA_SAMPLE_RANDOM, pdev->name, dmac); + if (ret) + goto out_unmap_regs; + + /* Enable the DMA controller */ + dmac_writel_lo(dmac, CFG, 1); + + register_dma_controller(&dmac->dma); + + printk(KERN_INFO + "dmac%d: DesignWare DMA controller at 0x%p irq %d\n", + dmac->dma.id, dmac->regs, platform_get_irq(pdev, 0)); + + return 0; + +out_unmap_regs: + iounmap(dmac->regs); +out_free_pool: + dma_pool_destroy(dmac->lli_pool); +out_disable_clk: + clk_disable(dmac->hclk); + clk_put(dmac->hclk); +out_free_dmac: + kfree(dmac); + return ret; +} + +static struct platform_driver dmac_driver = { + .probe = dmac_probe, + .driver = { + .name = "dmaca", + }, +}; + +static int __init dmac_init(void) +{ + return platform_driver_register(&dmac_driver); +} +subsys_initcall(dmac_init); + +static void __exit dmac_exit(void) +{ + platform_driver_unregister(&dmac_driver); +} +module_exit(dmac_exit); + +MODULE_DESCRIPTION("Synopsys DesignWare DMA Controller driver"); +MODULE_AUTHOR("Haavard Skinnemoen <hskinnemoen@atmel.com>"); +MODULE_LICENSE("GPL"); diff -urN linux-2.6.20.4-0rig/arch/avr32/drivers/dw-dmac.h linux-2.6.20.4-atmel/arch/avr32/drivers/dw-dmac.h --- linux-2.6.20.4-0rig/arch/avr32/drivers/dw-dmac.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/avr32/drivers/dw-dmac.h 2007-03-24 16:42:29.000000000 +0100 @@ -0,0 +1,42 @@ +/* + * Driver for the Synopsys DesignWare DMA Controller + * + * Copyright (C) 2005-2006 Atmel Corporation + * + * 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. + */ +#ifndef __AVR32_DW_DMAC_H__ +#define __AVR32_DW_DMAC_H__ + +#define DW_DMAC_CFG 0x398 +#define DW_DMAC_CH_EN 0x3a0 + +#define DW_DMAC_STATUS_XFER 0x2e8 +#define DW_DMAC_STATUS_BLOCK 0x2f0 +#define DW_DMAC_STATUS_ERROR 0x308 + +#define DW_DMAC_MASK_XFER 0x310 +#define DW_DMAC_MASK_BLOCK 0x318 +#define DW_DMAC_MASK_ERROR 0x330 + +#define DW_DMAC_CLEAR_XFER 0x338 +#define DW_DMAC_CLEAR_BLOCK 0x340 +#define DW_DMAC_CLEAR_ERROR 0x358 + +#define DW_DMAC_STATUS_INT 0x360 + +#define DW_DMAC_CHAN_SAR 0x000 +#define DW_DMAC_CHAN_DAR 0x008 +#define DW_DMAC_CHAN_LLP 0x010 +#define DW_DMAC_CHAN_CTL 0x018 +#define DW_DMAC_CHAN_SSTAT 0x020 +#define DW_DMAC_CHAN_DSTAT 0x028 +#define DW_DMAC_CHAN_SSTATAR 0x030 +#define DW_DMAC_CHAN_DSTATAR 0x038 +#define DW_DMAC_CHAN_CFG 0x040 +#define DW_DMAC_CHAN_SGR 0x048 +#define DW_DMAC_CHAN_DSR 0x050 + +#endif /* __AVR32_DW_DMAC_H__ */ diff -urN linux-2.6.20.4-0rig/arch/avr32/drivers/Makefile linux-2.6.20.4-atmel/arch/avr32/drivers/Makefile --- linux-2.6.20.4-0rig/arch/avr32/drivers/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/avr32/drivers/Makefile 2007-03-24 16:42:29.000000000 +0100 @@ -0,0 +1 @@ +obj-$(CONFIG_DW_DMAC) += dw-dmac.o diff -urN linux-2.6.20.4-0rig/arch/avr32/Kconfig linux-2.6.20.4-atmel/arch/avr32/Kconfig --- linux-2.6.20.4-0rig/arch/avr32/Kconfig 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/avr32/Kconfig 2007-03-24 16:42:29.000000000 +0100 @@ -112,6 +112,8 @@ bool "U-Boot (or similar) bootloader" endchoice +source "arch/avr32/mach-at32ap/Kconfig" + config LOAD_ADDRESS hex default 0x10000000 if LOADER_U_BOOT=y && CPU_AT32AP7000=y @@ -160,6 +162,10 @@ enabling Nexus-compliant debuggers to keep track of the PID of the currently executing task. +config DW_DMAC + tristate "Synopsys DesignWare DMA Controller support" + default y if CPU_AT32AP7000 + # FPU emulation goes here source "kernel/Kconfig.hz" @@ -195,6 +201,8 @@ source "fs/Kconfig" +source "arch/avr32/oprofile/Kconfig" + source "arch/avr32/Kconfig.debug" source "security/Kconfig" diff -urN linux-2.6.20.4-0rig/arch/avr32/kernel/cpu.c linux-2.6.20.4-atmel/arch/avr32/kernel/cpu.c --- linux-2.6.20.4-0rig/arch/avr32/kernel/cpu.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/avr32/kernel/cpu.c 2007-03-24 16:42:28.000000000 +0100 @@ -9,6 +9,7 @@ #include <linux/sysdev.h> #include <linux/seq_file.h> #include <linux/cpu.h> +#include <linux/module.h> #include <linux/percpu.h> #include <linux/param.h> #include <linux/errno.h> diff -urN linux-2.6.20.4-0rig/arch/avr32/kernel/dma-controller.c linux-2.6.20.4-atmel/arch/avr32/kernel/dma-controller.c --- linux-2.6.20.4-0rig/arch/avr32/kernel/dma-controller.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/avr32/kernel/dma-controller.c 2007-03-24 16:42:29.000000000 +0100 @@ -0,0 +1,34 @@ +/* + * Preliminary DMA controller framework for AVR32 + * + * Copyright (C) 2005-2006 Atmel Corporation + * + * 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 <asm/dma-controller.h> + +static LIST_HEAD(controllers); + +int register_dma_controller(struct dma_controller *dmac) +{ + static int next_id; + + dmac->id = next_id++; + list_add_tail(&dmac->list, &controllers); + + return 0; +} +EXPORT_SYMBOL(register_dma_controller); + +struct dma_controller *find_dma_controller(int id) +{ + struct dma_controller *dmac; + + list_for_each_entry(dmac, &controllers, list) + if (dmac->id == id) + return dmac; + return NULL; +} +EXPORT_SYMBOL(find_dma_controller); diff -urN linux-2.6.20.4-0rig/arch/avr32/kernel/irq.c linux-2.6.20.4-atmel/arch/avr32/kernel/irq.c --- linux-2.6.20.4-0rig/arch/avr32/kernel/irq.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/avr32/kernel/irq.c 2007-03-24 16:42:28.000000000 +0100 @@ -57,6 +57,7 @@ seq_printf(p, "%3d: ", i); for_each_online_cpu(cpu) seq_printf(p, "%10u ", kstat_cpu(cpu).irqs[i]); + seq_printf(p, " %8s", irq_desc[i].chip->name ? : "-"); seq_printf(p, " %s", action->name); for (action = action->next; action; action = action->next) seq_printf(p, ", %s", action->name); diff -urN linux-2.6.20.4-0rig/arch/avr32/kernel/Makefile linux-2.6.20.4-atmel/arch/avr32/kernel/Makefile --- linux-2.6.20.4-0rig/arch/avr32/kernel/Makefile 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/avr32/kernel/Makefile 2007-03-24 16:42:29.000000000 +0100 @@ -9,6 +9,7 @@ obj-y += setup.o traps.o semaphore.o ptrace.o obj-y += signal.o sys_avr32.o process.o time.o obj-y += init_task.o switch_to.o cpu.o +obj-y += dma-controller.o obj-$(CONFIG_MODULES) += module.o avr32_ksyms.o obj-$(CONFIG_KPROBES) += kprobes.o diff -urN linux-2.6.20.4-0rig/arch/avr32/kernel/setup.c linux-2.6.20.4-atmel/arch/avr32/kernel/setup.c --- linux-2.6.20.4-0rig/arch/avr32/kernel/setup.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/avr32/kernel/setup.c 2007-03-24 16:42:28.000000000 +0100 @@ -16,6 +16,7 @@ #include <linux/module.h> #include <linux/root_dev.h> #include <linux/cpu.h> +#include <linux/kernel.h> #include <asm/sections.h> #include <asm/processor.h> @@ -174,8 +175,7 @@ * Copy the data so the bootmem init code doesn't need to care * about it. */ - if (mem_range_next_free >= - (sizeof(mem_range_cache) / sizeof(mem_range_cache[0]))) + if (mem_range_next_free >= ARRAY_SIZE(mem_range_cache)) panic("Physical memory map too complex!\n"); new = &mem_range_cache[mem_range_next_free++]; diff -urN linux-2.6.20.4-0rig/arch/avr32/kernel/syscall_table.S linux-2.6.20.4-atmel/arch/avr32/kernel/syscall_table.S --- linux-2.6.20.4-0rig/arch/avr32/kernel/syscall_table.S 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/avr32/kernel/syscall_table.S 2007-03-24 16:42:28.000000000 +0100 @@ -8,14 +8,6 @@ * published by the Free Software Foundation. */ -#if !defined(CONFIG_NFSD) && !defined(CONFIG_NFSD_MODULE) -#define sys_nfsservctl sys_ni_syscall -#endif - -#if !defined(CONFIG_SYSV_IPC) -# define sys_ipc sys_ni_syscall -#endif - .section .rodata,"a",@progbits .type sys_call_table,@object .global sys_call_table @@ -129,7 +121,7 @@ .long sys_getitimer /* 105 */ .long sys_swapoff .long sys_sysinfo - .long sys_ipc + .long sys_ni_syscall /* was sys_ipc briefly */ .long sys_sendfile .long sys_setdomainname /* 110 */ .long sys_newuname @@ -287,4 +279,16 @@ .long sys_tee .long sys_vmsplice .long __sys_epoll_pwait /* 265 */ + .long sys_msgget + .long sys_msgsnd + .long sys_msgrcv + .long sys_msgctl + .long sys_semget /* 270 */ + .long sys_semop + .long sys_semctl + .long sys_semtimedop + .long sys_shmat + .long sys_shmget /* 275 */ + .long sys_shmdt + .long sys_shmctl .long sys_ni_syscall /* r8 is saturated at nr_syscalls */ diff -urN linux-2.6.20.4-0rig/arch/avr32/kernel/time.c linux-2.6.20.4-atmel/arch/avr32/kernel/time.c --- linux-2.6.20.4-0rig/arch/avr32/kernel/time.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/avr32/kernel/time.c 2007-03-24 16:42:29.000000000 +0100 @@ -21,6 +21,7 @@ #include <linux/profile.h> #include <linux/sysdev.h> +#include <asm/intc.h> #include <asm/div64.h> #include <asm/sysreg.h> #include <asm/io.h> @@ -136,6 +137,10 @@ { unsigned int count; + /* Check if interrupt is timer or performance counters */ + if (!(intc_get_pending(irq) & 1)) + return IRQ_NONE; + /* ack timer interrupt and try to set next interrupt */ count = avr32_hpt_read(); avr32_timer_ack(); @@ -164,7 +169,7 @@ static struct irqaction timer_irqaction = { .handler = timer_interrupt, - .flags = IRQF_DISABLED, + .flags = IRQF_DISABLED|IRQF_SHARED, .name = "timer", }; diff -urN linux-2.6.20.4-0rig/arch/avr32/kernel/traps.c linux-2.6.20.4-atmel/arch/avr32/kernel/traps.c --- linux-2.6.20.4-0rig/arch/avr32/kernel/traps.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/avr32/kernel/traps.c 2007-03-24 16:42:28.000000000 +0100 @@ -49,39 +49,45 @@ return; } +static inline int valid_stack_ptr(struct thread_info *tinfo, unsigned long p) +{ + return (p > (unsigned long)tinfo) + && (p < (unsigned long)tinfo + THREAD_SIZE - 3); +} + #ifdef CONFIG_FRAME_POINTER static inline void __show_trace(struct task_struct *tsk, unsigned long *sp, struct pt_regs *regs) { - unsigned long __user *fp; - unsigned long __user *last_fp = NULL; + unsigned long lr, fp; + struct thread_info *tinfo; - if (regs) { - fp = (unsigned long __user *)regs->r7; - } else if (tsk == current) { - register unsigned long __user *real_fp __asm__("r7"); - fp = real_fp; - } else { - fp = (unsigned long __user *)tsk->thread.cpu_context.r7; - } + tinfo = (struct thread_info *) + ((unsigned long)sp & ~(THREAD_SIZE - 1)); + + if (regs) + fp = regs->r7; + else if (tsk == current) + asm("mov %0, r7" : "=r"(fp)); + else + fp = tsk->thread.cpu_context.r7; /* - * Walk the stack until (a) we get an exception, (b) the frame - * pointer becomes zero, or (c) the frame pointer gets stuck - * at the same value. + * Walk the stack as long as the frame pointer (a) is within + * the kernel stack of the task, and (b) it doesn't move + * downwards. */ - while (fp && fp != last_fp) { - unsigned long lr, new_fp = 0; - - last_fp = fp; - if (__get_user(lr, fp)) - break; - if (fp && __get_user(new_fp, fp + 1)) - break; - fp = (unsigned long __user *)new_fp; + while (valid_stack_ptr(tinfo, fp)) { + unsigned long new_fp; + lr = *(unsigned long *)fp; printk(" [<%08lx>] ", lr); print_symbol("%s\n", lr); + + new_fp = *(unsigned long *)(fp + 4); + if (new_fp <= fp) + break; + fp = new_fp; } printk("\n"); } diff -urN linux-2.6.20.4-0rig/arch/avr32/lib/libgcc.h linux-2.6.20.4-atmel/arch/avr32/lib/libgcc.h --- linux-2.6.20.4-0rig/arch/avr32/lib/libgcc.h 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/avr32/lib/libgcc.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,33 +0,0 @@ -/* Definitions for various functions 'borrowed' from gcc-3.4.3 */ - -#define BITS_PER_UNIT 8 - -typedef int QItype __attribute__ ((mode (QI))); -typedef unsigned int UQItype __attribute__ ((mode (QI))); -typedef int HItype __attribute__ ((mode (HI))); -typedef unsigned int UHItype __attribute__ ((mode (HI))); -typedef int SItype __attribute__ ((mode (SI))); -typedef unsigned int USItype __attribute__ ((mode (SI))); -typedef int DItype __attribute__ ((mode (DI))); -typedef unsigned int UDItype __attribute__ ((mode (DI))); -typedef float SFtype __attribute__ ((mode (SF))); -typedef float DFtype __attribute__ ((mode (DF))); -typedef int word_type __attribute__ ((mode (__word__))); - -#define W_TYPE_SIZE (4 * BITS_PER_UNIT) -#define Wtype SItype -#define UWtype USItype -#define HWtype SItype -#define UHWtype USItype -#define DWtype DItype -#define UDWtype UDItype -#define __NW(a,b) __ ## a ## si ## b -#define __NDW(a,b) __ ## a ## di ## b - -struct DWstruct {Wtype high, low;}; - -typedef union -{ - struct DWstruct s; - DWtype ll; -} DWunion; diff -urN linux-2.6.20.4-0rig/arch/avr32/lib/longlong.h linux-2.6.20.4-atmel/arch/avr32/lib/longlong.h --- linux-2.6.20.4-0rig/arch/avr32/lib/longlong.h 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/avr32/lib/longlong.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,98 +0,0 @@ -/* longlong.h -- definitions for mixed size 32/64 bit arithmetic. - Copyright (C) 1991, 1992, 1994, 1995, 1996, 1997, 1998, 1999, 2000 - Free Software Foundation, Inc. - - This definition file 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, or (at your option) any later version. - - This definition file 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. */ - -/* Borrowed from gcc-3.4.3 */ - -#define __BITS4 (W_TYPE_SIZE / 4) -#define __ll_B ((UWtype) 1 << (W_TYPE_SIZE / 2)) -#define __ll_lowpart(t) ((UWtype) (t) & (__ll_B - 1)) -#define __ll_highpart(t) ((UWtype) (t) >> (W_TYPE_SIZE / 2)) - -#define count_leading_zeros(count, x) ((count) = __builtin_clz(x)) - -#define __udiv_qrnnd_c(q, r, n1, n0, d) \ - do { \ - UWtype __d1, __d0, __q1, __q0; \ - UWtype __r1, __r0, __m; \ - __d1 = __ll_highpart (d); \ - __d0 = __ll_lowpart (d); \ - \ - __r1 = (n1) % __d1; \ - __q1 = (n1) / __d1; \ - __m = (UWtype) __q1 * __d0; \ - __r1 = __r1 * __ll_B | __ll_highpart (n0); \ - if (__r1 < __m) \ - { \ - __q1--, __r1 += (d); \ - if (__r1 >= (d)) /* i.e. we didn't get carry when adding to __r1 */\ - if (__r1 < __m) \ - __q1--, __r1 += (d); \ - } \ - __r1 -= __m; \ - \ - __r0 = __r1 % __d1; \ - __q0 = __r1 / __d1; \ - __m = (UWtype) __q0 * __d0; \ - __r0 = __r0 * __ll_B | __ll_lowpart (n0); \ - if (__r0 < __m) \ - { \ - __q0--, __r0 += (d); \ - if (__r0 >= (d)) \ - if (__r0 < __m) \ - __q0--, __r0 += (d); \ - } \ - __r0 -= __m; \ - \ - (q) = (UWtype) __q1 * __ll_B | __q0; \ - (r) = __r0; \ - } while (0) - -#define udiv_qrnnd __udiv_qrnnd_c - -#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ - do { \ - UWtype __x; \ - __x = (al) - (bl); \ - (sh) = (ah) - (bh) - (__x > (al)); \ - (sl) = __x; \ - } while (0) - -#define umul_ppmm(w1, w0, u, v) \ - do { \ - UWtype __x0, __x1, __x2, __x3; \ - UHWtype __ul, __vl, __uh, __vh; \ - \ - __ul = __ll_lowpart (u); \ - __uh = __ll_highpart (u); \ - __vl = __ll_lowpart (v); \ - __vh = __ll_highpart (v); \ - \ - __x0 = (UWtype) __ul * __vl; \ - __x1 = (UWtype) __ul * __vh; \ - __x2 = (UWtype) __uh * __vl; \ - __x3 = (UWtype) __uh * __vh; \ - \ - __x1 += __ll_highpart (__x0);/* this can't give carry */ \ - __x1 += __x2; /* but this indeed can */ \ - if (__x1 < __x2) /* did we get it? */ \ - __x3 += __ll_B; /* yes, add it in the proper pos. */ \ - \ - (w1) = __x3 + __ll_highpart (__x1); \ - (w0) = __ll_lowpart (__x1) * __ll_B + __ll_lowpart (__x0); \ - } while (0) diff -urN linux-2.6.20.4-0rig/arch/avr32/mach-at32ap/at32ap7000.c linux-2.6.20.4-atmel/arch/avr32/mach-at32ap/at32ap7000.c --- linux-2.6.20.4-0rig/arch/avr32/mach-at32ap/at32ap7000.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/avr32/mach-at32ap/at32ap7000.c 2007-03-24 16:42:29.000000000 +0100 @@ -8,6 +8,7 @@ #include <linux/clk.h> #include <linux/init.h> #include <linux/platform_device.h> +#include <linux/spi/spi.h> #include <asm/io.h> @@ -310,8 +311,6 @@ { u32 control; - BUG_ON(clk->index > 7); - control = sm_readl(&system_manager, PM_GCCTRL + 4 * clk->index); if (enabled) control |= SM_BIT(CEN); @@ -325,11 +324,6 @@ u32 control; unsigned long div = 1; - BUG_ON(clk->index > 7); - - if (!clk->parent) - return 0; - control = sm_readl(&system_manager, PM_GCCTRL + 4 * clk->index); if (control & SM_BIT(DIVEN)) div = 2 * (SM_BFEXT(DIV, control) + 1); @@ -342,11 +336,6 @@ u32 control; unsigned long parent_rate, actual_rate, div; - BUG_ON(clk->index > 7); - - if (!clk->parent) - return 0; - parent_rate = clk->parent->get_rate(clk->parent); control = sm_readl(&system_manager, PM_GCCTRL + 4 * clk->index); @@ -373,11 +362,8 @@ { u32 control; - BUG_ON(clk->index > 7); - printk("clk %s: new parent %s (was %s)\n", - clk->name, parent->name, - clk->parent ? clk->parent->name : "(null)"); + clk->name, parent->name, clk->parent->name); control = sm_readl(&system_manager, PM_GCCTRL + 4 * clk->index); @@ -399,6 +385,22 @@ return 0; } +static void __init genclk_init_parent(struct clk *clk) +{ + u32 control; + struct clk *parent; + + BUG_ON(clk->index > 7); + + control = sm_readl(&system_manager, PM_GCCTRL + 4 * clk->index); + if (control & SM_BIT(OSCSEL)) + parent = (control & SM_BIT(PLLSEL)) ? &pll1 : &osc1; + else + parent = (control & SM_BIT(PLLSEL)) ? &pll0 : &osc0; + + clk->parent = parent; +} + /* -------------------------------------------------------------------- * System peripherals * -------------------------------------------------------------------- */ @@ -464,6 +466,17 @@ .users = 1, }; +static struct resource dmaca0_resource[] = { + { + .start = 0xff200000, + .end = 0xff20ffff, + .flags = IORESOURCE_MEM, + }, + IRQ(2), +}; +DEFINE_DEV(dmaca, 0); +DEV_CLK(hclk, dmaca0, hsb, 10); + /* -------------------------------------------------------------------- * PIO * -------------------------------------------------------------------- */ @@ -496,19 +509,28 @@ DEFINE_DEV(pio, 3); DEV_CLK(mck, pio3, pba, 13); +static struct resource pio4_resource[] = { + PBMEM(0xffe03800), + IRQ(17), +}; +DEFINE_DEV(pio, 4); +DEV_CLK(mck, pio4, pba, 14); + void __init at32_add_system_devices(void) { - system_manager.eim_first_irq = NR_INTERNAL_IRQS; + system_manager.eim_first_irq = EIM_IRQ_BASE; platform_device_register(&at32_sm_device); platform_device_register(&at32_intc0_device); platform_device_register(&smc0_device); platform_device_register(&pdc_device); + platform_device_register(&dmaca0_device); platform_device_register(&pio0_device); platform_device_register(&pio1_device); platform_device_register(&pio2_device); platform_device_register(&pio3_device); + platform_device_register(&pio4_device); } /* -------------------------------------------------------------------- @@ -521,7 +543,7 @@ }; static struct resource atmel_usart0_resource[] = { PBMEM(0xffe00c00), - IRQ(7), + IRQ(6), }; DEFINE_DEV_DATA(atmel_usart, 0); DEV_CLK(usart, atmel_usart0, pba, 4); @@ -583,7 +605,7 @@ select_peripheral(PB(17), PERIPH_B, 0); /* TXD */ } -static struct platform_device *at32_usarts[4]; +static struct platform_device *__initdata at32_usarts[4]; void __init at32_map_usart(unsigned int hw_id, unsigned int line) { @@ -728,32 +750,156 @@ /* -------------------------------------------------------------------- * SPI * -------------------------------------------------------------------- */ -static struct resource spi0_resource[] = { +static struct resource atmel_spi0_resource[] = { PBMEM(0xffe00000), IRQ(3), }; -DEFINE_DEV(spi, 0); -DEV_CLK(mck, spi0, pba, 0); +DEFINE_DEV(atmel_spi, 0); +DEV_CLK(spi_clk, atmel_spi0, pba, 0); + +static struct resource atmel_spi1_resource[] = { + PBMEM(0xffe00400), + IRQ(4), +}; +DEFINE_DEV(atmel_spi, 1); +DEV_CLK(spi_clk, atmel_spi1, pba, 1); + +static void __init +at32_spi_setup_slaves(unsigned int bus_num, struct spi_board_info *b, + unsigned int n, const u8 *pins) +{ + unsigned int pin, mode; + + for (; n; n--, b++) { + b->bus_num = bus_num; + if (b->chip_select >= 4) + continue; + pin = (unsigned)b->controller_data; + if (!pin) { + pin = pins[b->chip_select]; + b->controller_data = (void *)pin; + } + mode = AT32_GPIOF_OUTPUT; + if (!(b->mode & SPI_CS_HIGH)) + mode |= AT32_GPIOF_HIGH; + at32_select_gpio(pin, mode); + } +} -struct platform_device *__init at32_add_device_spi(unsigned int id) +struct platform_device *__init +at32_add_device_spi(unsigned int id, struct spi_board_info *b, unsigned int n) { + /* + * Manage the chipselects as GPIOs, normally using the same pins + * the SPI controller expects; but boards can use other pins. + */ + static u8 __initdata spi0_pins[] = + { GPIO_PIN_PA(3), GPIO_PIN_PA(4), + GPIO_PIN_PA(5), GPIO_PIN_PA(20), }; + static u8 __initdata spi1_pins[] = + { GPIO_PIN_PB(2), GPIO_PIN_PB(3), + GPIO_PIN_PB(4), GPIO_PIN_PA(27), }; struct platform_device *pdev; switch (id) { case 0: - pdev = &spi0_device; + pdev = &atmel_spi0_device; select_peripheral(PA(0), PERIPH_A, 0); /* MISO */ select_peripheral(PA(1), PERIPH_A, 0); /* MOSI */ select_peripheral(PA(2), PERIPH_A, 0); /* SCK */ - select_peripheral(PA(3), PERIPH_A, 0); /* NPCS0 */ - select_peripheral(PA(4), PERIPH_A, 0); /* NPCS1 */ - select_peripheral(PA(5), PERIPH_A, 0); /* NPCS2 */ + at32_spi_setup_slaves(0, b, n, spi0_pins); + break; + + case 1: + pdev = &atmel_spi1_device; + select_peripheral(PB(0), PERIPH_B, 0); /* MISO */ + select_peripheral(PB(1), PERIPH_B, 0); /* MOSI */ + select_peripheral(PB(5), PERIPH_B, 0); /* SCK */ + at32_spi_setup_slaves(1, b, n, spi1_pins); break; default: return NULL; } + spi_register_board_info(b, n); + platform_device_register(pdev); + return pdev; +} + +/* -------------------------------------------------------------------- + * TWI + * -------------------------------------------------------------------- */ + +static struct resource atmel_twi0_resource[] = { + PBMEM(0xffe00800), + IRQ(5), +}; +DEFINE_DEV(atmel_twi, 0); +DEV_CLK(pclk,atmel_twi0,pba,2); + +struct platform_device *__init +at32_add_device_twi(unsigned int id) +{ + struct platform_device *pdev; + + switch (id) { + case 0: + pdev = &atmel_twi0_device; + select_peripheral(PA(6), PERIPH_A, 0); /* SDA */ + select_peripheral(PA(7), PERIPH_A, 0); /* SCL */ + break; + + default: + return NULL; + } + + platform_device_register(pdev); + return pdev; +} + +/* -------------------------------------------------------------------- + * MMC + * -------------------------------------------------------------------- */ +static struct mci_platform_data atmel_mci0_data = { + .detect_pin = GPIO_PIN_NONE, + .wp_pin = GPIO_PIN_NONE, +}; +static struct resource atmel_mci0_resource[] = { + PBMEM(0xfff02400), + IRQ(28), +}; +DEFINE_DEV_DATA(atmel_mci, 0); +DEV_CLK(mci_clk, atmel_mci0, pbb, 9); + +struct platform_device *__init +at32_add_device_mci(unsigned int id, struct mci_platform_data *data) +{ + struct platform_device *pdev; + + switch (id) { + case 0: + pdev = &atmel_mci0_device; + select_peripheral(PA(10), PERIPH_A, 0); /* CLK */ + select_peripheral(PA(11), PERIPH_A, 0); /* CMD */ + select_peripheral(PA(12), PERIPH_A, 0); /* DATA0 */ + select_peripheral(PA(13), PERIPH_A, 0); /* DATA1 */ + select_peripheral(PA(14), PERIPH_A, 0); /* DATA2 */ + select_peripheral(PA(15), PERIPH_A, 0); /* DATA3 */ + break; + default: + return NULL; + } + + if (data) { + if (data->detect_pin != GPIO_PIN_NONE) + at32_select_gpio(data->detect_pin, 0); + if (data->wp_pin != GPIO_PIN_NONE) + at32_select_gpio(data->wp_pin, 0); + memcpy(pdev->dev.platform_data, data, + sizeof(struct mci_platform_data)); + } + platform_device_register(pdev); return pdev; } @@ -837,6 +983,199 @@ return pdev; } +/* -------------------------------------------------------------------- + * USB Device Controller + * -------------------------------------------------------------------- */ +static struct resource usba0_resource[] = { + { + .start = 0xff300000, + .end = 0xff3fffff, + .flags = IORESOURCE_MEM, + }, + PBMEM(0xfff03000), + IRQ(31), +}; +DEFINE_DEV(usba, 0); +DEV_CLK(pclk, usba0, pbb, 12); +DEV_CLK(hclk, usba0, hsb, 6); + +struct platform_device *__init at32_add_device_usba(unsigned int id) +{ + struct platform_device *pdev; + + switch (id) { + case 0: + pdev = &usba0_device; + /* USB pads are not multiplexed */ + break; + default: + return NULL; + } + + platform_device_register(pdev); + return pdev; +} + +/* -------------------------------------------------------------------- + * AC97C + * -------------------------------------------------------------------- */ +static struct resource atmel_ac97c0_resource[] = { + PBMEM(0xfff02800), + IRQ(29), +}; +DEFINE_DEV(atmel_ac97c, 0); +DEV_CLK(pclk, atmel_ac97c0, pbb, 10); + +struct platform_device *__init +at32_add_device_ac97c(unsigned int id) +{ + struct platform_device *pdev; + + switch (id) { + case 0: + pdev = &atmel_ac97c0_device; + select_peripheral(PB(20), PERIPH_B, 0); /* SYNC */ + select_peripheral(PB(21), PERIPH_B, 0); /* SDO */ + select_peripheral(PB(22), PERIPH_B, 0); /* SDI */ + select_peripheral(PB(23), PERIPH_B, 0); /* SCLK */ + break; + default: + return NULL; + } + + platform_device_register(pdev); + return pdev; +} + +/* -------------------------------------------------------------------- + * DAC + * -------------------------------------------------------------------- */ +static struct resource abdac0_resource[] = { + PBMEM(0xfff02000), + IRQ(27), +}; +DEFINE_DEV(abdac, 0); +DEV_CLK(pclk, abdac0, pbb, 8); +static struct clk abdac0_sample_clk = { + .name = "sample_clk", + .dev = &abdac0_device.dev, + .mode = genclk_mode, + .get_rate = genclk_get_rate, + .set_rate = genclk_set_rate, + .set_parent = genclk_set_parent, + .index = 6, +}; + +struct platform_device *__init +at32_add_device_abdac(unsigned int id) +{ + struct platform_device *pdev; + + switch (id) { + case 0: + pdev = &abdac0_device; + select_peripheral(PB(20), PERIPH_A, 0); /* DATA1 */ + select_peripheral(PB(21), PERIPH_A, 0); /* DATA0 */ + select_peripheral(PB(22), PERIPH_A, 0); /* DATAN1 */ + select_peripheral(PB(23), PERIPH_A, 0); /* DATAN0 */ + break; + default: + return NULL; + } + + platform_device_register(pdev); + return pdev; +} + +/* -------------------------------------------------------------------- + * ISI + * -------------------------------------------------------------------- */ +static struct resource atmel_isi0_resource[] = { + PBMEM(0xfff02c00), + IRQ(30), +}; +DEFINE_DEV(atmel_isi, 0); +DEV_CLK(hclk, atmel_isi0, hsb, 5); +DEV_CLK(pclk, atmel_isi0, pbb, 11); + +struct platform_device *__init +at32_add_device_isi(unsigned int id) +{ + struct platform_device *pdev; + + switch (id) { + case 0: + pdev = &atmel_isi0_device; + select_peripheral(PB(0), PERIPH_A, 0); /* DATA0 */ + select_peripheral(PB(1), PERIPH_A, 0); /* DATA1 */ + select_peripheral(PB(2), PERIPH_A, 0); /* DATA2 */ + select_peripheral(PB(3), PERIPH_A, 0); /* DATA3 */ + select_peripheral(PB(4), PERIPH_A, 0); /* DATA4 */ + select_peripheral(PB(5), PERIPH_A, 0); /* DATA5 */ + select_peripheral(PB(6), PERIPH_A, 0); /* DATA6 */ + select_peripheral(PB(7), PERIPH_A, 0); /* DATA7 */ + select_peripheral(PB(11), PERIPH_B, 0); /* DATA8 */ + select_peripheral(PB(12), PERIPH_B, 0); /* DATA9 */ + select_peripheral(PB(13), PERIPH_B, 0); /* DATA10 */ + select_peripheral(PB(14), PERIPH_B, 0); /* DATA11 */ + select_peripheral(PB(8), PERIPH_A, 0); /* HSYNC */ + select_peripheral(PB(9), PERIPH_A, 0); /* VSYNC */ + select_peripheral(PB(10), PERIPH_A, 0); /* PCLK */ + break; + + default: + return NULL; + } + + platform_device_register(pdev); + + return pdev; +} + +/* -------------------------------------------------------------------- + * GCLK + * -------------------------------------------------------------------- */ +static struct clk gclk0 = { + .name = "gclk0", + .mode = genclk_mode, + .get_rate = genclk_get_rate, + .set_rate = genclk_set_rate, + .set_parent = genclk_set_parent, + .index = 0, +}; +static struct clk gclk1 = { + .name = "gclk1", + .mode = genclk_mode, + .get_rate = genclk_get_rate, + .set_rate = genclk_set_rate, + .set_parent = genclk_set_parent, + .index = 1, +}; +static struct clk gclk2 = { + .name = "gclk2", + .mode = genclk_mode, + .get_rate = genclk_get_rate, + .set_rate = genclk_set_rate, + .set_parent = genclk_set_parent, + .index = 2, +}; +static struct clk gclk3 = { + .name = "gclk3", + .mode = genclk_mode, + .get_rate = genclk_get_rate, + .set_rate = genclk_set_rate, + .set_parent = genclk_set_parent, + .index = 3, +}; +static struct clk gclk4 = { + .name = "gclk4", + .mode = genclk_mode, + .get_rate = genclk_get_rate, + .set_rate = genclk_set_rate, + .set_parent = genclk_set_parent, + .index = 4, +}; + struct clk *at32_clock_list[] = { &osc32k, &osc0, @@ -855,11 +1194,13 @@ &smc0_mck, &pdc_hclk, &pdc_pclk, + &dmaca0_hclk, &pico_clk, &pio0_mck, &pio1_mck, &pio2_mck, &pio3_mck, + &pio4_mck, &atmel_usart0_usart, &atmel_usart1_usart, &atmel_usart2_usart, @@ -868,9 +1209,24 @@ &macb0_pclk, &macb1_hclk, &macb1_pclk, - &spi0_mck, + &atmel_spi0_spi_clk, + &atmel_spi1_spi_clk, + &atmel_twi0_pclk, + &atmel_mci0_mci_clk, &lcdc0_hclk, &lcdc0_pixclk, + &usba0_pclk, + &usba0_hclk, + &atmel_ac97c0_pclk, + &abdac0_pclk, + &abdac0_sample_clk, + &gclk0, + &gclk1, + &gclk2, + &gclk3, + &gclk4, + &atmel_isi0_hclk, + &atmel_isi0_pclk, }; unsigned int at32_nr_clocks = ARRAY_SIZE(at32_clock_list); @@ -880,6 +1236,7 @@ at32_init_pio(&pio1_device); at32_init_pio(&pio2_device); at32_init_pio(&pio3_device); + at32_init_pio(&pio4_device); } void __init at32_clock_init(void) @@ -898,6 +1255,14 @@ if (sm_readl(sm, PM_PLL1) & SM_BIT(PLLOSC)) pll1.parent = &osc1; + genclk_init_parent(&gclk0); + genclk_init_parent(&gclk1); + genclk_init_parent(&gclk2); + genclk_init_parent(&gclk3); + genclk_init_parent(&gclk4); + genclk_init_parent(&lcdc0_pixclk); + genclk_init_parent(&abdac0_sample_clk); + /* * Turn on all clocks that have at least one user already, and * turn off everything else. We only do this for module diff -urN linux-2.6.20.4-0rig/arch/avr32/mach-at32ap/clock.c linux-2.6.20.4-atmel/arch/avr32/mach-at32ap/clock.c --- linux-2.6.20.4-0rig/arch/avr32/mach-at32ap/clock.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/avr32/mach-at32ap/clock.c 2007-03-24 16:42:28.000000000 +0100 @@ -63,7 +63,11 @@ static void __clk_disable(struct clk *clk) { - BUG_ON(clk->users == 0); + if (clk->users == 0) { + printk(KERN_ERR "%s: mismatched disable\n", clk->name); + WARN_ON(1); + return; + } if (--clk->users == 0 && clk->mode) clk->mode(clk, 0); diff -urN linux-2.6.20.4-0rig/arch/avr32/mach-at32ap/extint.c linux-2.6.20.4-atmel/arch/avr32/mach-at32ap/extint.c --- linux-2.6.20.4-0rig/arch/avr32/mach-at32ap/extint.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/avr32/mach-at32ap/extint.c 2007-03-24 16:42:28.000000000 +0100 @@ -55,20 +55,11 @@ unsigned long flags; int ret = 0; + flow_type &= IRQ_TYPE_SENSE_MASK; if (flow_type == IRQ_TYPE_NONE) flow_type = IRQ_TYPE_LEVEL_LOW; desc = &irq_desc[irq]; - desc->status &= ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL); - desc->status |= flow_type & IRQ_TYPE_SENSE_MASK; - - if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) { - desc->status |= IRQ_LEVEL; - set_irq_handler(irq, handle_level_irq); - } else { - set_irq_handler(irq, handle_edge_irq); - } - spin_lock_irqsave(&sm->lock, flags); mode = sm_readl(sm, EIM_MODE); @@ -97,9 +88,16 @@ break; } - sm_writel(sm, EIM_MODE, mode); - sm_writel(sm, EIM_EDGE, edge); - sm_writel(sm, EIM_LEVEL, level); + if (ret == 0) { + sm_writel(sm, EIM_MODE, mode); + sm_writel(sm, EIM_EDGE, edge); + sm_writel(sm, EIM_LEVEL, level); + + if (flow_type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) + flow_type |= IRQ_LEVEL; + desc->status &= ~(IRQ_TYPE_SENSE_MASK | IRQ_LEVEL); + desc->status |= flow_type; + } spin_unlock_irqrestore(&sm->lock, flags); @@ -122,8 +120,6 @@ unsigned long status, pending; unsigned int i, ext_irq; - spin_lock(&sm->lock); - status = sm_readl(sm, EIM_ISR); pending = status & sm_readl(sm, EIM_IMR); @@ -133,10 +129,11 @@ ext_irq = i + sm->eim_first_irq; ext_desc = irq_desc + ext_irq; - ext_desc->handle_irq(ext_irq, ext_desc); + if (ext_desc->status & IRQ_LEVEL) + handle_level_irq(ext_irq, ext_desc); + else + handle_edge_irq(ext_irq, ext_desc); } - - spin_unlock(&sm->lock); } static int __init eim_init(void) @@ -168,8 +165,9 @@ sm->eim_chip = &eim_chip; for (i = 0; i < nr_irqs; i++) { + /* NOTE the handler we set here is ignored by the demux */ set_irq_chip_and_handler(sm->eim_first_irq + i, &eim_chip, - handle_edge_irq); + handle_level_irq); set_irq_chip_data(sm->eim_first_irq + i, sm); } diff -urN linux-2.6.20.4-0rig/arch/avr32/mach-at32ap/hsmc.c linux-2.6.20.4-atmel/arch/avr32/mach-at32ap/hsmc.c --- linux-2.6.20.4-0rig/arch/avr32/mach-at32ap/hsmc.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/avr32/mach-at32ap/hsmc.c 2007-03-24 16:42:29.000000000 +0100 @@ -75,12 +75,35 @@ return -EINVAL; } + switch (config->nwait_mode) { + case 0: + mode |= HSMC_BF(EXNW_MODE, HSMC_EXNW_MODE_DISABLED); + break; + case 1: + mode |= HSMC_BF(EXNW_MODE, HSMC_EXNW_MODE_RESERVED); + break; + case 2: + mode |= HSMC_BF(EXNW_MODE, HSMC_EXNW_MODE_FROZEN); + break; + case 3: + mode |= HSMC_BF(EXNW_MODE, HSMC_EXNW_MODE_READY); + break; + default: + return -EINVAL; + } + + if (config->tdf_cycles) { + mode |= HSMC_BF(TDF_CYCLES, config->tdf_cycles); + } + if (config->nrd_controlled) mode |= HSMC_BIT(READ_MODE); if (config->nwe_controlled) mode |= HSMC_BIT(WRITE_MODE); if (config->byte_write) mode |= HSMC_BIT(BAT); + if (config->tdf_mode) + mode |= HSMC_BIT(TDF_MODE); pr_debug("smc cs%d: setup/%08x pulse/%08x cycle/%08x mode/%08x\n", cs, setup, pulse, cycle, mode); diff -urN linux-2.6.20.4-0rig/arch/avr32/mach-at32ap/Kconfig linux-2.6.20.4-atmel/arch/avr32/mach-at32ap/Kconfig --- linux-2.6.20.4-0rig/arch/avr32/mach-at32ap/Kconfig 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/avr32/mach-at32ap/Kconfig 2007-03-24 16:42:29.000000000 +0100 @@ -0,0 +1,15 @@ +if PLATFORM_AT32AP + +menu "Atmel AVR32 AP options" + +config PIO_DEV + bool "PIO /dev interface" + select CONFIGFS_FS + default y + help + Say `Y' to enable a /dev interface to the Parallel I/O + Controller. + +endmenu + +endif diff -urN linux-2.6.20.4-0rig/arch/avr32/mach-at32ap/Makefile linux-2.6.20.4-atmel/arch/avr32/mach-at32ap/Makefile --- linux-2.6.20.4-0rig/arch/avr32/mach-at32ap/Makefile 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/avr32/mach-at32ap/Makefile 2007-03-24 16:42:28.000000000 +0100 @@ -1,2 +1,2 @@ -obj-y += at32ap.o clock.o pio.o intc.o extint.o hsmc.o +obj-y += at32ap.o clock.o intc.o extint.o pio.o hsmc.o obj-$(CONFIG_CPU_AT32AP7000) += at32ap7000.o diff -urN linux-2.6.20.4-0rig/arch/avr32/mach-at32ap/pio.c linux-2.6.20.4-atmel/arch/avr32/mach-at32ap/pio.c --- linux-2.6.20.4-0rig/arch/avr32/mach-at32ap/pio.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/avr32/mach-at32ap/pio.c 2007-03-24 16:42:29.000000000 +0100 @@ -12,7 +12,9 @@ #include <linux/debugfs.h> #include <linux/fs.h> #include <linux/platform_device.h> +#include <linux/irq.h> +#include <asm/gpio.h> #include <asm/io.h> #include <asm/arch/portmux.h> @@ -23,10 +25,11 @@ struct pio_device { void __iomem *regs; - const struct platform_device *pdev; + struct platform_device *pdev; struct clk *clk; u32 pinmux_mask; - char name[32]; + u32 gpio_mask; + char name[8]; }; static struct pio_device pio_dev[MAX_NR_PIO_DEVICES]; @@ -76,6 +79,9 @@ if (!(flags & AT32_GPIOF_PULLUP)) pio_writel(pio, PUDR, mask); + /* gpio_request NOT allowed */ + set_bit(pin_index, &pio->gpio_mask); + return; fail: @@ -99,19 +105,52 @@ goto fail; } - pio_writel(pio, PUER, mask); - if (flags & AT32_GPIOF_HIGH) - pio_writel(pio, SODR, mask); - else - pio_writel(pio, CODR, mask); - if (flags & AT32_GPIOF_OUTPUT) + if (flags & AT32_GPIOF_OUTPUT) { + if (flags & AT32_GPIOF_HIGH) + pio_writel(pio, SODR, mask); + else + pio_writel(pio, CODR, mask); + pio_writel(pio, PUDR, mask); pio_writel(pio, OER, mask); - else + } else { + if (flags & AT32_GPIOF_PULLUP) + pio_writel(pio, PUER, mask); + else + pio_writel(pio, PUDR, mask); + if (flags & AT32_GPIOF_DEGLITCH) + pio_writel(pio, IFER, mask); + else + pio_writel(pio, IFDR, mask); pio_writel(pio, ODR, mask); + } pio_writel(pio, PER, mask); - if (!(flags & AT32_GPIOF_PULLUP)) - pio_writel(pio, PUDR, mask); + + /* gpio_request now allowed */ + clear_bit(pin_index, &pio->gpio_mask); + + return; + +fail: + dump_stack(); +} + +/* Reserve a pin, preventing anyone else from changing its configuration. */ +void __init at32_reserve_pin(unsigned int pin) +{ + struct pio_device *pio; + unsigned int pin_index = pin & 0x1f; + + pio = gpio_to_pio(pin); + if (unlikely(!pio)) { + printk("pio: invalid pin %u\n", pin); + goto fail; + } + + if (unlikely(test_and_set_bit(pin_index, &pio->pinmux_mask))) { + printk("%s: pin %u is busy\n", pio->name, pin_index); + goto fail; + } return; @@ -119,20 +158,793 @@ dump_stack(); } +/*--------------------------------------------------------------------------*/ + +static unsigned int pio_id(struct pio_device *pio) +{ + return pio - pio_dev; +} + +static void __disable_gpio(struct pio_device *pio, u32 mask) +{ + pio_writel(pio, PUER, mask); + pio_writel(pio, ODR, mask); +} + +static void pio_dealloc_mask(struct pio_device *pio, u32 mask) +{ + u32 old, new; + + do { + old = pio->pinmux_mask; + new = old & ~mask; + } while (cmpxchg(&pio->pinmux_mask, old, new) != old); +} + +/* GPIO API */ + +int gpio_request(unsigned int gpio, const char *label) +{ + struct pio_device *pio; + unsigned int pin; + + pio = gpio_to_pio(gpio); + if (!pio) + return -ENODEV; + + pin = gpio & 0x1f; + if (test_and_set_bit(pin, &pio->gpio_mask)) + return -EBUSY; + + return 0; +} +EXPORT_SYMBOL(gpio_request); + +void gpio_free(unsigned int gpio) +{ + struct pio_device *pio; + unsigned int pin; + + pio = gpio_to_pio(gpio); + if (!pio) { + printk(KERN_ERR + "gpio: attempted to free invalid pin %u\n", gpio); + return; + } + + pin = gpio & 0x1f; + if (!test_and_clear_bit(pin, &pio->gpio_mask)) + printk(KERN_ERR "gpio: freeing free or non-gpio pin %s-%u\n", + pio->name, pin); +} +EXPORT_SYMBOL(gpio_free); + +int gpio_direction_input(unsigned int gpio) +{ + struct pio_device *pio; + unsigned int pin; + + pio = gpio_to_pio(gpio); + if (!pio) + return -ENODEV; + + pin = gpio & 0x1f; + pio_writel(pio, ODR, 1 << pin); + + return 0; +} +EXPORT_SYMBOL(gpio_direction_input); + +int gpio_direction_output(unsigned int gpio) +{ + struct pio_device *pio; + unsigned int pin; + + pio = gpio_to_pio(gpio); + if (!pio) + return -ENODEV; + + pin = gpio & 0x1f; + pio_writel(pio, OER, 1 << pin); + + return 0; +} +EXPORT_SYMBOL(gpio_direction_output); + +int gpio_get_value(unsigned int gpio) +{ + struct pio_device *pio = &pio_dev[gpio >> 5]; + + return (pio_readl(pio, PDSR) >> (gpio & 0x1f)) & 1; +} +EXPORT_SYMBOL(gpio_get_value); + +void gpio_set_value(unsigned int gpio, int value) +{ + struct pio_device *pio = &pio_dev[gpio >> 5]; + u32 mask; + + mask = 1 << (gpio & 0x1f); + if (value) + pio_writel(pio, SODR, mask); + else + pio_writel(pio, CODR, mask); +} +EXPORT_SYMBOL(gpio_set_value); + +/*--------------------------------------------------------------------------*/ + +/* GPIO IRQ support */ + +static void gpio_irq_mask(unsigned irq) +{ + unsigned gpio = irq_to_gpio(irq); + struct pio_device *pio = &pio_dev[gpio >> 5]; + + pio_writel(pio, IDR, 1 << (gpio & 0x1f)); +} + +static void gpio_irq_unmask(unsigned irq) +{ + unsigned gpio = irq_to_gpio(irq); + struct pio_device *pio = &pio_dev[gpio >> 5]; + + pio_writel(pio, IER, 1 << (gpio & 0x1f)); +} + +static int gpio_irq_type(unsigned irq, unsigned type) +{ + if (type != IRQ_TYPE_EDGE_BOTH && type != IRQ_TYPE_NONE) + return -EINVAL; + + return 0; +} + +static struct irq_chip gpio_irqchip = { + .name = "gpio", + .mask = gpio_irq_mask, + .unmask = gpio_irq_unmask, + .set_type = gpio_irq_type, +}; + +static void gpio_irq_handler(unsigned irq, struct irq_desc *desc) +{ + struct pio_device *pio = get_irq_chip_data(irq); + unsigned gpio_irq; + + gpio_irq = (unsigned) get_irq_data(irq); + for (;;) { + u32 isr; + struct irq_desc *d; + + /* ack pending GPIO interrupts */ + isr = pio_readl(pio, ISR) & pio_readl(pio, IMR); + if (!isr) + break; + do { + int i; + + i = ffs(isr) - 1; + isr &= ~(1 << i); + + i += gpio_irq; + d = &irq_desc[i]; + + d->handle_irq(i, d); + } while (isr); + } +} + +static void __init +gpio_irq_setup(struct pio_device *pio, int irq, int gpio_irq) +{ + unsigned i; + + set_irq_chip_data(irq, pio); + set_irq_data(irq, (void *) gpio_irq); + + for (i = 0; i < 32; i++, gpio_irq++) { + set_irq_chip_data(gpio_irq, pio); + set_irq_chip_and_handler(gpio_irq, &gpio_irqchip, + handle_simple_irq); + } + + set_irq_chained_handler(irq, gpio_irq_handler); +} + +/*--------------------------------------------------------------------------*/ + +#ifdef CONFIG_PIO_DEV +#include <linux/configfs.h> +#include <linux/cdev.h> +#include <linux/fs.h> +#include <linux/interrupt.h> +#include <linux/poll.h> +#include <linux/uaccess.h> +#include <linux/wait.h> + +#define GPIO_DEV_MAX 8 + +static struct class *gpio_dev_class; +static dev_t gpio_devt; + +struct gpio_item { + spinlock_t lock; + + struct pio_device *pio; + + int enabled; + int pio_id; + u32 pin_mask; + u32 oe_mask; + + /* Pin state last time we read it (for blocking reads) */ + u32 pin_state; + int changed; + + wait_queue_head_t change_wq; + struct fasync_struct *async_queue; + + int id; + struct class_device *gpio_dev; + struct cdev char_dev; + struct config_item item; +}; + +struct gpio_attribute { + struct configfs_attribute attr; + ssize_t (*show)(struct gpio_item *, char *); + ssize_t (*store)(struct gpio_item *, const char *, size_t); +}; + +static irqreturn_t gpio_dev_interrupt(int irq, void *dev_id) +{ + struct gpio_item *gpio = dev_id; + u32 old_state, new_state; + + old_state = gpio->pin_state; + new_state = pio_readl(gpio->pio, PDSR); + gpio->pin_state = new_state; + + if (new_state != old_state) { + gpio->changed = 1; + wake_up_interruptible(&gpio->change_wq); + + if (gpio->async_queue) + kill_fasync(&gpio->async_queue, SIGIO, POLL_IN); + } + + return IRQ_HANDLED; +} + +static int gpio_dev_open(struct inode *inode, struct file *file) +{ + struct gpio_item *gpio = container_of(inode->i_cdev, + struct gpio_item, + char_dev); + unsigned int irq; + unsigned int i; + int ret; + + nonseekable_open(inode, file); + config_item_get(&gpio->item); + file->private_data = gpio; + + gpio->pin_state = pio_readl(gpio->pio, PDSR) & gpio->pin_mask; + gpio->changed = 1; + + for (i = 0; i < 32; i++) { + if (gpio->pin_mask & (1 << i)) { + irq = gpio_to_irq(32 * pio_id(gpio->pio) + i); + ret = request_irq(irq, gpio_dev_interrupt, 0, + "gpio-dev", gpio); + if (ret) + goto err_irq; + } + } + + return 0; + +err_irq: + while (i--) { + if (gpio->pin_mask & (1 << i)) { + irq = gpio_to_irq(32 * pio_id(gpio->pio) + i); + free_irq(irq, gpio); + } + } + + config_item_put(&gpio->item); + + return ret; +} + +static int gpio_dev_fasync(int fd, struct file *file, int mode) +{ + struct gpio_item *gpio = file->private_data; + + return fasync_helper(fd, file, mode, &gpio->async_queue); +} + +static int gpio_dev_release(struct inode *inode, struct file *file) +{ + struct gpio_item *gpio = file->private_data; + unsigned int irq; + unsigned int i; + + gpio_dev_fasync(-1, file, 0); + + for (i = 0; i < 32; i++) { + if (gpio->pin_mask & (1 << i)) { + irq = gpio_to_irq(32 * pio_id(gpio->pio) + i); + free_irq(irq, gpio); + } + } + + config_item_put(&gpio->item); + + return 0; +} + +static unsigned int gpio_dev_poll(struct file *file, poll_table *wait) +{ + struct gpio_item *gpio = file->private_data; + unsigned int mask = 0; + + poll_wait(file, &gpio->change_wq, wait); + if (gpio->changed) + mask |= POLLIN | POLLRDNORM; + + return mask; +} + +static ssize_t gpio_dev_read(struct file *file, char __user *buf, + size_t count, loff_t *offset) +{ + struct gpio_item *gpio = file->private_data; + u32 value; + + spin_lock_irq(&gpio->lock); + while (!gpio->changed) { + spin_unlock_irq(&gpio->lock); + + if (file->f_flags & O_NONBLOCK) + return -EAGAIN; + + if (wait_event_interruptible(gpio->change_wq, gpio->changed)) + return -ERESTARTSYS; + + spin_lock_irq(&gpio->lock); + } + + gpio->changed = 0; + value = pio_readl(gpio->pio, PDSR) & gpio->pin_mask; + + spin_unlock_irq(&gpio->lock); + + count = min(count, (size_t)4); + if (copy_to_user(buf, &value, count)) + return -EFAULT; + + return count; +} + +static ssize_t gpio_dev_write(struct file *file, const char __user *buf, + size_t count, loff_t *offset) +{ + struct gpio_item *gpio = file->private_data; + u32 value = 0; + u32 mask = ~0UL; + + count = min(count, (size_t)4); + if (copy_from_user(&value, buf, count)) + return -EFAULT; + + /* Assuming big endian */ + mask <<= (4 - count) * 8; + mask &= gpio->pin_mask; + + pio_writel(gpio->pio, CODR, ~value & mask); + pio_writel(gpio->pio, SODR, value & mask); + + return count; +} + +static struct file_operations gpio_dev_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .open = gpio_dev_open, + .release = gpio_dev_release, + .fasync = gpio_dev_fasync, + .poll = gpio_dev_poll, + .read = gpio_dev_read, + .write = gpio_dev_write, +}; + +static struct gpio_item *to_gpio_item(struct config_item *item) +{ + return item ? container_of(item, struct gpio_item, item) : NULL; +} + +static ssize_t gpio_show_gpio_id(struct gpio_item *gpio, char *page) +{ + return sprintf(page, "%d\n", gpio->pio_id); +} + +static ssize_t gpio_store_gpio_id(struct gpio_item *gpio, + const char *page, size_t count) +{ + unsigned long id; + char *p = (char *)page; + ssize_t ret = -EINVAL; + + id = simple_strtoul(p, &p, 0); + if (!p || (*p && (*p != '\n'))) + return -EINVAL; + + /* Switching PIO is not allowed when live... */ + spin_lock(&gpio->lock); + if (!gpio->enabled) { + ret = -ENXIO; + if ((id < MAX_NR_PIO_DEVICES) && pio_dev[id].regs) { + gpio->pio_id = id; + ret = count; + } + } + spin_unlock(&gpio->lock); + + return ret; +} + +static ssize_t gpio_show_pin_mask(struct gpio_item *gpio, char *page) +{ + return sprintf(page, "0x%08x\n", gpio->pin_mask); +} + +static ssize_t gpio_store_pin_mask(struct gpio_item *gpio, + const char *page, size_t count) +{ + u32 new_mask; + char *p = (char *)page; + ssize_t ret = -EINVAL; + + new_mask = simple_strtoul(p, &p, 0); + if (!p || (*p && (*p != '\n'))) + return -EINVAL; + + /* Can't update the pin mask while live. */ + spin_lock(&gpio->lock); + if (!gpio->enabled) { + gpio->oe_mask &= new_mask; + gpio->pin_mask = new_mask; + ret = count; + } + spin_unlock(&gpio->lock); + + return ret; +} + +static ssize_t gpio_show_oe_mask(struct gpio_item *gpio, char *page) +{ + return sprintf(page, "0x%08x\n", gpio->oe_mask); +} + +static ssize_t gpio_store_oe_mask(struct gpio_item *gpio, + const char *page, size_t count) +{ + u32 mask; + char *p = (char *)page; + ssize_t ret = -EINVAL; + + mask = simple_strtoul(p, &p, 0); + if (!p || (*p && (*p != '\n'))) + return -EINVAL; + + spin_lock(&gpio->lock); + if (!gpio->enabled) { + gpio->oe_mask = mask & gpio->pin_mask; + ret = count; + } + spin_unlock(&gpio->lock); + + return ret; +} + +static ssize_t gpio_show_enabled(struct gpio_item *gpio, char *page) +{ + return sprintf(page, "%d\n", gpio->enabled); +} + +static ssize_t gpio_store_enabled(struct gpio_item *gpio, + const char *page, size_t count) +{ + struct pio_device *pio; + u32 old, new; + char *p = (char *)page; + int enabled; + int ret; + + enabled = simple_strtoul(p, &p, 0); + if (!p || (*p && (*p != '\n'))) + return -EINVAL; + + /* make it a boolean value */ + enabled = !!enabled; + + if (gpio->enabled == enabled) + /* Already enabled; do nothing. */ + return count; + + BUG_ON(gpio->id >= GPIO_DEV_MAX); + + if (!enabled) { + class_device_unregister(gpio->gpio_dev); + cdev_del(&gpio->char_dev); + __disable_gpio(gpio->pio, gpio->pin_mask); + pio_dealloc_mask(gpio->pio, gpio->pin_mask); + gpio->pio = NULL; + } else { + if (gpio->pio_id < 0 || !gpio->pin_mask) + return -ENODEV; + } + + /* Disallow any updates to gpio_id or pin_mask */ + spin_lock(&gpio->lock); + gpio->enabled = enabled; + spin_unlock(&gpio->lock); + + if (!enabled) + return count; + + /* Now, try to allocate the pins */ + ret = -EBUSY; + pio = gpio->pio = &pio_dev[gpio->pio_id]; + do { + old = pio->pinmux_mask; + if (old & gpio->pin_mask) + goto err_alloc_pins; + + new = old | gpio->pin_mask; + } while (cmpxchg(&pio->pinmux_mask, old, new) != old); + + pio_writel(pio, OER, gpio->oe_mask); + pio_writel(pio, PER, gpio->pin_mask); + + cdev_init(&gpio->char_dev, &gpio_dev_fops); + gpio->char_dev.owner = THIS_MODULE; + ret = cdev_add(&gpio->char_dev, MKDEV(MAJOR(gpio_devt), gpio->id), 1); + if (ret < 0) + goto err_cdev_add; + gpio->gpio_dev = class_device_create(gpio_dev_class, NULL, + MKDEV(MAJOR(gpio_devt), gpio->id), + &gpio->pio->pdev->dev, + "gpio%d", gpio->id); + if (IS_ERR(gpio->gpio_dev)) { + printk(KERN_ERR "failed to create gpio%d\n", gpio->id); + ret = PTR_ERR(gpio->gpio_dev); + goto err_class_dev; + } + + printk(KERN_INFO "created gpio%d (pio%d/0x%08x) as (%d:%d)\n", + gpio->id, pio_id(gpio->pio), gpio->pin_mask, + MAJOR(gpio->gpio_dev->devt), MINOR(gpio->gpio_dev->devt)); + + return 0; + +err_class_dev: + cdev_del(&gpio->char_dev); +err_cdev_add: + __disable_gpio(pio, gpio->pin_mask); + pio_dealloc_mask(pio, gpio->pin_mask); +err_alloc_pins: + spin_lock(&gpio->lock); + gpio->enabled = 0; + spin_unlock(&gpio->lock); + gpio->pio = NULL; + + return ret; +} + +static struct gpio_attribute gpio_item_attr_gpio_id = { + .attr = { + .ca_owner = THIS_MODULE, + .ca_name = "gpio_id", + .ca_mode = S_IRUGO | S_IWUSR, + }, + .show = gpio_show_gpio_id, + .store = gpio_store_gpio_id, +}; +static struct gpio_attribute gpio_item_attr_pin_mask = { + .attr = { + .ca_owner = THIS_MODULE, + .ca_name = "pin_mask", + .ca_mode = S_IRUGO | S_IWUSR, + }, + .show = gpio_show_pin_mask, + .store = gpio_store_pin_mask, +}; +static struct gpio_attribute gpio_item_attr_oe_mask = { + .attr = { + .ca_owner = THIS_MODULE, + .ca_name = "oe_mask", + .ca_mode = S_IRUGO | S_IWUSR, + }, + .show = gpio_show_oe_mask, + .store = gpio_store_oe_mask, +}; +static struct gpio_attribute gpio_item_attr_enabled = { + .attr = { + .ca_owner = THIS_MODULE, + .ca_name = "enabled", + .ca_mode = S_IRUGO | S_IWUSR, + }, + .show = gpio_show_enabled, + .store = gpio_store_enabled, +}; + +static struct configfs_attribute *gpio_item_attrs[] = { + &gpio_item_attr_gpio_id.attr, + &gpio_item_attr_pin_mask.attr, + &gpio_item_attr_oe_mask.attr, + &gpio_item_attr_enabled.attr, + NULL, +}; + +static ssize_t gpio_show_attr(struct config_item *item, + struct configfs_attribute *attr, + char *page) +{ + struct gpio_item *gpio_item = to_gpio_item(item); + struct gpio_attribute *gpio_attr + = container_of(attr, struct gpio_attribute, attr); + ssize_t ret = 0; + + if (gpio_attr->show) + ret = gpio_attr->show(gpio_item, page); + return ret; +} + +static ssize_t gpio_store_attr(struct config_item *item, + struct configfs_attribute *attr, + const char *page, size_t count) +{ + struct gpio_item *gpio_item = to_gpio_item(item); + struct gpio_attribute *gpio_attr + = container_of(attr, struct gpio_attribute, attr); + ssize_t ret = -EINVAL; + + if (gpio_attr->store) + ret = gpio_attr->store(gpio_item, page, count); + return ret; +} + +static void gpio_release(struct config_item *item) +{ + kfree(to_gpio_item(item)); +} + +static struct configfs_item_operations gpio_item_ops = { + .release = gpio_release, + .show_attribute = gpio_show_attr, + .store_attribute = gpio_store_attr, +}; + +static struct config_item_type gpio_item_type = { + .ct_item_ops = &gpio_item_ops, + .ct_attrs = gpio_item_attrs, + .ct_owner = THIS_MODULE, +}; + +static struct config_item *gpio_make_item(struct config_group *group, + const char *name) +{ + static int next_id; + struct gpio_item *gpio; + + if (next_id >= GPIO_DEV_MAX) + return NULL; + + gpio = kzalloc(sizeof(struct gpio_item), GFP_KERNEL); + if (!gpio) + return NULL; + + gpio->id = next_id++; + config_item_init_type_name(&gpio->item, name, &gpio_item_type); + spin_lock_init(&gpio->lock); + init_waitqueue_head(&gpio->change_wq); + + return &gpio->item; +} + +static void gpio_drop_item(struct config_group *group, + struct config_item *item) +{ + struct gpio_item *gpio = to_gpio_item(item); + struct pio_device *pio; + + spin_lock(&gpio->lock); + if (gpio->enabled) { + class_device_unregister(gpio->gpio_dev); + cdev_del(&gpio->char_dev); + } + + pio = gpio->pio; + if (pio) { + __disable_gpio(pio, gpio->pin_mask); + pio_dealloc_mask(pio, gpio->pin_mask); + gpio->pio = NULL; + } + spin_unlock(&gpio->lock); +} + +static struct configfs_group_operations gpio_group_ops = { + .make_item = gpio_make_item, + .drop_item = gpio_drop_item, +}; + +static struct config_item_type gpio_group_type = { + .ct_group_ops = &gpio_group_ops, + .ct_owner = THIS_MODULE, +}; + +static struct configfs_subsystem gpio_subsys = { + .su_group = { + .cg_item = { + .ci_namebuf = "gpio", + .ci_type = &gpio_group_type, + }, + }, +}; + +static int __init pio_init_dev(void) +{ + int err; + + gpio_dev_class = class_create(THIS_MODULE, "gpio-dev"); + if (IS_ERR(gpio_dev_class)) { + err = PTR_ERR(gpio_dev_class); + goto err_class_create; + } + + err = alloc_chrdev_region(&gpio_devt, 0, GPIO_DEV_MAX, "gpio"); + if (err < 0) + goto err_alloc_chrdev; + + /* Configfs initialization */ + config_group_init(&gpio_subsys.su_group); + init_MUTEX(&gpio_subsys.su_sem); + err = configfs_register_subsystem(&gpio_subsys); + if (err) + goto err_register_subsys; + + return 0; + +err_register_subsys: + unregister_chrdev_region(gpio_devt, GPIO_DEV_MAX); +err_alloc_chrdev: + class_destroy(gpio_dev_class); +err_class_create: + printk(KERN_WARNING "Failed to initialize gpio /dev interface\n"); + return err; +} +late_initcall(pio_init_dev); +#endif /* CONFIG_PIO_DEV */ + static int __init pio_probe(struct platform_device *pdev) { struct pio_device *pio = NULL; + int irq = platform_get_irq(pdev, 0); + int gpio_irq_base = GPIO_IRQ_BASE + pdev->id * 32; BUG_ON(pdev->id >= MAX_NR_PIO_DEVICES); pio = &pio_dev[pdev->id]; BUG_ON(!pio->regs); - /* TODO: Interrupts */ + gpio_irq_setup(pio, irq, gpio_irq_base); platform_set_drvdata(pdev, pio); - printk(KERN_INFO "%s: Atmel Port Multiplexer at 0x%p (irq %d)\n", - pio->name, pio->regs, platform_get_irq(pdev, 0)); + printk(KERN_DEBUG "%s: base 0x%p, irq %d chains %d..%d\n", + pio->name, pio->regs, irq, gpio_irq_base, gpio_irq_base + 31); return 0; } @@ -148,7 +960,7 @@ { return platform_driver_register(&pio_driver); } -subsys_initcall(pio_init); +postcore_initcall(pio_init); void __init at32_init_pio(struct platform_device *pdev) { @@ -184,6 +996,13 @@ pio->pdev = pdev; pio->regs = ioremap(regs->start, regs->end - regs->start + 1); - pio_writel(pio, ODR, ~0UL); - pio_writel(pio, PER, ~0UL); + /* + * request_gpio() is only valid for pins that have been + * explicitly configured as GPIO and not previously requested + */ + pio->gpio_mask = ~0UL; + + /* start with irqs disabled and acked */ + pio_writel(pio, IDR, ~0UL); + (void) pio_readl(pio, ISR); } diff -urN linux-2.6.20.4-0rig/arch/avr32/Makefile linux-2.6.20.4-atmel/arch/avr32/Makefile --- linux-2.6.20.4-0rig/arch/avr32/Makefile 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/avr32/Makefile 2007-03-24 16:42:29.000000000 +0100 @@ -30,6 +30,8 @@ core-$(CONFIG_LOADER_U_BOOT) += arch/avr32/boot/u-boot/ core-y += arch/avr32/kernel/ core-y += arch/avr32/mm/ +drivers-$(CONFIG_OPROFILE) += arch/avr32/oprofile/ +drivers-y += arch/avr32/drivers/ libs-y += arch/avr32/lib/ archincdir-$(CONFIG_PLATFORM_AT32AP) := arch-at32ap diff -urN linux-2.6.20.4-0rig/arch/avr32/mm/cache.c linux-2.6.20.4-atmel/arch/avr32/mm/cache.c --- linux-2.6.20.4-0rig/arch/avr32/mm/cache.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/avr32/mm/cache.c 2007-03-24 16:42:28.000000000 +0100 @@ -22,18 +22,34 @@ void invalidate_dcache_region(void *start, size_t size) { - unsigned long v, begin, end, linesz; + unsigned long v, begin, end, linesz, mask; + int flush = 0; linesz = boot_cpu_data.dcache.linesz; + mask = linesz - 1; - //printk("invalidate dcache: %p + %u\n", start, size); - - /* You asked for it, you got it */ - begin = (unsigned long)start & ~(linesz - 1); - end = ((unsigned long)start + size + linesz - 1) & ~(linesz - 1); + /* when first and/or last cachelines are shared, flush them + * instead of invalidating ... never discard valid data! + */ + begin = (unsigned long)start; + end = begin + size - 1; + + if (begin & mask) { + flush_dcache_line(start); + begin += linesz; + flush = 1; + } + if ((end & mask) != mask) { + flush_dcache_line((void *)end); + end -= linesz; + flush = 1; + } - for (v = begin; v < end; v += linesz) + /* remaining cachelines only need invalidation */ + for (v = begin; v <= end; v += linesz) invalidate_dcache_line((void *)v); + if (flush) + flush_write_buffer(); } void clean_dcache_region(void *start, size_t size) diff -urN linux-2.6.20.4-0rig/arch/avr32/mm/dma-coherent.c linux-2.6.20.4-atmel/arch/avr32/mm/dma-coherent.c --- linux-2.6.20.4-0rig/arch/avr32/mm/dma-coherent.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/avr32/mm/dma-coherent.c 2007-03-24 16:42:29.000000000 +0100 @@ -41,6 +41,13 @@ struct page *page, *free, *end; int order; + /* Following is a work-around (a.k.a. hack) to prevent pages + * with __GFP_COMP being passed to split_page() which cannot + * handle them. The real problem is that this flag probably + * should be 0 on AVR32 as it is not supported on this + * platform--see CONFIG_HUGETLB_PAGE. */ + gfp &= ~(__GFP_COMP); + size = PAGE_ALIGN(size); order = get_order(size); diff -urN linux-2.6.20.4-0rig/arch/avr32/oprofile/common.c linux-2.6.20.4-atmel/arch/avr32/oprofile/common.c --- linux-2.6.20.4-0rig/arch/avr32/oprofile/common.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/avr32/oprofile/common.c 2007-03-24 16:42:29.000000000 +0100 @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2005-2006 Atmel Corporation + * + * 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. + * + * Author: Ronny Pedersen + */ + +#define DEBUG +#include <linux/init.h> +#include <linux/oprofile.h> +#include <linux/errno.h> +#include <asm/semaphore.h> +#include <linux/sysdev.h> + +#include "op_avr32_model.h" +#include "op_counter.h" + +static struct op_avr32_model_spec *pc_model; +static int pc_enabled = 0; +static struct semaphore pc_sem; + + +static int pc_start(void); +static int pc_setup(void); +static void pc_stop(void); +static int pc_create_files(struct super_block *, struct dentry *); + + +struct op_counter_config counter_config[OP_MAX_COUNTER]; + +static int pc_suspend(struct sys_device *dev, u32 state) +{ + if (pc_enabled) + pc_stop(); + return 0; +} + +static int pc_resume(struct sys_device *dev) +{ + if (pc_enabled) + pc_start(); + return 0; +} + +static struct sysdev_class oprofile_sysclass = { + set_kset_name("oprofile"), + .resume = pc_resume, + .suspend = pc_suspend, +}; + +static struct sys_device device_oprofile = { + .id = 0, + .cls = &oprofile_sysclass, +}; + +static int __init init_driverfs(void) +{ + int ret; + + if (!(ret = sysdev_class_register(&oprofile_sysclass))) + ret = sysdev_register(&device_oprofile); + + return ret; +} + +static void exit_driverfs(void) +{ + sysdev_unregister(&device_oprofile); + sysdev_class_unregister(&oprofile_sysclass); +} + +static int pc_create_files(struct super_block *sb, struct dentry *root) +{ + unsigned int i; + + pr_debug("AVR32 Peformance Counters: create files\n"); + for (i = 0; i < pc_model->num_counters; i++) { + struct dentry *dir; + char buf[2]; + + snprintf(buf, sizeof buf, "%d", i); + dir = oprofilefs_mkdir(sb, root, buf); + oprofilefs_create_ulong(sb, dir, "enabled", + &counter_config[i].enabled); + oprofilefs_create_ulong(sb, dir, "event", + &counter_config[i].event); + oprofilefs_create_ulong(sb, dir, "count", + &counter_config[i].count); + oprofilefs_create_ulong(sb, dir, "unit_mask", + &counter_config[i].unit_mask); + oprofilefs_create_ulong(sb, dir, "kernel", + &counter_config[i].kernel); + oprofilefs_create_ulong(sb, dir, "user", + &counter_config[i].user); + } + + return 0; +} + +static int pc_setup(void) +{ + int ret; + + spin_lock(&oprofilefs_lock); + pr_debug("AVR32 Peformance Counters: setup\n"); + ret = pc_model->setup_ctrs(); + spin_unlock(&oprofilefs_lock); + return ret; +} + +static int pc_start(void) +{ + int ret = -EBUSY; + + down(&pc_sem); + if (!pc_enabled) { + pr_debug("AVR32 Peformance Counters: start\n"); + ret = pc_model->start(); + pc_enabled = !ret; + } + up(&pc_sem); + return ret; +} + +static void pc_stop(void) +{ + down(&pc_sem); + pr_debug("AVR32 Peformance Counters: stop\n"); + if (pc_enabled) + pc_model->stop(); + pc_enabled = 0; + up(&pc_sem); +} + +int __init pc_init(struct oprofile_operations *ops, + struct op_avr32_model_spec *spec) +{ + init_MUTEX(&pc_sem); + + if ( spec->init ) + if (spec->init() < 0) + return -ENODEV; + + pc_model = spec; + init_driverfs(); + ops->create_files = pc_create_files; + ops->setup = pc_setup; + ops->shutdown = pc_stop; + ops->start = pc_start; + ops->stop = pc_stop; + ops->cpu_type = pc_model->name; + printk(KERN_INFO "oprofile: using %s Performance Counters\n", + spec->name); + pr_debug("AVR32 Peformance Counters: pc_init\n"); + + return 0; +} + +void pc_exit(void) +{ + if (pc_model) { + pr_debug("AVR32 Peformance Counters: exit\n"); + exit_driverfs(); + pc_model = NULL; + } +} diff -urN linux-2.6.20.4-0rig/arch/avr32/oprofile/init.c linux-2.6.20.4-atmel/arch/avr32/oprofile/init.c --- linux-2.6.20.4-0rig/arch/avr32/oprofile/init.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/avr32/oprofile/init.c 2007-03-24 16:42:29.000000000 +0100 @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2005-2006 Atmel Corporation + * + * 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. + * + * Author: Ronny Pedersen + */ + +#include <linux/oprofile.h> +#include <linux/init.h> +#include <linux/errno.h> +#include "op_avr32_model.h" +#include "op_model_avr32.h" + +int __init oprofile_arch_init(struct oprofile_operations *ops) +{ + int ret = -ENODEV; + + ret = pc_init(ops, &op_avr32_spec); + + return ret; +} + +void oprofile_arch_exit(void) +{ + pc_exit(); +} diff -urN linux-2.6.20.4-0rig/arch/avr32/oprofile/Kconfig linux-2.6.20.4-atmel/arch/avr32/oprofile/Kconfig --- linux-2.6.20.4-0rig/arch/avr32/oprofile/Kconfig 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/avr32/oprofile/Kconfig 2007-03-24 16:42:29.000000000 +0100 @@ -0,0 +1,23 @@ + +menu "Profiling support" + depends on EXPERIMENTAL + +config PROFILING + bool "Profiling support (EXPERIMENTAL)" + help + Say Y here to enable the extended profiling support mechanisms used + by profilers such as OProfile. + + +config OPROFILE + tristate "OProfile system profiling (EXPERIMENTAL)" + depends on PROFILING + help + OProfile is a profiling system capable of profiling the + whole system, including the kernel, kernel modules, libraries, + and applications. + + If unsure, say N. + +endmenu + diff -urN linux-2.6.20.4-0rig/arch/avr32/oprofile/Makefile linux-2.6.20.4-atmel/arch/avr32/oprofile/Makefile --- linux-2.6.20.4-0rig/arch/avr32/oprofile/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/avr32/oprofile/Makefile 2007-03-24 16:42:29.000000000 +0100 @@ -0,0 +1,10 @@ +obj-$(CONFIG_OPROFILE) += oprofile.o + +DRIVER_OBJS = $(addprefix ../../../drivers/oprofile/, \ + oprof.o cpu_buffer.o buffer_sync.o \ + event_buffer.o oprofile_files.o \ + oprofilefs.o oprofile_stats.o \ + timer_int.o ) + +oprofile-y := $(DRIVER_OBJS) init.o common.o +oprofile-y += op_model_avr32.o diff -urN linux-2.6.20.4-0rig/arch/avr32/oprofile/op_avr32_model.h linux-2.6.20.4-atmel/arch/avr32/oprofile/op_avr32_model.h --- linux-2.6.20.4-0rig/arch/avr32/oprofile/op_avr32_model.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/avr32/oprofile/op_avr32_model.h 2007-03-24 16:42:29.000000000 +0100 @@ -0,0 +1,25 @@ +/* + * interface to AVR32 machine specific operations + * + * Copyright (C) 2005-2006 Atmel Corporation + * + * 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. + * + * Author: Ronny Pedersen + */ + +#ifndef OP_AVR32_MODEL_H +#define OP_AVR32_MODEL_H + +struct op_avr32_model_spec { + int (*init)(void); + unsigned int num_counters; + int (*setup_ctrs)(void); + int (*start)(void); + void (*stop)(void); + char *name; +}; + +#endif /* OP_AVR32_MODEL_H */ diff -urN linux-2.6.20.4-0rig/arch/avr32/oprofile/op_counter.h linux-2.6.20.4-atmel/arch/avr32/oprofile/op_counter.h --- linux-2.6.20.4-0rig/arch/avr32/oprofile/op_counter.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/avr32/oprofile/op_counter.h 2007-03-24 16:42:29.000000000 +0100 @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2005-2006 Atmel Corporation + * + * 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. + * + * Author: Ronny Pedersen + */ +#ifndef OP_COUNTER_H +#define OP_COUNTER_H + +#define OP_MAX_COUNTER 3 + +/* Per performance monitor configuration as set via + * oprofilefs. + */ +struct op_counter_config { + unsigned long count; + unsigned long enabled; + unsigned long event; + unsigned long unit_mask; + unsigned long kernel; + unsigned long user; +}; + +extern struct op_counter_config counter_config[]; + +#endif /* OP_COUNTER_H */ diff -urN linux-2.6.20.4-0rig/arch/avr32/oprofile/op_model_avr32.c linux-2.6.20.4-atmel/arch/avr32/oprofile/op_model_avr32.c --- linux-2.6.20.4-0rig/arch/avr32/oprofile/op_model_avr32.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/avr32/oprofile/op_model_avr32.c 2007-03-24 16:42:29.000000000 +0100 @@ -0,0 +1,222 @@ +/* + * AVR32 Performance Counter Driver + * + * Copyright (C) 2005-2006 Atmel Corporation + * + * 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. + * + * Authors: Sondre Garsjoe, Ronny Pedersen + */ + +#define DEBUG + +#include <linux/types.h> +#include <linux/errno.h> +#include <linux/sched.h> +#include <linux/oprofile.h> +#include <linux/interrupt.h> +#include <asm/intc.h> +#include <asm/irq.h> +#include <asm/system.h> +#include <asm/sysreg.h> + +#include "op_counter.h" +#include "op_avr32_model.h" + + +#define PC_ENABLE 0x001 /* Enable counters */ +#define PCNT_RESET 0x002 /* Reset event counters */ +#define CCNT_RESET 0x004 /* Reset clock counter */ +#define PC_RESET (CCNT_RESET | PCNT_RESET) +#define PC_CNT64 0x008 /* Make CCNT count every 64th cycle */ + + +#define EVT_UNUSED 0xFF + +struct pc_counter { + volatile unsigned long ovf; + unsigned long reset_counter; +}; + +enum { PCCNT, PCNT0, PCNT1, MAX_COUNTERS }; + +#define PCCNT_IE (1 << 4) +#define PCNT0_IE (1 << 5) +#define PCNT1_IE (1 << 6) + +#define PCCNT_F (1 << 8) +#define PCNT0_F (1 << 9) +#define PCNT1_F (1 << 10) + +#define AVR32_PC_IRQ 0 + +static const u32 int_mask[MAX_COUNTERS] = { PCCNT_IE, PCNT0_IE, PCNT1_IE }; +static const u32 ovf_mask[MAX_COUNTERS] = { PCCNT_F, PCNT0_F, PCNT1_F }; + +static struct pc_counter results[MAX_COUNTERS]; + +static void write_pccr(u32 val) +{ + sysreg_write(PCCR, val); +} + +static u32 read_pccr(void) +{ + return sysreg_read(PCCR); +} + +static u32 read_counter(int counter) +{ + switch (counter) { + case PCCNT: + return sysreg_read(PCCNT); + case PCNT0: + return sysreg_read(PCNT0); + case PCNT1: + return sysreg_read(PCNT1); + default: + return 0; + } +} + +static void write_counter(int counter, u32 val) +{ + switch (counter) { + case PCCNT: + sysreg_write(PCCNT, val); + case PCNT0: + sysreg_write(PCNT0, val); + case PCNT1: + sysreg_write(PCNT1, val); + default: + break; + } +} + +static int avr32_setup_ctrs(void) +{ + u32 pccr; + int i; + + for (i = PCCNT; i < MAX_COUNTERS; i++) { + if (counter_config[i].enabled) + continue; + + counter_config[i].event = EVT_UNUSED; + } + + pccr = ((counter_config[PCNT1].event << 18) + | (counter_config[PCNT0].event << 12)); + pr_debug("avr32_setup_ctrs: pccr: %#08x\n", pccr); + write_pccr(pccr); + + for (i = PCCNT; i < MAX_COUNTERS; i++) { + if (counter_config[i].event == EVT_UNUSED) { + counter_config[i].event = 0; + continue; + } + + results[i].reset_counter = counter_config[i].count; + write_counter(i, -(u32)counter_config[i].count); + pr_debug("avr32_setup_ctrs: counter%d %#08x from %#08lx\n", + i, read_counter(i), counter_config[i].count); + } + + return 0; +} + +static void inline check_ctrs(void) +{ + int i; + u32 pccr = read_pccr(); + + /* Writeback clears overflow flag */ + write_pccr(pccr & ~PC_ENABLE); + + for (i = PCCNT; i < MAX_COUNTERS; i++) { + if (!(int_mask[i] & pccr)) + continue; + + if (pccr & ovf_mask[i]) + results[i].ovf++; + } +} + +static irqreturn_t avr32_pc_interrupt(int irq, void *arg, + struct pt_regs *regs) +{ + int i; + + /* Check if this is a performance counter interrupt */ + if (!(intc_get_pending(irq) & 2)) + return IRQ_NONE; + + check_ctrs(); + + for (i = PCCNT; i < MAX_COUNTERS; i++) { + if (!results[i].ovf) + continue; + + write_counter(i, -(u32)results[i].reset_counter); + oprofile_add_sample(regs, i); + results[i].ovf--; + } + + /* Enable Performance Counter */ + write_pccr(read_pccr() | PC_ENABLE); + + return IRQ_HANDLED; +} + +static void avr32_pc_stop(void) +{ + write_pccr(read_pccr() & ~PC_ENABLE); + + free_irq(AVR32_PC_IRQ, results); +} + +static int avr32_pc_start(void) +{ + int i, ret; + u32 pccr = read_pccr(); + + ret = request_irq(AVR32_PC_IRQ, avr32_pc_interrupt, IRQF_SHARED | IRQF_DISABLED, + "AVR32 Performance Counter", (void *)results); + + if (ret < 0) { + printk(KERN_ERR + "oprofile: unable to request IRQ%d for AVR32" + " Performance Counter\n", + AVR32_PC_IRQ); + return ret; + } + + /* Enable interrupts */ + for (i = PCCNT; i < MAX_COUNTERS; i++) { + if (counter_config[i].enabled) + pccr |= int_mask[i]; + } + + /* Disable scaler */ + pccr &= ~PC_CNT64; + + /* Enable Performance Counter */ + pccr |= PC_ENABLE; + + write_pccr(pccr); + pr_debug("avr32_pc_start: pc: %#08x\n", pccr); + return 0; +} + + +struct op_avr32_model_spec op_avr32_spec = { + .init = 0, + .setup_ctrs = avr32_setup_ctrs, + .start = avr32_pc_start, + .stop = avr32_pc_stop, + .num_counters = MAX_COUNTERS, + .name = "avr32/at32ap7000", +}; + diff -urN linux-2.6.20.4-0rig/arch/avr32/oprofile/op_model_avr32.h linux-2.6.20.4-atmel/arch/avr32/oprofile/op_model_avr32.h --- linux-2.6.20.4-0rig/arch/avr32/oprofile/op_model_avr32.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/arch/avr32/oprofile/op_model_avr32.h 2007-03-24 16:42:29.000000000 +0100 @@ -0,0 +1,21 @@ +/** + * AVR32 Machine Specific Operations + * + * Copyright (C) 2005-2006 Atmel Corporation + * + * 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. + * + * Author: Ronny Pedersen + */ +#ifndef OP_MODEL_AVR32_H +#define OP_MODEL_AVR32_H + +extern struct op_avr32_model_spec op_avr32_spec; +extern int pc_init(struct oprofile_operations *ops, + struct op_avr32_model_spec *spec); +extern void pc_exit(void); + + +#endif diff -urN linux-2.6.20.4-0rig/drivers/char/at91_spi.c linux-2.6.20.4-atmel/drivers/char/at91_spi.c --- linux-2.6.20.4-0rig/drivers/char/at91_spi.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/char/at91_spi.c 2007-03-24 16:39:15.000000000 +0100 @@ -0,0 +1,336 @@ +/* + * Serial Peripheral Interface (SPI) driver for the Atmel AT91RM9200 (Thunder) + * + * Copyright (C) SAN People (Pty) Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/init.h> +#include <linux/dma-mapping.h> +#include <linux/module.h> +#include <linux/sched.h> +#include <linux/completion.h> +#include <linux/interrupt.h> +#include <linux/clk.h> +#include <linux/platform_device.h> +#include <linux/atmel_pdc.h> +#include <asm/io.h> +#include <asm/semaphore.h> + +#include <asm/arch/at91_spi.h> +#include <asm/arch/board.h> +#include <asm/arch/spi.h> + +#undef DEBUG_SPI + +static struct spi_local spi_dev[NR_SPI_DEVICES]; /* state of the SPI devices */ +static int spi_enabled = 0; +static struct semaphore spi_lock; /* protect access to SPI bus */ +static int current_device = -1; /* currently selected SPI device */ +static struct clk *spi_clk; /* SPI clock */ +static void __iomem *spi_base; /* SPI peripheral base-address */ + +DECLARE_COMPLETION(transfer_complete); + + +#define at91_spi_read(reg) __raw_readl(spi_base + (reg)) +#define at91_spi_write(reg, val) __raw_writel((val), spi_base + (reg)) + + +/* ......................................................................... */ + +/* + * Access and enable the SPI bus. + * This MUST be called before any transfers are performed. + */ +void spi_access_bus(short device) +{ + /* Ensure that requested device is valid */ + if ((device < 0) || (device >= NR_SPI_DEVICES)) + panic("at91_spi: spi_access_bus called with invalid device"); + + if (spi_enabled == 0) { + clk_enable(spi_clk); /* Enable Peripheral clock */ + at91_spi_write(AT91_SPI_CR, AT91_SPI_SPIEN); /* Enable SPI */ +#ifdef DEBUG_SPI + printk("SPI on\n"); +#endif + } + spi_enabled++; + + /* Lock the SPI bus */ + down(&spi_lock); + current_device = device; + + /* Configure SPI bus for device */ + at91_spi_write(AT91_SPI_MR, AT91_SPI_MSTR | AT91_SPI_MODFDIS | (spi_dev[device].pcs << 16)); +} + +/* + * Relinquish control of the SPI bus. + */ +void spi_release_bus(short device) +{ + if (device != current_device) + panic("at91_spi: spi_release called with invalid device"); + + /* Release the SPI bus */ + current_device = -1; + up(&spi_lock); + + spi_enabled--; + if (spi_enabled == 0) { + at91_spi_write(AT91_SPI_CR, AT91_SPI_SPIDIS); /* Disable SPI */ + clk_disable(spi_clk); /* Disable Peripheral clock */ +#ifdef DEBUG_SPI + printk("SPI off\n"); +#endif + } +} + +/* + * Perform a data transfer over the SPI bus + */ +int spi_transfer(struct spi_transfer_list* list) +{ + struct spi_local *device = (struct spi_local *) &spi_dev[current_device]; + int tx_size; + + if (!list) + panic("at91_spi: spi_transfer called with NULL transfer list"); + if (current_device == -1) + panic("at91_spi: spi_transfer called without acquiring bus"); + +#ifdef DEBUG_SPI + printk("SPI transfer start [%i]\n", list->nr_transfers); +#endif + + /* If we are in 16-bit mode, we need to modify what we pass to the PDC */ + tx_size = (at91_spi_read(AT91_SPI_CSR(current_device)) & AT91_SPI_BITS_16) ? 2 : 1; + + /* Store transfer list */ + device->xfers = list; + list->curr = 0; + + /* Assume there must be at least one transfer */ + device->tx = dma_map_single(NULL, list->tx[0], list->txlen[0], DMA_TO_DEVICE); + device->rx = dma_map_single(NULL, list->rx[0], list->rxlen[0], DMA_FROM_DEVICE); + + /* Program PDC registers */ + at91_spi_write(ATMEL_PDC_TPR, device->tx); + at91_spi_write(ATMEL_PDC_RPR, device->rx); + at91_spi_write(ATMEL_PDC_TCR, list->txlen[0] / tx_size); + at91_spi_write(ATMEL_PDC_RCR, list->rxlen[0] / tx_size); + + /* Is there a second transfer? */ + if (list->nr_transfers > 1) { + device->txnext = dma_map_single(NULL, list->tx[1], list->txlen[1], DMA_TO_DEVICE); + device->rxnext = dma_map_single(NULL, list->rx[1], list->rxlen[1], DMA_FROM_DEVICE); + + /* Program Next PDC registers */ + at91_spi_write(ATMEL_PDC_TNPR, device->txnext); + at91_spi_write(ATMEL_PDC_RNPR, device->rxnext); + at91_spi_write(ATMEL_PDC_TNCR, list->txlen[1] / tx_size); + at91_spi_write(ATMEL_PDC_RNCR, list->rxlen[1] / tx_size); + } + else { + device->txnext = 0; + device->rxnext = 0; + at91_spi_write(ATMEL_PDC_TNCR, 0); + at91_spi_write(ATMEL_PDC_RNCR, 0); + } + + // TODO: If we are doing consecutive transfers (at high speed, or + // small buffers), then it might be worth modifying the 'Delay between + // Consecutive Transfers' in the CSR registers. + // This is an issue if we cannot chain the next buffer fast enough + // in the interrupt handler. + + /* Enable transmitter and receiver */ + at91_spi_write(ATMEL_PDC_PTCR, ATMEL_PDC_RXTEN | ATMEL_PDC_TXTEN); + + at91_spi_write(AT91_SPI_IER, AT91_SPI_ENDRX); /* enable buffer complete interrupt */ + wait_for_completion(&transfer_complete); + +#ifdef DEBUG_SPI + printk("SPI transfer end\n"); +#endif + + return 0; +} + +/* ......................................................................... */ + +/* + * Handle interrupts from the SPI controller. + */ +static irqreturn_t at91spi_interrupt(int irq, void *dev_id) +{ + unsigned int status; + struct spi_local *device = (struct spi_local *) &spi_dev[current_device]; + struct spi_transfer_list *list = device->xfers; + +#ifdef DEBUG_SPI + printk("SPI interrupt %i\n", current_device); +#endif + + if (!list) + panic("at91_spi: spi_interrupt with a NULL transfer list"); + + status = at91_spi_read(AT91_SPI_SR) & at91_spi_read(AT91_SPI_IMR); /* read status */ + + dma_unmap_single(NULL, device->tx, list->txlen[list->curr], DMA_TO_DEVICE); + dma_unmap_single(NULL, device->rx, list->rxlen[list->curr], DMA_FROM_DEVICE); + + device->tx = device->txnext; /* move next transfer to current transfer */ + device->rx = device->rxnext; + + list->curr = list->curr + 1; + if (list->curr == list->nr_transfers) { /* all transfers complete */ + at91_spi_write(AT91_SPI_IDR, AT91_SPI_ENDRX); /* disable interrupt */ + + /* Disable transmitter and receiver */ + at91_spi_write(ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS); + + device->xfers = NULL; + complete(&transfer_complete); + } + else if (list->curr+1 == list->nr_transfers) { /* no more next transfers */ + device->txnext = 0; + device->rxnext = 0; + at91_spi_write(ATMEL_PDC_TNCR, 0); + at91_spi_write(ATMEL_PDC_RNCR, 0); + } + else { + int i = (list->curr)+1; + + /* If we are in 16-bit mode, we need to modify what we pass to the PDC */ + int tx_size = (at91_spi_read(AT91_SPI_CSR(current_device)) & AT91_SPI_BITS_16) ? 2 : 1; + + device->txnext = dma_map_single(NULL, list->tx[i], list->txlen[i], DMA_TO_DEVICE); + device->rxnext = dma_map_single(NULL, list->rx[i], list->rxlen[i], DMA_FROM_DEVICE); + at91_spi_write(ATMEL_PDC_TNPR, device->txnext); + at91_spi_write(ATMEL_PDC_RNPR, device->rxnext); + at91_spi_write(ATMEL_PDC_TNCR, list->txlen[i] / tx_size); + at91_spi_write(ATMEL_PDC_RNCR, list->rxlen[i] / tx_size); + } + return IRQ_HANDLED; +} + +/* ......................................................................... */ + +/* + * Initialize the SPI controller + */ +static int __init at91spi_probe(struct platform_device *pdev) +{ + int i; + unsigned long scbr; + struct resource *res; + + init_MUTEX(&spi_lock); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) + return -ENXIO; + + if (!request_mem_region(res->start, res->end - res->start + 1, "at91_spi")) + return -EBUSY; + + spi_base = ioremap(res->start, res->end - res->start + 1); + if (!spi_base) { + release_mem_region(res->start, res->end - res->start + 1); + return -ENOMEM; + } + + spi_clk = clk_get(NULL, "spi_clk"); + if (IS_ERR(spi_clk)) { + printk(KERN_ERR "at91_spi: no clock defined\n"); + iounmap(spi_base); + release_mem_region(res->start, res->end - res->start + 1); + return -ENODEV; + } + + at91_spi_write(AT91_SPI_CR, AT91_SPI_SWRST); /* software reset of SPI controller */ + + /* + * Calculate the correct SPI baud-rate divisor. + */ + scbr = clk_get_rate(spi_clk) / (2 * DEFAULT_SPI_CLK); + scbr = scbr + 1; /* round up */ + + printk(KERN_INFO "at91_spi: Baud rate set to %ld\n", clk_get_rate(spi_clk) / (2 * scbr)); + + /* Set Chip Select registers to good defaults */ + for (i = 0; i < 4; i++) { + at91_spi_write(AT91_SPI_CSR(i), AT91_SPI_CPOL | AT91_SPI_BITS_8 | (16 << 16) | (scbr << 8)); + } + + at91_spi_write(ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS); + + memset(&spi_dev, 0, sizeof(spi_dev)); + spi_dev[0].pcs = 0xE; + spi_dev[1].pcs = 0xD; + spi_dev[2].pcs = 0xB; + spi_dev[3].pcs = 0x7; + + if (request_irq(AT91RM9200_ID_SPI, at91spi_interrupt, 0, "spi", NULL)) { + clk_put(spi_clk); + iounmap(spi_base); + release_mem_region(res->start, res->end - res->start + 1); + return -EBUSY; + } + + at91_spi_write(AT91_SPI_CR, AT91_SPI_SPIEN); /* Enable SPI */ + + return 0; +} + +static int __devexit at91spi_remove(struct platform_device *pdev) +{ + struct resource *res; + + at91_spi_write(AT91_SPI_CR, AT91_SPI_SPIDIS); /* Disable SPI */ + clk_put(spi_clk); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + iounmap(spi_base); + release_mem_region(res->start, res->end - res->start + 1); + + free_irq(AT91RM9200_ID_SPI, 0); + return 0; +} + +static struct platform_driver at91spi_driver = { + .probe = at91spi_probe, + .remove = __devexit_p(at91spi_remove), + .driver = { + .name = "at91_spi", + .owner = THIS_MODULE, + }, +}; + +static int __init at91spi_init(void) +{ + return platform_driver_register(&at91spi_driver); +} + +static void __exit at91spi_exit(void) +{ + platform_driver_unregister(&at91spi_driver); +} + +EXPORT_SYMBOL(spi_access_bus); +EXPORT_SYMBOL(spi_release_bus); +EXPORT_SYMBOL(spi_transfer); + +module_init(at91spi_init); +module_exit(at91spi_exit); + +MODULE_LICENSE("GPL") +MODULE_AUTHOR("Andrew Victor") +MODULE_DESCRIPTION("SPI driver for Atmel AT91RM9200") diff -urN linux-2.6.20.4-0rig/drivers/char/at91_spidev.c linux-2.6.20.4-atmel/drivers/char/at91_spidev.c --- linux-2.6.20.4-0rig/drivers/char/at91_spidev.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/char/at91_spidev.c 2007-03-24 16:39:15.000000000 +0100 @@ -0,0 +1,236 @@ +/* + * User-space interface to the SPI bus on Atmel AT91RM9200 + * + * Copyright (C) 2003 SAN People (Pty) Ltd + * + * Based on SPI driver by Rick Bronson + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/highmem.h> +#include <linux/pagemap.h> +#include <asm/arch/spi.h> + +#ifdef CONFIG_DEVFS_FS +#include <linux/devfs_fs_kernel.h> +#endif + + +#undef DEBUG_SPIDEV + +/* ......................................................................... */ + +/* + * Read or Write to SPI bus. + */ +static ssize_t spidev_rd_wr(struct file *file, char *buf, size_t count, loff_t *offset) +{ + unsigned int spi_device = (unsigned int) file->private_data; + + struct mm_struct * mm; + struct page ** maplist; + struct spi_transfer_list* list; + int pgcount; + + unsigned int ofs, pagelen; + int res, i, err; + + if (!count) { + return 0; + } + + list = kmalloc(sizeof(struct spi_transfer_list), GFP_KERNEL); + if (!list) { + return -ENOMEM; + } + + mm = current->mm; + + pgcount = ((unsigned long)buf+count+PAGE_SIZE-1)/PAGE_SIZE - (unsigned long)buf/PAGE_SIZE; + + if (pgcount >= MAX_SPI_TRANSFERS) { + kfree(list); + return -EFBIG; + } + + maplist = kmalloc (pgcount * sizeof (struct page *), GFP_KERNEL); + + if (!maplist) { + kfree(list); + return -ENOMEM; + } + flush_cache_all(); + down_read(&mm->mmap_sem); + err= get_user_pages(current, mm, (unsigned long)buf, pgcount, 1, 0, maplist, NULL); + up_read(&mm->mmap_sem); + + if (err < 0) { + kfree(list); + kfree(maplist); + return err; + } + pgcount = err; + +#ifdef DEBUG_SPIDEV + printk("spidev_rd_rw: %i %i\n", count, pgcount); +#endif + + /* Set default return value = transfer length */ + res = count; + + /* + * At this point, the virtual area buf[0] .. buf[count-1] will have + * corresponding pages mapped in the physical memory and locked until + * we unmap the kiobuf. The pages cannot be swapped out or moved + * around. + */ + ofs = (unsigned long) buf & (PAGE_SIZE -1); + pagelen = PAGE_SIZE - ofs; + if (count < pagelen) + pagelen = count; + + for (i = 0; i < pgcount; i++) { + flush_dcache_page(maplist[i]); + + list->tx[i] = list->rx[i] = page_address(maplist[i]) + ofs; + list->txlen[i] = list->rxlen[i] = pagelen; + +#ifdef DEBUG_SPIDEV + printk(" %i: %x (%i)\n", i, list->tx[i], list->txlen[i]); +#endif + + ofs = 0; /* all subsequent transfers start at beginning of a page */ + count = count - pagelen; + pagelen = (count < PAGE_SIZE) ? count : PAGE_SIZE; + } + list->nr_transfers = pgcount; + + /* Perform transfer on SPI bus */ + spi_access_bus(spi_device); + spi_transfer(list); + spi_release_bus(spi_device); + + while (pgcount--) { + page_cache_release (maplist[pgcount]); + } + flush_cache_all(); + + kfree(maplist); + kfree(list); + + return res; +} + +static int spidev_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) +{ + int spi_device = MINOR(inode->i_rdev); + + if (spi_device >= NR_SPI_DEVICES) + return -ENODEV; + + // TODO: This interface can be used to configure the SPI bus. + // Configurable options could include: Speed, Clock Polarity, Clock Phase + + switch(cmd) { + default: + return -ENOIOCTLCMD; + } +} + +/* + * Open the SPI device + */ +static int spidev_open(struct inode *inode, struct file *file) +{ + unsigned int spi_device = MINOR(inode->i_rdev); + + if (spi_device >= NR_SPI_DEVICES) + return -ENODEV; + + /* + * 'private_data' is actually a pointer, but we overload it with the + * value we want to store. + */ + file->private_data = (void *)spi_device; + + return 0; +} + +/* + * Close the SPI device + */ +static int spidev_close(struct inode *inode, struct file *file) +{ + return 0; +} + +/* ......................................................................... */ + +static struct file_operations spidev_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .read = spidev_rd_wr, + .write = (int (*) (struct file *file, const char *buf, size_t count, loff_t *offset))spidev_rd_wr, + .ioctl = spidev_ioctl, + .open = spidev_open, + .release = spidev_close, +}; + +/* + * Install the SPI /dev interface driver + */ +static int __init at91_spidev_init(void) +{ +#ifdef CONFIG_DEVFS_FS + int i; +#endif + + if (register_chrdev(SPI_MAJOR, "spi", &spidev_fops)) { + printk(KERN_ERR "at91_spidev: Unable to get major %d for SPI bus\n", SPI_MAJOR); + return -EIO; + } + +#ifdef CONFIG_DEVFS_FS + devfs_mk_dir("spi"); + for (i = 0; i < NR_SPI_DEVICES; i++) { + devfs_mk_cdev(MKDEV(SPI_MAJOR, i), S_IFCHR | S_IRUSR | S_IWUSR, "spi/%d",i); + } +#endif + printk(KERN_INFO "AT91 SPI driver loaded\n"); + + return 0; +} + +/* + * Remove the SPI /dev interface driver + */ +static void __exit at91_spidev_exit(void) +{ +#ifdef CONFIG_DEVFS_FS + int i; + for (i = 0; i < NR_SPI_DEVICES; i++) { + devfs_remove("spi/%d", i); + } + + devfs_remove("spi"); +#endif + + if (unregister_chrdev(SPI_MAJOR, "spi")) { + printk(KERN_ERR "at91_spidev: Unable to release major %d for SPI bus\n", SPI_MAJOR); + return; + } +} + +module_init(at91_spidev_init); +module_exit(at91_spidev_exit); + +MODULE_LICENSE("GPL") +MODULE_AUTHOR("Andrew Victor") +MODULE_DESCRIPTION("SPI /dev interface for Atmel AT91RM9200") diff -urN linux-2.6.20.4-0rig/drivers/char/Kconfig linux-2.6.20.4-atmel/drivers/char/Kconfig --- linux-2.6.20.4-0rig/drivers/char/Kconfig 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/char/Kconfig 2007-03-24 16:39:15.000000000 +0100 @@ -1030,5 +1030,21 @@ sysfs directory, /sys/devices/platform/telco_clock, with a number of files for controlling the behavior of this hardware. +config AT91_SPI + bool "SPI driver (legacy) for AT91RM9200 processors" + depends on ARCH_AT91RM9200 + default y + help + The SPI driver gives access to this serial bus on the AT91RM9200 + processor. + +config AT91_SPIDEV + bool "SPI device interface (legacy) for AT91RM9200 processors" + depends on ARCH_AT91RM9200 && AT91_SPI + default n + help + The SPI driver gives user mode access to this serial + bus on the AT91RM9200 processor. + endmenu diff -urN linux-2.6.20.4-0rig/drivers/char/Makefile linux-2.6.20.4-atmel/drivers/char/Makefile --- linux-2.6.20.4-0rig/drivers/char/Makefile 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/char/Makefile 2007-03-24 16:39:15.000000000 +0100 @@ -90,6 +90,8 @@ obj-$(CONFIG_GPIO_VR41XX) += vr41xx_giu.o obj-$(CONFIG_TANBAC_TB0219) += tb0219.o obj-$(CONFIG_TELCLOCK) += tlclk.o +obj-$(CONFIG_AT91_SPI) += at91_spi.o +obj-$(CONFIG_AT91_SPIDEV) += at91_spidev.o obj-$(CONFIG_WATCHDOG) += watchdog/ obj-$(CONFIG_MWAVE) += mwave/ diff -urN linux-2.6.20.4-0rig/drivers/i2c/busses/atmeltwi.h linux-2.6.20.4-atmel/drivers/i2c/busses/atmeltwi.h --- linux-2.6.20.4-0rig/drivers/i2c/busses/atmeltwi.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/i2c/busses/atmeltwi.h 2007-03-24 16:42:29.000000000 +0100 @@ -0,0 +1,117 @@ +/* + * Register definitions for the Atmel Two-Wire Interface + */ + +#ifndef __ASM_AVR32_TWI_H__ +#define __ASM_AVR32_TWI_H__ + +/* TWI register offsets */ +#define TWI_CR 0x0000 +#define TWI_MMR 0x0004 +#define TWI_SMR 0x0008 +#define TWI_IADR 0x000c +#define TWI_CWGR 0x0010 +#define TWI_SR 0x0020 +#define TWI_IER 0x0024 +#define TWI_IDR 0x0028 +#define TWI_IMR 0x002c +#define TWI_RHR 0x0030 +#define TWI_THR 0x0034 + +/* Bitfields in CR */ +#define TWI_START_OFFSET 0 +#define TWI_START_SIZE 1 +#define TWI_STOP_OFFSET 1 +#define TWI_STOP_SIZE 1 +#define TWI_MSEN_OFFSET 2 +#define TWI_MSEN_SIZE 1 +#define TWI_MSDIS_OFFSET 3 +#define TWI_MSDIS_SIZE 1 +#define TWI_SVEN_OFFSET 4 +#define TWI_SVEN_SIZE 1 +#define TWI_SVDIS_OFFSET 5 +#define TWI_SVDIS_SIZE 1 +#define TWI_SWRST_OFFSET 7 +#define TWI_SWRST_SIZE 1 + +/* Bitfields in MMR */ +#define TWI_IADRSZ_OFFSET 8 +#define TWI_IADRSZ_SIZE 2 +#define TWI_MREAD_OFFSET 12 +#define TWI_MREAD_SIZE 1 +#define TWI_DADR_OFFSET 16 +#define TWI_DADR_SIZE 7 + +/* Bitfields in SMR */ +#define TWI_SADR_OFFSET 16 +#define TWI_SADR_SIZE 7 + +/* Bitfields in IADR */ +#define TWI_IADR_OFFSET 0 +#define TWI_IADR_SIZE 24 + +/* Bitfields in CWGR */ +#define TWI_CLDIV_OFFSET 0 +#define TWI_CLDIV_SIZE 8 +#define TWI_CHDIV_OFFSET 8 +#define TWI_CHDIV_SIZE 8 +#define TWI_CKDIV_OFFSET 16 +#define TWI_CKDIV_SIZE 3 + +/* Bitfields in SR */ +#define TWI_TXCOMP_OFFSET 0 +#define TWI_TXCOMP_SIZE 1 +#define TWI_RXRDY_OFFSET 1 +#define TWI_RXRDY_SIZE 1 +#define TWI_TXRDY_OFFSET 2 +#define TWI_TXRDY_SIZE 1 +#define TWI_SVDIR_OFFSET 3 +#define TWI_SVDIR_SIZE 1 +#define TWI_SVACC_OFFSET 4 +#define TWI_SVACC_SIZE 1 +#define TWI_GCACC_OFFSET 5 +#define TWI_GCACC_SIZE 1 +#define TWI_OVRE_OFFSET 6 +#define TWI_OVRE_SIZE 1 +#define TWI_UNRE_OFFSET 7 +#define TWI_UNRE_SIZE 1 +#define TWI_NACK_OFFSET 8 +#define TWI_NACK_SIZE 1 +#define TWI_ARBLST_OFFSET 9 +#define TWI_ARBLST_SIZE 1 + +/* Bitfields in RHR */ +#define TWI_RXDATA_OFFSET 0 +#define TWI_RXDATA_SIZE 8 + +/* Bitfields in THR */ +#define TWI_TXDATA_OFFSET 0 +#define TWI_TXDATA_SIZE 8 + +/* Constants for IADRSZ */ +#define TWI_IADRSZ_NO_ADDR 0 +#define TWI_IADRSZ_ONE_BYTE 1 +#define TWI_IADRSZ_TWO_BYTES 2 +#define TWI_IADRSZ_THREE_BYTES 3 + +/* Bit manipulation macros */ +#define TWI_BIT(name) \ + (1 << TWI_##name##_OFFSET) +#define TWI_BF(name,value) \ + (((value) & ((1 << TWI_##name##_SIZE) - 1)) \ + << TWI_##name##_OFFSET) +#define TWI_BFEXT(name,value) \ + (((value) >> TWI_##name##_OFFSET) \ + & ((1 << TWI_##name##_SIZE) - 1)) +#define TWI_BFINS(name,value,old) \ + (((old) & ~(((1 << TWI_##name##_SIZE) - 1) \ + << TWI_##name##_OFFSET)) \ + | TWI_BF(name,value)) + +/* Register access macros */ +#define twi_readl(port,reg) \ + __raw_readl((port)->regs + TWI_##reg) +#define twi_writel(port,reg,value) \ + __raw_writel((value), (port)->regs + TWI_##reg) + +#endif /* __ASM_AVR32_TWI_H__ */ diff -urN linux-2.6.20.4-0rig/drivers/i2c/busses/i2c-at91.c linux-2.6.20.4-atmel/drivers/i2c/busses/i2c-at91.c --- linux-2.6.20.4-0rig/drivers/i2c/busses/i2c-at91.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/i2c/busses/i2c-at91.c 2007-03-24 16:39:15.000000000 +0100 @@ -31,8 +31,11 @@ #include <asm/arch/board.h> #include <asm/arch/cpu.h> -#define TWI_CLOCK 100000 /* Hz. max 400 Kbits/sec */ +/* Clockrate is configurable - max 400 Kbits/sec */ +static unsigned int clockrate = CONFIG_I2C_AT91_CLOCKRATE; +module_param(clockrate, uint, 0); +MODULE_PARM_DESC(clockrate, "The TWI clockrate"); static struct clk *twi_clk; static void __iomem *twi_base; @@ -53,7 +56,7 @@ at91_twi_write(AT91_TWI_CR, AT91_TWI_MSEN); /* Set Master mode */ /* Calcuate clock dividers */ - cdiv = (clk_get_rate(twi_clk) / (2 * TWI_CLOCK)) - 3; + cdiv = (clk_get_rate(twi_clk) / (2 * clockrate)) - 3; cdiv = cdiv + 1; /* round up */ ckdiv = 0; while (cdiv > 255) { @@ -63,9 +66,14 @@ if (cpu_is_at91rm9200()) { /* AT91RM9200 Errata #22 */ if (ckdiv > 5) { - printk(KERN_ERR "AT91 I2C: Invalid TWI_CLOCK value!\n"); + printk(KERN_ERR "AT91 I2C: Invalid TWI clockrate!\n"); ckdiv = 5; } + } else { + if (ckdiv > 7) { + printk(KERN_ERR "AT91 I2C: Invalid TWI clockrate!\n"); + ckdiv = 7; + } } at91_twi_write(AT91_TWI_CWGR, (ckdiv << 16) | (cdiv << 8) | cdiv); diff -urN linux-2.6.20.4-0rig/drivers/i2c/busses/i2c-atmeltwi.c linux-2.6.20.4-atmel/drivers/i2c/busses/i2c-atmeltwi.c --- linux-2.6.20.4-0rig/drivers/i2c/busses/i2c-atmeltwi.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/i2c/busses/i2c-atmeltwi.c 2007-03-24 16:42:29.000000000 +0100 @@ -0,0 +1,348 @@ +/* + * i2c Support for Atmel's Two-Wire Interface (TWI) + * + * Based on the work of Copyright (C) 2004 Rick Bronson + * Converted to 2.6 by Andrew Victor <andrew at sanpeople.com> + * Ported to AVR32 and heavily modified by Espen Krangnes <ekrangnes at atmel.com> + * + * Copyright (C) 2006 Atmel Corporation + * + * Borrowed heavily from the original work by: + * Copyright (C) 2000 Philip Edelbrock <phil at stimpy.netroedge.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + + +#include <linux/err.h> +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/types.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/init.h> +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <linux/irq.h> +#include <linux/platform_device.h> +#include <linux/completion.h> +#include <asm/io.h> +#include <linux/time.h> +#include "atmeltwi.h" + +static unsigned int baudrate = CONFIG_I2C_ATMELTWI_BAUDRATE; +module_param(baudrate, uint, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); +MODULE_PARM_DESC(baudrate, "The TWI baudrate"); + + +struct atmel_twi { + void __iomem *regs; + struct i2c_adapter adapter; + struct clk *pclk; + spinlock_t lock; + struct completion comp; + u32 intmask; + u8 *buf; + u8 len; + u8 acks_left; + unsigned int irq; + +}; +#define to_atmel_twi(adap) container_of(adap, struct atmel_twi, adapter) + +/* + * Initialize the TWI hardware registers. + */ +static int __devinit twi_hwinit(struct atmel_twi *twi) +{ + unsigned long cdiv, ckdiv=0; + + twi_writel(twi, IDR, ~0UL); + twi_writel(twi, CR, TWI_BIT(SWRST)); /*Reset peripheral*/ + twi_readl(twi, SR); + + cdiv = (clk_get_rate(twi->pclk) / (2 * baudrate)) - 4; + + while (cdiv > 255) { + ckdiv++; + cdiv = cdiv >> 1; + } + + if (ckdiv > 7) + return -EINVAL; + else + twi_writel(twi, CWGR, (TWI_BF(CKDIV, ckdiv) + | TWI_BF(CHDIV, cdiv) + | TWI_BF(CLDIV, cdiv))); + return 0; +} + +/* + * Waits for the i2c status register to set the specified bitmask + * Returns 0 if timed out (~100ms). + */ +static short twi_wait_for_completion(struct atmel_twi *twi, + u32 mask) +{ + int timeout = msecs_to_jiffies(100); + + twi->intmask = mask; + init_completion(&twi->comp); + + twi_writel(twi, IER, mask); + + if(!wait_for_completion_timeout(&twi->comp, timeout)) + return -ETIMEDOUT; + + return 0; +} + +/* + * Generic i2c master transfer entrypoint. + */ +static int twi_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) +{ + struct atmel_twi *twi = to_atmel_twi(adap); + struct i2c_msg *pmsg; + int i; + + /* get first message */ + pmsg = msgs; + + dev_dbg(&adap->dev, "twi_xfer: processing %d messages:\n", num); + + for (i = 0; i < num; i++, pmsg++) { + + twi->len = pmsg->len; + twi->buf = pmsg->buf; + twi->acks_left = pmsg->len; + twi_writel(twi, MMR, TWI_BF(DADR, pmsg->addr) | + (pmsg->flags & I2C_M_RD ? TWI_BIT(MREAD) : 0)); + twi_writel(twi, IADR, TWI_BF(IADR, pmsg->addr)); + + dev_dbg(&adap->dev,"#%d: internal addr %d %s byte%s %s 0x%02x\n", + i,pmsg->len, pmsg->flags & I2C_M_RD ? "reading" : "writing", + pmsg->len > 1 ? "s" : "", + pmsg->flags & I2C_M_RD ? "from" : "to", pmsg->addr); + + /* enable */ + twi_writel(twi, CR, TWI_BIT(MSEN)); + + if (pmsg->flags & I2C_M_RD) { + twi_writel(twi, CR, TWI_BIT(START)); + if ( twi_wait_for_completion(twi,TWI_BIT(RXRDY))==-ETIMEDOUT ) { + dev_dbg(&adap->dev, "RXRDY timeout. Stopped with %d bytes left\n", + twi->acks_left); + return -ETIMEDOUT; + } + + /* Send Stop, and Wait until transfer is finished */ + if ( twi_wait_for_completion(twi,TWI_BIT(TXCOMP))==-ETIMEDOUT ) { + dev_dbg(&adap->dev, "TXCOMP timeout\n"); + return -ETIMEDOUT; + } + + } else { + twi_writel(twi, THR, twi->buf[0]); + if ( twi_wait_for_completion(twi,TWI_BIT(TXRDY))==-ETIMEDOUT ) { + dev_dbg(&adap->dev, "TXRDY timeout. Stopped with %d bytes left\n", + twi->acks_left); + return -ETIMEDOUT; + } + } + + /* Disable TWI interface */ + twi_writel(twi, CR, TWI_BIT(MSDIS)); + + } /* end cur msg */ + + return i; +} + + +static irqreturn_t twi_interrupt(int irq, void *dev_id) +{ + struct atmel_twi *twi = dev_id; + int status = twi_readl(twi, SR); + + if (twi->intmask & status){ + if (twi->intmask & TWI_BIT(NACK)) { + goto nack; + } else if (twi->intmask & TWI_BIT(RXRDY)){ + twi->buf[twi->len - twi->acks_left] = twi_readl(twi,RHR); + if(--twi->acks_left==1) + twi_writel(twi, CR, TWI_BIT(STOP)); + if (twi->acks_left==0) + goto complete; + } else if (twi->intmask & TWI_BIT(TXRDY)) { + twi->acks_left--; + if (twi->acks_left==0) { + twi->intmask = TWI_BIT(TXCOMP); + twi_writel(twi, IER, TWI_BIT(TXCOMP)); + } else + twi_writel(twi, THR, twi->buf[twi->len - twi->acks_left]); + } else if (twi->intmask & TWI_BIT(TXCOMP)) { + goto complete; + } + } + + return IRQ_HANDLED; + +nack: + printk(KERN_INFO "NACK received!\n"); + +complete: + twi_writel(twi, IDR, ~0UL); + complete(&twi->comp); + + return IRQ_HANDLED; + +} + + +/* + * Return list of supported functionality. + */ +static u32 twi_func(struct i2c_adapter *adapter) +{ + return I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL; +} + +/* For now, we only handle combined mode (smbus) */ +static struct i2c_algorithm twi_algorithm = { + .master_xfer = twi_xfer, + .functionality = twi_func, +}; + +/* + * Main initialization routine. + */ +static int __devinit twi_probe(struct platform_device *pdev) +{ + struct atmel_twi *twi; + struct resource *regs; + struct clk *pclk; + struct i2c_adapter *adapter; + int rc, irq; + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) + return -ENXIO; + + pclk = clk_get(&pdev->dev, "pclk"); + if (IS_ERR(pclk)) + return PTR_ERR(pclk); + clk_enable(pclk); + + rc = -ENOMEM; + twi = kzalloc(sizeof(struct atmel_twi), GFP_KERNEL); + if (!twi) { + dev_err(&pdev->dev, "can't allocate interface!\n"); + goto err_alloc_twi; + } + + twi->pclk = pclk; + twi->regs = ioremap(regs->start, regs->end - regs->start + 1); + if (!twi->regs) + goto err_ioremap; + + irq = platform_get_irq(pdev,0); + rc = request_irq(irq, twi_interrupt, 0, "twi", twi); + if (rc) { + dev_err(&pdev->dev, "can't bind irq!\n"); + goto err_irq; + } + twi->irq = irq; + + rc = twi_hwinit(twi); + if (rc) { + dev_err(&pdev->dev, "Unable to set baudrate\n"); + goto err_hw_init; + } + + adapter = &twi->adapter; + sprintf(adapter->name, "TWI"); + adapter->algo = &twi_algorithm; + adapter->class = I2C_CLASS_HWMON; + adapter->dev.parent = &pdev->dev; + + platform_set_drvdata(pdev, twi); + + rc = i2c_add_adapter(adapter); + if (rc) { + dev_err(&pdev->dev, "Adapter %s registration failed\n", + adapter->name); + goto err_register; + } + + dev_info(&pdev->dev, "Atmel TWI i2c bus device (baudrate %dk) at 0x%08lx.\n", + baudrate/1000, (unsigned long)regs->start); + + return 0; + + +err_register: + platform_set_drvdata(pdev, NULL); + +err_hw_init: + free_irq(irq, twi); + +err_irq: + iounmap(twi->regs); + +err_ioremap: + kfree(twi); + +err_alloc_twi: + clk_disable(pclk); + clk_put(pclk); + + return rc; +} + +static int __devexit twi_remove(struct platform_device *pdev) +{ + struct atmel_twi *twi = platform_get_drvdata(pdev); + int res; + + platform_set_drvdata(pdev, NULL); + res = i2c_del_adapter(&twi->adapter); + twi_writel(twi, CR, TWI_BIT(MSDIS)); + iounmap(twi->regs); + clk_disable(twi->pclk); + clk_put(twi->pclk); + free_irq(twi->irq, twi); + kfree(twi); + + return res; +} + +static struct platform_driver twi_driver = { + .probe = twi_probe, + .remove = __devexit_p(twi_remove), + .driver = { + .name = "atmel_twi", + .owner = THIS_MODULE, + }, +}; + +static int __init atmel_twi_init(void) +{ + return platform_driver_register(&twi_driver); +} + +static void __exit atmel_twi_exit(void) +{ + platform_driver_unregister(&twi_driver); +} + +module_init(atmel_twi_init); +module_exit(atmel_twi_exit); + +MODULE_AUTHOR("Espen Krangnes"); +MODULE_DESCRIPTION("I2C driver for Atmel TWI"); +MODULE_LICENSE("GPL"); diff -urN linux-2.6.20.4-0rig/drivers/i2c/busses/Kconfig linux-2.6.20.4-atmel/drivers/i2c/busses/Kconfig --- linux-2.6.20.4-0rig/drivers/i2c/busses/Kconfig 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/i2c/busses/Kconfig 2007-03-24 16:42:29.000000000 +0100 @@ -5,6 +5,26 @@ menu "I2C Hardware Bus support" depends on I2C +config I2C_ATMELTWI + tristate "Atmel TWI/I2C" + depends on I2C + help + Atmel on-chip TWI controller. Say Y if you have an AT32 or + AT91-based device and want to use its built-in TWI + functionality. Atmel's TWI is compatible with Philips' I2C + protocol. If in doubt, say NO + +config I2C_ATMELTWI_BAUDRATE + prompt "Atmel TWI baudrate" + depends on I2C_ATMELTWI + int + default 100000 + help + Set the TWI/I2C baudrate. This will alter the default value. A + different baudrate can be set by using a module parameter as well. If + no parameter is provided when loading, this is the value that will be + used. + config I2C_ALI1535 tristate "ALI 1535" depends on I2C && PCI @@ -81,6 +101,14 @@ This supports the use of the I2C interface on Atmel AT91 processors. +config I2C_AT91_CLOCKRATE + prompt "Atmel AT91 I2C/TWI clock-rate" + depends on I2C_AT91 + int + default 100000 + help + Set the AT91 I2C/TWI clock-rate. + config I2C_AU1550 tristate "Au1550/Au1200 SMBus interface" depends on I2C && (SOC_AU1550 || SOC_AU1200) diff -urN linux-2.6.20.4-0rig/drivers/i2c/busses/Makefile linux-2.6.20.4-atmel/drivers/i2c/busses/Makefile --- linux-2.6.20.4-0rig/drivers/i2c/busses/Makefile 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/i2c/busses/Makefile 2007-03-24 16:42:29.000000000 +0100 @@ -46,6 +46,7 @@ obj-$(CONFIG_I2C_VOODOO3) += i2c-voodoo3.o obj-$(CONFIG_SCx200_ACB) += scx200_acb.o obj-$(CONFIG_SCx200_I2C) += scx200_i2c.o +obj-$(CONFIG_I2C_ATMELTWI) += i2c-atmeltwi.o ifeq ($(CONFIG_I2C_DEBUG_BUS),y) EXTRA_CFLAGS += -DDEBUG diff -urN linux-2.6.20.4-0rig/drivers/input/touchscreen/ads7846.c linux-2.6.20.4-atmel/drivers/input/touchscreen/ads7846.c --- linux-2.6.20.4-0rig/drivers/input/touchscreen/ads7846.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/input/touchscreen/ads7846.c 2007-03-24 16:39:15.000000000 +0100 @@ -17,8 +17,9 @@ * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ -#include <linux/device.h> +#include <linux/hwmon.h> #include <linux/init.h> +#include <linux/err.h> #include <linux/delay.h> #include <linux/input.h> #include <linux/interrupt.h> @@ -38,7 +39,8 @@ /* * This code has been heavily tested on a Nokia 770, and lightly * tested on other ads7846 devices (OSK/Mistral, Lubbock). - * Support for ads7843 and ads7845 has only been stubbed in. + * Support for ads7843 tested on Atmel at91sam926x-EK. + * Support for ads7845 has only been stubbed in. * * IRQ handling needs a workaround because of a shortcoming in handling * edge triggered IRQs on some platforms like the OMAP1/2. These @@ -54,7 +56,8 @@ * files. */ -#define TS_POLL_PERIOD msecs_to_jiffies(10) +#define TS_POLL_DELAY (1 * 1000000) /* ns delay before the first sample */ +#define TS_POLL_PERIOD (5 * 1000000) /* ns delay between samples */ /* this driver doesn't aim at the peak continuous sample rate */ #define SAMPLE_BITS (8 /*cmd*/ + 16 /*sample*/ + 2 /* before, after */) @@ -63,12 +66,12 @@ /* For portability, we can't read 12 bit values using SPI (which * would make the controller deliver them as native byteorder u16 * with msbs zeroed). Instead, we read them as two 8-bit values, - * which need byteswapping then range adjustment. + * *** WHICH NEED BYTESWAPPING *** and range adjustment. */ - __be16 x; - __be16 y; - __be16 z1, z2; - int ignore; + u16 x; + u16 y; + u16 z1, z2; + int ignore; }; struct ads7846 { @@ -76,7 +79,12 @@ char phys[32]; struct spi_device *spi; + +#if defined(CONFIG_HWMON) || defined(CONFIG_HWMON_MODULE) struct attribute_group *attr_group; + struct class_device *hwmon; +#endif + u16 model; u16 vref_delay_usecs; u16 x_plate_ohms; @@ -99,13 +107,16 @@ u16 debounce_rep; spinlock_t lock; - struct timer_list timer; /* P: lock */ + struct hrtimer timer; unsigned pendown:1; /* P: lock */ unsigned pending:1; /* P: lock */ // FIXME remove "irq_disabled" unsigned irq_disabled:1; /* P: lock */ unsigned disabled:1; + int (*filter)(void *data, int data_idx, int *val); + void *filter_data; + void (*filter_cleanup)(void *data); int (*get_pendown_state)(void); }; @@ -142,15 +153,16 @@ #define MAX_12BIT ((1<<12)-1) /* leave ADC powered up (disables penirq) between differential samples */ -#define READ_12BIT_DFR(x) (ADS_START | ADS_A2A1A0_d_ ## x \ - | ADS_12_BIT | ADS_DFR) - -#define READ_Y (READ_12BIT_DFR(y) | ADS_PD10_ADC_ON) -#define READ_Z1 (READ_12BIT_DFR(z1) | ADS_PD10_ADC_ON) -#define READ_Z2 (READ_12BIT_DFR(z2) | ADS_PD10_ADC_ON) +#define READ_12BIT_DFR(x, adc, vref) (ADS_START | ADS_A2A1A0_d_ ## x \ + | ADS_12_BIT | ADS_DFR | \ + (adc ? ADS_PD10_ADC_ON : 0) | (vref ? ADS_PD10_REF_ON : 0)) + +#define READ_Y(vref) (READ_12BIT_DFR(y, 1, vref)) +#define READ_Z1(vref) (READ_12BIT_DFR(z1, 1, vref)) +#define READ_Z2(vref) (READ_12BIT_DFR(z2, 1, vref)) -#define READ_X (READ_12BIT_DFR(x) | ADS_PD10_ADC_ON) -#define PWRDOWN (READ_12BIT_DFR(y) | ADS_PD10_PDOWN) /* LAST */ +#define READ_X(vref) (READ_12BIT_DFR(x, 1, vref)) +#define PWRDOWN (READ_12BIT_DFR(y, 0, 0)) /* LAST */ /* single-ended samples need to first power up reference voltage; * we leave both ADC and VREF powered @@ -158,14 +170,19 @@ #define READ_12BIT_SER(x) (ADS_START | ADS_A2A1A0_ ## x \ | ADS_12_BIT | ADS_SER) -#define REF_ON (READ_12BIT_DFR(x) | ADS_PD10_ALL_ON) -#define REF_OFF (READ_12BIT_DFR(y) | ADS_PD10_PDOWN) +#define REF_ON (READ_12BIT_DFR(x, 1, 1)) +#define REF_OFF (READ_12BIT_DFR(y, 0, 0)) /*--------------------------------------------------------------------------*/ /* * Non-touchscreen sensors only use single-ended conversions. + * The range is GND..vREF. The ads7843 and ads7835 must use external vREF; + * ads7846 lets that pin be unconnected, to use internal vREF. */ +static unsigned vREF_mV; +module_param(vREF_mV, uint, 0); +MODULE_PARM_DESC(vREF_mV, "external vREF voltage, in milliVolts"); struct ser_req { u8 ref_on; @@ -193,50 +210,53 @@ struct ser_req *req = kzalloc(sizeof *req, GFP_KERNEL); int status; int sample; - int i; + int use_internal; if (!req) return -ENOMEM; spi_message_init(&req->msg); - /* activate reference, so it has time to settle; */ - req->ref_on = REF_ON; - req->xfer[0].tx_buf = &req->ref_on; - req->xfer[0].len = 1; - req->xfer[1].rx_buf = &req->scratch; - req->xfer[1].len = 2; - - /* - * for external VREF, 0 usec (and assume it's always on); - * for 1uF, use 800 usec; - * no cap, 100 usec. - */ - req->xfer[1].delay_usecs = ts->vref_delay_usecs; + /* FIXME boards with ads7846 might use external vref instead ... */ + use_internal = (ts->model == 7846); + + /* maybe turn on internal vREF, and let it settle */ + if (use_internal) { + req->ref_on = REF_ON; + req->xfer[0].tx_buf = &req->ref_on; + req->xfer[0].len = 1; + spi_message_add_tail(&req->xfer[0], &req->msg); + + req->xfer[1].rx_buf = &req->scratch; + req->xfer[1].len = 2; + + /* for 1uF, settle for 800 usec; no cap, 100 usec. */ + req->xfer[1].delay_usecs = ts->vref_delay_usecs; + spi_message_add_tail(&req->xfer[1], &req->msg); + } /* take sample */ req->command = (u8) command; req->xfer[2].tx_buf = &req->command; req->xfer[2].len = 1; + spi_message_add_tail(&req->xfer[2], &req->msg); + req->xfer[3].rx_buf = &req->sample; req->xfer[3].len = 2; + spi_message_add_tail(&req->xfer[3], &req->msg); /* REVISIT: take a few more samples, and compare ... */ - /* turn off reference */ - req->ref_off = REF_OFF; + /* converter in low power mode & enable PENIRQ */ + req->ref_off = PWRDOWN; req->xfer[4].tx_buf = &req->ref_off; req->xfer[4].len = 1; + spi_message_add_tail(&req->xfer[4], &req->msg); + req->xfer[5].rx_buf = &req->scratch; req->xfer[5].len = 2; - CS_CHANGE(req->xfer[5]); - - /* group all the transfers together, so we can't interfere with - * reading touchscreen state; disable penirq while sampling - */ - for (i = 0; i < 6; i++) - spi_message_add_tail(&req->xfer[i], &req->msg); + spi_message_add_tail(&req->xfer[5], &req->msg); ts->irq_disabled = 1; disable_irq(spi->irq); @@ -256,25 +276,173 @@ return status ? status : sample; } -#define SHOW(name) static ssize_t \ +#if defined(CONFIG_HWMON) || defined(CONFIG_HWMON_MODULE) + +#define SHOW(name, var, adjust) static ssize_t \ name ## _show(struct device *dev, struct device_attribute *attr, char *buf) \ { \ + struct ads7846 *ts = dev_get_drvdata(dev); \ ssize_t v = ads7846_read12_ser(dev, \ - READ_12BIT_SER(name) | ADS_PD10_ALL_ON); \ + READ_12BIT_SER(var) | ADS_PD10_ALL_ON); \ if (v < 0) \ return v; \ - return sprintf(buf, "%u\n", (unsigned) v); \ + return sprintf(buf, "%u\n", adjust(ts, v)); \ } \ static DEVICE_ATTR(name, S_IRUGO, name ## _show, NULL); -SHOW(temp0) -SHOW(temp1) -SHOW(vaux) -SHOW(vbatt) + +/* Sysfs conventions report temperatures in millidegrees Celcius. + * ADS7846 could use the low-accuracy two-sample scheme, but can't do the high + * accuracy scheme without calibration data. For now we won't try either; + * userspace sees raw sensor values, and must scale/calibrate appropriately. + */ +static inline unsigned null_adjust(struct ads7846 *ts, ssize_t v) +{ + return v; +} + +SHOW(temp0, temp0, null_adjust) /* temp1_input */ +SHOW(temp1, temp1, null_adjust) /* temp2_input */ + + +/* sysfs conventions report voltages in millivolts. We can convert voltages + * if we know vREF. userspace may need to scale vAUX to match the board's + * external resistors; we assume that vBATT only uses the internal ones. + */ +static inline unsigned vaux_adjust(struct ads7846 *ts, ssize_t v) +{ + unsigned retval = v; + + /* external resistors may scale vAUX into 0..vREF */ + retval *= vREF_mV; + retval = retval >> 12; + return retval; +} + +static inline unsigned vbatt_adjust(struct ads7846 *ts, ssize_t v) +{ + unsigned retval = vaux_adjust(ts, v); + + /* ads7846 has a resistor ladder to scale this signal down */ + if (ts->model == 7846) + retval *= 4; + return retval; +} + +SHOW(in0_input, vaux, vaux_adjust) +SHOW(in1_input, vbatt, vbatt_adjust) + + +static struct attribute *ads7846_attributes[] = { + &dev_attr_temp0.attr, + &dev_attr_temp1.attr, + &dev_attr_in0_input.attr, + &dev_attr_in1_input.attr, + NULL, +}; + +static struct attribute_group ads7846_attr_group = { + .attrs = ads7846_attributes, +}; + +static struct attribute *ads7843_attributes[] = { + &dev_attr_in0_input.attr, + &dev_attr_in1_input.attr, + NULL, +}; + +static struct attribute_group ads7843_attr_group = { + .attrs = ads7843_attributes, +}; + +static struct attribute *ads7845_attributes[] = { + &dev_attr_in0_input.attr, + NULL, +}; + +static struct attribute_group ads7845_attr_group = { + .attrs = ads7845_attributes, +}; + +static int ads784x_hwmon_register(struct spi_device *spi, struct ads7846 *ts) +{ + struct class_device *hwmon; + int err; + + /* hwmon sensors need a reference voltage */ + switch (ts->model) { + case 7846: + if (!vREF_mV) { + dev_dbg(&spi->dev, "assuming 2.5V internal vREF\n"); + vREF_mV = 2500; + } + break; + case 7845: + case 7843: + if (!vREF_mV) { + dev_warn(&spi->dev, + "external vREF for ADS%d not specified\n", + ts->model); + return 0; + } + break; + } + + /* different chips have different sensor groups */ + switch (ts->model) { + case 7846: + ts->attr_group = &ads7846_attr_group; + break; + case 7845: + ts->attr_group = &ads7845_attr_group; + break; + case 7843: + ts->attr_group = &ads7843_attr_group; + break; + default: + dev_dbg(&spi->dev, "ADS%d not recognized\n", ts->model); + return 0; + } + + err = sysfs_create_group(&spi->dev.kobj, ts->attr_group); + if (err) + return err; + + hwmon = hwmon_device_register(&spi->dev); + if (IS_ERR(hwmon)) { + sysfs_remove_group(&spi->dev.kobj, ts->attr_group); + return PTR_ERR(hwmon); + } + + ts->hwmon = hwmon; + return 0; +} + +static void ads784x_hwmon_unregister(struct spi_device *spi, + struct ads7846 *ts) +{ + if (ts->hwmon) { + sysfs_remove_group(&spi->dev.kobj, ts->attr_group); + hwmon_device_unregister(ts->hwmon); + } +} + +#else +static inline int ads784x_hwmon_register(struct spi_device *spi, + struct ads7846 *ts) +{ + return 0; +} + +static inline void ads784x_hwmon_unregister(struct spi_device *spi, + struct ads7846 *ts) +{ +} +#endif static int is_pen_down(struct device *dev) { - struct ads7846 *ts = dev_get_drvdata(dev); + struct ads7846 *ts = dev_get_drvdata(dev); return ts->pendown; } @@ -318,46 +486,14 @@ static DEVICE_ATTR(disable, 0664, ads7846_disable_show, ads7846_disable_store); -static struct attribute *ads7846_attributes[] = { - &dev_attr_temp0.attr, - &dev_attr_temp1.attr, - &dev_attr_vbatt.attr, - &dev_attr_vaux.attr, - &dev_attr_pen_down.attr, - &dev_attr_disable.attr, - NULL, -}; - -static struct attribute_group ads7846_attr_group = { - .attrs = ads7846_attributes, -}; - -/* - * ads7843/7845 don't have temperature sensors, and - * use the other sensors a bit differently too - */ - -static struct attribute *ads7843_attributes[] = { - &dev_attr_vbatt.attr, - &dev_attr_vaux.attr, +static struct attribute *ads784x_attributes[] = { &dev_attr_pen_down.attr, &dev_attr_disable.attr, NULL, }; -static struct attribute_group ads7843_attr_group = { - .attrs = ads7843_attributes, -}; - -static struct attribute *ads7845_attributes[] = { - &dev_attr_vaux.attr, - &dev_attr_pen_down.attr, - &dev_attr_disable.attr, - NULL, -}; - -static struct attribute_group ads7845_attr_group = { - .attrs = ads7845_attributes, +static struct attribute_group ads784x_attr_group = { + .attrs = ads784x_attributes, }; /*--------------------------------------------------------------------------*/ @@ -373,25 +509,22 @@ static void ads7846_rx(void *ads) { struct ads7846 *ts = ads; - struct input_dev *input_dev = ts->input; unsigned Rt; - unsigned sync = 0; u16 x, y, z1, z2; - unsigned long flags; - /* adjust: on-wire is a must-ignore bit, a BE12 value, then padding; - * built from two 8 bit values written msb-first. + /* ads7846_rx_val() did in-place conversion (including byteswap) from + * on-the-wire format as part of debouncing to get stable readings. */ - x = (be16_to_cpu(ts->tc.x) >> 3) & 0x0fff; - y = (be16_to_cpu(ts->tc.y) >> 3) & 0x0fff; - z1 = (be16_to_cpu(ts->tc.z1) >> 3) & 0x0fff; - z2 = (be16_to_cpu(ts->tc.z2) >> 3) & 0x0fff; + x = ts->tc.x; + y = ts->tc.y; + z1 = ts->tc.z1; + z2 = ts->tc.z2; /* range filtering */ if (x == MAX_12BIT) x = 0; - if (likely(x && z1 && !device_suspended(&ts->spi->dev))) { + if (likely(x && z1)) { /* compute touch pressure resistance using equation #2 */ Rt = z2; Rt -= z1; @@ -402,101 +535,134 @@ } else Rt = 0; + if (ts->model == 7843) + Rt = ts->pressure_max / 2; + + /* Sample found inconsistent by debouncing or pressure is beyond - * the maximum. Don't report it to user space, repeat at least - * once more the measurement */ + * the maximum. Don't report it to user space, repeat at least + * once more the measurement + */ if (ts->tc.ignore || Rt > ts->pressure_max) { - mod_timer(&ts->timer, jiffies + TS_POLL_PERIOD); +#ifdef VERBOSE + pr_debug("%s: ignored %d pressure %d\n", + ts->spi->dev.bus_id, ts->tc.ignore, Rt); +#endif + hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD), + HRTIMER_REL); return; } - /* NOTE: "pendown" is inferred from pressure; we don't rely on - * being able to check nPENIRQ status, or "friendly" trigger modes - * (both-edges is much better than just-falling or low-level). - * - * REVISIT: some boards may require reading nPENIRQ; it's - * needed on 7843. and 7845 reads pressure differently... + /* NOTE: We can't rely on the pressure to determine the pen down + * state, even this controller has a pressure sensor. The pressure + * value can fluctuate for quite a while after lifting the pen and + * in some cases may not even settle at the expected value. * - * REVISIT: the touchscreen might not be connected; this code - * won't notice that, even if nPENIRQ never fires ... + * The only safe way to check for the pen up condition is in the + * timer by reading the pen signal state (it's a GPIO _and_ IRQ). */ - if (!ts->pendown && Rt != 0) { - input_report_key(input_dev, BTN_TOUCH, 1); - sync = 1; - } else if (ts->pendown && Rt == 0) { - input_report_key(input_dev, BTN_TOUCH, 0); - sync = 1; - } - if (Rt) { - input_report_abs(input_dev, ABS_X, x); - input_report_abs(input_dev, ABS_Y, y); - sync = 1; - } + struct input_dev *input = ts->input; - if (sync) { - input_report_abs(input_dev, ABS_PRESSURE, Rt); - input_sync(input_dev); - } - -#ifdef VERBOSE - if (Rt || ts->pendown) - pr_debug("%s: %d/%d/%d%s\n", ts->spi->dev.bus_id, - x, y, Rt, Rt ? "" : " UP"); + if (!ts->pendown) { + input_report_key(input, BTN_TOUCH, 1); + ts->pendown = 1; +#ifdef VERBOSE + dev_dbg(&ts->spi->dev, "DOWN\n"); #endif + } + input_report_abs(input, ABS_X, x); + input_report_abs(input, ABS_Y, y); + input_report_abs(input, ABS_PRESSURE, Rt); + + input_sync(input); +#ifdef VERBOSE + dev_dbg(&ts->spi->dev, "%4d/%4d/%4d\n", x, y, Rt); +#endif + } - spin_lock_irqsave(&ts->lock, flags); - - ts->pendown = (Rt != 0); - mod_timer(&ts->timer, jiffies + TS_POLL_PERIOD); - - spin_unlock_irqrestore(&ts->lock, flags); + hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD), HRTIMER_REL); } -static void ads7846_debounce(void *ads) +static int ads7846_debounce(void *ads, int data_idx, int *val) { struct ads7846 *ts = ads; - struct spi_message *m; - struct spi_transfer *t; - int val; - int status; - m = &ts->msg[ts->msg_idx]; - t = list_entry(m->transfers.prev, struct spi_transfer, transfer_list); - val = (be16_to_cpu(*(__be16 *)t->rx_buf) >> 3) & 0x0fff; - if (!ts->read_cnt || (abs(ts->last_read - val) > ts->debounce_tol)) { + if (!ts->read_cnt || (abs(ts->last_read - *val) > ts->debounce_tol)) { + /* Start over collecting consistent readings. */ + ts->read_rep = 0; /* Repeat it, if this was the first read or the read * wasn't consistent enough. */ if (ts->read_cnt < ts->debounce_max) { - ts->last_read = val; + ts->last_read = *val; ts->read_cnt++; + return ADS7846_FILTER_REPEAT; } else { /* Maximum number of debouncing reached and still * not enough number of consistent readings. Abort * the whole sample, repeat it in the next sampling * period. */ - ts->tc.ignore = 1; ts->read_cnt = 0; - /* Last message will contain ads7846_rx() as the - * completion function. - */ - m = ts->last_msg; + return ADS7846_FILTER_IGNORE; } - /* Start over collecting consistent readings. */ - ts->read_rep = 0; } else { if (++ts->read_rep > ts->debounce_rep) { /* Got a good reading for this coordinate, * go for the next one. */ - ts->tc.ignore = 0; - ts->msg_idx++; ts->read_cnt = 0; ts->read_rep = 0; - m++; - } else + return ADS7846_FILTER_OK; + } else { /* Read more values that are consistent. */ ts->read_cnt++; + return ADS7846_FILTER_REPEAT; + } + } +} + +static int ads7846_no_filter(void *ads, int data_idx, int *val) +{ + return ADS7846_FILTER_OK; +} + +static void ads7846_rx_val(void *ads) +{ + struct ads7846 *ts = ads; + struct spi_message *m; + struct spi_transfer *t; + u16 *rx_val; + int val; + int action; + int status; + + m = &ts->msg[ts->msg_idx]; + t = list_entry(m->transfers.prev, struct spi_transfer, transfer_list); + rx_val = t->rx_buf; + + /* adjust: on-wire is a must-ignore bit, a BE12 value, then padding; + * built from two 8 bit values written msb-first. + */ + val = be16_to_cpu(*rx_val) >> 3; + + action = ts->filter(ts->filter_data, ts->msg_idx, &val); + switch (action) { + case ADS7846_FILTER_REPEAT: + break; + case ADS7846_FILTER_IGNORE: + ts->tc.ignore = 1; + /* Last message will contain ads7846_rx() as the + * completion function. + */ + m = ts->last_msg; + break; + case ADS7846_FILTER_OK: + *rx_val = val; + ts->tc.ignore = 0; + m = &ts->msg[++ts->msg_idx]; + break; + default: + BUG(); } status = spi_async(ts->spi, m); if (status) @@ -504,21 +670,34 @@ status); } -static void ads7846_timer(unsigned long handle) +static int ads7846_timer(struct hrtimer *handle) { - struct ads7846 *ts = (void *)handle; + struct ads7846 *ts = container_of(handle, struct ads7846, timer); int status = 0; spin_lock_irq(&ts->lock); - if (unlikely(ts->msg_idx && !ts->pendown)) { + if (unlikely(!ts->get_pendown_state() || + device_suspended(&ts->spi->dev))) { + if (ts->pendown) { + struct input_dev *input = ts->input; + + input_report_key(input, BTN_TOUCH, 0); + input_report_abs(input, ABS_PRESSURE, 0); + input_sync(input); + + ts->pendown = 0; +#ifdef VERBOSE + dev_dbg(&ts->spi->dev, "UP\n"); +#endif + } + /* measurement cycle ended */ if (!device_suspended(&ts->spi->dev)) { ts->irq_disabled = 0; enable_irq(ts->spi->irq); } ts->pending = 0; - ts->msg_idx = 0; } else { /* pen is still down, continue with the measurement */ ts->msg_idx = 0; @@ -528,6 +707,7 @@ } spin_unlock_irq(&ts->lock); + return HRTIMER_NORESTART; } static irqreturn_t ads7846_irq(int irq, void *handle) @@ -546,7 +726,8 @@ ts->irq_disabled = 1; disable_irq(ts->spi->irq); ts->pending = 1; - mod_timer(&ts->timer, jiffies); + hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_DELAY), + HRTIMER_REL); } } spin_unlock_irqrestore(&ts->lock, flags); @@ -632,6 +813,7 @@ struct ads7846_platform_data *pdata = spi->dev.platform_data; struct spi_message *m; struct spi_transfer *x; + int vref; int err; if (!spi->irq) { @@ -665,6 +847,10 @@ * may not. So we stick to very-portable 8 bit words, both RX and TX. */ spi->bits_per_word = 8; + spi->mode = SPI_MODE_1; + err = spi_setup(spi); + if (err < 0) + return err; ts = kzalloc(sizeof(struct ads7846), GFP_KERNEL); input_dev = input_allocate_device(); @@ -679,8 +865,7 @@ ts->spi = spi; ts->input = input_dev; - init_timer(&ts->timer); - ts->timer.data = (unsigned long) ts; + hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_REL); ts->timer.function = ads7846_timer; spin_lock_init(&ts->lock); @@ -689,14 +874,25 @@ ts->vref_delay_usecs = pdata->vref_delay_usecs ? : 100; ts->x_plate_ohms = pdata->x_plate_ohms ? : 400; ts->pressure_max = pdata->pressure_max ? : ~0; - if (pdata->debounce_max) { + + if (pdata->filter != NULL) { + if (pdata->filter_init != NULL) { + err = pdata->filter_init(pdata, &ts->filter_data); + if (err < 0) + goto err_free_mem; + } + ts->filter = pdata->filter; + ts->filter_cleanup = pdata->filter_cleanup; + } else if (pdata->debounce_max) { ts->debounce_max = pdata->debounce_max; + if (ts->debounce_max < 2) + ts->debounce_max = 2; ts->debounce_tol = pdata->debounce_tol; ts->debounce_rep = pdata->debounce_rep; - if (ts->debounce_rep > ts->debounce_max + 1) - ts->debounce_rep = ts->debounce_max - 1; + ts->filter = ads7846_debounce; + ts->filter_data = ts; } else - ts->debounce_tol = ~0; + ts->filter = ads7846_no_filter; ts->get_pendown_state = pdata->get_pendown_state; snprintf(ts->phys, sizeof(ts->phys), "%s/input0", spi->dev.bus_id); @@ -718,6 +914,8 @@ input_set_abs_params(input_dev, ABS_PRESSURE, pdata->pressure_min, pdata->pressure_max, 0, 0); + vref = pdata->keep_vref_on; + /* set up the transfers to read touchscreen state; this assumes we * use formula #2 for pressure, not #3. */ @@ -727,7 +925,7 @@ spi_message_init(m); /* y- still on; turn on only y+ (and ADC) */ - ts->read_y = READ_Y; + ts->read_y = READ_Y(vref); x->tx_buf = &ts->read_y; x->len = 1; spi_message_add_tail(x, m); @@ -737,7 +935,7 @@ x->len = 2; spi_message_add_tail(x, m); - m->complete = ads7846_debounce; + m->complete = ads7846_rx_val; m->context = ts; m++; @@ -745,7 +943,7 @@ /* turn y- off, x+ on, then leave in lowpower */ x++; - ts->read_x = READ_X; + ts->read_x = READ_X(vref); x->tx_buf = &ts->read_x; x->len = 1; spi_message_add_tail(x, m); @@ -755,7 +953,7 @@ x->len = 2; spi_message_add_tail(x, m); - m->complete = ads7846_debounce; + m->complete = ads7846_rx_val; m->context = ts; /* turn y+ off, x- on; we'll use formula #2 */ @@ -764,7 +962,7 @@ spi_message_init(m); x++; - ts->read_z1 = READ_Z1; + ts->read_z1 = READ_Z1(vref); x->tx_buf = &ts->read_z1; x->len = 1; spi_message_add_tail(x, m); @@ -774,14 +972,14 @@ x->len = 2; spi_message_add_tail(x, m); - m->complete = ads7846_debounce; + m->complete = ads7846_rx_val; m->context = ts; m++; spi_message_init(m); x++; - ts->read_z2 = READ_Z2; + ts->read_z2 = READ_Z2(vref); x->tx_buf = &ts->read_z2; x->len = 1; spi_message_add_tail(x, m); @@ -791,7 +989,7 @@ x->len = 2; spi_message_add_tail(x, m); - m->complete = ads7846_debounce; + m->complete = ads7846_rx_val; m->context = ts; } @@ -820,31 +1018,24 @@ spi->dev.driver->name, ts)) { dev_dbg(&spi->dev, "irq %d busy?\n", spi->irq); err = -EBUSY; - goto err_free_mem; + goto err_cleanup_filter; } + err = ads784x_hwmon_register(spi, ts); + if (err) + goto err_free_irq; + dev_info(&spi->dev, "touchscreen, irq %d\n", spi->irq); - /* take a first sample, leaving nPENIRQ active; avoid + /* take a first sample, leaving nPENIRQ active and vREF off; avoid * the touchscreen, in case it's not connected. */ (void) ads7846_read12_ser(&spi->dev, READ_12BIT_SER(vaux) | ADS_PD10_ALL_ON); - switch (ts->model) { - case 7846: - ts->attr_group = &ads7846_attr_group; - break; - case 7845: - ts->attr_group = &ads7845_attr_group; - break; - default: - ts->attr_group = &ads7843_attr_group; - break; - } - err = sysfs_create_group(&spi->dev.kobj, ts->attr_group); + err = sysfs_create_group(&spi->dev.kobj, &ads784x_attr_group); if (err) - goto err_free_irq; + goto err_remove_hwmon; err = input_register_device(input_dev); if (err) @@ -853,9 +1044,14 @@ return 0; err_remove_attr_group: - sysfs_remove_group(&spi->dev.kobj, ts->attr_group); + sysfs_remove_group(&spi->dev.kobj, &ads784x_attr_group); + err_remove_hwmon: + ads784x_hwmon_unregister(spi, ts); err_free_irq: free_irq(spi->irq, ts); + err_cleanup_filter: + if (ts->filter_cleanup) + ts->filter_cleanup(ts->filter_data); err_free_mem: input_free_device(input_dev); kfree(ts); @@ -866,16 +1062,20 @@ { struct ads7846 *ts = dev_get_drvdata(&spi->dev); + ads784x_hwmon_unregister(spi, ts); input_unregister_device(ts->input); ads7846_suspend(spi, PMSG_SUSPEND); - sysfs_remove_group(&spi->dev.kobj, ts->attr_group); + sysfs_remove_group(&spi->dev.kobj, &ads784x_attr_group); free_irq(ts->spi->irq, ts); /* suspend left the IRQ disabled */ enable_irq(ts->spi->irq); + if (ts->filter_cleanup) + ts->filter_cleanup(ts->filter_data); + kfree(ts); dev_dbg(&spi->dev, "unregistered touchscreen\n"); diff -urN linux-2.6.20.4-0rig/drivers/leds/Kconfig linux-2.6.20.4-atmel/drivers/leds/Kconfig --- linux-2.6.20.4-0rig/drivers/leds/Kconfig 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/leds/Kconfig 2007-03-24 16:39:15.000000000 +0100 @@ -76,6 +76,13 @@ This option enables support for the Soekris net4801 and net4826 error LED. +config LEDS_AT91 + tristate "LED support using AT91 GPIOs" + depends on LEDS_CLASS && ARCH_AT91 && !LEDS + help + This option enables support for LEDs connected to GPIO lines + on AT91-based boards. + config LEDS_WRAP tristate "LED Support for the WRAP series LEDs" depends on LEDS_CLASS && SCx200_GPIO diff -urN linux-2.6.20.4-0rig/drivers/leds/leds-at91.c linux-2.6.20.4-atmel/drivers/leds/leds-at91.c --- linux-2.6.20.4-0rig/drivers/leds/leds-at91.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/leds/leds-at91.c 2007-03-24 16:39:15.000000000 +0100 @@ -0,0 +1,140 @@ +/* + * AT91 GPIO based LED driver + * + * Copyright (C) 2006 David Brownell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> +#include <linux/leds.h> + +#include <asm/arch/board.h> +#include <asm/arch/gpio.h> + +static LIST_HEAD(at91_led_list); /* list of AT91 LEDs */ + +struct at91_led { + struct led_classdev cdev; + struct list_head list; + struct at91_gpio_led *led_data; +}; + +/* + * Change the state of the LED. + */ +static void at91_led_set(struct led_classdev *cdev, enum led_brightness value) +{ + struct at91_led *led = container_of(cdev, struct at91_led, cdev); + short active = (value == LED_OFF); + + if (led->led_data->flags & 1) /* active high/low? */ + active = !active; + at91_set_gpio_value(led->led_data->gpio, value == LED_OFF); +} + +static int __devexit at91_led_remove(struct platform_device *pdev) +{ + struct at91_led *led; + + list_for_each_entry (led, &at91_led_list, list) + led_classdev_unregister(&led->cdev); + +#warning "Free allocated memory" + // TODO: Free memory. kfree(led); + + return 0; +} + +static int __init at91_led_probe(struct platform_device *pdev) +{ + int status = 0; + struct at91_gpio_led *pdata = pdev->dev.platform_data; + unsigned nr_leds; + struct at91_led *led; + + if (!pdata) + return -ENODEV; + + nr_leds = pdata->index; /* first index stores number of LEDs */ + + while (nr_leds--) { + led = kzalloc(sizeof(struct at91_led), GFP_KERNEL); + if (!led) { + dev_err(&pdev->dev, "No memory for device\n"); + status = -ENOMEM; + goto cleanup; + } + led->led_data = pdata; + led->cdev.name = pdata->name; + led->cdev.brightness_set = at91_led_set, + led->cdev.default_trigger = pdata->trigger; + + status = led_classdev_register(&pdev->dev, &led->cdev); + if (status < 0) { + dev_err(&pdev->dev, "led_classdev_register failed - %d\n", status); +cleanup: + at91_led_remove(pdev); + break; + } + list_add(&led->list, &at91_led_list); + pdata++; + } + return status; +} + +#ifdef CONFIG_PM +static int at91_led_suspend(struct platform_device *dev, pm_message_t state) +{ + struct at91_led *led; + + list_for_each_entry (led, &at91_led_list, list) + led_classdev_suspend(&led->cdev); + + return 0; +} + +static int at91_led_resume(struct platform_device *dev) +{ + struct at91_led *led; + + list_for_each_entry (led, &at91_led_list, list) + led_classdev_resume(&led->cdev); + + return 0; +} +#else +#define at91_led_suspend NULL +#define at91_led_resume NULL +#endif + +static struct platform_driver at91_led_driver = { + .probe = at91_led_probe, + .remove = __devexit_p(at91_led_remove), + .suspend = at91_led_suspend, + .resume = at91_led_resume, + .driver = { + .name = "at91_leds", + .owner = THIS_MODULE, + }, +}; + +static int __init at91_led_init(void) +{ + return platform_driver_register(&at91_led_driver); +} +module_init(at91_led_init); + +static void __exit at91_led_exit(void) +{ + platform_driver_unregister(&at91_led_driver); +} +module_exit(at91_led_exit); + +MODULE_DESCRIPTION("AT91 GPIO LED driver"); +MODULE_AUTHOR("David Brownell"); +MODULE_LICENSE("GPL"); diff -urN linux-2.6.20.4-0rig/drivers/leds/Makefile linux-2.6.20.4-atmel/drivers/leds/Makefile --- linux-2.6.20.4-0rig/drivers/leds/Makefile 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/leds/Makefile 2007-03-24 16:39:15.000000000 +0100 @@ -14,6 +14,7 @@ obj-$(CONFIG_LEDS_AMS_DELTA) += leds-ams-delta.o obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o obj-$(CONFIG_LEDS_WRAP) += leds-wrap.o +obj-$(CONFIG_LEDS_AT91) += leds-at91.o # LED Triggers obj-$(CONFIG_LEDS_TRIGGER_TIMER) += ledtrig-timer.o diff -urN linux-2.6.20.4-0rig/drivers/mmc/at91_mci.c linux-2.6.20.4-atmel/drivers/mmc/at91_mci.c --- linux-2.6.20.4-0rig/drivers/mmc/at91_mci.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/mmc/at91_mci.c 2007-03-24 16:39:15.000000000 +0100 @@ -64,6 +64,7 @@ #include <linux/err.h> #include <linux/dma-mapping.h> #include <linux/clk.h> +#include <linux/atmel_pdc.h> #include <linux/mmc/host.h> #include <linux/mmc/protocol.h> @@ -75,7 +76,7 @@ #include <asm/arch/cpu.h> #include <asm/arch/gpio.h> #include <asm/arch/at91_mci.h> -#include <asm/arch/at91_pdc.h> + #define DRIVER_NAME "at91_mci" @@ -85,8 +86,8 @@ #define FL_SENT_STOP (1 << 1) #define AT91_MCI_ERRORS (AT91_MCI_RINDE | AT91_MCI_RDIRE | AT91_MCI_RCRCE \ - | AT91_MCI_RENDE | AT91_MCI_RTOE | AT91_MCI_DCRCE \ - | AT91_MCI_DTOE | AT91_MCI_OVRE | AT91_MCI_UNRE) + | AT91_MCI_RENDE | AT91_MCI_RTOE | AT91_MCI_DCRCE \ + | AT91_MCI_DTOE | AT91_MCI_OVRE | AT91_MCI_UNRE) #define at91_mci_read(host, reg) __raw_readl((host)->baseaddr + (reg)) #define at91_mci_write(host, reg, val) __raw_writel((val), (host)->baseaddr + (reg)) @@ -211,13 +212,13 @@ /* Check to see if this needs filling */ if (i == 0) { - if (at91_mci_read(host, AT91_PDC_RCR) != 0) { + if (at91_mci_read(host, ATMEL_PDC_RCR) != 0) { pr_debug("Transfer active in current\n"); continue; } } else { - if (at91_mci_read(host, AT91_PDC_RNCR) != 0) { + if (at91_mci_read(host, ATMEL_PDC_RNCR) != 0) { pr_debug("Transfer active in next\n"); continue; } @@ -234,12 +235,12 @@ pr_debug("dma address = %08X, length = %d\n", sg->dma_address, sg->length); if (i == 0) { - at91_mci_write(host, AT91_PDC_RPR, sg->dma_address); - at91_mci_write(host, AT91_PDC_RCR, sg->length / 4); + at91_mci_write(host, ATMEL_PDC_RPR, sg->dma_address); + at91_mci_write(host, ATMEL_PDC_RCR, sg->length / 4); } else { - at91_mci_write(host, AT91_PDC_RNPR, sg->dma_address); - at91_mci_write(host, AT91_PDC_RNCR, sg->length / 4); + at91_mci_write(host, ATMEL_PDC_RNPR, sg->dma_address); + at91_mci_write(host, ATMEL_PDC_RNCR, sg->length / 4); } } @@ -303,37 +304,12 @@ at91mci_pre_dma_read(host); else { at91_mci_write(host, AT91_MCI_IER, AT91_MCI_RXBUFF); - at91_mci_write(host, AT91_PDC_PTCR, AT91_PDC_RXTDIS | AT91_PDC_TXTDIS); + at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS); } pr_debug("post dma read done\n"); } -/* - * Handle transmitted data - */ -static void at91_mci_handle_transmitted(struct at91mci_host *host) -{ - struct mmc_command *cmd; - struct mmc_data *data; - - pr_debug("Handling the transmit\n"); - - /* Disable the transfer */ - at91_mci_write(host, AT91_PDC_PTCR, AT91_PDC_RXTDIS | AT91_PDC_TXTDIS); - - /* Now wait for cmd ready */ - at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_TXBUFE); - at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY); - - cmd = host->cmd; - if (!cmd) return; - - data = cmd->data; - if (!data) return; - - data->bytes_xfered = host->total_length; -} /* * Enable the controller @@ -431,15 +407,15 @@ cmd->opcode, cmdr, cmd->arg, blocks, block_length, at91_mci_read(host, AT91_MCI_MR)); if (!data) { - at91_mci_write(host, AT91_PDC_PTCR, AT91_PDC_TXTDIS | AT91_PDC_RXTDIS); - at91_mci_write(host, AT91_PDC_RPR, 0); - at91_mci_write(host, AT91_PDC_RCR, 0); - at91_mci_write(host, AT91_PDC_RNPR, 0); - at91_mci_write(host, AT91_PDC_RNCR, 0); - at91_mci_write(host, AT91_PDC_TPR, 0); - at91_mci_write(host, AT91_PDC_TCR, 0); - at91_mci_write(host, AT91_PDC_TNPR, 0); - at91_mci_write(host, AT91_PDC_TNCR, 0); + at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTDIS | ATMEL_PDC_RXTDIS); + at91_mci_write(host, ATMEL_PDC_RPR, 0); + at91_mci_write(host, ATMEL_PDC_RCR, 0); + at91_mci_write(host, ATMEL_PDC_RNPR, 0); + at91_mci_write(host, ATMEL_PDC_RNCR, 0); + at91_mci_write(host, ATMEL_PDC_TPR, 0); + at91_mci_write(host, ATMEL_PDC_TCR, 0); + at91_mci_write(host, ATMEL_PDC_TNPR, 0); + at91_mci_write(host, ATMEL_PDC_TNCR, 0); at91_mci_write(host, AT91_MCI_ARGR, cmd->arg); at91_mci_write(host, AT91_MCI_CMDR, cmdr); @@ -452,7 +428,7 @@ /* * Disable the PDC controller */ - at91_mci_write(host, AT91_PDC_PTCR, AT91_PDC_RXTDIS | AT91_PDC_TXTDIS); + at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS); if (cmdr & AT91_MCI_TRCMD_START) { data->bytes_xfered = 0; @@ -474,15 +450,15 @@ */ host->total_length = block_length * blocks; host->buffer = dma_alloc_coherent(NULL, - host->total_length, - &host->physical_address, GFP_KERNEL); + host->total_length, + &host->physical_address, GFP_KERNEL); at91mci_sg_to_dma(host, data); pr_debug("Transmitting %d bytes\n", host->total_length); - at91_mci_write(host, AT91_PDC_TPR, host->physical_address); - at91_mci_write(host, AT91_PDC_TCR, host->total_length / 4); + at91_mci_write(host, ATMEL_PDC_TPR, host->physical_address); + at91_mci_write(host, ATMEL_PDC_TCR, host->total_length / 4); ier = AT91_MCI_TXBUFE; } } @@ -497,9 +473,9 @@ if (cmdr & AT91_MCI_TRCMD_START) { if (cmdr & AT91_MCI_TRDIR) - at91_mci_write(host, AT91_PDC_PTCR, AT91_PDC_RXTEN); + at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTEN); else - at91_mci_write(host, AT91_PDC_PTCR, AT91_PDC_TXTEN); + at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_TXTEN); } return ier; } @@ -561,9 +537,7 @@ pr_debug("Status = %08X [%08X %08X %08X %08X]\n", status, cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]); - if (status & (AT91_MCI_RINDE | AT91_MCI_RDIRE | AT91_MCI_RCRCE | - AT91_MCI_RENDE | AT91_MCI_RTOE | AT91_MCI_DCRCE | - AT91_MCI_DTOE | AT91_MCI_OVRE | AT91_MCI_UNRE)) { + if (status & AT91_MCI_ERRORS) { if ((status & AT91_MCI_RCRCE) && ((cmd->opcode == MMC_SEND_OP_COND) || (cmd->opcode == SD_APP_OP_COND))) { cmd->error = MMC_ERR_NONE; @@ -601,6 +575,32 @@ } /* + * Handle transmitted data + */ +static void at91_mci_handle_transmitted(struct at91mci_host *host) +{ + struct mmc_command *cmd; + struct mmc_data *data; + + pr_debug("Handling the transmit\n"); + + /* Disable the transfer */ + at91_mci_write(host, ATMEL_PDC_PTCR, ATMEL_PDC_RXTDIS | ATMEL_PDC_TXTDIS); + + /* Now wait for cmd ready */ + at91_mci_write(host, AT91_MCI_IDR, AT91_MCI_TXBUFE); + at91_mci_write(host, AT91_MCI_IER, AT91_MCI_NOTBUSY); + + cmd = host->cmd; + if (!cmd) return; + + data = cmd->data; + if (!data) return; + + data->bytes_xfered = host->total_length; +} + +/* * Set the IOS */ static void at91_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) @@ -665,15 +665,15 @@ int_status = at91_mci_read(host, AT91_MCI_SR); int_mask = at91_mci_read(host, AT91_MCI_IMR); - + pr_debug("MCI irq: status = %08X, %08X, %08X\n", int_status, int_mask, int_status & int_mask); - + int_status = int_status & int_mask; if (int_status & AT91_MCI_ERRORS) { completed = 1; - + if (int_status & AT91_MCI_UNRE) pr_debug("MMC: Underrun error\n"); if (int_status & AT91_MCI_OVRE) @@ -821,7 +821,7 @@ mmc->f_min = 375000; mmc->f_max = 25000000; mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; - mmc->caps = MMC_CAP_BYTEBLOCK; + mmc->caps = MMC_CAP_BYTEBLOCK | MMC_CAP_MULTIWRITE; host = mmc_priv(mmc); host->mmc = mmc; @@ -853,15 +853,15 @@ host->baseaddr = ioremap(res->start, res->end - res->start + 1); if (!host->baseaddr) { clk_put(host->mci_clk); - mmc_free_host(mmc); release_mem_region(res->start, res->end - res->start + 1); + mmc_free_host(mmc); return -ENOMEM; } /* * Reset hardware */ - clk_enable(host->mci_clk); /* Enable the peripheral clock */ + clk_enable(host->mci_clk); /* Enable the peripheral clock */ at91_mci_disable(host); at91_mci_enable(host); @@ -874,9 +874,9 @@ printk(KERN_ERR "AT91 MMC: Failed to request MCI interrupt\n"); clk_disable(host->mci_clk); clk_put(host->mci_clk); - mmc_free_host(mmc); iounmap(host->baseaddr); release_mem_region(res->start, res->end - res->start + 1); + mmc_free_host(mmc); return ret; } @@ -930,13 +930,13 @@ mmc_remove_host(mmc); free_irq(host->irq, host); - clk_disable(host->mci_clk); /* Disable the peripheral clock */ - clk_put(host->mci_clk); - iounmap(host->baseaddr); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); release_mem_region(res->start, res->end - res->start + 1); + clk_disable(host->mci_clk); /* Disable the peripheral clock */ + clk_put(host->mci_clk); + mmc_free_host(mmc); platform_set_drvdata(pdev, NULL); pr_debug("MCI Removed\n"); diff -urN linux-2.6.20.4-0rig/drivers/mmc/atmel-mci.c linux-2.6.20.4-atmel/drivers/mmc/atmel-mci.c --- linux-2.6.20.4-0rig/drivers/mmc/atmel-mci.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/mmc/atmel-mci.c 2007-03-24 16:42:29.000000000 +0100 @@ -0,0 +1,1218 @@ +/* + * Atmel MultiMedia Card Interface driver + * + * Copyright (C) 2004-2006 Atmel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/blkdev.h> +#include <linux/clk.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/ioport.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include <linux/mmc/host.h> +#include <linux/mmc/protocol.h> + +#include <asm/dma-controller.h> +#include <asm/io.h> +#include <asm/arch/board.h> +#include <asm/arch/gpio.h> + +#include "atmel-mci.h" + +#define DRIVER_NAME "atmel_mci" + +#define MCI_CMD_ERROR_FLAGS (MCI_BIT(RINDE) | MCI_BIT(RDIRE) | \ + MCI_BIT(RCRCE) | MCI_BIT(RENDE) | \ + MCI_BIT(RTOE)) +#define MCI_DATA_ERROR_FLAGS (MCI_BIT(DCRCE) | MCI_BIT(DTOE) | \ + MCI_BIT(OVRE) | MCI_BIT(UNRE)) + +enum { + EVENT_CMD_COMPLETE = 0, + EVENT_CMD_ERROR, + EVENT_DATA_COMPLETE, + EVENT_DATA_ERROR, + EVENT_STOP_SENT, + EVENT_STOP_COMPLETE, + EVENT_STOP_ERROR, + EVENT_DMA_ERROR, + EVENT_CARD_DETECT, +}; + +struct atmel_mci_dma { + struct dma_request_sg req; + unsigned short rx_periph_id; + unsigned short tx_periph_id; +}; + +struct atmel_mci { + struct mmc_host *mmc; + void __iomem *regs; + struct atmel_mci_dma dma; + + struct mmc_request *mrq; + struct mmc_command *cmd; + struct mmc_data *data; + + u32 stop_cmdr; + u32 stop_iflags; + + struct tasklet_struct tasklet; + unsigned long pending_events; + unsigned long completed_events; + u32 error_status; + + int present; + int detect_pin; + int wp_pin; + + unsigned long bus_hz; + unsigned long mapbase; + struct clk *mck; + struct platform_device *pdev; + +#ifdef CONFIG_DEBUG_FS + struct dentry *debugfs_root; + struct dentry *debugfs_regs; + struct dentry *debugfs_req; + struct dentry *debugfs_pending_events; + struct dentry *debugfs_completed_events; +#endif +}; + +/* Those printks take an awful lot of time... */ +#ifndef DEBUG +static unsigned int fmax = 15000000U; +#else +static unsigned int fmax = 1000000U; +#endif +module_param(fmax, uint, 0444); +MODULE_PARM_DESC(fmax, "Max frequency in Hz of the MMC bus clock"); + +/* Test bit macros for completed events */ +#define mci_cmd_is_complete(host) \ + test_bit(EVENT_CMD_COMPLETE, &host->completed_events) +#define mci_cmd_error_is_complete(host) \ + test_bit(EVENT_CMD_ERROR, &host->completed_events) +#define mci_data_is_complete(host) \ + test_bit(EVENT_DATA_COMPLETE, &host->completed_events) +#define mci_data_error_is_complete(host) \ + test_bit(EVENT_DATA_ERROR, &host->completed_events) +#define mci_stop_sent_is_complete(host) \ + test_bit(EVENT_STOP_SENT, &host->completed_events) +#define mci_stop_is_complete(host) \ + test_bit(EVENT_STOP_COMPLETE, &host->completed_events) +#define mci_stop_error_is_complete(host) \ + test_bit(EVENT_STOP_ERROR, &host->completed_events) +#define mci_dma_error_is_complete(host) \ + test_bit(EVENT_DMA_ERROR, &host->completed_events) +#define mci_card_detect_is_complete(host) \ + test_bit(EVENT_CARD_DETECT, &host->completed_events) + +/* Test and clear bit macros for pending events */ +#define mci_clear_cmd_is_pending(host) \ + test_and_clear_bit(EVENT_CMD_COMPLETE, &host->pending_events) +#define mci_clear_cmd_error_is_pending(host) \ + test_and_clear_bit(EVENT_CMD_ERROR, &host->pending_events) +#define mci_clear_data_is_pending(host) \ + test_and_clear_bit(EVENT_DATA_COMPLETE, &host->pending_events) +#define mci_clear_data_error_is_pending(host) \ + test_and_clear_bit(EVENT_DATA_ERROR, &host->pending_events) +#define mci_clear_stop_sent_is_pending(host) \ + test_and_clear_bit(EVENT_STOP_SENT, &host->pending_events) +#define mci_clear_stop_is_pending(host) \ + test_and_clear_bit(EVENT_STOP_COMPLETE, &host->pending_events) +#define mci_clear_stop_error_is_pending(host) \ + test_and_clear_bit(EVENT_STOP_ERROR, &host->pending_events) +#define mci_clear_dma_error_is_pending(host) \ + test_and_clear_bit(EVENT_DMA_ERROR, &host->pending_events) +#define mci_clear_card_detect_is_pending(host) \ + test_and_clear_bit(EVENT_CARD_DETECT, &host->pending_events) + +/* Test and set bit macros for completed events */ +#define mci_set_cmd_is_completed(host) \ + test_and_set_bit(EVENT_CMD_COMPLETE, &host->completed_events) +#define mci_set_cmd_error_is_completed(host) \ + test_and_set_bit(EVENT_CMD_ERROR, &host->completed_events) +#define mci_set_data_is_completed(host) \ + test_and_set_bit(EVENT_DATA_COMPLETE, &host->completed_events) +#define mci_set_data_error_is_completed(host) \ + test_and_set_bit(EVENT_DATA_ERROR, &host->completed_events) +#define mci_set_stop_sent_is_completed(host) \ + test_and_set_bit(EVENT_STOP_SENT, &host->completed_events) +#define mci_set_stop_is_completed(host) \ + test_and_set_bit(EVENT_STOP_COMPLETE, &host->completed_events) +#define mci_set_stop_error_is_completed(host) \ + test_and_set_bit(EVENT_STOP_ERROR, &host->completed_events) +#define mci_set_dma_error_is_completed(host) \ + test_and_set_bit(EVENT_DMA_ERROR, &host->completed_events) +#define mci_set_card_detect_is_completed(host) \ + test_and_set_bit(EVENT_CARD_DETECT, &host->completed_events) + +/* Set bit macros for completed events */ +#define mci_set_cmd_complete(host) \ + set_bit(EVENT_CMD_COMPLETE, &host->completed_events) +#define mci_set_cmd_error_complete(host) \ + set_bit(EVENT_CMD_ERROR, &host->completed_events) +#define mci_set_data_complete(host) \ + set_bit(EVENT_DATA_COMPLETE, &host->completed_events) +#define mci_set_data_error_complete(host) \ + set_bit(EVENT_DATA_ERROR, &host->completed_events) +#define mci_set_stop_sent_complete(host) \ + set_bit(EVENT_STOP_SENT, &host->completed_events) +#define mci_set_stop_complete(host) \ + set_bit(EVENT_STOP_COMPLETE, &host->completed_events) +#define mci_set_stop_error_complete(host) \ + set_bit(EVENT_STOP_ERROR, &host->completed_events) +#define mci_set_dma_error_complete(host) \ + set_bit(EVENT_DMA_ERROR, &host->completed_events) +#define mci_set_card_detect_complete(host) \ + set_bit(EVENT_CARD_DETECT, &host->completed_events) + +/* Set bit macros for pending events */ +#define mci_set_cmd_pending(host) \ + set_bit(EVENT_CMD_COMPLETE, &host->pending_events) +#define mci_set_cmd_error_pending(host) \ + set_bit(EVENT_CMD_ERROR, &host->pending_events) +#define mci_set_data_pending(host) \ + set_bit(EVENT_DATA_COMPLETE, &host->pending_events) +#define mci_set_data_error_pending(host) \ + set_bit(EVENT_DATA_ERROR, &host->pending_events) +#define mci_set_stop_sent_pending(host) \ + set_bit(EVENT_STOP_SENT, &host->pending_events) +#define mci_set_stop_pending(host) \ + set_bit(EVENT_STOP_COMPLETE, &host->pending_events) +#define mci_set_stop_error_pending(host) \ + set_bit(EVENT_STOP_ERROR, &host->pending_events) +#define mci_set_dma_error_pending(host) \ + set_bit(EVENT_DMA_ERROR, &host->pending_events) +#define mci_set_card_detect_pending(host) \ + set_bit(EVENT_CARD_DETECT, &host->pending_events) + +/* Clear bit macros for pending events */ +#define mci_clear_cmd_pending(host) \ + clear_bit(EVENT_CMD_COMPLETE, &host->pending_events) +#define mci_clear_cmd_error_pending(host) \ + clear_bit(EVENT_CMD_ERROR, &host->pending_events) +#define mci_clear_data_pending(host) \ + clear_bit(EVENT_DATA_COMPLETE, &host->pending_events) +#define mci_clear_data_error_pending(host) \ + clear_bit(EVENT_DATA_ERROR, &host->pending_events) +#define mci_clear_stop_sent_pending(host) \ + clear_bit(EVENT_STOP_SENT, &host->pending_events) +#define mci_clear_stop_pending(host) \ + clear_bit(EVENT_STOP_COMPLETE, &host->pending_events) +#define mci_clear_stop_error_pending(host) \ + clear_bit(EVENT_STOP_ERROR, &host->pending_events) +#define mci_clear_dma_error_pending(host) \ + clear_bit(EVENT_DMA_ERROR, &host->pending_events) +#define mci_clear_card_detect_pending(host) \ + clear_bit(EVENT_CARD_DETECT, &host->pending_events) + + +#ifdef CONFIG_DEBUG_FS +#include <linux/debugfs.h> + +#define DBG_REQ_BUF_SIZE (4096 - sizeof(unsigned int)) + +struct req_dbg_data { + unsigned int nbytes; + char str[DBG_REQ_BUF_SIZE]; +}; + +static int req_dbg_open(struct inode *inode, struct file *file) +{ + struct atmel_mci *host; + struct mmc_request *mrq; + struct mmc_command *cmd, *stop; + struct mmc_data *data; + struct req_dbg_data *priv; + char *str; + unsigned long n = 0; + + priv = kzalloc(DBG_REQ_BUF_SIZE, GFP_KERNEL); + if (!priv) + return -ENOMEM; + str = priv->str; + + mutex_lock(&inode->i_mutex); + host = inode->i_private; + + spin_lock_irq(&host->mmc->lock); + mrq = host->mrq; + if (mrq) { + cmd = mrq->cmd; + data = mrq->data; + stop = mrq->stop; + n = snprintf(str, DBG_REQ_BUF_SIZE, + "CMD%u(0x%x) %x %x %x %x %x (err %u)\n", + cmd->opcode, cmd->arg, cmd->flags, + cmd->resp[0], cmd->resp[1], cmd->resp[2], + cmd->resp[3], cmd->error); + if (n < DBG_REQ_BUF_SIZE && data) + n += snprintf(str + n, DBG_REQ_BUF_SIZE - n, + "DATA %u * %u (%u) %x (err %u)\n", + data->blocks, data->blksz, + data->bytes_xfered, data->flags, + data->error); + if (n < DBG_REQ_BUF_SIZE && stop) + n += snprintf(str + n, DBG_REQ_BUF_SIZE - n, + "CMD%u(0x%x) %x %x %x %x %x (err %u)\n", + stop->opcode, stop->arg, stop->flags, + stop->resp[0], stop->resp[1], + stop->resp[2], stop->resp[3], + stop->error); + } + spin_unlock_irq(&host->mmc->lock); + mutex_unlock(&inode->i_mutex); + + priv->nbytes = min(n, DBG_REQ_BUF_SIZE); + file->private_data = priv; + + return 0; +} + +static ssize_t req_dbg_read(struct file *file, char __user *buf, + size_t nbytes, loff_t *ppos) +{ + struct req_dbg_data *priv = file->private_data; + + return simple_read_from_buffer(buf, nbytes, ppos, + priv->str, priv->nbytes); +} + +static int req_dbg_release(struct inode *inode, struct file *file) +{ + kfree(file->private_data); + return 0; +} + +static const struct file_operations req_dbg_fops = { + .owner = THIS_MODULE, + .open = req_dbg_open, + .llseek = no_llseek, + .read = req_dbg_read, + .release = req_dbg_release, +}; + +static int regs_dbg_open(struct inode *inode, struct file *file) +{ + struct atmel_mci *host; + unsigned int i; + u32 *data; + int ret = -ENOMEM; + + mutex_lock(&inode->i_mutex); + host = inode->i_private; + data = kmalloc(inode->i_size, GFP_KERNEL); + if (!data) + goto out; + + spin_lock_irq(&host->mmc->lock); + for (i = 0; i < inode->i_size / 4; i++) + data[i] = __raw_readl(host->regs + i * 4); + spin_unlock_irq(&host->mmc->lock); + + file->private_data = data; + ret = 0; + +out: + mutex_unlock(&inode->i_mutex); + + return ret; +} + +static ssize_t regs_dbg_read(struct file *file, char __user *buf, + size_t nbytes, loff_t *ppos) +{ + struct inode *inode = file->f_dentry->d_inode; + int ret; + + mutex_lock(&inode->i_mutex); + ret = simple_read_from_buffer(buf, nbytes, ppos, + file->private_data, + file->f_dentry->d_inode->i_size); + mutex_unlock(&inode->i_mutex); + + return ret; +} + +static int regs_dbg_release(struct inode *inode, struct file *file) +{ + kfree(file->private_data); + return 0; +} + +static const struct file_operations regs_dbg_fops = { + .owner = THIS_MODULE, + .open = regs_dbg_open, + .llseek = generic_file_llseek, + .read = regs_dbg_read, + .release = regs_dbg_release, +}; + +static void atmci_init_debugfs(struct atmel_mci *host) +{ + struct mmc_host *mmc; + struct dentry *root, *regs; + struct resource *res; + + mmc = host->mmc; + root = debugfs_create_dir(mmc_hostname(mmc), NULL); + if (IS_ERR(root) || !root) + goto err_root; + host->debugfs_root = root; + + regs = debugfs_create_file("regs", 0400, root, host, ®s_dbg_fops); + if (!regs) + goto err_regs; + + res = platform_get_resource(host->pdev, IORESOURCE_MEM, 0); + regs->d_inode->i_size = res->end - res->start + 1; + host->debugfs_regs = regs; + + host->debugfs_req = debugfs_create_file("req", 0400, root, + host, &req_dbg_fops); + if (!host->debugfs_req) + goto err_req; + + host->debugfs_pending_events + = debugfs_create_u32("pending_events", 0400, root, + (u32 *)&host->pending_events); + if (!host->debugfs_pending_events) + goto err_pending_events; + + host->debugfs_completed_events + = debugfs_create_u32("completed_events", 0400, root, + (u32 *)&host->completed_events); + if (!host->debugfs_completed_events) + goto err_completed_events; + + return; + +err_completed_events: + debugfs_remove(host->debugfs_pending_events); +err_pending_events: + debugfs_remove(host->debugfs_req); +err_req: + debugfs_remove(host->debugfs_regs); +err_regs: + debugfs_remove(host->debugfs_root); +err_root: + host->debugfs_root = NULL; + dev_err(&host->pdev->dev, + "failed to initialize debugfs for %s\n", + mmc_hostname(mmc)); +} + +static void atmci_cleanup_debugfs(struct atmel_mci *host) +{ + if (host->debugfs_root) { + debugfs_remove(host->debugfs_completed_events); + debugfs_remove(host->debugfs_pending_events); + debugfs_remove(host->debugfs_req); + debugfs_remove(host->debugfs_regs); + debugfs_remove(host->debugfs_root); + host->debugfs_root = NULL; + } +} +#else +static inline void atmci_init_debugfs(struct atmel_mci *host) +{ + +} + +static inline void atmci_cleanup_debugfs(struct atmel_mci *host) +{ + +} +#endif /* CONFIG_DEBUG_FS */ + +static inline unsigned int ns_to_clocks(struct atmel_mci *host, + unsigned int ns) +{ + return (ns * (host->bus_hz / 1000000) + 999) / 1000; +} + +static void atmci_set_timeout(struct atmel_mci *host, + struct mmc_data *data) +{ + static unsigned dtomul_to_shift[] = { + 0, 4, 7, 8, 10, 12, 16, 20 + }; + unsigned timeout; + unsigned dtocyc, dtomul; + + timeout = ns_to_clocks(host, data->timeout_ns) + data->timeout_clks; + + for (dtomul = 0; dtomul < 8; dtomul++) { + unsigned shift = dtomul_to_shift[dtomul]; + dtocyc = (timeout + (1 << shift) - 1) >> shift; + if (dtocyc < 15) + break; + } + + if (dtomul >= 8) { + dtomul = 7; + dtocyc = 15; + } + + pr_debug("%s: setting timeout to %u cycles\n", + mmc_hostname(host->mmc), + dtocyc << dtomul_to_shift[dtomul]); + mci_writel(host, DTOR, (MCI_BF(DTOMUL, dtomul) + | MCI_BF(DTOCYC, dtocyc))); +} + +/* + * Return mask with interrupt flags to be handled for this command. + */ +static u32 atmci_prepare_command(struct mmc_host *mmc, + struct mmc_command *cmd, + u32 *cmd_flags) +{ + u32 cmdr; + u32 iflags; + + cmd->error = MMC_ERR_NONE; + + cmdr = 0; + BUG_ON(MCI_BFEXT(CMDNB, cmdr) != 0); + cmdr = MCI_BFINS(CMDNB, cmd->opcode, cmdr); + + if (cmd->flags & MMC_RSP_PRESENT) { + if (cmd->flags & MMC_RSP_136) + cmdr |= MCI_BF(RSPTYP, MCI_RSPTYP_136_BIT); + else + cmdr |= MCI_BF(RSPTYP, MCI_RSPTYP_48_BIT); + } + + /* + * This should really be MAXLAT_5 for CMD2 and ACMD41, but + * it's too difficult to determine whether this is an ACMD or + * not. Better make it 64. + */ + cmdr |= MCI_BIT(MAXLAT); + + if (mmc->ios.bus_mode == MMC_BUSMODE_OPENDRAIN) + cmdr |= MCI_BIT(OPDCMD); + + iflags = MCI_BIT(CMDRDY) | MCI_CMD_ERROR_FLAGS; + if (!(cmd->flags & MMC_RSP_CRC)) + iflags &= ~MCI_BIT(RCRCE); + + pr_debug("%s: cmd: op %02x arg %08x flags %08x, cmdflags %08lx\n", + mmc_hostname(mmc), cmd->opcode, cmd->arg, cmd->flags, + (unsigned long)cmdr); + + *cmd_flags = cmdr; + return iflags; +} + +static void atmci_start_command(struct atmel_mci *host, + struct mmc_command *cmd, + u32 cmd_flags) +{ + WARN_ON(host->cmd); + host->cmd = cmd; + + mci_writel(host, ARGR, cmd->arg); + mci_writel(host, CMDR, cmd_flags); + + if (cmd->data) + dma_start_request(host->dma.req.req.dmac, + host->dma.req.req.channel); +} + +/* + * Returns a mask of flags to be set in the command register when the + * command to start the transfer is to be sent. + */ +static u32 atmci_prepare_data(struct mmc_host *mmc, struct mmc_data *data) +{ + struct atmel_mci *host = mmc_priv(mmc); + u32 cmd_flags; + + WARN_ON(host->data); + host->data = data; + + atmci_set_timeout(host, data); + mci_writel(host, BLKR, (MCI_BF(BCNT, data->blocks) + | MCI_BF(BLKLEN, data->blksz))); + host->dma.req.block_size = data->blksz; + host->dma.req.nr_blocks = data->blocks; + + cmd_flags = MCI_BF(TRCMD, MCI_TRCMD_START_TRANS); + if (data->flags & MMC_DATA_STREAM) + cmd_flags |= MCI_BF(TRTYP, MCI_TRTYP_STREAM); + else if (data->blocks > 1) + cmd_flags |= MCI_BF(TRTYP, MCI_TRTYP_MULTI_BLOCK); + else + cmd_flags |= MCI_BF(TRTYP, MCI_TRTYP_BLOCK); + + if (data->flags & MMC_DATA_READ) { + cmd_flags |= MCI_BIT(TRDIR); + host->dma.req.nr_sg + = dma_map_sg(&host->pdev->dev, data->sg, + data->sg_len, DMA_FROM_DEVICE); + host->dma.req.periph_id = host->dma.rx_periph_id; + host->dma.req.direction = DMA_DIR_PERIPH_TO_MEM; + host->dma.req.data_reg = host->mapbase + MCI_RDR; + } else { + host->dma.req.nr_sg + = dma_map_sg(&host->pdev->dev, data->sg, + data->sg_len, DMA_TO_DEVICE); + host->dma.req.periph_id = host->dma.tx_periph_id; + host->dma.req.direction = DMA_DIR_MEM_TO_PERIPH; + host->dma.req.data_reg = host->mapbase + MCI_TDR; + } + host->dma.req.sg = data->sg; + + dma_prepare_request_sg(host->dma.req.req.dmac, &host->dma.req); + + return cmd_flags; +} + +static void atmci_request(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct atmel_mci *host = mmc_priv(mmc); + struct mmc_data *data = mrq->data; + u32 iflags; + u32 cmdflags = 0; + + iflags = mci_readl(host, IMR); + if (iflags) + printk("WARNING: IMR=0x%08x\n", mci_readl(host, IMR)); + + WARN_ON(host->mrq != NULL); + host->mrq = mrq; + host->pending_events = 0; + host->completed_events = 0; + + iflags = atmci_prepare_command(mmc, mrq->cmd, &cmdflags); + + if (mrq->stop) { + BUG_ON(!data); + + host->stop_iflags = atmci_prepare_command(mmc, mrq->stop, + &host->stop_cmdr); + host->stop_cmdr |= MCI_BF(TRCMD, MCI_TRCMD_STOP_TRANS); + if (!(data->flags & MMC_DATA_WRITE)) + host->stop_cmdr |= MCI_BIT(TRDIR); + if (data->flags & MMC_DATA_STREAM) + host->stop_cmdr |= MCI_BF(TRTYP, MCI_TRTYP_STREAM); + else + host->stop_cmdr |= MCI_BF(TRTYP, MCI_TRTYP_MULTI_BLOCK); + } + if (data) { + cmdflags |= atmci_prepare_data(mmc, data); + iflags |= MCI_DATA_ERROR_FLAGS; + } + + atmci_start_command(host, mrq->cmd, cmdflags); + mci_writel(host, IER, iflags); +} + +static void atmci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct atmel_mci *host = mmc_priv(mmc); + + if (ios->clock) { + u32 clkdiv; + + clkdiv = host->bus_hz / (2 * ios->clock) - 1; + if (clkdiv > 255) + clkdiv = 255; + mci_writel(host, MR, (clkdiv + | MCI_BIT(WRPROOF) + | MCI_BIT(RDPROOF))); + } + + switch (ios->bus_width) { + case MMC_BUS_WIDTH_1: + mci_writel(host, SDCR, 0); + break; + case MMC_BUS_WIDTH_4: + mci_writel(host, SDCR, MCI_BIT(SDCBUS)); + break; + } + + switch (ios->power_mode) { + case MMC_POWER_OFF: + mci_writel(host, CR, MCI_BIT(MCIDIS)); + break; + case MMC_POWER_UP: + mci_writel(host, CR, MCI_BIT(SWRST)); + break; + case MMC_POWER_ON: + mci_writel(host, CR, MCI_BIT(MCIEN)); + break; + } +} + +static int atmci_get_ro(struct mmc_host *mmc) +{ + int read_only = 0; + struct atmel_mci *host = mmc_priv(mmc); + + if (host->wp_pin >= 0) { + read_only = gpio_get_value(host->wp_pin); + pr_debug("%s: card is %s\n", mmc_hostname(mmc), + read_only ? "read-only" : "read-write"); + } else { + pr_debug("%s: no pin for checking read-only switch." + " Assuming write-enable.\n", mmc_hostname(mmc)); + } + + return read_only; +} + +static struct mmc_host_ops atmci_ops = { + .request = atmci_request, + .set_ios = atmci_set_ios, + .get_ro = atmci_get_ro, +}; + +static void atmci_request_end(struct mmc_host *mmc, struct mmc_request *mrq) +{ + struct atmel_mci *host = mmc_priv(mmc); + + WARN_ON(host->cmd || host->data); + host->mrq = NULL; + + mmc_request_done(mmc, mrq); +} + +static void send_stop_cmd(struct mmc_host *mmc, struct mmc_data *data, + u32 flags) +{ + struct atmel_mci *host = mmc_priv(mmc); + + atmci_start_command(host, data->stop, host->stop_cmdr | flags); + mci_writel(host, IER, host->stop_iflags); +} + +static void atmci_data_complete(struct atmel_mci *host, struct mmc_data *data) +{ + host->data = NULL; + dma_unmap_sg(&host->pdev->dev, data->sg, host->dma.req.nr_sg, + ((data->flags & MMC_DATA_WRITE) + ? DMA_TO_DEVICE : DMA_FROM_DEVICE)); + + /* + * Data might complete before command for very short transfers + * (like READ_SCR) + */ + if (mci_cmd_is_complete(host) + && (!data->stop || mci_stop_is_complete(host))) + atmci_request_end(host->mmc, data->mrq); +} + +static void atmci_command_error(struct mmc_host *mmc, + struct mmc_command *cmd, + u32 status) +{ + pr_debug("%s: command error: status=0x%08x\n", + mmc_hostname(mmc), status); + + if (status & MCI_BIT(RTOE)) + cmd->error = MMC_ERR_TIMEOUT; + else if (status & MCI_BIT(RCRCE)) + cmd->error = MMC_ERR_BADCRC; + else + cmd->error = MMC_ERR_FAILED; +} + +static void atmci_tasklet_func(unsigned long priv) +{ + struct mmc_host *mmc = (struct mmc_host *)priv; + struct atmel_mci *host = mmc_priv(mmc); + struct mmc_request *mrq = host->mrq; + struct mmc_data *data = host->data; + + pr_debug("atmci_tasklet: pending/completed/mask %lx/%lx/%x\n", + host->pending_events, host->completed_events, + mci_readl(host, IMR)); + + if (mci_clear_cmd_error_is_pending(host)) { + struct mmc_command *cmd; + + mci_set_cmd_error_complete(host); + mci_clear_cmd_pending(host); + cmd = host->mrq->cmd; + + if (cmd->data) { + dma_stop_request(host->dma.req.req.dmac, + host->dma.req.req.channel); + host->data = NULL; + } + + atmci_command_error(mmc, cmd, host->error_status); + atmci_request_end(mmc, cmd->mrq); + } + if (mci_clear_stop_error_is_pending(host)) { + mci_set_stop_error_complete(host); + mci_clear_stop_pending(host); + atmci_command_error(mmc, host->mrq->stop, + host->error_status); + if (!host->data) + atmci_request_end(mmc, host->mrq); + } + if (mci_clear_cmd_is_pending(host)) { + mci_set_cmd_complete(host); + if (!mrq->data || mci_data_is_complete(host) + || mci_data_error_is_complete(host)) + atmci_request_end(mmc, mrq); + } + if (mci_clear_stop_is_pending(host)) { + mci_set_stop_complete(host); + if (mci_data_is_complete(host) + || mci_data_error_is_complete(host)) + atmci_request_end(mmc, mrq); + } + if (mci_clear_dma_error_is_pending(host)) { + mci_set_dma_error_complete(host); + mci_clear_data_pending(host); + + /* DMA controller got bus error => invalid address */ + data->error = MMC_ERR_INVALID; + + printk(KERN_DEBUG "%s: dma error after %u bytes xfered\n", + mmc_hostname(mmc), host->data->bytes_xfered); + + if (data->stop + && !mci_set_stop_sent_is_completed(host)) + /* TODO: Check if card is still present */ + send_stop_cmd(host->mmc, data, 0); + + atmci_data_complete(host, data); + } + if (mci_clear_data_error_is_pending(host)) { + u32 status = host->error_status; + + mci_set_data_error_complete(host); + mci_clear_data_pending(host); + + dma_stop_request(host->dma.req.req.dmac, + host->dma.req.req.channel); + + printk(KERN_DEBUG "%s: data error: status=0x%08x\n", + mmc_hostname(host->mmc), status); + + if (status & MCI_BIT(DCRCE)) { + printk(KERN_DEBUG "%s: Data CRC error\n", + mmc_hostname(host->mmc)); + data->error = MMC_ERR_BADCRC; + } else if (status & MCI_BIT(DTOE)) { + printk(KERN_DEBUG "%s: Data Timeout error\n", + mmc_hostname(host->mmc)); + data->error = MMC_ERR_TIMEOUT; + } else { + printk(KERN_DEBUG "%s: Data FIFO error\n", + mmc_hostname(host->mmc)); + data->error = MMC_ERR_FIFO; + } + printk(KERN_DEBUG "%s: Bytes xfered: %u\n", + mmc_hostname(host->mmc), data->bytes_xfered); + + if (data->stop + && !mci_set_stop_sent_is_completed(host)) + /* TODO: Check if card is still present */ + send_stop_cmd(host->mmc, data, 0); + + atmci_data_complete(host, data); + } + if (mci_clear_data_is_pending(host)) { + mci_set_data_complete(host); + data->bytes_xfered = data->blocks * data->blksz; + atmci_data_complete(host, data); + } + if (mci_clear_card_detect_is_pending(host)) { + /* Reset controller if card is gone */ + if (!host->present) { + mci_writel(host, CR, MCI_BIT(SWRST)); + mci_writel(host, IDR, ~0UL); + mci_writel(host, CR, MCI_BIT(MCIEN)); + } + + /* Clean up queue if present */ + if (mrq) { + if (!mci_cmd_is_complete(host) + && !mci_cmd_error_is_complete(host)) { + mrq->cmd->error = MMC_ERR_TIMEOUT; + } + if (mrq->data && !mci_data_is_complete(host) + && !mci_data_error_is_complete(host)) { + dma_stop_request(host->dma.req.req.dmac, + host->dma.req.req.channel); + host->data->error = MMC_ERR_TIMEOUT; + atmci_data_complete(host, data); + } + if (mrq->stop && !mci_stop_is_complete(host) + && !mci_stop_error_is_complete(host)) { + mrq->stop->error = MMC_ERR_TIMEOUT; + } + + host->cmd = NULL; + atmci_request_end(mmc, mrq); + } + mmc_detect_change(host->mmc, msecs_to_jiffies(100)); + } +} + +static void atmci_cmd_interrupt(struct mmc_host *mmc, u32 status) +{ + struct atmel_mci *host = mmc_priv(mmc); + struct mmc_command *cmd = host->cmd; + + /* + * Read the response now so that we're free to send a new + * command immediately. + */ + cmd->resp[0] = mci_readl(host, RSPR); + cmd->resp[1] = mci_readl(host, RSPR); + cmd->resp[2] = mci_readl(host, RSPR); + cmd->resp[3] = mci_readl(host, RSPR); + + mci_writel(host, IDR, MCI_BIT(CMDRDY) | MCI_CMD_ERROR_FLAGS); + host->cmd = NULL; + + if (mci_stop_sent_is_complete(host)) + mci_set_stop_pending(host); + else + mci_set_cmd_pending(host); + + tasklet_schedule(&host->tasklet); +} + +static void atmci_xfer_complete(struct dma_request *_req) +{ + struct dma_request_sg *req = to_dma_request_sg(_req); + struct atmel_mci_dma *dma; + struct atmel_mci *host; + struct mmc_data *data; + + dma = container_of(req, struct atmel_mci_dma, req); + host = container_of(dma, struct atmel_mci, dma); + data = host->data; + + if (data->stop && !mci_set_stop_sent_is_completed(host)) + send_stop_cmd(host->mmc, data, 0); + + if (data->flags & MMC_DATA_READ) { + mci_writel(host, IDR, MCI_DATA_ERROR_FLAGS); + mci_set_data_pending(host); + tasklet_schedule(&host->tasklet); + } else { + /* + * For the WRITE case, wait for NOTBUSY. This function + * is called when everything has been written to the + * controller, not when the card is done programming. + */ + mci_writel(host, IER, MCI_BIT(NOTBUSY)); + } +} + +static void atmci_dma_error(struct dma_request *_req) +{ + struct dma_request_sg *req = to_dma_request_sg(_req); + struct atmel_mci_dma *dma; + struct atmel_mci *host; + + dma = container_of(req, struct atmel_mci_dma, req); + host = container_of(dma, struct atmel_mci, dma); + + mci_writel(host, IDR, (MCI_BIT(NOTBUSY) + | MCI_DATA_ERROR_FLAGS)); + + mci_set_dma_error_pending(host); + tasklet_schedule(&host->tasklet); +} + +static irqreturn_t atmci_interrupt(int irq, void *dev_id) +{ + struct mmc_host *mmc = dev_id; + struct atmel_mci *host = mmc_priv(mmc); + u32 status, mask, pending; + + spin_lock(&mmc->lock); + + status = mci_readl(host, SR); + mask = mci_readl(host, IMR); + pending = status & mask; + + do { + if (pending & MCI_CMD_ERROR_FLAGS) { + mci_writel(host, IDR, (MCI_BIT(CMDRDY) + | MCI_BIT(NOTBUSY) + | MCI_CMD_ERROR_FLAGS + | MCI_DATA_ERROR_FLAGS)); + host->error_status = status; + host->cmd = NULL; + if (mci_stop_sent_is_complete(host)) + mci_set_stop_error_pending(host); + else + mci_set_cmd_error_pending(host); + tasklet_schedule(&host->tasklet); + break; + } + if (pending & MCI_DATA_ERROR_FLAGS) { + mci_writel(host, IDR, (MCI_BIT(NOTBUSY) + | MCI_DATA_ERROR_FLAGS)); + host->error_status = status; + mci_set_data_error_pending(host); + tasklet_schedule(&host->tasklet); + break; + } + if (pending & MCI_BIT(CMDRDY)) + atmci_cmd_interrupt(mmc, status); + if (pending & MCI_BIT(NOTBUSY)) { + mci_writel(host, IDR, (MCI_BIT(NOTBUSY) + | MCI_DATA_ERROR_FLAGS)); + mci_set_data_pending(host); + tasklet_schedule(&host->tasklet); + } + + status = mci_readl(host, SR); + mask = mci_readl(host, IMR); + pending = status & mask; + } while (pending); + + spin_unlock(&mmc->lock); + + return IRQ_HANDLED; +} + +static irqreturn_t atmci_detect_change(int irq, void *dev_id) +{ + struct mmc_host *mmc = dev_id; + struct atmel_mci *host = mmc_priv(mmc); + + int present = !gpio_get_value(irq_to_gpio(irq)); + + if (present != host->present) { + pr_debug("%s: card %s\n", mmc_hostname(host->mmc), + present ? "inserted" : "removed"); + host->present = present; + mci_set_card_detect_pending(host); + tasklet_schedule(&host->tasklet); + } + return IRQ_HANDLED; +} + +static int __devinit atmci_probe(struct platform_device *pdev) +{ + struct mci_platform_data *board; + struct atmel_mci *host; + struct mmc_host *mmc; + struct resource *regs; + int irq; + int ret; + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) + return -ENXIO; + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + board = pdev->dev.platform_data; + + mmc = mmc_alloc_host(sizeof(struct atmel_mci), &pdev->dev); + if (!mmc) + return -ENOMEM; + + host = mmc_priv(mmc); + host->pdev = pdev; + host->mmc = mmc; + if (board) { + host->detect_pin = board->detect_pin; + host->wp_pin = board->wp_pin; + } else { + host->detect_pin = -1; + host->detect_pin = -1; + } + + host->mck = clk_get(&pdev->dev, "mci_clk"); + if (IS_ERR(host->mck)) { + ret = PTR_ERR(host->mck); + goto out_free_host; + } + clk_enable(host->mck); + + ret = -ENOMEM; + host->regs = ioremap(regs->start, regs->end - regs->start + 1); + if (!host->regs) + goto out_disable_clk; + + host->bus_hz = clk_get_rate(host->mck); + host->mapbase = regs->start; + + mmc->ops = &atmci_ops; + mmc->f_min = (host->bus_hz + 511) / 512; + mmc->f_max = min((unsigned int)(host->bus_hz / 2), fmax); + mmc->ocr_avail = 0x00100000; + mmc->caps |= MMC_CAP_4_BIT_DATA; + + tasklet_init(&host->tasklet, atmci_tasklet_func, (unsigned long)mmc); + + ret = request_irq(irq, atmci_interrupt, 0, "mmci", mmc); + if (ret) + goto out_unmap; + + /* Assume card is present if we don't have a detect pin */ + host->present = 1; + if (host->detect_pin >= 0) { + if (gpio_request(host->detect_pin, "mmc_detect")) { + printk(KERN_WARNING "%s: no detect pin available\n", + mmc_hostname(host->mmc)); + host->detect_pin = -1; + } else { + host->present = !gpio_get_value(host->detect_pin); + } + } + if (host->wp_pin >= 0) { + if (gpio_request(host->wp_pin, "mmc_wp")) { + printk(KERN_WARNING "%s: no WP pin available\n", + mmc_hostname(host->mmc)); + host->wp_pin = -1; + } + } + + /* TODO: Get this information from platform data */ + ret = -ENOMEM; + host->dma.req.req.dmac = find_dma_controller(0); + if (!host->dma.req.req.dmac) { + printk(KERN_ERR + "mmci: No DMA controller available, aborting\n"); + goto out_free_irq; + } + ret = dma_alloc_channel(host->dma.req.req.dmac); + if (ret < 0) { + printk(KERN_ERR + "mmci: Unable to allocate DMA channel, aborting\n"); + goto out_free_irq; + } + host->dma.req.req.channel = ret; + host->dma.req.width = DMA_WIDTH_32BIT; + host->dma.req.req.xfer_complete = atmci_xfer_complete; + host->dma.req.req.block_complete = NULL; // atmci_block_complete; + host->dma.req.req.error = atmci_dma_error; + host->dma.rx_periph_id = 0; + host->dma.tx_periph_id = 1; + + mci_writel(host, CR, MCI_BIT(SWRST)); + mci_writel(host, IDR, ~0UL); + mci_writel(host, CR, MCI_BIT(MCIEN)); + + platform_set_drvdata(pdev, host); + + mmc_add_host(mmc); + + if (host->detect_pin >= 0) { + ret = request_irq(gpio_to_irq(host->detect_pin), + atmci_detect_change, + IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, + DRIVER_NAME, mmc); + if (ret) { + printk(KERN_ERR + "%s: could not request IRQ %d for detect pin\n", + mmc_hostname(mmc), + gpio_to_irq(host->detect_pin)); + gpio_free(host->detect_pin); + host->detect_pin = -1; + } + } + + printk(KERN_INFO "%s: Atmel MCI controller at 0x%08lx irq %d\n", + mmc_hostname(mmc), host->mapbase, irq); + + atmci_init_debugfs(host); + + return 0; + +out_free_irq: + if (host->detect_pin >= 0) + gpio_free(host->detect_pin); + if (host->wp_pin >= 0) + gpio_free(host->wp_pin); + free_irq(irq, mmc); +out_unmap: + iounmap(host->regs); +out_disable_clk: + clk_disable(host->mck); + clk_put(host->mck); +out_free_host: + mmc_free_host(mmc); + return ret; +} + +static int __devexit atmci_remove(struct platform_device *pdev) +{ + struct atmel_mci *host = platform_get_drvdata(pdev); + + platform_set_drvdata(pdev, NULL); + + if (host) { + atmci_cleanup_debugfs(host); + + if (host->detect_pin >= 0) { + free_irq(gpio_to_irq(host->detect_pin), host->mmc); + cancel_delayed_work(&host->mmc->detect); + gpio_free(host->detect_pin); + } + + mmc_remove_host(host->mmc); + + mci_writel(host, IDR, ~0UL); + mci_writel(host, CR, MCI_BIT(MCIDIS)); + mci_readl(host, SR); + + dma_release_channel(host->dma.req.req.dmac, + host->dma.req.req.channel); + + if (host->wp_pin >= 0) + gpio_free(host->wp_pin); + + free_irq(platform_get_irq(pdev, 0), host->mmc); + iounmap(host->regs); + + clk_disable(host->mck); + clk_put(host->mck); + + mmc_free_host(host->mmc); + } + return 0; +} + +static struct platform_driver atmci_driver = { + .probe = atmci_probe, + .remove = __devexit_p(atmci_remove), + .driver = { + .name = DRIVER_NAME, + }, +}; + +static int __init atmci_init(void) +{ + return platform_driver_register(&atmci_driver); +} + +static void __exit atmci_exit(void) +{ + platform_driver_unregister(&atmci_driver); +} + +module_init(atmci_init); +module_exit(atmci_exit); + +MODULE_DESCRIPTION("Atmel Multimedia Card Interface driver"); +MODULE_LICENSE("GPL"); diff -urN linux-2.6.20.4-0rig/drivers/mmc/atmel-mci.h linux-2.6.20.4-atmel/drivers/mmc/atmel-mci.h --- linux-2.6.20.4-0rig/drivers/mmc/atmel-mci.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/mmc/atmel-mci.h 2007-03-24 16:42:29.000000000 +0100 @@ -0,0 +1,192 @@ +/* + * Atmel MultiMedia Card Interface driver + * + * Copyright (C) 2004-2006 Atmel Corporation + * + * 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. + */ +#ifndef __DRIVERS_MMC_ATMEL_MCI_H__ +#define __DRIVERS_MMC_ATMEL_MCI_H__ + +/* MCI register offsets */ +#define MCI_CR 0x0000 +#define MCI_MR 0x0004 +#define MCI_DTOR 0x0008 +#define MCI_SDCR 0x000c +#define MCI_ARGR 0x0010 +#define MCI_CMDR 0x0014 +#define MCI_BLKR 0x0018 +#define MCI_RSPR 0x0020 +#define MCI_RSPR1 0x0024 +#define MCI_RSPR2 0x0028 +#define MCI_RSPR3 0x002c +#define MCI_RDR 0x0030 +#define MCI_TDR 0x0034 +#define MCI_SR 0x0040 +#define MCI_IER 0x0044 +#define MCI_IDR 0x0048 +#define MCI_IMR 0x004c + +/* Bitfields in CR */ +#define MCI_MCIEN_OFFSET 0 +#define MCI_MCIEN_SIZE 1 +#define MCI_MCIDIS_OFFSET 1 +#define MCI_MCIDIS_SIZE 1 +#define MCI_PWSEN_OFFSET 2 +#define MCI_PWSEN_SIZE 1 +#define MCI_PWSDIS_OFFSET 3 +#define MCI_PWSDIS_SIZE 1 +#define MCI_SWRST_OFFSET 7 +#define MCI_SWRST_SIZE 1 + +/* Bitfields in MR */ +#define MCI_CLKDIV_OFFSET 0 +#define MCI_CLKDIV_SIZE 8 +#define MCI_PWSDIV_OFFSET 8 +#define MCI_PWSDIV_SIZE 3 +#define MCI_RDPROOF_OFFSET 11 +#define MCI_RDPROOF_SIZE 1 +#define MCI_WRPROOF_OFFSET 12 +#define MCI_WRPROOF_SIZE 1 +#define MCI_DMAPADV_OFFSET 14 +#define MCI_DMAPADV_SIZE 1 +#define MCI_BLKLEN_OFFSET 16 +#define MCI_BLKLEN_SIZE 16 + +/* Bitfields in DTOR */ +#define MCI_DTOCYC_OFFSET 0 +#define MCI_DTOCYC_SIZE 4 +#define MCI_DTOMUL_OFFSET 4 +#define MCI_DTOMUL_SIZE 3 + +/* Bitfields in SDCR */ +#define MCI_SDCSEL_OFFSET 0 +#define MCI_SDCSEL_SIZE 4 +#define MCI_SDCBUS_OFFSET 7 +#define MCI_SDCBUS_SIZE 1 + +/* Bitfields in ARGR */ +#define MCI_ARG_OFFSET 0 +#define MCI_ARG_SIZE 32 + +/* Bitfields in CMDR */ +#define MCI_CMDNB_OFFSET 0 +#define MCI_CMDNB_SIZE 6 +#define MCI_RSPTYP_OFFSET 6 +#define MCI_RSPTYP_SIZE 2 +#define MCI_SPCMD_OFFSET 8 +#define MCI_SPCMD_SIZE 3 +#define MCI_OPDCMD_OFFSET 11 +#define MCI_OPDCMD_SIZE 1 +#define MCI_MAXLAT_OFFSET 12 +#define MCI_MAXLAT_SIZE 1 +#define MCI_TRCMD_OFFSET 16 +#define MCI_TRCMD_SIZE 2 +#define MCI_TRDIR_OFFSET 18 +#define MCI_TRDIR_SIZE 1 +#define MCI_TRTYP_OFFSET 19 +#define MCI_TRTYP_SIZE 2 + +/* Bitfields in BLKR */ +#define MCI_BCNT_OFFSET 0 +#define MCI_BCNT_SIZE 16 + +/* Bitfields in RSPRn */ +#define MCI_RSP_OFFSET 0 +#define MCI_RSP_SIZE 32 + +/* Bitfields in SR/IER/IDR/IMR */ +#define MCI_CMDRDY_OFFSET 0 +#define MCI_CMDRDY_SIZE 1 +#define MCI_RXRDY_OFFSET 1 +#define MCI_RXRDY_SIZE 1 +#define MCI_TXRDY_OFFSET 2 +#define MCI_TXRDY_SIZE 1 +#define MCI_BLKE_OFFSET 3 +#define MCI_BLKE_SIZE 1 +#define MCI_DTIP_OFFSET 4 +#define MCI_DTIP_SIZE 1 +#define MCI_NOTBUSY_OFFSET 5 +#define MCI_NOTBUSY_SIZE 1 +#define MCI_ENDRX_OFFSET 6 +#define MCI_ENDRX_SIZE 1 +#define MCI_ENDTX_OFFSET 7 +#define MCI_ENDTX_SIZE 1 +#define MCI_RXBUFF_OFFSET 14 +#define MCI_RXBUFF_SIZE 1 +#define MCI_TXBUFE_OFFSET 15 +#define MCI_TXBUFE_SIZE 1 +#define MCI_RINDE_OFFSET 16 +#define MCI_RINDE_SIZE 1 +#define MCI_RDIRE_OFFSET 17 +#define MCI_RDIRE_SIZE 1 +#define MCI_RCRCE_OFFSET 18 +#define MCI_RCRCE_SIZE 1 +#define MCI_RENDE_OFFSET 19 +#define MCI_RENDE_SIZE 1 +#define MCI_RTOE_OFFSET 20 +#define MCI_RTOE_SIZE 1 +#define MCI_DCRCE_OFFSET 21 +#define MCI_DCRCE_SIZE 1 +#define MCI_DTOE_OFFSET 22 +#define MCI_DTOE_SIZE 1 +#define MCI_OVRE_OFFSET 30 +#define MCI_OVRE_SIZE 1 +#define MCI_UNRE_OFFSET 31 +#define MCI_UNRE_SIZE 1 + +/* Constants for DTOMUL */ +#define MCI_DTOMUL_1_CYCLE 0 +#define MCI_DTOMUL_16_CYCLES 1 +#define MCI_DTOMUL_128_CYCLES 2 +#define MCI_DTOMUL_256_CYCLES 3 +#define MCI_DTOMUL_1024_CYCLES 4 +#define MCI_DTOMUL_4096_CYCLES 5 +#define MCI_DTOMUL_65536_CYCLES 6 +#define MCI_DTOMUL_1048576_CYCLES 7 + +/* Constants for RSPTYP */ +#define MCI_RSPTYP_NO_RESP 0 +#define MCI_RSPTYP_48_BIT 1 +#define MCI_RSPTYP_136_BIT 2 + +/* Constants for SPCMD */ +#define MCI_SPCMD_NO_SPEC_CMD 0 +#define MCI_SPCMD_INIT_CMD 1 +#define MCI_SPCMD_SYNC_CMD 2 +#define MCI_SPCMD_INT_CMD 4 +#define MCI_SPCMD_INT_RESP 5 + +/* Constants for TRCMD */ +#define MCI_TRCMD_NO_TRANS 0 +#define MCI_TRCMD_START_TRANS 1 +#define MCI_TRCMD_STOP_TRANS 2 + +/* Constants for TRTYP */ +#define MCI_TRTYP_BLOCK 0 +#define MCI_TRTYP_MULTI_BLOCK 1 +#define MCI_TRTYP_STREAM 2 + +/* Bit manipulation macros */ +#define MCI_BIT(name) \ + (1 << MCI_##name##_OFFSET) +#define MCI_BF(name,value) \ + (((value) & ((1 << MCI_##name##_SIZE) - 1)) \ + << MCI_##name##_OFFSET) +#define MCI_BFEXT(name,value) \ + (((value) >> MCI_##name##_OFFSET) \ + & ((1 << MCI_##name##_SIZE) - 1)) +#define MCI_BFINS(name,value,old) \ + (((old) & ~(((1 << MCI_##name##_SIZE) - 1) \ + << MCI_##name##_OFFSET)) \ + | MCI_BF(name,value)) + +/* Register access macros */ +#define mci_readl(port,reg) \ + __raw_readl((port)->regs + MCI_##reg) +#define mci_writel(port,reg,value) \ + __raw_writel((value), (port)->regs + MCI_##reg) + +#endif /* __DRIVERS_MMC_ATMEL_MCI_H__ */ diff -urN linux-2.6.20.4-0rig/drivers/mmc/Kconfig linux-2.6.20.4-atmel/drivers/mmc/Kconfig --- linux-2.6.20.4-0rig/drivers/mmc/Kconfig 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/mmc/Kconfig 2007-03-24 16:42:29.000000000 +0100 @@ -71,6 +71,16 @@ If unsure, say N. +config MMC_ATMELMCI + tristate "Atmel Multimedia Card Interface support" + depends on AVR32 && MMC + help + This selects the Atmel Multimedia Card Interface. If you have + a AT91 (ARM) or AT32 (AVR32) platform with a Multimedia Card + slot, say Y or M here. + + If unsure, say N. + config MMC_WBSD tristate "Winbond W83L51xD SD/MMC Card Interface support" depends on MMC && ISA_DMA_API diff -urN linux-2.6.20.4-0rig/drivers/mmc/Makefile linux-2.6.20.4-atmel/drivers/mmc/Makefile --- linux-2.6.20.4-0rig/drivers/mmc/Makefile 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/mmc/Makefile 2007-03-24 16:42:29.000000000 +0100 @@ -23,6 +23,7 @@ obj-$(CONFIG_MMC_AU1X) += au1xmmc.o obj-$(CONFIG_MMC_OMAP) += omap.o obj-$(CONFIG_MMC_AT91) += at91_mci.o +obj-$(CONFIG_MMC_ATMELMCI) += atmel-mci.o obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o mmc_core-y := mmc.o mmc_sysfs.o diff -urN linux-2.6.20.4-0rig/drivers/mtd/chips/cfi_cmdset_0001.c linux-2.6.20.4-atmel/drivers/mtd/chips/cfi_cmdset_0001.c --- linux-2.6.20.4-0rig/drivers/mtd/chips/cfi_cmdset_0001.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/mtd/chips/cfi_cmdset_0001.c 2007-03-24 16:42:29.000000000 +0100 @@ -47,6 +47,7 @@ #define I82802AC 0x00ac #define MANUFACTURER_ST 0x0020 #define M50LPW080 0x002F +#define AT49BV640D 0x02de static int cfi_intelext_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); static int cfi_intelext_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); @@ -153,6 +154,47 @@ } #endif +/* Atmel chips don't use the same PRI format as Intel chips */ +static void fixup_convert_atmel_pri(struct mtd_info *mtd, void *param) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + struct cfi_pri_intelext *extp = cfi->cmdset_priv; + struct cfi_pri_atmel atmel_pri; + uint32_t features = 0; + + /* Reverse byteswapping */ + extp->FeatureSupport = cpu_to_le32(extp->FeatureSupport); + extp->BlkStatusRegMask = cpu_to_le16(extp->BlkStatusRegMask); + extp->ProtRegAddr = cpu_to_le16(extp->ProtRegAddr); + + memcpy(&atmel_pri, extp, sizeof(atmel_pri)); + memset((char *)extp + 5, 0, sizeof(*extp) - 5); + + printk(KERN_ERR "atmel Features: %02x\n", atmel_pri.Features); + + if (atmel_pri.Features & 0x01) /* chip erase supported */ + features |= (1<<0); + if (atmel_pri.Features & 0x02) /* erase suspend supported */ + features |= (1<<1); + if (atmel_pri.Features & 0x04) /* program suspend supported */ + features |= (1<<2); + if (atmel_pri.Features & 0x08) /* simultaneous operations supported */ + features |= (1<<9); + if (atmel_pri.Features & 0x20) /* page mode read supported */ + features |= (1<<7); + if (atmel_pri.Features & 0x40) /* queued erase supported */ + features |= (1<<4); + if (atmel_pri.Features & 0x80) /* Protection bits supported */ + features |= (1<<6); + + extp->FeatureSupport = features; + + /* burst write mode not supported */ + cfi->cfiq->BufWriteTimeoutTyp = 0; + cfi->cfiq->BufWriteTimeoutMax = 0; +} + #ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE /* Some Intel Strata Flash prior to FPO revision C has bugs in this area */ static void fixup_intel_strataflash(struct mtd_info *mtd, void* param) @@ -221,6 +263,7 @@ } static struct cfi_fixup cfi_fixup_table[] = { + { CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri, NULL }, #ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE { CFI_MFR_ANY, CFI_ID_ANY, fixup_intel_strataflash, NULL }, #endif diff -urN linux-2.6.20.4-0rig/drivers/mtd/chips/cfi_cmdset_0002.c linux-2.6.20.4-atmel/drivers/mtd/chips/cfi_cmdset_0002.c --- linux-2.6.20.4-0rig/drivers/mtd/chips/cfi_cmdset_0002.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/mtd/chips/cfi_cmdset_0002.c 2007-03-24 16:42:29.000000000 +0100 @@ -185,6 +185,10 @@ extp->TopBottom = 2; else extp->TopBottom = 3; + + /* burst write mode not supported */ + cfi->cfiq->BufWriteTimeoutTyp = 0; + cfi->cfiq->BufWriteTimeoutMax = 0; } static void fixup_use_secsi(struct mtd_info *mtd, void *param) @@ -217,6 +221,7 @@ } static struct cfi_fixup cfi_fixup_table[] = { + { CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri, NULL }, #ifdef AMD_BOOTLOC_BUG { CFI_MFR_AMD, CFI_ID_ANY, fixup_amd_bootblock, NULL }, #endif @@ -229,7 +234,6 @@ #if !FORCE_WORD_WRITE { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL, }, #endif - { CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri, NULL }, { 0, 0, NULL, NULL } }; static struct cfi_fixup jedec_fixup_table[] = { diff -urN linux-2.6.20.4-0rig/drivers/mtd/devices/at91_dataflash.c linux-2.6.20.4-atmel/drivers/mtd/devices/at91_dataflash.c --- linux-2.6.20.4-0rig/drivers/mtd/devices/at91_dataflash.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/mtd/devices/at91_dataflash.c 2007-03-24 16:39:15.000000000 +0100 @@ -0,0 +1,640 @@ +/* + * Atmel DataFlash driver for Atmel AT91RM9200 (Thunder) + * + * Copyright (C) SAN People (Pty) Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. +*/ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/pci.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> + +#include <asm/arch/spi.h> + +#undef DEBUG_DATAFLASH + +#define DATAFLASH_MAX_DEVICES 4 /* max number of dataflash devices */ +#undef DATAFLASH_ALWAYS_ADD_DEVICE /* always add whole device when using partitions? */ + +#define OP_READ_CONTINUOUS 0xE8 +#define OP_READ_PAGE 0xD2 +#define OP_READ_BUFFER1 0xD4 +#define OP_READ_BUFFER2 0xD6 +#define OP_READ_STATUS 0xD7 + +#define OP_ERASE_PAGE 0x81 +#define OP_ERASE_BLOCK 0x50 + +#define OP_TRANSFER_BUF1 0x53 +#define OP_TRANSFER_BUF2 0x55 +#define OP_COMPARE_BUF1 0x60 +#define OP_COMPARE_BUF2 0x61 + +#define OP_PROGRAM_VIA_BUF1 0x82 +#define OP_PROGRAM_VIA_BUF2 0x85 + +struct dataflash_local +{ + int spi; /* SPI chip-select number */ + + unsigned int page_size; /* number of bytes per page */ + unsigned short page_offset; /* page offset in flash address */ +}; + + +/* Detected DataFlash devices */ +static struct mtd_info* mtd_devices[DATAFLASH_MAX_DEVICES]; +static int nr_devices = 0; + +/* ......................................................................... */ + +#ifdef CONFIG_MTD_PARTITIONS + +static struct mtd_partition static_partitions_2M[] = +{ + { + .name = "bootloader", + .offset = 0, + .size = 1 * 32 * 8 * 528, /* 1st sector = 32 blocks * 8 pages * 528 bytes */ + .mask_flags = MTD_WRITEABLE, /* read-only */ + }, + { + .name = "kernel", + .offset = MTDPART_OFS_NXTBLK, + .size = 6 * 32 * 8 * 528, /* 6 sectors */ + }, + { + .name = "filesystem", + .offset = MTDPART_OFS_NXTBLK, + .size = MTDPART_SIZ_FULL, /* rest = 9 sectors */ + } +}; + +static struct mtd_partition static_partitions_4M[] = +{ + { + .name = "bootloader", + .offset = 0, + .size = 1 * 64 * 8 * 528, /* 1st sector = 64 blocks * 8 pages * 528 bytes */ + .mask_flags = MTD_WRITEABLE, /* read-only */ + }, + { + .name = "kernel", + .offset = MTDPART_OFS_NXTBLK, + .size = 4 * 64 * 8 * 528, /* 4 sectors */ + }, + { + .name = "filesystem", + .offset = MTDPART_OFS_NXTBLK, + .size = MTDPART_SIZ_FULL, /* rest = 11 sectors */ + } +}; + +#if defined(CONFIG_MACH_KAFA) +static struct mtd_partition static_partitions_8M[] = +{ + { + name: "romboot", + offset: 0, + size: 16 * 1056, /* 160 Kb */ + mask_flags: MTD_WRITEABLE, /* read-only */ + }, + { + name: "uboot", + offset: MTDPART_OFS_APPEND, /* Sperry, NXTBLK is broken */ + size: 128 * 1056, /* 1 MB */ + }, + { + name: "kernel", + offset: MTDPART_OFS_APPEND, /* Sperry, NXTBLK is broken */ + size: 1024 * 1056, /* 1 MB */ + }, + { + name: "filesystem", + offset: MTDPART_OFS_APPEND, /* Sperry, NXTBLK is broken */ + size: MTDPART_SIZ_FULL, + } +}; + +#else + +static struct mtd_partition static_partitions_8M[] = +{ + { + .name = "bootloader", + .offset = 0, + .size = 1 * 32 * 8 * 1056, /* 1st sector = 32 blocks * 8 pages * 1056 bytes */ + .mask_flags = MTD_WRITEABLE, /* read-only */ + }, + { + .name = "kernel", + .offset = MTDPART_OFS_NXTBLK, + .size = 5 * 32 * 8 * 1056, /* 5 sectors */ + }, + { + .name = "filesystem", + .offset = MTDPART_OFS_NXTBLK, + .size = MTDPART_SIZ_FULL, /* rest = 26 sectors */ + } +}; +#endif + +static const char *part_probes[] = { "cmdlinepart", NULL, }; + +#endif + +/* ......................................................................... */ + +/* Allocate a single SPI transfer descriptor. We're assuming that if multiple + SPI transfers occur at the same time, spi_access_bus() will serialize them. + If this is not valid, then either (i) each dataflash 'priv' structure + needs it's own transfer descriptor, (ii) we lock this one, or (iii) use + another mechanism. */ +static struct spi_transfer_list* spi_transfer_desc; + +/* + * Perform a SPI transfer to access the DataFlash device. + */ +static int do_spi_transfer(int nr, char* tx, int tx_len, char* rx, int rx_len, + char* txnext, int txnext_len, char* rxnext, int rxnext_len) +{ + struct spi_transfer_list* list = spi_transfer_desc; + + list->tx[0] = tx; list->txlen[0] = tx_len; + list->rx[0] = rx; list->rxlen[0] = rx_len; + + list->tx[1] = txnext; list->txlen[1] = txnext_len; + list->rx[1] = rxnext; list->rxlen[1] = rxnext_len; + + list->nr_transfers = nr; + + return spi_transfer(list); +} + +/* ......................................................................... */ + +/* + * Poll the DataFlash device until it is READY. + */ +static void at91_dataflash_waitready(void) +{ + char* command = kmalloc(2, GFP_KERNEL); + + if (!command) + return; + + do { + command[0] = OP_READ_STATUS; + command[1] = 0; + + do_spi_transfer(1, command, 2, command, 2, NULL, 0, NULL, 0); + } while ((command[1] & 0x80) == 0); + + kfree(command); +} + +/* + * Return the status of the DataFlash device. + */ +static unsigned short at91_dataflash_status(void) +{ + unsigned short status; + char* command = kmalloc(2, GFP_KERNEL); + + if (!command) + return 0; + + command[0] = OP_READ_STATUS; + command[1] = 0; + + do_spi_transfer(1, command, 2, command, 2, NULL, 0, NULL, 0); + status = command[1]; + + kfree(command); + return status; +} + +/* ......................................................................... */ + +/* + * Erase blocks of flash. + */ +static int at91_dataflash_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct dataflash_local *priv = (struct dataflash_local *) mtd->priv; + unsigned int pageaddr; + char* command; + +#ifdef DEBUG_DATAFLASH + printk("dataflash_erase: addr=%i len=%i\n", instr->addr, instr->len); +#endif + + /* Sanity checks */ + if (instr->addr + instr->len > mtd->size) + return -EINVAL; + if ((instr->len % mtd->erasesize != 0) || (instr->len % priv->page_size != 0)) + return -EINVAL; + if ((instr->addr % priv->page_size) != 0) + return -EINVAL; + + command = kmalloc(4, GFP_KERNEL); + if (!command) + return -ENOMEM; + + while (instr->len > 0) { + /* Calculate flash page address */ + pageaddr = (instr->addr / priv->page_size) << priv->page_offset; + + command[0] = OP_ERASE_PAGE; + command[1] = (pageaddr & 0x00FF0000) >> 16; + command[2] = (pageaddr & 0x0000FF00) >> 8; + command[3] = 0; +#ifdef DEBUG_DATAFLASH + printk("ERASE: (%x) %x %x %x [%i]\n", command[0], command[1], command[2], command[3], pageaddr); +#endif + + /* Send command to SPI device */ + spi_access_bus(priv->spi); + do_spi_transfer(1, command, 4, command, 4, NULL, 0, NULL, 0); + + at91_dataflash_waitready(); /* poll status until ready */ + spi_release_bus(priv->spi); + + instr->addr += priv->page_size; /* next page */ + instr->len -= priv->page_size; + } + + kfree(command); + + /* Inform MTD subsystem that erase is complete */ + instr->state = MTD_ERASE_DONE; + if (instr->callback) + instr->callback(instr); + + return 0; +} + +/* + * Read from the DataFlash device. + * from : Start offset in flash device + * len : Amount to read + * retlen : About of data actually read + * buf : Buffer containing the data + */ +static int at91_dataflash_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) +{ + struct dataflash_local *priv = (struct dataflash_local *) mtd->priv; + unsigned int addr; + char* command; + +#ifdef DEBUG_DATAFLASH + printk("dataflash_read: %lli .. %lli\n", from, from+len); +#endif + + *retlen = 0; + + /* Sanity checks */ + if (!len) + return 0; + if (from + len > mtd->size) + return -EINVAL; + + /* Calculate flash page/byte address */ + addr = (((unsigned)from / priv->page_size) << priv->page_offset) + ((unsigned)from % priv->page_size); + + command = kmalloc(8, GFP_KERNEL); + if (!command) + return -ENOMEM; + + command[0] = OP_READ_CONTINUOUS; + command[1] = (addr & 0x00FF0000) >> 16; + command[2] = (addr & 0x0000FF00) >> 8; + command[3] = (addr & 0x000000FF); +#ifdef DEBUG_DATAFLASH + printk("READ: (%x) %x %x %x\n", command[0], command[1], command[2], command[3]); +#endif + + /* Send command to SPI device */ + spi_access_bus(priv->spi); + do_spi_transfer(2, command, 8, command, 8, buf, len, buf, len); + spi_release_bus(priv->spi); + + *retlen = len; + kfree(command); + return 0; +} + +/* + * Write to the DataFlash device. + * to : Start offset in flash device + * len : Amount to write + * retlen : Amount of data actually written + * buf : Buffer containing the data + */ +static int at91_dataflash_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) +{ + struct dataflash_local *priv = (struct dataflash_local *) mtd->priv; + unsigned int pageaddr, addr, offset, writelen; + size_t remaining; + u_char *writebuf; + unsigned short status; + int res = 0; + char* command; + char* tmpbuf = NULL; + +#ifdef DEBUG_DATAFLASH + printk("dataflash_write: %lli .. %lli\n", to, to+len); +#endif + + *retlen = 0; + + /* Sanity checks */ + if (!len) + return 0; + if (to + len > mtd->size) + return -EINVAL; + + command = kmalloc(4, GFP_KERNEL); + if (!command) + return -ENOMEM; + + pageaddr = ((unsigned)to / priv->page_size); + offset = ((unsigned)to % priv->page_size); + if (offset + len > priv->page_size) + writelen = priv->page_size - offset; + else + writelen = len; + writebuf = (u_char *)buf; + remaining = len; + + /* Allocate temporary buffer */ + tmpbuf = kmalloc(priv->page_size, GFP_KERNEL); + if (!tmpbuf) { + kfree(command); + return -ENOMEM; + } + + /* Gain access to the SPI bus */ + spi_access_bus(priv->spi); + + while (remaining > 0) { +#ifdef DEBUG_DATAFLASH + printk("write @ %i:%i len=%i\n", pageaddr, offset, writelen); +#endif + + /* (1) Transfer to Buffer1 */ + if (writelen != priv->page_size) { + addr = pageaddr << priv->page_offset; + command[0] = OP_TRANSFER_BUF1; + command[1] = (addr & 0x00FF0000) >> 16; + command[2] = (addr & 0x0000FF00) >> 8; + command[3] = 0; +#ifdef DEBUG_DATAFLASH + printk("TRANSFER: (%x) %x %x %x\n", command[0], command[1], command[2], command[3]); +#endif + do_spi_transfer(1, command, 4, command, 4, NULL, 0, NULL, 0); + at91_dataflash_waitready(); + } + + /* (2) Program via Buffer1 */ + addr = (pageaddr << priv->page_offset) + offset; + command[0] = OP_PROGRAM_VIA_BUF1; + command[1] = (addr & 0x00FF0000) >> 16; + command[2] = (addr & 0x0000FF00) >> 8; + command[3] = (addr & 0x000000FF); +#ifdef DEBUG_DATAFLASH + printk("PROGRAM: (%x) %x %x %x\n", command[0], command[1], command[2], command[3]); +#endif + do_spi_transfer(2, command, 4, command, 4, writebuf, writelen, tmpbuf, writelen); + at91_dataflash_waitready(); + + /* (3) Compare to Buffer1 */ + addr = pageaddr << priv->page_offset; + command[0] = OP_COMPARE_BUF1; + command[1] = (addr & 0x00FF0000) >> 16; + command[2] = (addr & 0x0000FF00) >> 8; + command[3] = 0; +#ifdef DEBUG_DATAFLASH + printk("COMPARE: (%x) %x %x %x\n", command[0], command[1], command[2], command[3]); +#endif + do_spi_transfer(1, command, 4, command, 4, NULL, 0, NULL, 0); + at91_dataflash_waitready(); + + /* Get result of the compare operation */ + status = at91_dataflash_status(); + if ((status & 0x40) == 1) { + printk("at91_dataflash: Write error on page %i\n", pageaddr); + remaining = 0; + res = -EIO; + } + + remaining = remaining - writelen; + pageaddr++; + offset = 0; + writebuf += writelen; + *retlen += writelen; + + if (remaining > priv->page_size) + writelen = priv->page_size; + else + writelen = remaining; + } + + /* Release SPI bus */ + spi_release_bus(priv->spi); + + kfree(tmpbuf); + kfree(command); + return res; +} + +/* ......................................................................... */ + +/* + * Initialize and register DataFlash device with MTD subsystem. + */ +static int __init add_dataflash(int channel, char *name, int IDsize, + int nr_pages, int pagesize, int pageoffset) +{ + struct mtd_info *device; + struct dataflash_local *priv; +#ifdef CONFIG_MTD_PARTITIONS + struct mtd_partition *mtd_parts = 0; + int mtd_parts_nr = 0; +#endif + + if (nr_devices >= DATAFLASH_MAX_DEVICES) { + printk(KERN_ERR "at91_dataflash: Too many devices detected\n"); + return 0; + } + + device = kmalloc(sizeof(struct mtd_info) + strlen(name) + 8, GFP_KERNEL); + if (!device) + return -ENOMEM; + memset(device, 0, sizeof(struct mtd_info)); + + device->name = (char *)&device[1]; + sprintf(device->name, "%s.spi%d", name, channel); + device->size = nr_pages * pagesize; + device->erasesize = pagesize; + device->writesize = pagesize; + device->owner = THIS_MODULE; + device->type = MTD_DATAFLASH; + device->flags = MTD_WRITEABLE; + device->erase = at91_dataflash_erase; + device->read = at91_dataflash_read; + device->write = at91_dataflash_write; + + priv = (struct dataflash_local *) kmalloc(sizeof(struct dataflash_local), GFP_KERNEL); + if (!priv) { + kfree(device); + return -ENOMEM; + } + memset(priv, 0, sizeof(struct dataflash_local)); + + priv->spi = channel; + priv->page_size = pagesize; + priv->page_offset = pageoffset; + device->priv = priv; + + mtd_devices[nr_devices] = device; + nr_devices++; + printk("at91_dataflash: %s detected [spi%i] (%i bytes)\n", name, channel, device->size); + +#ifdef CONFIG_MTD_PARTITIONS +#ifdef CONFIG_MTD_CMDLINE_PARTS + mtd_parts_nr = parse_mtd_partitions(device, part_probes, &mtd_parts, 0); +#endif + if (mtd_parts_nr <= 0) { + switch (IDsize) { + case SZ_2M: + mtd_parts = static_partitions_2M; + mtd_parts_nr = ARRAY_SIZE(static_partitions_2M); + break; + case SZ_4M: + mtd_parts = static_partitions_4M; + mtd_parts_nr = ARRAY_SIZE(static_partitions_4M); + break; + case SZ_8M: + mtd_parts = static_partitions_8M; + mtd_parts_nr = ARRAY_SIZE(static_partitions_8M); + break; + } + } + + if (mtd_parts_nr > 0) { +#ifdef DATAFLASH_ALWAYS_ADD_DEVICE + add_mtd_device(device); +#endif + return add_mtd_partitions(device, mtd_parts, mtd_parts_nr); + } +#endif + return add_mtd_device(device); /* add whole device */ +} + +/* + * Detect and initialize DataFlash device connected to specified SPI channel. + * + * Device Density ID code Nr Pages Page Size Page offset + * AT45DB011B 1Mbit (128K) xx0011xx (0x0c) 512 264 9 + * AT45DB021B 2Mbit (256K) xx0101xx (0x14) 1025 264 9 + * AT45DB041B 4Mbit (512K) xx0111xx (0x1c) 2048 264 9 + * AT45DB081B 8Mbit (1M) xx1001xx (0x24) 4096 264 9 + * AT45DB0161B 16Mbit (2M) xx1011xx (0x2c) 4096 528 10 + * AT45DB0321B 32Mbit (4M) xx1101xx (0x34) 8192 528 10 + * AT45DB0642 64Mbit (8M) xx1111xx (0x3c) 8192 1056 11 + * AT45DB1282 128Mbit (16M) xx0100xx (0x10) 16384 1056 11 + */ +static int __init at91_dataflash_detect(int channel) +{ + int res = 0; + unsigned short status; + + spi_access_bus(channel); + status = at91_dataflash_status(); + spi_release_bus(channel); + if (status != 0xff) { /* no dataflash device there */ + switch (status & 0x3c) { + case 0x0c: /* 0 0 1 1 */ + res = add_dataflash(channel, "AT45DB011B", SZ_128K, 512, 264, 9); + break; + case 0x14: /* 0 1 0 1 */ + res = add_dataflash(channel, "AT45DB021B", SZ_256K, 1025, 264, 9); + break; + case 0x1c: /* 0 1 1 1 */ + res = add_dataflash(channel, "AT45DB041B", SZ_512K, 2048, 264, 9); + break; + case 0x24: /* 1 0 0 1 */ + res = add_dataflash(channel, "AT45DB081B", SZ_1M, 4096, 264, 9); + break; + case 0x2c: /* 1 0 1 1 */ + res = add_dataflash(channel, "AT45DB161B", SZ_2M, 4096, 528, 10); + break; + case 0x34: /* 1 1 0 1 */ + res = add_dataflash(channel, "AT45DB321B", SZ_4M, 8192, 528, 10); + break; + case 0x3c: /* 1 1 1 1 */ + res = add_dataflash(channel, "AT45DB642", SZ_8M, 8192, 1056, 11); + break; +// Currently unsupported since Atmel removed the "Main Memory Program via Buffer" commands. +// case 0x10: /* 0 1 0 0 */ +// res = add_dataflash(channel, "AT45DB1282", SZ_16M, 16384, 1056, 11); +// break; + default: + printk(KERN_ERR "at91_dataflash: Unknown device (%x)\n", status & 0x3c); + } + } + + return res; +} + +static int __init at91_dataflash_init(void) +{ + spi_transfer_desc = kmalloc(sizeof(struct spi_transfer_list), GFP_KERNEL); + if (!spi_transfer_desc) + return -ENOMEM; + + /* DataFlash (SPI chip select 0) */ + at91_dataflash_detect(0); + +#ifdef CONFIG_MTD_AT91_DATAFLASH_CARD + /* DataFlash card (SPI chip select 3) */ + at91_dataflash_detect(3); +#endif + + return 0; +} + +static void __exit at91_dataflash_exit(void) +{ + int i; + + for (i = 0; i < DATAFLASH_MAX_DEVICES; i++) { + if (mtd_devices[i]) { +#ifdef CONFIG_MTD_PARTITIONS + del_mtd_partitions(mtd_devices[i]); +#else + del_mtd_device(mtd_devices[i]); +#endif + kfree(mtd_devices[i]->priv); + kfree(mtd_devices[i]); + } + } + nr_devices = 0; + kfree(spi_transfer_desc); +} + + +module_init(at91_dataflash_init); +module_exit(at91_dataflash_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Andrew Victor"); +MODULE_DESCRIPTION("DataFlash driver for Atmel AT91RM9200"); diff -urN linux-2.6.20.4-0rig/drivers/mtd/devices/Kconfig linux-2.6.20.4-atmel/drivers/mtd/devices/Kconfig --- linux-2.6.20.4-0rig/drivers/mtd/devices/Kconfig 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/mtd/devices/Kconfig 2007-03-24 16:39:15.000000000 +0100 @@ -267,5 +267,11 @@ LinuxBIOS or if you need to recover a DiskOnChip Millennium on which you have managed to wipe the first block. -endmenu +config MTD_AT91_DATAFLASH + tristate "AT91RM9200 DataFlash AT45DBxxx (legacy driver)" + depends on MTD && ARCH_AT91RM9200 && AT91_SPI + help + This enables access to the DataFlash (AT45DBxxx) on the AT91RM9200. + If you have such a board, say 'Y'. +endmenu diff -urN linux-2.6.20.4-0rig/drivers/mtd/devices/Makefile linux-2.6.20.4-atmel/drivers/mtd/devices/Makefile --- linux-2.6.20.4-0rig/drivers/mtd/devices/Makefile 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/mtd/devices/Makefile 2007-03-24 16:39:15.000000000 +0100 @@ -17,3 +17,4 @@ obj-$(CONFIG_MTD_BLOCK2MTD) += block2mtd.o obj-$(CONFIG_MTD_DATAFLASH) += mtd_dataflash.o obj-$(CONFIG_MTD_M25P80) += m25p80.o +obj-$(CONFIG_MTD_AT91_DATAFLASH)+= at91_dataflash.o diff -urN linux-2.6.20.4-0rig/drivers/mtd/nand/at91_nand.c linux-2.6.20.4-atmel/drivers/mtd/nand/at91_nand.c --- linux-2.6.20.4-0rig/drivers/mtd/nand/at91_nand.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/mtd/nand/at91_nand.c 2007-03-24 16:39:15.000000000 +0100 @@ -82,6 +82,10 @@ at91_set_gpio_value(host->board->enable_pin, 1); } +#ifdef CONFIG_MTD_PARTITIONS +const char *part_probes[] = { "cmdlinepart", NULL }; +#endif + /* * Probe for the NAND device. */ @@ -151,6 +155,12 @@ #ifdef CONFIG_MTD_PARTITIONS if (host->board->partition_info) partitions = host->board->partition_info(mtd->size, &num_partitions); +#ifdef CONFIG_MTD_CMDLINE_PARTS + else { + mtd->name = "at91_nand"; + num_partitions = parse_mtd_partitions(mtd, part_probes, &partitions, 0); + } +#endif if ((!partitions) || (num_partitions == 0)) { printk(KERN_ERR "at91_nand: No parititions defined, or unsupported device.\n"); diff -urN linux-2.6.20.4-0rig/drivers/net/arm/at91_ether.c linux-2.6.20.4-atmel/drivers/net/arm/at91_ether.c --- linux-2.6.20.4-0rig/drivers/net/arm/at91_ether.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/net/arm/at91_ether.c 2007-03-24 16:39:15.000000000 +0100 @@ -943,14 +943,22 @@ struct net_device *dev; struct at91_private *lp; unsigned int val; - int res; + struct resource *res; + int ret; dev = alloc_etherdev(sizeof(struct at91_private)); if (!dev) return -ENOMEM; - dev->base_addr = AT91_VA_BASE_EMAC; - dev->irq = AT91RM9200_ID_EMAC; + /* Get I/O base address and IRQ */ + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + free_netdev(dev); + return -ENODEV; + } + dev->base_addr = res->start; + dev->irq = platform_get_irq(pdev, 0); + SET_MODULE_OWNER(dev); /* Install the interrupt handler */ @@ -1023,12 +1031,12 @@ lp->phy_address = phy_address; /* MDI address of PHY */ /* Register the network interface */ - res = register_netdev(dev); - if (res) { + ret = register_netdev(dev); + if (ret) { free_irq(dev->irq, dev); free_netdev(dev); dma_free_coherent(NULL, sizeof(struct recv_desc_bufs), lp->dlist, (dma_addr_t)lp->dlist_phys); - return res; + return ret; } /* Determine current link speed */ @@ -1103,7 +1111,7 @@ case MII_LXT971A_ID: /* Intel LXT971A: PHY_ID1 = 0x13, PHY_ID2 = 78E0 */ case MII_RTL8201_ID: /* Realtek RTL8201: PHY_ID1 = 0, PHY_ID2 = 0x8201 */ case MII_BCM5221_ID: /* Broadcom BCM5221: PHY_ID1 = 0x40, PHY_ID2 = 0x61e0 */ - case MII_DP83847_ID: /* National Semiconductor DP83847: */ + case MII_DP83847_ID: /* National Semiconductor DP83847: */ case MII_AC101L_ID: /* Altima AC101L: PHY_ID1 = 0x22, PHY_ID2 = 0x5520 */ case MII_KS8721_ID: /* Micrel KS8721: PHY_ID1 = 0x22, PHY_ID2 = 0x1610 */ detected = at91ether_setup(phy_id, phy_address, pdev, ether_clk); diff -urN linux-2.6.20.4-0rig/drivers/net/Kconfig linux-2.6.20.4-atmel/drivers/net/Kconfig --- linux-2.6.20.4-0rig/drivers/net/Kconfig 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/net/Kconfig 2007-03-24 16:39:15.000000000 +0100 @@ -190,7 +190,7 @@ config MACB tristate "Atmel MACB support" - depends on NET_ETHERNET && AVR32 + depends on NET_ETHERNET && (AVR32 || ARCH_AT91SAM9260 || ARCH_AT91SAM9263) select MII help The Atmel MACB ethernet interface is found on many AT32 and AT91 diff -urN linux-2.6.20.4-0rig/drivers/net/macb.c linux-2.6.20.4-atmel/drivers/net/macb.c --- linux-2.6.20.4-0rig/drivers/net/macb.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/net/macb.c 2007-03-24 16:42:28.000000000 +0100 @@ -883,27 +883,15 @@ static int macb_get_settings(struct net_device *dev, struct ethtool_cmd *cmd) { struct macb *bp = netdev_priv(dev); - int ret; - unsigned long flags; - - spin_lock_irqsave(&bp->lock, flags); - ret = mii_ethtool_gset(&bp->mii, cmd); - spin_unlock_irqrestore(&bp->lock, flags); - return ret; + return mii_ethtool_gset(&bp->mii, cmd); } static int macb_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) { struct macb *bp = netdev_priv(dev); - int ret; - unsigned long flags; - - spin_lock_irqsave(&bp->lock, flags); - ret = mii_ethtool_sset(&bp->mii, cmd); - spin_unlock_irqrestore(&bp->lock, flags); - return ret; + return mii_ethtool_sset(&bp->mii, cmd); } static void macb_get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) @@ -932,17 +920,11 @@ static int macb_ioctl(struct net_device *dev, struct ifreq *rq, int cmd) { struct macb *bp = netdev_priv(dev); - int ret; - unsigned long flags; if (!netif_running(dev)) return -EINVAL; - spin_lock_irqsave(&bp->lock, flags); - ret = generic_mii_ioctl(&bp->mii, if_mii(rq), cmd, NULL); - spin_unlock_irqrestore(&bp->lock, flags); - - return ret; + return generic_mii_ioctl(&bp->mii, if_mii(rq), cmd, NULL); } static ssize_t macb_mii_show(const struct class_device *cd, char *buf, @@ -1046,6 +1028,14 @@ spin_lock_init(&bp->lock); +#if defined(CONFIG_ARCH_AT91) + bp->pclk = clk_get(&pdev->dev, "macb_clk"); + if (IS_ERR(bp->pclk)) { + dev_err(&pdev->dev, "failed to get macb_clk\n"); + goto err_out_free_dev; + } + clk_enable(bp->pclk); +#else bp->pclk = clk_get(&pdev->dev, "pclk"); if (IS_ERR(bp->pclk)) { dev_err(&pdev->dev, "failed to get pclk\n"); @@ -1059,6 +1049,7 @@ clk_enable(bp->pclk); clk_enable(bp->hclk); +#endif bp->regs = ioremap(regs->start, regs->end - regs->start + 1); if (!bp->regs) { @@ -1119,9 +1110,17 @@ pdata = pdev->dev.platform_data; if (pdata && pdata->is_rmii) +#if defined(CONFIG_ARCH_AT91) + macb_writel(bp, USRIO, (MACB_BIT(RMII) | MACB_BIT(CLKEN)) ); +#else macb_writel(bp, USRIO, 0); +#endif else +#if defined(CONFIG_ARCH_AT91) + macb_writel(bp, USRIO, MACB_BIT(CLKEN)); +#else macb_writel(bp, USRIO, MACB_BIT(MII)); +#endif bp->tx_pending = DEF_TX_RING_PENDING; @@ -1148,9 +1147,11 @@ err_out_iounmap: iounmap(bp->regs); err_out_disable_clocks: +#ifndef CONFIG_ARCH_AT91 clk_disable(bp->hclk); - clk_disable(bp->pclk); clk_put(bp->hclk); +#endif + clk_disable(bp->pclk); err_out_put_pclk: clk_put(bp->pclk); err_out_free_dev: @@ -1173,9 +1174,11 @@ unregister_netdev(dev); free_irq(dev->irq, dev); iounmap(bp->regs); +#ifndef CONFIG_ARCH_AT91 clk_disable(bp->hclk); - clk_disable(bp->pclk); clk_put(bp->hclk); +#endif + clk_disable(bp->pclk); clk_put(bp->pclk); free_netdev(dev); platform_set_drvdata(pdev, NULL); diff -urN linux-2.6.20.4-0rig/drivers/net/macb.h linux-2.6.20.4-atmel/drivers/net/macb.h --- linux-2.6.20.4-0rig/drivers/net/macb.h 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/net/macb.h 2007-03-24 16:39:15.000000000 +0100 @@ -200,7 +200,7 @@ #define MACB_SOF_OFFSET 30 #define MACB_SOF_SIZE 2 -/* Bitfields in USRIO */ +/* Bitfields in USRIO (AVR32) */ #define MACB_MII_OFFSET 0 #define MACB_MII_SIZE 1 #define MACB_EAM_OFFSET 1 @@ -210,6 +210,12 @@ #define MACB_TX_PAUSE_ZERO_OFFSET 3 #define MACB_TX_PAUSE_ZERO_SIZE 1 +/* Bitfields in USRIO (AT91) */ +#define MACB_RMII_OFFSET 0 +#define MACB_RMII_SIZE 1 +#define MACB_CLKEN_OFFSET 1 +#define MACB_CLKEN_SIZE 1 + /* Bitfields in WOL */ #define MACB_IP_OFFSET 0 #define MACB_IP_SIZE 16 diff -urN linux-2.6.20.4-0rig/drivers/pcmcia/at91_cf.c linux-2.6.20.4-atmel/drivers/pcmcia/at91_cf.c --- linux-2.6.20.4-0rig/drivers/pcmcia/at91_cf.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/pcmcia/at91_cf.c 2007-03-24 16:39:15.000000000 +0100 @@ -333,20 +333,27 @@ struct at91_cf_data *board = cf->board; pcmcia_socket_dev_suspend(&pdev->dev, mesg); + if (device_may_wakeup(&pdev->dev)) { enable_irq_wake(board->det_pin); if (board->irq_pin) enable_irq_wake(board->irq_pin); - } else { - disable_irq_wake(board->det_pin); - if (board->irq_pin) - disable_irq_wake(board->irq_pin); } + return 0; } static int at91_cf_resume(struct platform_device *pdev) { + struct at91_cf_socket *cf = platform_get_drvdata(pdev); + struct at91_cf_data *board = cf->board; + + if (device_may_wakeup(&pdev->dev)) { + disable_irq_wake(board->det_pin); + if (board->irq_pin) + disable_irq_wake(board->irq_pin); + } + pcmcia_socket_dev_resume(&pdev->dev); return 0; } diff -urN linux-2.6.20.4-0rig/drivers/serial/atmel_serial.c linux-2.6.20.4-atmel/drivers/serial/atmel_serial.c --- linux-2.6.20.4-0rig/drivers/serial/atmel_serial.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/serial/atmel_serial.c 2007-03-24 16:42:29.000000000 +0100 @@ -7,6 +7,8 @@ * Based on drivers/char/serial_sa1100.c, by Deep Blue Solutions Ltd. * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. * + * DMA support added by Chip Coldwell. + * * 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 @@ -33,12 +35,14 @@ #include <linux/sysrq.h> #include <linux/tty_flip.h> #include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/atmel_pdc.h> #include <asm/io.h> #include <asm/mach/serial_at91.h> #include <asm/arch/board.h> -#include <asm/arch/at91_pdc.h> + #ifdef CONFIG_ARM #include <asm/arch/cpu.h> #include <asm/arch/gpio.h> @@ -46,6 +50,11 @@ #include "atmel_serial.h" +#define SUPPORT_PDC +#define PDC_BUFFER_SIZE (L1_CACHE_BYTES << 3) +#warning "Revisit" +#define PDC_RX_TIMEOUT (3 * 10) /* 3 bytes */ + #if defined(CONFIG_SERIAL_ATMEL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) #define SUPPORT_SYSRQ #endif @@ -73,39 +82,46 @@ #define ATMEL_ISR_PASS_LIMIT 256 -#define UART_PUT_CR(port,v) writel(v, (port)->membase + ATMEL_US_CR) -#define UART_GET_MR(port) readl((port)->membase + ATMEL_US_MR) -#define UART_PUT_MR(port,v) writel(v, (port)->membase + ATMEL_US_MR) -#define UART_PUT_IER(port,v) writel(v, (port)->membase + ATMEL_US_IER) -#define UART_PUT_IDR(port,v) writel(v, (port)->membase + ATMEL_US_IDR) -#define UART_GET_IMR(port) readl((port)->membase + ATMEL_US_IMR) -#define UART_GET_CSR(port) readl((port)->membase + ATMEL_US_CSR) -#define UART_GET_CHAR(port) readl((port)->membase + ATMEL_US_RHR) -#define UART_PUT_CHAR(port,v) writel(v, (port)->membase + ATMEL_US_THR) -#define UART_GET_BRGR(port) readl((port)->membase + ATMEL_US_BRGR) -#define UART_PUT_BRGR(port,v) writel(v, (port)->membase + ATMEL_US_BRGR) -#define UART_PUT_RTOR(port,v) writel(v, (port)->membase + ATMEL_US_RTOR) +#define UART_PUT_CR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_CR) +#define UART_GET_MR(port) __raw_readl((port)->membase + ATMEL_US_MR) +#define UART_PUT_MR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_MR) +#define UART_PUT_IER(port,v) __raw_writel(v, (port)->membase + ATMEL_US_IER) +#define UART_PUT_IDR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_IDR) +#define UART_GET_IMR(port) __raw_readl((port)->membase + ATMEL_US_IMR) +#define UART_GET_CSR(port) __raw_readl((port)->membase + ATMEL_US_CSR) +#define UART_GET_CHAR(port) __raw_readl((port)->membase + ATMEL_US_RHR) +#define UART_PUT_CHAR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_THR) +#define UART_GET_BRGR(port) __raw_readl((port)->membase + ATMEL_US_BRGR) +#define UART_PUT_BRGR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_BRGR) +#define UART_PUT_RTOR(port,v) __raw_writel(v, (port)->membase + ATMEL_US_RTOR) -// #define UART_GET_CR(port) readl((port)->membase + ATMEL_US_CR) // is write-only +// #define UART_GET_CR(port) __raw_readl((port)->membase + ATMEL_US_CR) // is write-only /* PDC registers */ -#define UART_PUT_PTCR(port,v) writel(v, (port)->membase + ATMEL_PDC_PTCR) -#define UART_GET_PTSR(port) readl((port)->membase + ATMEL_PDC_PTSR) +#define UART_PUT_PTCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_PTCR) +#define UART_GET_PTSR(port) __raw_readl((port)->membase + ATMEL_PDC_PTSR) -#define UART_PUT_RPR(port,v) writel(v, (port)->membase + ATMEL_PDC_RPR) -#define UART_GET_RPR(port) readl((port)->membase + ATMEL_PDC_RPR) -#define UART_PUT_RCR(port,v) writel(v, (port)->membase + ATMEL_PDC_RCR) -#define UART_PUT_RNPR(port,v) writel(v, (port)->membase + ATMEL_PDC_RNPR) -#define UART_PUT_RNCR(port,v) writel(v, (port)->membase + ATMEL_PDC_RNCR) - -#define UART_PUT_TPR(port,v) writel(v, (port)->membase + ATMEL_PDC_TPR) -#define UART_PUT_TCR(port,v) writel(v, (port)->membase + ATMEL_PDC_TCR) -//#define UART_PUT_TNPR(port,v) writel(v, (port)->membase + ATMEL_PDC_TNPR) -//#define UART_PUT_TNCR(port,v) writel(v, (port)->membase + ATMEL_PDC_TNCR) +#define UART_PUT_RPR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_RPR) +#define UART_GET_RPR(port) __raw_readl((port)->membase + ATMEL_PDC_RPR) +#define UART_PUT_RCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_RCR) +#define UART_PUT_RNPR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_RNPR) +#define UART_PUT_RNCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_RNCR) + +#define UART_PUT_TPR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_TPR) +#define UART_PUT_TCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_TCR) +//#define UART_PUT_TNPR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_TNPR) +//#define UART_PUT_TNCR(port,v) __raw_writel(v, (port)->membase + ATMEL_PDC_TNCR) static int (*atmel_open_hook)(struct uart_port *); static void (*atmel_close_hook)(struct uart_port *); +struct atmel_dma_buffer { + unsigned char *buf; + dma_addr_t dma_addr; + size_t dma_size; + unsigned int ofs; +}; + /* * We wrap our port structure around the generic uart_port. */ @@ -113,10 +129,20 @@ struct uart_port uart; /* uart */ struct clk *clk; /* uart clock */ unsigned short suspended; /* is port suspended? */ + + short use_dma_rx; /* enable PDC receiver */ + short pdc_rx_idx; /* current PDC RX buffer */ + struct atmel_dma_buffer pdc_rx[2]; /* PDC receier */ + + short use_dma_tx; /* enable PDC transmitter */ + struct atmel_dma_buffer pdc_tx; /* PDC transmitter */ }; static struct atmel_uart_port atmel_ports[ATMEL_MAX_UART]; +#define PDC_RX_BUF(port) &(port)->pdc_rx[(port)->pdc_rx_idx] +#define PDC_RX_SWITCH(port) (port)->pdc_rx_idx = !(port)->pdc_rx_idx + #ifdef SUPPORT_SYSRQ static struct console atmel_console; #endif @@ -204,7 +230,12 @@ { struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port; - UART_PUT_IDR(port, ATMEL_US_TXRDY); + if (atmel_port->use_dma_tx) { + UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS); /* disable PDC transmit */ + UART_PUT_IDR(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE); + } + else + UART_PUT_IDR(port, ATMEL_US_TXRDY); } /* @@ -214,7 +245,17 @@ { struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port; - UART_PUT_IER(port, ATMEL_US_TXRDY); + if (atmel_port->use_dma_tx) { + if (UART_GET_PTSR(port) & ATMEL_PDC_TXTEN) + /* The transmitter is already running. Yes, we + really need this.*/ + return; + + UART_PUT_IER(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE); + UART_PUT_PTCR(port, ATMEL_PDC_TXTEN); /* re-enable PDC transmit */ + } + else + UART_PUT_IER(port, ATMEL_US_TXRDY); } /* @@ -224,7 +265,12 @@ { struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port; - UART_PUT_IDR(port, ATMEL_US_RXRDY); + if (atmel_port->use_dma_rx) { + UART_PUT_PTCR(port, ATMEL_PDC_RXTDIS); /* disable PDC receive */ + UART_PUT_IDR(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT); + } + else + UART_PUT_IDR(port, ATMEL_US_RXRDY); } /* @@ -247,6 +293,134 @@ } /* + * Receive data via the PDC. A buffer has been fulled. + */ +static void atmel_pdc_endrx(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port; + struct tty_struct *tty = port->info->tty; + struct atmel_dma_buffer *pdc = PDC_RX_BUF(atmel_port); + unsigned int count; + + count = pdc->dma_size - pdc->ofs; + if (likely(count > 0)) { + dma_sync_single_for_cpu(port->dev, pdc->dma_addr, pdc->dma_size, DMA_FROM_DEVICE); + tty_insert_flip_string(tty, pdc->buf + pdc->ofs, count); + tty_flip_buffer_push(tty); + + port->icount.rx += count; + } + + /* Set this buffer as the next receive buffer */ + pdc->ofs = 0; + UART_PUT_RNPR(port, pdc->dma_addr); + UART_PUT_RNCR(port, pdc->dma_size); + + /* Switch to next buffer */ + PDC_RX_SWITCH(atmel_port); /* next PDC buffer */ +} + +/* + * Receive data via the PDC. At least one byte was received, but the + * buffer was not full when the inter-character timeout expired. + */ +static void atmel_pdc_timeout(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port; + struct tty_struct *tty = port->info->tty; + struct atmel_dma_buffer *pdc = PDC_RX_BUF(atmel_port); + /* unsigned */ int ofs, count; + + ofs = UART_GET_RPR(port) - pdc->dma_addr; /* current DMA adress */ + count = ofs - pdc->ofs; + + if (likely(count > 0)) { + dma_sync_single_for_cpu(port->dev, pdc->dma_addr, pdc->dma_size, DMA_FROM_DEVICE); + tty_insert_flip_string(tty, pdc->buf + pdc->ofs, count); + tty_flip_buffer_push(tty); + + pdc->ofs = ofs; + port->icount.rx += count; + } + + /* reset the UART timeout */ + UART_PUT_CR(port, ATMEL_US_STTTO); +} + +/* + * Deal with parity, framing and overrun errors. + */ +static void atmel_pdc_rxerr(struct uart_port *port, unsigned int status) +{ + /* clear error */ + UART_PUT_CR(port, ATMEL_US_RSTSTA); + + if (status & ATMEL_US_RXBRK) { + status &= ~(ATMEL_US_PARE | ATMEL_US_FRAME); /* ignore side-effect */ + port->icount.brk++; + } + if (status & ATMEL_US_PARE) + port->icount.parity++; + if (status & ATMEL_US_FRAME) + port->icount.frame++; + if (status & ATMEL_US_OVRE) + port->icount.overrun++; +} + +/* + * A transmission via the PDC is complete. + */ +static void atmel_pdc_endtx(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port; + struct circ_buf *xmit = &port->info->xmit; + struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx; + + xmit->tail += pdc->ofs; + if (xmit->tail >= SERIAL_XMIT_SIZE) + xmit->tail -= SERIAL_XMIT_SIZE; + + port->icount.tx += pdc->ofs; + pdc->ofs = 0; + + if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) + uart_write_wakeup(port); +} + +/* + * The PDC transmitter is idle, so either start the next transfer or + * disable the transmitter. + */ +static void atmel_pdc_txbufe(struct uart_port *port) +{ + struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port; + struct circ_buf *xmit = &port->info->xmit; + struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx; + int count; + + if (!uart_circ_empty(xmit)) { + /* more to transmit - setup next transfer */ + UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS); /* disable PDC transmit */ + dma_sync_single_for_device(port->dev, pdc->dma_addr, pdc->dma_size, DMA_TO_DEVICE); + + if (xmit->tail < xmit->head) + count = xmit->head - xmit->tail; + else + count = SERIAL_XMIT_SIZE - xmit->tail; + pdc->ofs = count; + + UART_PUT_TPR(port, pdc->dma_addr + xmit->tail); + UART_PUT_TCR(port, count); + UART_PUT_PTCR(port, ATMEL_PDC_TXTEN); /* re-enable PDC transmit */ + } + else { + /* nothing left to transmit - disable the transmitter */ + UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS); /* disable PDC transmit */ + UART_PUT_IDR(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE); + } +} + +/* * Characters received (called from interrupt handler) */ static void atmel_rx_chars(struct uart_port *port) @@ -348,6 +522,14 @@ status = UART_GET_CSR(port); pending = status & UART_GET_IMR(port); while (pending) { + /* PDC receive */ + if (pending & ATMEL_US_ENDRX) + atmel_pdc_endrx(port); + if (pending & ATMEL_US_TIMEOUT) + atmel_pdc_timeout(port); + if (atmel_port->use_dma_rx && pending & (ATMEL_US_RXBRK | ATMEL_US_OVRE | ATMEL_US_FRAME | ATMEL_US_PARE)) + atmel_pdc_rxerr(port, pending); + /* Interrupt receive */ if (pending & ATMEL_US_RXRDY) atmel_rx_chars(port); @@ -362,6 +544,12 @@ if (pending & (ATMEL_US_RIIC | ATMEL_US_DSRIC | ATMEL_US_DCDIC | ATMEL_US_CTSIC)) wake_up_interruptible(&port->info->delta_msr_wait); + /* PDC transmit */ + if (pending & ATMEL_US_ENDTX) + atmel_pdc_endtx(port); + if (pending & ATMEL_US_TXBUFE) + atmel_pdc_txbufe(port); + /* Interrupt transmit */ if (pending & ATMEL_US_TXRDY) atmel_tx_chars(port); @@ -400,6 +588,47 @@ } /* + * Initialize DMA (if necessary) + */ + if (atmel_port->use_dma_rx) { + int i; + + for (i = 0; i < 2; i++) { + struct atmel_dma_buffer *pdc = &atmel_port->pdc_rx[i]; + + pdc->buf = kmalloc(PDC_BUFFER_SIZE, GFP_KERNEL); + if (pdc->buf == NULL) { + if (i != 0) { + dma_unmap_single(port->dev, atmel_port->pdc_rx[0].dma_addr, PDC_BUFFER_SIZE, DMA_FROM_DEVICE); + kfree(atmel_port->pdc_rx[0].buf); + } + free_irq(port->irq, port); + return -ENOMEM; + } + pdc->dma_addr = dma_map_single(port->dev, pdc->buf, PDC_BUFFER_SIZE, DMA_FROM_DEVICE); + pdc->dma_size = PDC_BUFFER_SIZE; + pdc->ofs = 0; + } + + atmel_port->pdc_rx_idx = 0; + + UART_PUT_RPR(port, atmel_port->pdc_rx[0].dma_addr); + UART_PUT_RCR(port, PDC_BUFFER_SIZE); + + UART_PUT_RNPR(port, atmel_port->pdc_rx[1].dma_addr); + UART_PUT_RNCR(port, PDC_BUFFER_SIZE); + } + if (atmel_port->use_dma_tx) { + struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx; + struct circ_buf *xmit = &port->info->xmit; + + pdc->buf = xmit->buf; + pdc->dma_addr = dma_map_single(port->dev, pdc->buf, SERIAL_XMIT_SIZE, DMA_TO_DEVICE); + pdc->dma_size = SERIAL_XMIT_SIZE; + pdc->ofs = 0; + } + + /* * If there is a specific "open" function (to register * control line interrupts) */ @@ -417,7 +646,15 @@ UART_PUT_CR(port, ATMEL_US_RSTSTA | ATMEL_US_RSTRX); UART_PUT_CR(port, ATMEL_US_TXEN | ATMEL_US_RXEN); /* enable xmit & rcvr */ - UART_PUT_IER(port, ATMEL_US_RXRDY); /* enable receive only */ + if (atmel_port->use_dma_rx) { + UART_PUT_RTOR(port, PDC_RX_TIMEOUT); /* set UART timeout */ + UART_PUT_CR(port, ATMEL_US_STTTO); + + UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT); + UART_PUT_PTCR(port, ATMEL_PDC_RXTEN); /* enable PDC controller */ + } + else + UART_PUT_IER(port, ATMEL_US_RXRDY); /* enable receive only */ return 0; } @@ -430,6 +667,25 @@ struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port; /* + * Shut-down the DMA. + */ + if (atmel_port->use_dma_rx) { + int i; + + for (i = 0; i < 2; i++) { + struct atmel_dma_buffer *pdc = &atmel_port->pdc_rx[i]; + + dma_unmap_single(port->dev, pdc->dma_addr, pdc->dma_size, DMA_FROM_DEVICE); + kfree(pdc->buf); + } + } + if (atmel_port->use_dma_tx) { + struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx; + + dma_unmap_single(port->dev, pdc->dma_addr, pdc->dma_size, DMA_TO_DEVICE); + } + + /* * Disable all interrupts, port and break condition. */ UART_PUT_CR(port, ATMEL_US_RSTSTA); @@ -480,6 +736,7 @@ */ static void atmel_set_termios(struct uart_port *port, struct ktermios * termios, struct ktermios * old) { + struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port; unsigned long flags; unsigned int mode, imr, quot, baud; @@ -533,6 +790,9 @@ if (termios->c_iflag & (BRKINT | PARMRK)) port->read_status_mask |= ATMEL_US_RXBRK; + if (atmel_port->use_dma_rx) /* need to enable error interrupts */ + UART_PUT_IER(port, port->read_status_mask); + /* * Characters to ignore */ @@ -711,6 +971,11 @@ clk_enable(atmel_port->clk); port->uartclk = clk_get_rate(atmel_port->clk); } + +#ifdef SUPPORT_PDC + atmel_port->use_dma_rx = data->use_dma_rx; + atmel_port->use_dma_tx = data->use_dma_tx; +#endif } /* diff -urN linux-2.6.20.4-0rig/drivers/spi/atmel_spi.c linux-2.6.20.4-atmel/drivers/spi/atmel_spi.c --- linux-2.6.20.4-0rig/drivers/spi/atmel_spi.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/spi/atmel_spi.c 2007-03-24 16:39:15.000000000 +0100 @@ -0,0 +1,699 @@ +/* + * Driver for Atmel AT32 and AT91 SPI Controllers + * + * Copyright (C) 2006 Atmel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/err.h> +#include <linux/interrupt.h> +#include <linux/spi/spi.h> + +#include <asm/io.h> +#include <asm/arch/board.h> +#include <asm/arch/gpio.h> + +#ifdef CONFIG_ARCH_AT91 +#include <asm/arch/cpu.h> +#endif + +#include "atmel_spi.h" + +/* + * The core SPI transfer engine just talks to a register bank to set up + * DMA transfers; transfer queue progress is driven by IRQs. The clock + * framework provides the base clock, subdivided for each spi_device. + * + * Newer controllers, marked with "new_1" flag, have: + * - CR.LASTXFER + * - SPI_MR.DIV32 may become FDIV or must-be-zero (here: always zero) + * - SPI_SR.TXEMPTY, SPI_SR.NSSR (and corresponding irqs) + * - SPI_CSRx.CSAAT + * - SPI_CSRx.SBCR allows faster clocking + */ +struct atmel_spi { + spinlock_t lock; + + void __iomem *regs; + int irq; + struct clk *clk; + struct platform_device *pdev; + unsigned new_1:1; + + u8 stopping; + struct list_head queue; + struct spi_transfer *current_transfer; + unsigned long remaining_bytes; + + void *buffer; + dma_addr_t buffer_dma; +}; + +#define BUFFER_SIZE PAGE_SIZE +#define INVALID_DMA_ADDRESS 0xffffffff + +/* + * Earlier SPI controllers (e.g. on at91rm9200) have a design bug whereby + * they assume that spi slave device state will not change on deselect, so + * that automagic deselection is OK. Not so! Workaround uses nCSx pins + * as GPIOs; or newer controllers have CSAAT and friends. + * + * Since the CSAAT functionality is a bit weird on newer controllers + * as well, we use GPIO to control nCSx pins on all controllers. + */ + +static inline void cs_activate(struct spi_device *spi) +{ + unsigned gpio = (unsigned) spi->controller_data; + unsigned active = spi->mode & SPI_CS_HIGH; + + dev_dbg(&spi->dev, "activate %u%s\n", gpio, active ? " (high)" : ""); +#ifdef CONFIG_ARCH_AT91 + at91_set_gpio_value(gpio, active); +#else + gpio_set_value(gpio, active); +#endif +} + +static inline void cs_deactivate(struct spi_device *spi) +{ + unsigned gpio = (unsigned) spi->controller_data; + unsigned active = spi->mode & SPI_CS_HIGH; + + dev_dbg(&spi->dev, "DEactivate %u%s\n", gpio, active ? " (low)" : ""); +#ifdef CONFIG_ARCH_AT91 + at91_set_gpio_value(gpio, !active); +#else + gpio_set_value(gpio, !active); +#endif +} + +/* + * Submit next transfer for DMA. + * lock is held, spi irq is blocked + */ +static void atmel_spi_next_xfer(struct spi_master *master, + struct spi_message *msg) +{ + struct atmel_spi *as = spi_master_get_devdata(master); + struct spi_transfer *xfer; + u32 len; + dma_addr_t tx_dma, rx_dma; + + xfer = as->current_transfer; + if (!xfer || as->remaining_bytes == 0) { + if (xfer) + xfer = list_entry(xfer->transfer_list.next, + struct spi_transfer, transfer_list); + else + xfer = list_entry(msg->transfers.next, + struct spi_transfer, transfer_list); + as->remaining_bytes = xfer->len; + as->current_transfer = xfer; + } + + len = as->remaining_bytes; + + tx_dma = xfer->tx_dma; + rx_dma = xfer->rx_dma; + + /* use scratch buffer only when rx or tx data is unspecified */ + if (rx_dma == INVALID_DMA_ADDRESS) { + rx_dma = as->buffer_dma; + if (len > BUFFER_SIZE) + len = BUFFER_SIZE; + } + if (tx_dma == INVALID_DMA_ADDRESS) { + tx_dma = as->buffer_dma; + if (len > BUFFER_SIZE) + len = BUFFER_SIZE; + memset(as->buffer, 0, len); + dma_sync_single_for_device(&as->pdev->dev, + as->buffer_dma, len, DMA_TO_DEVICE); + } + + spi_writel(as, RPR, rx_dma); + spi_writel(as, TPR, tx_dma); + + as->remaining_bytes -= len; + if (msg->spi->bits_per_word > 8) + len >>= 1; + + /* REVISIT: when xfer->delay_usecs == 0, the PDC "next transfer" + * mechanism might help avoid the IRQ latency between transfers + * + * We're also waiting for ENDRX before we start the next + * transfer because we need to handle some difficult timing + * issues otherwise. If we wait for ENDTX in one transfer and + * then starts waiting for ENDRX in the next, it's difficult + * to tell the difference between the ENDRX interrupt we're + * actually waiting for and the ENDRX interrupt of the + * previous transfer. + * + * It should be doable, though. Just not now... + */ + spi_writel(as, TNCR, 0); + spi_writel(as, RNCR, 0); + spi_writel(as, IER, SPI_BIT(ENDRX) | SPI_BIT(OVRES)); + + dev_dbg(&msg->spi->dev, + " start xfer %p: len %u tx %p/%08x rx %p/%08x imr %03x\n", + xfer, xfer->len, xfer->tx_buf, xfer->tx_dma, + xfer->rx_buf, xfer->rx_dma, spi_readl(as, IMR)); + + wmb(); + spi_writel(as, TCR, len); + spi_writel(as, RCR, len); + spi_writel(as, PTCR, SPI_BIT(TXTEN) | SPI_BIT(RXTEN)); +} + +static void atmel_spi_next_message(struct spi_master *master) +{ + struct atmel_spi *as = spi_master_get_devdata(master); + struct spi_message *msg; + u32 mr; + + BUG_ON(as->current_transfer); + + msg = list_entry(as->queue.next, struct spi_message, queue); + + /* Select the chip */ + mr = spi_readl(as, MR); + mr = SPI_BFINS(PCS, ~(1 << msg->spi->chip_select), mr); + spi_writel(as, MR, mr); + cs_activate(msg->spi); + + atmel_spi_next_xfer(master, msg); +} + +static void +atmel_spi_dma_map_xfer(struct atmel_spi *as, struct spi_transfer *xfer) +{ + xfer->tx_dma = xfer->rx_dma = INVALID_DMA_ADDRESS; + if (xfer->tx_buf) + xfer->tx_dma = dma_map_single(&as->pdev->dev, + (void *) xfer->tx_buf, xfer->len, + DMA_TO_DEVICE); + if (xfer->rx_buf) + xfer->rx_dma = dma_map_single(&as->pdev->dev, + xfer->rx_buf, xfer->len, + DMA_FROM_DEVICE); +} + +static void atmel_spi_dma_unmap_xfer(struct spi_master *master, + struct spi_transfer *xfer) +{ + if (xfer->tx_dma != INVALID_DMA_ADDRESS) + dma_unmap_single(master->cdev.dev, xfer->tx_dma, + xfer->len, DMA_TO_DEVICE); + if (xfer->rx_dma != INVALID_DMA_ADDRESS) + dma_unmap_single(master->cdev.dev, xfer->rx_dma, + xfer->len, DMA_FROM_DEVICE); +} + +static void +atmel_spi_msg_done(struct spi_master *master, struct atmel_spi *as, + struct spi_message *msg, int status) +{ + cs_deactivate(msg->spi); + list_del(&msg->queue); + msg->status = status; + + dev_dbg(master->cdev.dev, + "xfer complete: %u bytes transferred\n", + msg->actual_length); + + spin_unlock(&as->lock); + msg->complete(msg->context); + spin_lock(&as->lock); + + as->current_transfer = NULL; + + /* continue if needed */ + if (list_empty(&as->queue) || as->stopping) + spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS)); + else + atmel_spi_next_message(master); +} + +static irqreturn_t +atmel_spi_interrupt(int irq, void *dev_id) +{ + struct spi_master *master = dev_id; + struct atmel_spi *as = spi_master_get_devdata(master); + struct spi_message *msg; + struct spi_transfer *xfer; + u32 status, pending, imr; + int ret = IRQ_NONE; + + spin_lock(&as->lock); + + xfer = as->current_transfer; + msg = list_entry(as->queue.next, struct spi_message, queue); + + imr = spi_readl(as, IMR); + status = spi_readl(as, SR); + pending = status & imr; + + if (pending & SPI_BIT(OVRES)) { + int timeout; + + ret = IRQ_HANDLED; + + spi_writel(as, IDR, (SPI_BIT(ENDTX) | SPI_BIT(ENDRX) + | SPI_BIT(OVRES))); + + /* + * When we get an overrun, we disregard the current + * transfer. Data will not be copied back from any + * bounce buffer and msg->actual_len will not be + * updated with the last xfer. + * + * We will also not process any remaning transfers in + * the message. + * + * First, stop the transfer and unmap the DMA buffers. + */ + spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS)); + if (!msg->is_dma_mapped) + atmel_spi_dma_unmap_xfer(master, xfer); + + /* REVISIT: udelay in irq is unfriendly */ + if (xfer->delay_usecs) + udelay(xfer->delay_usecs); + + dev_warn(master->cdev.dev, "fifo overrun (%u/%u remaining)\n", + spi_readl(as, TCR), spi_readl(as, RCR)); + + /* + * Clean up DMA registers and make sure the data + * registers are empty. + */ + spi_writel(as, RNCR, 0); + spi_writel(as, TNCR, 0); + spi_writel(as, RCR, 0); + spi_writel(as, TCR, 0); + for (timeout = 1000; timeout; timeout--) + if (spi_readl(as, SR) & SPI_BIT(TXEMPTY)) + break; + if (!timeout) + dev_warn(master->cdev.dev, + "timeout waiting for TXEMPTY"); + while (spi_readl(as, SR) & SPI_BIT(RDRF)) + spi_readl(as, RDR); + + /* Clear any overrun happening while cleaning up */ + spi_readl(as, SR); + + atmel_spi_msg_done(master, as, msg, -EIO); + } else if (pending & SPI_BIT(ENDRX)) { + ret = IRQ_HANDLED; + + spi_writel(as, IDR, pending); + + if (as->remaining_bytes == 0) { + msg->actual_length += xfer->len; + + if (!msg->is_dma_mapped) + atmel_spi_dma_unmap_xfer(master, xfer); + + /* REVISIT: udelay in irq is unfriendly */ + if (xfer->delay_usecs) + udelay(xfer->delay_usecs); + + if (msg->transfers.prev == &xfer->transfer_list) { + /* report completed message */ + atmel_spi_msg_done(master, as, msg, 0); + } else { + if (xfer->cs_change) { + cs_deactivate(msg->spi); + udelay(1); + cs_activate(msg->spi); + } + + /* + * Not done yet. Submit the next transfer. + * + * FIXME handle protocol options for xfer + */ + atmel_spi_next_xfer(master, msg); + } + } else { + /* + * Keep going, we still have data to send in + * the current transfer. + */ + atmel_spi_next_xfer(master, msg); + } + } + + spin_unlock(&as->lock); + + return ret; +} + +#define MODEBITS (SPI_CPOL | SPI_CPHA | SPI_CS_HIGH) + +static int atmel_spi_setup(struct spi_device *spi) +{ + struct atmel_spi *as; + u32 scbr, csr; + unsigned int bits = spi->bits_per_word; + unsigned long bus_hz, sck_hz; + unsigned int npcs_pin; + int ret; + + as = spi_master_get_devdata(spi->master); + + if (as->stopping) + return -ESHUTDOWN; + + if (spi->chip_select > spi->master->num_chipselect) { + dev_dbg(&spi->dev, + "setup: invalid chipselect %u (%u defined)\n", + spi->chip_select, spi->master->num_chipselect); + return -EINVAL; + } + + if (bits == 0) + bits = 8; + if (bits < 8 || bits > 16) { + dev_dbg(&spi->dev, + "setup: invalid bits_per_word %u (8 to 16)\n", + bits); + return -EINVAL; + } + + if (spi->mode & ~MODEBITS) { + dev_dbg(&spi->dev, "setup: unsupported mode bits %x\n", + spi->mode & ~MODEBITS); + return -EINVAL; + } + + /* speed zero convention is used by some upper layers */ + bus_hz = clk_get_rate(as->clk); + if (spi->max_speed_hz) { + /* assume div32/fdiv/mbz == 0 */ + if (!as->new_1) + bus_hz /= 2; + scbr = ((bus_hz + spi->max_speed_hz - 1) + / spi->max_speed_hz); + if (scbr >= (1 << SPI_SCBR_SIZE)) { + dev_dbg(&spi->dev, "setup: %d Hz too slow, scbr %u\n", + spi->max_speed_hz, scbr); + return -EINVAL; + } + } else + scbr = 0xff; + sck_hz = bus_hz / scbr; + + csr = SPI_BF(SCBR, scbr) | SPI_BF(BITS, bits - 8); + if (spi->mode & SPI_CPOL) + csr |= SPI_BIT(CPOL); + if (!(spi->mode & SPI_CPHA)) + csr |= SPI_BIT(NCPHA); + + /* TODO: DLYBS and DLYBCT */ + csr |= SPI_BF(DLYBS, 10); + csr |= SPI_BF(DLYBCT, 10); + + /* chipselect must have been muxed as GPIO (e.g. in board setup) */ + npcs_pin = (unsigned int)spi->controller_data; + if (!spi->controller_state) { +#ifndef CONFIG_ARCH_AT91 + ret = gpio_request(npcs_pin, "spi_npcs"); + if (ret) + return ret; +#endif + spi->controller_state = (void *)npcs_pin; +#ifdef CONFIG_ARCH_AT91 + at91_set_gpio_output(npcs_pin, 0); +#else + gpio_direction_output(npcs_pin); +#endif + } + + dev_dbg(&spi->dev, + "setup: %lu Hz bpw %u mode 0x%x -> csr%d %08x\n", + sck_hz, bits, spi->mode, spi->chip_select, csr); + + spi_writel(as, CSR0 + 4 * spi->chip_select, csr); + + return 0; +} + +static int atmel_spi_transfer(struct spi_device *spi, struct spi_message *msg) +{ + struct atmel_spi *as; + struct spi_transfer *xfer; + unsigned long flags; + struct device *controller = spi->master->cdev.dev; + + as = spi_master_get_devdata(spi->master); + + dev_dbg(controller, "new message %p submitted for %s\n", + msg, spi->dev.bus_id); + + if (unlikely(list_empty(&msg->transfers) + || !spi->max_speed_hz)) + return -EINVAL; + + if (as->stopping) + return -ESHUTDOWN; + + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + if (!(xfer->tx_buf || xfer->rx_buf)) { + dev_dbg(&spi->dev, "missing rx or tx buf\n"); + return -EINVAL; + } + + /* FIXME implement these protocol options!! */ + if (xfer->bits_per_word || xfer->speed_hz) { + dev_dbg(&spi->dev, "no protocol options yet\n"); + return -ENOPROTOOPT; + } + } + + /* scrub dcache "early" */ + if (!msg->is_dma_mapped) { + list_for_each_entry(xfer, &msg->transfers, transfer_list) + atmel_spi_dma_map_xfer(as, xfer); + } + + list_for_each_entry(xfer, &msg->transfers, transfer_list) { + dev_dbg(controller, + " xfer %p: len %u tx %p/%08x rx %p/%08x\n", + xfer, xfer->len, + xfer->tx_buf, xfer->tx_dma, + xfer->rx_buf, xfer->rx_dma); + } + + msg->status = -EINPROGRESS; + msg->actual_length = 0; + + spin_lock_irqsave(&as->lock, flags); + list_add_tail(&msg->queue, &as->queue); + if (!as->current_transfer) + atmel_spi_next_message(spi->master); + spin_unlock_irqrestore(&as->lock, flags); + + return 0; +} + +static void atmel_spi_cleanup(const struct spi_device *spi) +{ +#ifndef CONFIG_ARCH_AT91 + if (spi->controller_state) + gpio_free((unsigned int)spi->controller_data); +#endif +} + +/*-------------------------------------------------------------------------*/ + +static int __init atmel_spi_probe(struct platform_device *pdev) +{ + struct resource *regs; + int irq; + struct clk *clk; + int ret; + struct spi_master *master; + struct atmel_spi *as; + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) + return -ENXIO; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + clk = clk_get(&pdev->dev, "spi_clk"); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + /* setup spi core then atmel-specific driver state */ + ret = -ENOMEM; + master = spi_alloc_master(&pdev->dev, sizeof *as); + if (!master) + goto out_free; + + master->bus_num = pdev->id; + master->num_chipselect = 4; + master->setup = atmel_spi_setup; + master->transfer = atmel_spi_transfer; + master->cleanup = atmel_spi_cleanup; + platform_set_drvdata(pdev, master); + + as = spi_master_get_devdata(master); + + as->buffer = dma_alloc_coherent(&pdev->dev, BUFFER_SIZE, + &as->buffer_dma, GFP_KERNEL); + if (!as->buffer) + goto out_free; + + spin_lock_init(&as->lock); + INIT_LIST_HEAD(&as->queue); + as->pdev = pdev; + as->regs = ioremap(regs->start, (regs->end - regs->start) + 1); + if (!as->regs) + goto out_free_buffer; + as->irq = irq; + as->clk = clk; +#ifdef CONFIG_ARCH_AT91 + if (!cpu_is_at91rm9200()) + as->new_1 = 1; +#endif + + ret = request_irq(irq, atmel_spi_interrupt, 0, + pdev->dev.bus_id, master); + if (ret) + goto out_unmap_regs; + + /* Initialize the hardware */ + clk_enable(clk); + spi_writel(as, CR, SPI_BIT(SWRST)); + spi_writel(as, MR, SPI_BIT(MSTR) | SPI_BIT(MODFDIS)); + spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS)); + spi_writel(as, CR, SPI_BIT(SPIEN)); + + /* go! */ + dev_info(&pdev->dev, "Atmel SPI Controller at 0x%08lx (irq %d)\n", + (unsigned long)regs->start, irq); + + ret = spi_register_master(master); + if (ret) + goto out_reset_hw; + + return 0; + +out_reset_hw: + spi_writel(as, CR, SPI_BIT(SWRST)); + clk_disable(clk); + free_irq(irq, master); +out_unmap_regs: + iounmap(as->regs); +out_free_buffer: + dma_free_coherent(&pdev->dev, BUFFER_SIZE, as->buffer, + as->buffer_dma); +out_free: + clk_put(clk); + spi_master_put(master); + return ret; +} + +static int __exit atmel_spi_remove(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct atmel_spi *as = spi_master_get_devdata(master); + struct spi_message *msg; + + /* reset the hardware and block queue progress */ + spin_lock_irq(&as->lock); + as->stopping = 1; + spi_writel(as, CR, SPI_BIT(SWRST)); + spi_readl(as, SR); + spin_unlock_irq(&as->lock); + + /* Terminate remaining queued transfers */ + list_for_each_entry(msg, &as->queue, queue) { + /* REVISIT unmapping the dma is a NOP on ARM and AVR32 + * but we shouldn't depend on that... + */ + msg->status = -ESHUTDOWN; + msg->complete(msg->context); + } + + dma_free_coherent(&pdev->dev, BUFFER_SIZE, as->buffer, + as->buffer_dma); + + clk_disable(as->clk); + clk_put(as->clk); + free_irq(as->irq, master); + iounmap(as->regs); + + spi_unregister_master(master); + + return 0; +} + +#ifdef CONFIG_PM + +static int atmel_spi_suspend(struct platform_device *pdev, pm_message_t mesg) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct atmel_spi *as = spi_master_get_devdata(master); + + clk_disable(as->clk); + return 0; +} + +static int atmel_spi_resume(struct platform_device *pdev) +{ + struct spi_master *master = platform_get_drvdata(pdev); + struct atmel_spi *as = spi_master_get_devdata(master); + + clk_enable(as->clk); + return 0; +} + +#else +#define atmel_spi_suspend NULL +#define atmel_spi_resume NULL +#endif + + +static struct platform_driver atmel_spi_driver = { + .driver = { + .name = "atmel_spi", + .owner = THIS_MODULE, + }, + .suspend = atmel_spi_suspend, + .resume = atmel_spi_resume, + .remove = __exit_p(atmel_spi_remove), +}; + +static int __init atmel_spi_init(void) +{ + return platform_driver_probe(&atmel_spi_driver, atmel_spi_probe); +} +module_init(atmel_spi_init); + +static void __exit atmel_spi_exit(void) +{ + platform_driver_unregister(&atmel_spi_driver); +} +module_exit(atmel_spi_exit); + +MODULE_DESCRIPTION("Atmel AT32/AT91 SPI Controller driver"); +MODULE_AUTHOR("Haavard Skinnemoen <hskinnemoen@atmel.com>"); +MODULE_LICENSE("GPL"); diff -urN linux-2.6.20.4-0rig/drivers/spi/atmel_spi.h linux-2.6.20.4-atmel/drivers/spi/atmel_spi.h --- linux-2.6.20.4-0rig/drivers/spi/atmel_spi.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/spi/atmel_spi.h 2007-03-24 16:43:57.000000000 +0100 @@ -0,0 +1,167 @@ +/* + * Register definitions for Atmel Serial Peripheral Interface (SPI) + * + * Copyright (C) 2006 Atmel Corporation + * + * 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. + */ +#ifndef __ATMEL_SPI_H__ +#define __ATMEL_SPI_H__ + +/* SPI register offsets */ +#define SPI_CR 0x0000 +#define SPI_MR 0x0004 +#define SPI_RDR 0x0008 +#define SPI_TDR 0x000c +#define SPI_SR 0x0010 +#define SPI_IER 0x0014 +#define SPI_IDR 0x0018 +#define SPI_IMR 0x001c +#define SPI_CSR0 0x0030 +#define SPI_CSR1 0x0034 +#define SPI_CSR2 0x0038 +#define SPI_CSR3 0x003c +#define SPI_RPR 0x0100 +#define SPI_RCR 0x0104 +#define SPI_TPR 0x0108 +#define SPI_TCR 0x010c +#define SPI_RNPR 0x0110 +#define SPI_RNCR 0x0114 +#define SPI_TNPR 0x0118 +#define SPI_TNCR 0x011c +#define SPI_PTCR 0x0120 +#define SPI_PTSR 0x0124 + +/* Bitfields in CR */ +#define SPI_SPIEN_OFFSET 0 +#define SPI_SPIEN_SIZE 1 +#define SPI_SPIDIS_OFFSET 1 +#define SPI_SPIDIS_SIZE 1 +#define SPI_SWRST_OFFSET 7 +#define SPI_SWRST_SIZE 1 +#define SPI_LASTXFER_OFFSET 24 +#define SPI_LASTXFER_SIZE 1 + +/* Bitfields in MR */ +#define SPI_MSTR_OFFSET 0 +#define SPI_MSTR_SIZE 1 +#define SPI_PS_OFFSET 1 +#define SPI_PS_SIZE 1 +#define SPI_PCSDEC_OFFSET 2 +#define SPI_PCSDEC_SIZE 1 +#define SPI_FDIV_OFFSET 3 +#define SPI_FDIV_SIZE 1 +#define SPI_MODFDIS_OFFSET 4 +#define SPI_MODFDIS_SIZE 1 +#define SPI_LLB_OFFSET 7 +#define SPI_LLB_SIZE 1 +#define SPI_PCS_OFFSET 16 +#define SPI_PCS_SIZE 4 +#define SPI_DLYBCS_OFFSET 24 +#define SPI_DLYBCS_SIZE 8 + +/* Bitfields in RDR */ +#define SPI_RD_OFFSET 0 +#define SPI_RD_SIZE 16 + +/* Bitfields in TDR */ +#define SPI_TD_OFFSET 0 +#define SPI_TD_SIZE 16 + +/* Bitfields in SR */ +#define SPI_RDRF_OFFSET 0 +#define SPI_RDRF_SIZE 1 +#define SPI_TDRE_OFFSET 1 +#define SPI_TDRE_SIZE 1 +#define SPI_MODF_OFFSET 2 +#define SPI_MODF_SIZE 1 +#define SPI_OVRES_OFFSET 3 +#define SPI_OVRES_SIZE 1 +#define SPI_ENDRX_OFFSET 4 +#define SPI_ENDRX_SIZE 1 +#define SPI_ENDTX_OFFSET 5 +#define SPI_ENDTX_SIZE 1 +#define SPI_RXBUFF_OFFSET 6 +#define SPI_RXBUFF_SIZE 1 +#define SPI_TXBUFE_OFFSET 7 +#define SPI_TXBUFE_SIZE 1 +#define SPI_NSSR_OFFSET 8 +#define SPI_NSSR_SIZE 1 +#define SPI_TXEMPTY_OFFSET 9 +#define SPI_TXEMPTY_SIZE 1 +#define SPI_SPIENS_OFFSET 16 +#define SPI_SPIENS_SIZE 1 + +/* Bitfields in CSR0 */ +#define SPI_CPOL_OFFSET 0 +#define SPI_CPOL_SIZE 1 +#define SPI_NCPHA_OFFSET 1 +#define SPI_NCPHA_SIZE 1 +#define SPI_CSAAT_OFFSET 3 +#define SPI_CSAAT_SIZE 1 +#define SPI_BITS_OFFSET 4 +#define SPI_BITS_SIZE 4 +#define SPI_SCBR_OFFSET 8 +#define SPI_SCBR_SIZE 8 +#define SPI_DLYBS_OFFSET 16 +#define SPI_DLYBS_SIZE 8 +#define SPI_DLYBCT_OFFSET 24 +#define SPI_DLYBCT_SIZE 8 + +/* Bitfields in RCR */ +#define SPI_RXCTR_OFFSET 0 +#define SPI_RXCTR_SIZE 16 + +/* Bitfields in TCR */ +#define SPI_TXCTR_OFFSET 0 +#define SPI_TXCTR_SIZE 16 + +/* Bitfields in RNCR */ +#define SPI_RXNCR_OFFSET 0 +#define SPI_RXNCR_SIZE 16 + +/* Bitfields in TNCR */ +#define SPI_TXNCR_OFFSET 0 +#define SPI_TXNCR_SIZE 16 + +/* Bitfields in PTCR */ +#define SPI_RXTEN_OFFSET 0 +#define SPI_RXTEN_SIZE 1 +#define SPI_RXTDIS_OFFSET 1 +#define SPI_RXTDIS_SIZE 1 +#define SPI_TXTEN_OFFSET 8 +#define SPI_TXTEN_SIZE 1 +#define SPI_TXTDIS_OFFSET 9 +#define SPI_TXTDIS_SIZE 1 + +/* Constants for BITS */ +#define SPI_BITS_8_BPT 0 +#define SPI_BITS_9_BPT 1 +#define SPI_BITS_10_BPT 2 +#define SPI_BITS_11_BPT 3 +#define SPI_BITS_12_BPT 4 +#define SPI_BITS_13_BPT 5 +#define SPI_BITS_14_BPT 6 +#define SPI_BITS_15_BPT 7 +#define SPI_BITS_16_BPT 8 + +/* Bit manipulation macros */ +#define SPI_BIT(name) \ + (1 << SPI_##name##_OFFSET) +#define SPI_BF(name,value) \ + (((value) & ((1 << SPI_##name##_SIZE) - 1)) << SPI_##name##_OFFSET) +#define SPI_BFEXT(name,value) \ + (((value) >> SPI_##name##_OFFSET) & ((1 << SPI_##name##_SIZE) - 1)) +#define SPI_BFINS(name,value,old) \ + ( ((old) & ~(((1 << SPI_##name##_SIZE) - 1) << SPI_##name##_OFFSET)) \ + | SPI_BF(name,value)) + +/* Register access macros */ +#define spi_readl(port,reg) \ + __raw_readl((port)->regs + SPI_##reg) +#define spi_writel(port,reg,value) \ + __raw_writel((value), (port)->regs + SPI_##reg) + +#endif /* __ATMEL_SPI_H__ */ diff -urN linux-2.6.20.4-0rig/drivers/spi/Kconfig linux-2.6.20.4-atmel/drivers/spi/Kconfig --- linux-2.6.20.4-0rig/drivers/spi/Kconfig 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/spi/Kconfig 2007-03-24 16:43:49.000000000 +0100 @@ -51,6 +51,21 @@ comment "SPI Master Controller Drivers" depends on SPI_MASTER +config SPI_ATMEL + tristate "Atmel SPI Controller" + depends on (ARCH_AT91 || AVR32) && SPI_MASTER + select SPI_AT91_MANUAL_CS if ARCH_AT91RM9200 + help + This selects a driver for the Atmel SPI Controller, present on + many AT32 (AVR32) and AT91 (ARM) chips. + +config SPI_ATMEL + tristate "Atmel SPI Controller" + depends on (ARCH_AT91 || AVR32) && SPI_MASTER + help + This selects a driver for the Atmel SPI Controller, present on + many AT32 (AVR32) and AT91 (ARM) chips. + config SPI_BITBANG tristate "Bitbanging SPI master" depends on SPI_MASTER && EXPERIMENTAL @@ -75,6 +90,24 @@ inexpensive battery powered microcontroller evaluation board. This same cable can be used to flash new firmware. +config SPI_AT91 + tristate "AT91RM9200 Bitbang SPI Master" + depends on SPI_MASTER && ARCH_AT91RM9200 && !SPI_ATMEL && EXPERIMENTAL + select SPI_BITBANG + select SPI_AT91_MANUAL_CS + help + This is dumb PIO bitbanging driver for the Atmel AT91RM9200. + The SPI_ATMEL driver will be its replacement, using the native + SPI hardware and its DMA controller. + +config SPI_AT91_MANUAL_CS + bool + depends on ARCH_AT91RM9200 + help + Works around an AT91RM9200 problem whereby the SPI chip-select + will be wrongly disabled. The workaround uses those pins as + GPIOs instead of letting the SPI controller manage them. + config SPI_MPC83xx tristate "Freescale MPC83xx SPI controller" depends on SPI_MASTER && PPC_83xx && EXPERIMENTAL diff -urN linux-2.6.20.4-0rig/drivers/spi/Makefile linux-2.6.20.4-atmel/drivers/spi/Makefile --- linux-2.6.20.4-0rig/drivers/spi/Makefile 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/spi/Makefile 2007-03-24 16:39:15.000000000 +0100 @@ -12,11 +12,14 @@ # SPI master controller drivers (bus) obj-$(CONFIG_SPI_BITBANG) += spi_bitbang.o +obj-$(CONFIG_SPI_ATMEL) += atmel_spi.o obj-$(CONFIG_SPI_BUTTERFLY) += spi_butterfly.o obj-$(CONFIG_SPI_PXA2XX) += pxa2xx_spi.o obj-$(CONFIG_SPI_MPC83xx) += spi_mpc83xx.o obj-$(CONFIG_SPI_S3C24XX_GPIO) += spi_s3c24xx_gpio.o obj-$(CONFIG_SPI_S3C24XX) += spi_s3c24xx.o +obj-$(CONFIG_SPI_AT91) += spi_at91_bitbang.o +obj-$(CONFIG_SPI_ATMEL) += atmel_spi.o # ... add above this line ... # SPI protocol drivers (device/link on bus) diff -urN linux-2.6.20.4-0rig/drivers/spi/spi_at91_bitbang.c linux-2.6.20.4-atmel/drivers/spi/spi_at91_bitbang.c --- linux-2.6.20.4-0rig/drivers/spi/spi_at91_bitbang.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/spi/spi_at91_bitbang.c 2007-03-24 16:39:15.000000000 +0100 @@ -0,0 +1,207 @@ +/* + * at91_spi.c - at91 SPI driver (BOOTSTRAP/BITBANG VERSION) + * + * Copyright (C) 2006 David Brownell + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/platform_device.h> + +#include <linux/spi/spi.h> +#include <linux/spi/spi_bitbang.h> + +#include <asm/arch/gpio.h> + + +/* + * FIXME this bitbanging version is just to help bootstrap systems until + * there's a native SPI+IRQ+DMA controller driver ... such a driver should + * be a drop-in replacement for this one, and much faster. + * + * remember: + * + * - other at91 parts (like at91sam9) have multiple controllers + * and different pin muxing; this version is at91rm9200 specfic. + * + * - at91sam9261 SPI0 pins are directly muxed with MMC/SD pins. + * + * - rm9200 spi chipselects drop wrongly, so the native driver + * will need to use gpios much like this does. + * + * - real hardware only allows 8..16 bits per word, while this + * bitbanger allows 1..32 (incompatible superset). + * + * - this disregards clock parameters. with inlined gpio calls, + * gcc 3.4.4 produces about 1.5 mbit/sec, more than 2x faster + * than using the subroutined veresion from txrx_word(). + * + * - suspend/resume and <linux/clk.h> support is missing ... + */ + +#define spi_miso_bit AT91_PIN_PA0 +#define spi_mosi_bit AT91_PIN_PA1 +#define spi_sck_bit AT91_PIN_PA2 + +struct at91_spi { + struct spi_bitbang bitbang; + struct platform_device *pdev; +}; + +/*----------------------------------------------------------------------*/ + +static inline void setsck(struct spi_device *spi, int is_on) +{ + at91_set_gpio_value(spi_sck_bit, is_on); +} + +static inline void setmosi(struct spi_device *spi, int is_on) +{ + at91_set_gpio_value(spi_mosi_bit, is_on); +} + +static inline int getmiso(struct spi_device *spi) +{ + return at91_get_gpio_value(spi_miso_bit); +} + +static void at91_spi_chipselect(struct spi_device *spi, int is_active) +{ + unsigned long cs = (unsigned long) spi->controller_data; + + /* set default clock polarity */ + if (is_active) + setsck(spi, spi->mode & SPI_CPOL); + + /* only support active-low (default) */ + at91_set_gpio_value(cs, !is_active); +} + +/* + * NOTE: this is "as fast as we can"; it should be a function of + * the device clock ... + */ +#define spidelay(X) do{} while(0) + +#define EXPAND_BITBANG_TXRX +#include <linux/spi/spi_bitbang.h> + +static u32 at91_spi_txrx_word_mode0(struct spi_device *spi, + unsigned nsecs, u32 word, u8 bits) +{ + return bitbang_txrx_be_cpha0(spi, nsecs, 0, word, 8); +} + +static u32 at91_spi_txrx_word_mode1(struct spi_device *spi, + unsigned nsecs, u32 word, u8 bits) +{ + return bitbang_txrx_be_cpha1(spi, nsecs, 0, word, 8); +} + +static u32 at91_spi_txrx_word_mode2(struct spi_device *spi, + unsigned nsecs, u32 word, u8 bits) +{ + return bitbang_txrx_be_cpha0(spi, nsecs, 1, word, 8); +} + +static u32 at91_spi_txrx_word_mode3(struct spi_device *spi, + unsigned nsecs, u32 word, u8 bits) +{ + return bitbang_txrx_be_cpha1(spi, nsecs, 1, word, 8); +} + +/*----------------------------------------------------------------------*/ + +static int __init at91_spi_probe(struct platform_device *pdev) +{ + int status; + struct spi_master *master; + struct at91_spi *at91_spi; + + if (pdev->id != 0) /* SPI0 bus */ + return -EINVAL; + + master = spi_alloc_master(&pdev->dev, sizeof *at91_spi); + if (!master) + return -ENOMEM; + + at91_spi = spi_master_get_devdata(master); + at91_spi->pdev = pdev; + platform_set_drvdata(pdev, at91_spi); + + /* SPI and bitbang hookup */ + master->bus_num = 0; + master->num_chipselect = 4; + + at91_spi->bitbang.master = spi_master_get(master); + at91_spi->bitbang.chipselect = at91_spi_chipselect; + at91_spi->bitbang.txrx_word[SPI_MODE_0] = at91_spi_txrx_word_mode0; + at91_spi->bitbang.txrx_word[SPI_MODE_1] = at91_spi_txrx_word_mode1; + at91_spi->bitbang.txrx_word[SPI_MODE_2] = at91_spi_txrx_word_mode2; + at91_spi->bitbang.txrx_word[SPI_MODE_3] = at91_spi_txrx_word_mode3; + + status = spi_bitbang_start(&at91_spi->bitbang); + if (status < 0) + (void) spi_master_put(at91_spi->bitbang.master); + + return status; +} + +static int __exit at91_spi_remove(struct platform_device *pdev) +{ + struct at91_spi *at91_spi = platform_get_drvdata(pdev); + int status; + + /* stop() unregisters child devices too */ + status = spi_bitbang_stop(&at91_spi->bitbang); + (void) spi_master_put(at91_spi->bitbang.master); + + platform_set_drvdata(pdev, NULL); + return status; +} + +static struct platform_driver at91_spi_driver = { + .probe = at91_spi_probe, + .remove = __exit_p(at91_spi_remove), + .driver = { + .name = "at91_spi", + .owner = THIS_MODULE, + }, +}; + +static int __init at91_spi_init(void) +{ + at91_set_gpio_output(spi_sck_bit, 0); + at91_set_gpio_output(spi_mosi_bit, 0); + at91_set_gpio_input(spi_miso_bit, 1 /* pullup */); + + /* register driver */ + return platform_driver_register(&at91_spi_driver); +} + +static void __exit at91_spi_exit(void) +{ + platform_driver_unregister(&at91_spi_driver); +} + +device_initcall(at91_spi_init); +module_exit(at91_spi_exit); + +MODULE_ALIAS("at91_spi.0"); + +MODULE_DESCRIPTION("AT91 SPI support (BOOTSTRAP/BITBANG VERSION)"); +MODULE_AUTHOR("David Brownell"); +MODULE_LICENSE("GPL"); diff -urN linux-2.6.20.4-0rig/drivers/usb/gadget/at91_udc.c linux-2.6.20.4-atmel/drivers/usb/gadget/at91_udc.c --- linux-2.6.20.4-0rig/drivers/usb/gadget/at91_udc.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/usb/gadget/at91_udc.c 2007-03-24 16:39:15.000000000 +0100 @@ -287,8 +287,8 @@ ep->stopped = stopped; /* ep0 is always ready; other endpoints need a non-empty queue */ - if (list_empty(&ep->queue) && ep->int_mask != (1 << 0)) - at91_udp_write(udc, AT91_UDP_IDR, ep->int_mask); + if (list_empty(&ep->queue) && (ep->id != 0)) + at91_udp_write(udc, AT91_UDP_IDR, 1 << ep->id); } /*-------------------------------------------------------------------------*/ @@ -541,7 +541,7 @@ * reset/init endpoint fifo. NOTE: leaves fifo_bank alone, * since endpoint resets don't reset hw pingpong state. */ - at91_udp_write(dev, AT91_UDP_RST_EP, ep->int_mask); + at91_udp_write(dev, AT91_UDP_RST_EP, 1 << ep->id); at91_udp_write(dev, AT91_UDP_RST_EP, 0); local_irq_restore(flags); @@ -567,7 +567,7 @@ /* reset fifos and endpoint */ if (ep->udc->clocked) { - at91_udp_write(udc, AT91_UDP_RST_EP, ep->int_mask); + at91_udp_write(udc, AT91_UDP_RST_EP, 1 << ep->id); at91_udp_write(udc, AT91_UDP_RST_EP, 0); __raw_writel(0, ep->creg); } @@ -715,7 +715,7 @@ if (req && !status) { list_add_tail (&req->queue, &ep->queue); - at91_udp_write(dev, AT91_UDP_IER, ep->int_mask); + at91_udp_write(dev, AT91_UDP_IER, 1 << ep->id); } done: local_irq_restore(flags); @@ -774,7 +774,7 @@ csr |= AT91_UDP_FORCESTALL; VDBG("halt %s\n", ep->ep.name); } else { - at91_udp_write(udc, AT91_UDP_RST_EP, ep->int_mask); + at91_udp_write(udc, AT91_UDP_RST_EP, 1 << ep->id); at91_udp_write(udc, AT91_UDP_RST_EP, 0); csr &= ~AT91_UDP_FORCESTALL; } @@ -913,14 +913,15 @@ at91_udp_write(udc, AT91_UDP_TXVC, 0); if (cpu_is_at91rm9200()) at91_set_gpio_value(udc->board.pullup_pin, 1); - else if (cpu_is_at91sam9260()) { + else if (cpu_is_at91sam9260() || cpu_is_at91sam9263()) { u32 txvc = at91_udp_read(udc, AT91_UDP_TXVC); - + txvc |= AT91_UDP_TXVC_PUON; at91_udp_write(udc, AT91_UDP_TXVC, txvc); - } else if (cpu_is_at91sam9261()) { + } + else if (cpu_is_at91sam9261()) { u32 usbpucr; - + usbpucr = at91_sys_read(AT91_MATRIX_USBPUCR); usbpucr |= AT91_MATRIX_USBPUCR_PUON; at91_sys_write(AT91_MATRIX_USBPUCR, usbpucr); @@ -928,20 +929,20 @@ } else { stop_activity(udc); at91_udp_write(udc, AT91_UDP_TXVC, AT91_UDP_TXVC_TXVDIS); - if (cpu_is_at91rm9200()) - at91_set_gpio_value(udc->board.pullup_pin, 0); - else if (cpu_is_at91sam9260()) { - u32 txvc = at91_udp_read(udc, AT91_UDP_TXVC); - - txvc &= ~AT91_UDP_TXVC_PUON; - at91_udp_write(udc, AT91_UDP_TXVC, txvc); - } else if (cpu_is_at91sam9261()) { - u32 usbpucr; - - usbpucr = at91_sys_read(AT91_MATRIX_USBPUCR); - usbpucr &= ~AT91_MATRIX_USBPUCR_PUON; - at91_sys_write(AT91_MATRIX_USBPUCR, usbpucr); - } + if (cpu_is_at91rm9200()) + at91_set_gpio_value(udc->board.pullup_pin, 0); + else if (cpu_is_at91sam9260() || cpu_is_at91sam9263()) { + u32 txvc = at91_udp_read(udc, AT91_UDP_TXVC); + + txvc &= ~AT91_UDP_TXVC_PUON; + at91_udp_write(udc, AT91_UDP_TXVC, txvc); + } else if (cpu_is_at91sam9261()) { + u32 usbpucr; + + usbpucr = at91_sys_read(AT91_MATRIX_USBPUCR); + usbpucr &= ~AT91_MATRIX_USBPUCR_PUON; + at91_sys_write(AT91_MATRIX_USBPUCR, usbpucr); + } clk_off(udc); } } @@ -1228,7 +1229,7 @@ } else if (ep->is_in) goto stall; - at91_udp_write(udc, AT91_UDP_RST_EP, ep->int_mask); + at91_udp_write(udc, AT91_UDP_RST_EP, 1 << ep->id); at91_udp_write(udc, AT91_UDP_RST_EP, 0); tmp = __raw_readl(ep->creg); tmp |= CLR_FX; @@ -1504,15 +1505,16 @@ } }, .ep[0] = { + .id = 0, .ep = { .name = ep0name, .ops = &at91_ep_ops, }, .udc = &controller, .maxpacket = 8, - .int_mask = 1 << 0, }, .ep[1] = { + .id = 1, .ep = { .name = "ep1", .ops = &at91_ep_ops, @@ -1520,9 +1522,9 @@ .udc = &controller, .is_pingpong = 1, .maxpacket = 64, - .int_mask = 1 << 1, }, .ep[2] = { + .id = 2, .ep = { .name = "ep2", .ops = &at91_ep_ops, @@ -1530,9 +1532,9 @@ .udc = &controller, .is_pingpong = 1, .maxpacket = 64, - .int_mask = 1 << 2, }, .ep[3] = { + .id = 3, .ep = { /* could actually do bulk too */ .name = "ep3-int", @@ -1540,9 +1542,9 @@ }, .udc = &controller, .maxpacket = 8, - .int_mask = 1 << 3, }, .ep[4] = { + .id = 4, .ep = { .name = "ep4", .ops = &at91_ep_ops, @@ -1550,9 +1552,9 @@ .udc = &controller, .is_pingpong = 1, .maxpacket = 256, - .int_mask = 1 << 4, }, .ep[5] = { + .id = 5, .ep = { .name = "ep5", .ops = &at91_ep_ops, @@ -1560,7 +1562,6 @@ .udc = &controller, .is_pingpong = 1, .maxpacket = 256, - .int_mask = 1 << 5, }, /* ep6 and ep7 are also reserved (custom silicon might use them) */ }; @@ -1679,9 +1680,7 @@ if (!res) return -ENXIO; - if (!request_mem_region(res->start, - res->end - res->start + 1, - driver_name)) { + if (!request_mem_region(res->start, res->end - res->start + 1, driver_name)) { DBG("someone's using UDC memory\n"); return -EBUSY; } @@ -1807,16 +1806,13 @@ || !wake || at91_suspend_entering_slow_clock()) { pullup(udc, 0); - disable_irq_wake(udc->udp_irq); + wake = 0; } else enable_irq_wake(udc->udp_irq); - if (udc->board.vbus_pin > 0) { - if (wake) - enable_irq_wake(udc->board.vbus_pin); - else - disable_irq_wake(udc->board.vbus_pin); - } + udc->active_suspend = wake; + if (udc->board.vbus_pin > 0 && wake) + enable_irq_wake(udc->board.vbus_pin); return 0; } @@ -1824,8 +1820,14 @@ { struct at91_udc *udc = platform_get_drvdata(pdev); + if (udc->board.vbus_pin > 0 && udc->active_suspend) + disable_irq_wake(udc->board.vbus_pin); + /* maybe reconnect to host; if so, clocks on */ - pullup(udc, 1); + if (udc->active_suspend) + disable_irq_wake(udc->udp_irq); + else + pullup(udc, 1); return 0; } #else diff -urN linux-2.6.20.4-0rig/drivers/usb/gadget/at91_udc.h linux-2.6.20.4-atmel/drivers/usb/gadget/at91_udc.h --- linux-2.6.20.4-0rig/drivers/usb/gadget/at91_udc.h 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/usb/gadget/at91_udc.h 2007-03-24 16:39:15.000000000 +0100 @@ -105,10 +105,10 @@ struct usb_ep ep; struct list_head queue; struct at91_udc *udc; + u8 id; void __iomem *creg; unsigned maxpacket:16; - u8 int_mask; unsigned is_pingpong:1; unsigned stopped:1; @@ -136,6 +136,7 @@ unsigned wait_for_addr_ack:1; unsigned wait_for_config_ack:1; unsigned selfpowered:1; + unsigned active_suspend:1; u8 addr; struct at91_udc_data board; struct clk *iclk, *fclk; diff -urN linux-2.6.20.4-0rig/drivers/usb/gadget/ether.c linux-2.6.20.4-atmel/drivers/usb/gadget/ether.c --- linux-2.6.20.4-0rig/drivers/usb/gadget/ether.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/usb/gadget/ether.c 2007-03-24 16:42:28.000000000 +0100 @@ -266,6 +266,10 @@ #define DEV_CONFIG_CDC #endif +#ifdef CONFIG_USB_GADGET_HUSB2DEV +#define DEV_CONFIG_CDC +#endif + /* For CDC-incapable hardware, choose the simple cdc subset. * Anything that talks bulk (without notable bugs) can do this. @@ -428,7 +432,7 @@ #define DEV_RNDIS_CONFIG_VALUE 2 /* rndis; optional */ static struct usb_device_descriptor -device_desc = { +device_desc __attribute__((aligned(2))) = { .bLength = sizeof device_desc, .bDescriptorType = USB_DT_DEVICE, @@ -454,7 +458,7 @@ }; static struct usb_config_descriptor -eth_config = { +eth_config __attribute__((aligned(2))) = { .bLength = sizeof eth_config, .bDescriptorType = USB_DT_CONFIG, @@ -468,7 +472,7 @@ #ifdef CONFIG_USB_ETH_RNDIS static struct usb_config_descriptor -rndis_config = { +rndis_config __attribute__((aligned(2))) = { .bLength = sizeof rndis_config, .bDescriptorType = USB_DT_CONFIG, @@ -493,7 +497,7 @@ #ifdef DEV_CONFIG_CDC static struct usb_interface_descriptor -control_intf = { +control_intf __attribute__((aligned(2))) = { .bLength = sizeof control_intf, .bDescriptorType = USB_DT_INTERFACE, @@ -509,7 +513,7 @@ #ifdef CONFIG_USB_ETH_RNDIS static const struct usb_interface_descriptor -rndis_control_intf = { +rndis_control_intf __attribute__((aligned(2))) = { .bLength = sizeof rndis_control_intf, .bDescriptorType = USB_DT_INTERFACE, @@ -524,7 +528,7 @@ #if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) -static const struct usb_cdc_header_desc header_desc = { +static const struct usb_cdc_header_desc __attribute__((aligned(2))) header_desc = { .bLength = sizeof header_desc, .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = USB_CDC_HEADER_TYPE, @@ -566,7 +570,8 @@ #ifdef DEV_CONFIG_CDC -static const struct usb_cdc_ether_desc ether_desc = { +static const struct usb_cdc_ether_desc +ether_desc __attribute__((aligned(2))) = { .bLength = sizeof ether_desc, .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = USB_CDC_ETHERNET_TYPE, @@ -601,7 +606,7 @@ #define STATUS_BYTECOUNT 16 /* 8 byte header + data */ static struct usb_endpoint_descriptor -fs_status_desc = { +fs_status_desc __attribute__((aligned(2))) = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -632,7 +637,7 @@ /* ... but the "real" data interface has two bulk endpoints */ static const struct usb_interface_descriptor -data_intf = { +data_intf __attribute__((aligned(2))) = { .bLength = sizeof data_intf, .bDescriptorType = USB_DT_INTERFACE, @@ -652,7 +657,7 @@ /* RNDIS doesn't activate by changing to the "real" altsetting */ static const struct usb_interface_descriptor -rndis_data_intf = { +rndis_data_intf __attribute__((aligned(2))) = { .bLength = sizeof rndis_data_intf, .bDescriptorType = USB_DT_INTERFACE, @@ -675,7 +680,7 @@ */ static const struct usb_interface_descriptor -subset_data_intf = { +subset_data_intf __attribute__((aligned(2))) = { .bLength = sizeof subset_data_intf, .bDescriptorType = USB_DT_INTERFACE, @@ -692,7 +697,7 @@ static struct usb_endpoint_descriptor -fs_source_desc = { +fs_source_desc __attribute__((aligned(2))) = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -701,7 +706,7 @@ }; static struct usb_endpoint_descriptor -fs_sink_desc = { +fs_sink_desc __attribute__((aligned(2))) = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -767,7 +772,7 @@ #if defined(DEV_CONFIG_CDC) || defined(CONFIG_USB_ETH_RNDIS) static struct usb_endpoint_descriptor -hs_status_desc = { +hs_status_desc __attribute__((aligned(2))) = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -778,7 +783,7 @@ #endif /* DEV_CONFIG_CDC */ static struct usb_endpoint_descriptor -hs_source_desc = { +hs_source_desc __attribute__((aligned(2))) = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -787,7 +792,7 @@ }; static struct usb_endpoint_descriptor -hs_sink_desc = { +hs_sink_desc __attribute__((aligned(2))) = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -796,7 +801,7 @@ }; static struct usb_qualifier_descriptor -dev_qualifier = { +dev_qualifier __attribute__((aligned(2))) = { .bLength = sizeof dev_qualifier, .bDescriptorType = USB_DT_DEVICE_QUALIFIER, diff -urN linux-2.6.20.4-0rig/drivers/usb/gadget/file_storage.c linux-2.6.20.4-atmel/drivers/usb/gadget/file_storage.c --- linux-2.6.20.4-0rig/drivers/usb/gadget/file_storage.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/usb/gadget/file_storage.c 2007-03-24 16:42:28.000000000 +0100 @@ -854,7 +854,7 @@ #define CONFIG_VALUE 1 static struct usb_device_descriptor -device_desc = { +device_desc __attribute__((aligned(2))) = { .bLength = sizeof device_desc, .bDescriptorType = USB_DT_DEVICE, @@ -873,7 +873,7 @@ }; static struct usb_config_descriptor -config_desc = { +config_desc __attribute__((aligned(2))) = { .bLength = sizeof config_desc, .bDescriptorType = USB_DT_CONFIG, @@ -896,7 +896,7 @@ /* There is only one interface. */ static struct usb_interface_descriptor -intf_desc = { +intf_desc __attribute__((aligned(2))) = { .bLength = sizeof intf_desc, .bDescriptorType = USB_DT_INTERFACE, @@ -911,7 +911,7 @@ * and interrupt-in. */ static struct usb_endpoint_descriptor -fs_bulk_in_desc = { +fs_bulk_in_desc __attribute__((aligned(2))) = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -921,7 +921,7 @@ }; static struct usb_endpoint_descriptor -fs_bulk_out_desc = { +fs_bulk_out_desc __attribute__((aligned(2))) = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -931,7 +931,7 @@ }; static struct usb_endpoint_descriptor -fs_intr_in_desc = { +fs_intr_in_desc __attribute__((aligned(2))) = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -963,7 +963,7 @@ * for the config descriptor. */ static struct usb_qualifier_descriptor -dev_qualifier = { +dev_qualifier __attribute__((aligned(2))) = { .bLength = sizeof dev_qualifier, .bDescriptorType = USB_DT_DEVICE_QUALIFIER, @@ -974,7 +974,7 @@ }; static struct usb_endpoint_descriptor -hs_bulk_in_desc = { +hs_bulk_in_desc __attribute__((aligned(2))) = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -984,7 +984,7 @@ }; static struct usb_endpoint_descriptor -hs_bulk_out_desc = { +hs_bulk_out_desc __attribute__((aligned(2))) = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -995,7 +995,7 @@ }; static struct usb_endpoint_descriptor -hs_intr_in_desc = { +hs_intr_in_desc __attribute__((aligned(2))) = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, diff -urN linux-2.6.20.4-0rig/drivers/usb/gadget/gadget_chips.h linux-2.6.20.4-atmel/drivers/usb/gadget/gadget_chips.h --- linux-2.6.20.4-0rig/drivers/usb/gadget/gadget_chips.h 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/usb/gadget/gadget_chips.h 2007-03-24 16:42:29.000000000 +0100 @@ -75,6 +75,12 @@ #define gadget_is_pxa27x(g) 0 #endif +#ifdef CONFIG_USB_GADGET_HUSB2DEV +#define gadget_is_husb2dev(g) !strcmp("husb2_udc", (g)->name) +#else +#define gadget_is_husb2dev(g) 0 +#endif + #ifdef CONFIG_USB_GADGET_S3C2410 #define gadget_is_s3c2410(g) !strcmp("s3c2410_udc", (g)->name) #else @@ -169,5 +175,7 @@ return 0x16; else if (gadget_is_mpc8272(gadget)) return 0x17; + else if (gadget_is_husb2dev(gadget)) + return 0x80; return -ENOENT; } diff -urN linux-2.6.20.4-0rig/drivers/usb/gadget/husb2_udc.c linux-2.6.20.4-atmel/drivers/usb/gadget/husb2_udc.c --- linux-2.6.20.4-0rig/drivers/usb/gadget/husb2_udc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/usb/gadget/husb2_udc.c 2007-03-24 16:42:29.000000000 +0100 @@ -0,0 +1,1997 @@ +/* + * Driver for the Atmel HUSB2device high speed USB device controller + * + * Copyright (C) 2005-2006 Atmel Corporation + * + * 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. + */ +#undef DEBUG + +#include <linux/clk.h> +#include <linux/module.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/list.h> +#include <linux/platform_device.h> +#include <linux/usb_ch9.h> +#include <linux/usb_gadget.h> +#include <linux/dmapool.h> +#include <linux/delay.h> + +#include <asm/io.h> + +#include "husb2_udc.h" + +#define DRIVER_VERSION "0.9" + +#define DMA_ADDR_INVALID (~(dma_addr_t)0) + +#define FIFO_IOMEM_ID 0 +#define CTRL_IOMEM_ID 1 + +#ifdef DEBUG +#define DBG_ERR 0x0001 /* report all error returns */ +#define DBG_HW 0x0002 /* debug hardware initialization */ +#define DBG_GADGET 0x0004 /* calls to/from gadget driver */ +#define DBG_INT 0x0008 /* interrupts */ +#define DBG_BUS 0x0010 /* report changes in bus state */ +#define DBG_QUEUE 0x0020 /* debug request queue processing */ +#define DBG_FIFO 0x0040 /* debug FIFO contents */ +#define DBG_DMA 0x0080 /* debug DMA handling */ +#define DBG_REQ 0x0100 /* print out queued request length */ +#define DBG_ALL 0xffff +#define DBG_NONE 0x0000 + +#define DEBUG_LEVEL (DBG_ERR|DBG_REQ) +#define DBG(level, fmt, ...) \ + do { \ + if ((level) & DEBUG_LEVEL) \ + printk(KERN_DEBUG "udc: " fmt, ## __VA_ARGS__); \ + } while (0) +#else +#define DBG(level, fmt...) +#endif + +static struct husb2_udc the_udc; + +#ifdef CONFIG_DEBUG_FS +#include <linux/debugfs.h> +#include <asm/uaccess.h> + +static int queue_dbg_open(struct inode *inode, struct file *file) +{ + struct husb2_ep *ep = inode->i_private; + struct husb2_request *req, *req_copy; + struct list_head *queue_data; + + queue_data = kmalloc(sizeof(*queue_data), GFP_KERNEL); + if (!queue_data) + return -ENOMEM; + INIT_LIST_HEAD(queue_data); + + spin_lock_irq(&ep->udc->lock); + list_for_each_entry(req, &ep->queue, queue) { + req_copy = kmalloc(sizeof(*req_copy), GFP_ATOMIC); + if (!req_copy) + goto fail; + memcpy(req_copy, req, sizeof(*req_copy)); + list_add_tail(&req_copy->queue, queue_data); + } + spin_unlock_irq(&ep->udc->lock); + + file->private_data = queue_data; + return 0; + +fail: + spin_unlock_irq(&ep->udc->lock); + list_for_each_entry_safe(req, req_copy, queue_data, queue) { + list_del(&req->queue); + kfree(req); + } + kfree(queue_data); + return -ENOMEM; +} + +/* + * bbbbbbbb llllllll IZS sssss nnnn FDL\n\0 + * + * b: buffer address + * l: buffer length + * I/i: interrupt/no interrupt + * Z/z: zero/no zero + * S/s: short ok/short not ok + * s: status + * n: nr_packets + * F/f: submitted/not submitted to FIFO + * D/d: using/not using DMA + * L/l: last transaction/not last transaction + */ +static ssize_t queue_dbg_read(struct file *file, char __user *buf, + size_t nbytes, loff_t *ppos) +{ + struct list_head *queue = file->private_data; + struct husb2_request *req, *tmp_req; + size_t len, remaining, actual = 0; + char tmpbuf[38]; + + if (!access_ok(VERIFY_WRITE, buf, nbytes)) + return -EFAULT; + + mutex_lock(&file->f_dentry->d_inode->i_mutex); + list_for_each_entry_safe(req, tmp_req, queue, queue) { + len = snprintf(tmpbuf, sizeof(tmpbuf), + "%8p %08x %c%c%c %5d %4u %c%c%c\n", + req->req.buf, req->req.length, + req->req.no_interrupt ? 'i' : 'I', + req->req.zero ? 'Z' : 'z', + req->req.short_not_ok ? 's' : 'S', + req->req.status, + req->nr_pkts, + req->submitted ? 'F' : 'f', + req->using_dma ? 'D' : 'd', + req->last_transaction ? 'L' : 'l'); + len = min(len, sizeof(tmpbuf)); + if (len > nbytes) + break; + + list_del(&req->queue); + kfree(req); + + remaining = __copy_to_user(buf, tmpbuf, len); + actual += len - remaining; + if (remaining) + break; + + nbytes -= len; + buf += len; + } + mutex_unlock(&file->f_dentry->d_inode->i_mutex); + + return actual; +} + +static int queue_dbg_release(struct inode *inode, struct file *file) +{ + struct list_head *queue_data = file->private_data; + struct husb2_request *req, *tmp_req; + + list_for_each_entry_safe(req, tmp_req, queue_data, queue) { + list_del(&req->queue); + kfree(req); + } + kfree(queue_data); + return 0; +} + +static int regs_dbg_open(struct inode *inode, struct file *file) +{ + struct husb2_udc *udc; + unsigned int i; + u32 *data; + int ret = -ENOMEM; + + mutex_lock(&inode->i_mutex); + udc = inode->i_private; + data = kmalloc(inode->i_size, GFP_KERNEL); + if (!data) + goto out; + + spin_lock_irq(&udc->lock); + for (i = 0; i < inode->i_size / 4; i++) + data[i] = __raw_readl(udc->regs + i * 4); + spin_unlock_irq(&udc->lock); + + file->private_data = data; + ret = 0; + +out: + mutex_unlock(&inode->i_mutex); + + return ret; +} + +static ssize_t regs_dbg_read(struct file *file, char __user *buf, + size_t nbytes, loff_t *ppos) +{ + struct inode *inode = file->f_dentry->d_inode; + int ret; + + mutex_lock(&inode->i_mutex); + ret = simple_read_from_buffer(buf, nbytes, ppos, + file->private_data, + file->f_dentry->d_inode->i_size); + mutex_unlock(&inode->i_mutex); + + return ret; +} + +static int regs_dbg_release(struct inode *inode, struct file *file) +{ + kfree(file->private_data); + return 0; +} + +const struct file_operations queue_dbg_fops = { + .owner = THIS_MODULE, + .open = queue_dbg_open, + .llseek = no_llseek, + .read = queue_dbg_read, + .release = queue_dbg_release, +}; + +const struct file_operations regs_dbg_fops = { + .owner = THIS_MODULE, + .open = regs_dbg_open, + .llseek = generic_file_llseek, + .read = regs_dbg_read, + .release = regs_dbg_release, +}; + +static void husb2_ep_init_debugfs(struct husb2_udc *udc, + struct husb2_ep *ep) +{ + struct dentry *ep_root; + + ep_root = debugfs_create_dir(ep_name(ep), udc->debugfs_root); + if (!ep_root) + goto err_root; + ep->debugfs_dir = ep_root; + + ep->debugfs_queue = debugfs_create_file("queue", 0400, ep_root, + ep, &queue_dbg_fops); + if (!ep->debugfs_queue) + goto err_queue; + + if (ep_can_dma(ep)) { + ep->debugfs_dma_status + = debugfs_create_u32("dma_status", 0400, ep_root, + &ep->last_dma_status); + if (!ep->debugfs_dma_status) + goto err_dma_status; + } + + return; + +err_dma_status: + debugfs_remove(ep->debugfs_queue); +err_queue: + debugfs_remove(ep_root); +err_root: + dev_err(&ep->udc->pdev->dev, + "failed to create debugfs directory for %s\n", ep_name(ep)); +} + +static void husb2_ep_cleanup_debugfs(struct husb2_ep *ep) +{ + debugfs_remove(ep->debugfs_queue); + debugfs_remove(ep->debugfs_dma_status); + debugfs_remove(ep->debugfs_dir); + ep->debugfs_dma_status = NULL; + ep->debugfs_dir = NULL; +} + +static void husb2_init_debugfs(struct husb2_udc *udc) +{ + struct dentry *root, *regs; + struct resource *regs_resource; + + root = debugfs_create_dir(udc->gadget.name, NULL); + if (IS_ERR(root) || !root) + goto err_root; + udc->debugfs_root = root; + + regs = debugfs_create_file("regs", 0400, root, udc, ®s_dbg_fops); + if (!regs) + goto err_regs; + + regs_resource = platform_get_resource(udc->pdev, IORESOURCE_MEM, + CTRL_IOMEM_ID); + regs->d_inode->i_size = regs_resource->end - regs_resource->start + 1; + udc->debugfs_regs = regs; + + husb2_ep_init_debugfs(udc, to_husb2_ep(udc->gadget.ep0)); + + return; + +err_regs: + debugfs_remove(root); +err_root: + udc->debugfs_root = NULL; + dev_err(&udc->pdev->dev, "debugfs is not available\n"); +} + +static void husb2_cleanup_debugfs(struct husb2_udc *udc) +{ + husb2_ep_cleanup_debugfs(to_husb2_ep(udc->gadget.ep0)); + debugfs_remove(udc->debugfs_regs); + debugfs_remove(udc->debugfs_root); + udc->debugfs_regs = NULL; + udc->debugfs_root = NULL; +} +#else +static inline void husb2_ep_init_debugfs(struct husb2_udc *udc, + struct husb2_ep *ep) +{ + +} + +static inline void husb2_ep_cleanup_debugfs(struct husb2_ep *ep) +{ + +} + +static inline void husb2_init_debugfs(struct husb2_udc *udc) +{ + +} + +static inline void husb2_cleanup_debugfs(struct husb2_udc *udc) +{ + +} +#endif + +static void copy_to_fifo(void __iomem *fifo, void *buf, int len) +{ + unsigned long tmp; + + DBG(DBG_FIFO, "copy to FIFO (len %d):\n", len); + for (; len > 0; len -= 4, buf += 4, fifo += 4) { + tmp = *(unsigned long *)buf; + if (len >= 4) { + DBG(DBG_FIFO, " -> %08lx\n", tmp); + __raw_writel(tmp, fifo); + } else { + do { + DBG(DBG_FIFO, " -> %02lx\n", tmp >> 24); + __raw_writeb(tmp >> 24, fifo); + fifo++; + tmp <<= 8; + } while (--len); + break; + } + } +} + +static void copy_from_fifo(void *buf, void __iomem *fifo, int len) +{ + union { + unsigned long *w; + unsigned char *b; + } p; + unsigned long tmp; + + DBG(DBG_FIFO, "copy from FIFO (len %d):\n", len); + for (p.w = buf; len > 0; len -= 4, p.w++, fifo += 4) { + if (len >= 4) { + tmp = __raw_readl(fifo); + *p.w = tmp; + DBG(DBG_FIFO, " -> %08lx\n", tmp); + } else { + do { + tmp = __raw_readb(fifo); + *p.b = tmp; + DBG(DBG_FIFO, " -> %02lx\n", tmp); + fifo++, p.b++; + } while (--len); + } + } +} + +static void next_fifo_transaction(struct husb2_ep *ep, + struct husb2_request *req) +{ + unsigned int transaction_len; + + transaction_len = req->req.length - req->req.actual; + req->last_transaction = 1; + if (transaction_len > ep->ep.maxpacket) { + transaction_len = ep->ep.maxpacket; + req->last_transaction = 0; + } else if (transaction_len == ep->ep.maxpacket + && req->req.zero) { + req->last_transaction = 0; + } + DBG(DBG_QUEUE, "%s: submit_transaction, req %p (length %d)%s\n", + ep_name(ep), req, transaction_len, + req->last_transaction ? ", done" : ""); + + copy_to_fifo(ep->fifo, req->req.buf + req->req.actual, transaction_len); + husb2_ep_writel(ep, SET_STA, HUSB2_BIT(TX_PK_RDY)); + req->req.actual += transaction_len; +} + +static void submit_request(struct husb2_ep *ep, struct husb2_request *req) +{ + DBG(DBG_QUEUE, "%s: submit_request: req %p (length %d)\n", + ep_name(ep), req, req->req.length); + + req->req.actual = 0; + req->submitted = 1; + + if (req->using_dma) { + if (req->req.length == 0) { + husb2_ep_writel(ep, CTL_ENB, HUSB2_BIT(TX_PK_RDY)); + } else { + husb2_ep_writel(ep, CTL_DIS, HUSB2_BIT(TX_PK_RDY)); + husb2_dma_writel(ep, NXT_DSC, + req->packet[0].desc_dma); + husb2_dma_writel(ep, CONTROL, HUSB2_BIT(DMA_LINK)); + } + } else { + next_fifo_transaction(ep, req); + if (req->last_transaction) + husb2_ep_writel(ep, CTL_ENB, HUSB2_BIT(TX_COMPLETE)); + else + husb2_ep_writel(ep, CTL_ENB, HUSB2_BIT(TX_PK_RDY)); + } +} + +static void submit_next_request(struct husb2_ep *ep) +{ + struct husb2_request *req; + + if (list_empty(&ep->queue)) { + husb2_ep_writel(ep, CTL_DIS, (HUSB2_BIT(TX_PK_RDY) + | HUSB2_BIT(RX_BK_RDY))); + return; + } + + req = list_entry(ep->queue.next, struct husb2_request, queue); + if (!req->submitted) + submit_request(ep, req); +} + +static void send_status(struct husb2_udc *udc, struct husb2_ep *ep) +{ + ep->state = STATUS_STAGE_IN; + husb2_ep_writel(ep, SET_STA, HUSB2_BIT(TX_PK_RDY)); + husb2_ep_writel(ep, CTL_ENB, HUSB2_BIT(TX_COMPLETE)); +} + +static void receive_data(struct husb2_ep *ep) +{ + struct husb2_udc *udc = ep->udc; + struct husb2_request *req; + unsigned long status; + unsigned int bytecount, nr_busy; + int is_complete = 0; + + status = husb2_ep_readl(ep, STA); + nr_busy = HUSB2_BFEXT(BUSY_BANKS, status); + + DBG(DBG_QUEUE, "receive data: nr_busy=%u\n", nr_busy); + + while (nr_busy > 0) { + if (list_empty(&ep->queue)) { + husb2_ep_writel(ep, CTL_DIS, HUSB2_BIT(RX_BK_RDY)); + break; + } + req = list_entry(ep->queue.next, + struct husb2_request, queue); + + bytecount = HUSB2_BFEXT(BYTE_COUNT, status); + + if (status & (1 << 31)) + is_complete = 1; + if (req->req.actual + bytecount >= req->req.length) { + is_complete = 1; + bytecount = req->req.length - req->req.actual; + } + + copy_from_fifo(req->req.buf + req->req.actual, + ep->fifo, bytecount); + req->req.actual += bytecount; + + husb2_ep_writel(ep, CLR_STA, HUSB2_BIT(RX_BK_RDY)); + + if (is_complete) { + DBG(DBG_QUEUE, "%s: request done\n", ep_name(ep)); + req->req.status = 0; + list_del_init(&req->queue); + req->req.complete(&ep->ep, &req->req); + } + + status = husb2_ep_readl(ep, STA); + nr_busy = HUSB2_BFEXT(BUSY_BANKS, status); + + if (is_complete && ep_is_control(ep)) { + BUG_ON(nr_busy != 0); + send_status(udc, ep); + break; + } + } +} + +static void request_complete(struct husb2_ep *ep, + struct husb2_request *req, + int status) +{ + struct husb2_udc *udc = ep->udc; + int i; + + BUG_ON(!list_empty(&req->queue)); + + if (req->req.status == -EINPROGRESS) + req->req.status = status; + + if (req->packet) { + for (i = 0; i < req->nr_pkts; i++) + dma_pool_free(udc->desc_pool, req->packet[i].desc, + req->packet[i].desc_dma); + kfree(req->packet); + req->packet = NULL; + dma_unmap_single(&udc->pdev->dev, + req->req.dma, req->req.length, + (ep_is_in(ep) + ? DMA_TO_DEVICE : DMA_FROM_DEVICE)); + req->req.dma = DMA_ADDR_INVALID; + } + + DBG(DBG_GADGET | DBG_REQ, + "%s: req %p complete: status %d, actual %u\n", + ep_name(ep), req, req->req.status, req->req.actual); + req->req.complete(&ep->ep, &req->req); +} + +static void request_complete_list(struct husb2_ep *ep, + struct list_head *list, + int status) +{ + struct husb2_request *req, *tmp_req; + + list_for_each_entry_safe(req, tmp_req, list, queue) { + list_del_init(&req->queue); + request_complete(ep, req, status); + } +} + +static int husb2_ep_enable(struct usb_ep *_ep, + const struct usb_endpoint_descriptor *desc) +{ + struct husb2_ep *ep = to_husb2_ep(_ep); + struct husb2_udc *udc = ep->udc; + unsigned long flags, ept_cfg, maxpacket; + + DBG(DBG_GADGET, "%s: ep_enable: desc=%p\n", ep_name(ep), desc); + + maxpacket = le16_to_cpu(desc->wMaxPacketSize); + + if (ep->index == 0 + || desc->bDescriptorType != USB_DT_ENDPOINT + || ((desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK) + != ep->index) + || maxpacket == 0 + || maxpacket > ep->fifo_size) { + DBG(DBG_ERR, "ep_enable: Invalid argument"); + return -EINVAL; + } + + if (((desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) + == USB_ENDPOINT_XFER_ISOC) + && !(ep->capabilities & HUSB2_EP_CAP_ISOC)) { + DBG(DBG_ERR, "ep_enable: %s is not isoc capable\n", + ep_name(ep)); + return -EINVAL; + } + + if (maxpacket <= 8) + ept_cfg = HUSB2_BF(EPT_SIZE, HUSB2_EPT_SIZE_8); + else + /* LSB is bit 1, not 0 */ + ept_cfg = HUSB2_BF(EPT_SIZE, fls(maxpacket - 1) - 3); + DBG(DBG_HW, "%s: EPT_SIZE = %lu (maxpacket = %lu)\n", + ep_name(ep), ept_cfg, maxpacket); + + if ((desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) + ept_cfg |= HUSB2_BIT(EPT_DIR); + + switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { + case USB_ENDPOINT_XFER_CONTROL: + ept_cfg |= HUSB2_BF(EPT_TYPE, HUSB2_EPT_TYPE_CONTROL); + break; + case USB_ENDPOINT_XFER_ISOC: + ept_cfg |= HUSB2_BF(EPT_TYPE, HUSB2_EPT_TYPE_ISO); + break; + case USB_ENDPOINT_XFER_BULK: + ept_cfg |= HUSB2_BF(EPT_TYPE, HUSB2_EPT_TYPE_BULK); + break; + case USB_ENDPOINT_XFER_INT: + ept_cfg |= HUSB2_BF(EPT_TYPE, HUSB2_EPT_TYPE_INT); + break; + } + ept_cfg |= HUSB2_BF(BK_NUMBER, ep->nr_banks); + + spin_lock_irqsave(&ep->udc->lock, flags); + + if (ep->desc) { + spin_unlock_irqrestore(&ep->udc->lock, flags); + DBG(DBG_ERR, "ep%d already enabled\n", ep->index); + return -EBUSY; + } + + ep->desc = desc; + ep->ep.maxpacket = maxpacket; + + husb2_ep_writel(ep, CFG, ept_cfg); + husb2_ep_writel(ep, CTL_ENB, HUSB2_BIT(EPT_ENABLE)); + + if (ep_can_dma(ep)) { + husb2_writel(udc, INT_ENB, + (husb2_readl(udc, INT_ENB) + | HUSB2_BF(EPT_INT, 1 << ep->index) + | HUSB2_BF(DMA_INT, 1 << ep->index))); + husb2_ep_writel(ep, CTL_ENB, HUSB2_BIT(AUTO_VALID)); + } else { + husb2_writel(udc, INT_ENB, + (husb2_readl(udc, INT_ENB) + | HUSB2_BF(EPT_INT, 1 << ep->index))); + } + + spin_unlock_irqrestore(&udc->lock, flags); + + DBG(DBG_HW, "EPT_CFG%d after init: %#08lx\n", ep->index, + (unsigned long)husb2_ep_readl(ep, CFG)); + DBG(DBG_HW, "INT_ENB after init: %#08lx\n", + (unsigned long)husb2_readl(udc, INT_ENB)); + + husb2_ep_init_debugfs(udc, ep); + + return 0; +} + +static int husb2_ep_disable(struct usb_ep *_ep) +{ + struct husb2_ep *ep = to_husb2_ep(_ep); + struct husb2_udc *udc = ep->udc; + LIST_HEAD(req_list); + unsigned long flags; + + DBG(DBG_GADGET, "ep_disable: %s\n", ep_name(ep)); + + husb2_ep_cleanup_debugfs(ep); + + spin_lock_irqsave(&udc->lock, flags); + + if (!ep->desc) { + spin_unlock_irqrestore(&udc->lock, flags); + DBG(DBG_ERR, "ep_disable: %s not enabled\n", + ep_name(ep)); + return -EINVAL; + } + ep->desc = NULL; + + list_splice_init(&ep->queue, &req_list); + if (ep_can_dma(ep)) { + husb2_dma_writel(ep, CONTROL, 0); + husb2_dma_writel(ep, ADDRESS, 0); + husb2_dma_readl(ep, STATUS); + } + husb2_ep_writel(ep, CTL_DIS, HUSB2_BIT(EPT_ENABLE)); + husb2_writel(udc, INT_ENB, (husb2_readl(udc, INT_ENB) + & ~HUSB2_BF(EPT_INT, 1 << ep->index))); + + spin_unlock_irqrestore(&udc->lock, flags); + + request_complete_list(ep, &req_list, -ESHUTDOWN); + + return 0; +} + +static struct usb_request * +husb2_ep_alloc_request(struct usb_ep *_ep, unsigned gfp_flags) +{ + struct husb2_request *req; + + DBG(DBG_GADGET, "ep_alloc_request: %p, 0x%x\n", _ep, gfp_flags); + + req = kzalloc(sizeof(*req), gfp_flags); + if (!req) + return NULL; + + INIT_LIST_HEAD(&req->queue); + req->req.dma = DMA_ADDR_INVALID; + + return &req->req; +} + +static void +husb2_ep_free_request(struct usb_ep *_ep, struct usb_request *_req) +{ + struct husb2_request *req = to_husb2_req(_req); + + DBG(DBG_GADGET, "ep_free_request: %p, %p\n", _ep, _req); + + kfree(req); +} + +static void *husb2_ep_alloc_buffer(struct usb_ep *_ep, unsigned bytes, + dma_addr_t *dma, unsigned gfp_flags) +{ + struct husb2_ep *ep = to_husb2_ep(_ep); + void *buf; + + /* + * We depend on kmalloc() returning cache-aligned memory. This + * is normally guaranteed as long as we allocate a whole + * cacheline or more. + * + * When CONFIG_DEBUG_SLAB is enabled, however, the slab + * allocator inserts red zones and ownership information, + * causing the slab objects to be misaligned. + * + * One alternative would be to use dma_alloc_coherent, but + * that would make us unable to allocate anything less than a + * page at a time. + */ +#ifdef CONFIG_DEBUG_SLAB +# error The HUSB2 UDC driver breaks with SLAB debugging enabled +#endif + + if (bytes < L1_CACHE_BYTES) + bytes = L1_CACHE_BYTES; + + buf = kmalloc(bytes, gfp_flags); + + /* + * Seems like we have to map the buffer any chance we get. + * ether.c wants us to initialize the dma member of a + * different request than the one receiving the buffer, so one + * never knows... + * + * Ah, screw it. The ether driver is probably wrong, and this + * is not the right place to do the mapping. The driver + * shouldn't mess with our DMA mappings anyway. + */ + *dma = DMA_ADDR_INVALID; + + DBG(DBG_GADGET, "ep_alloc_buffer: %s, %u, 0x%x -> %p\n", + ep_name(ep), bytes, gfp_flags, buf); + + return buf; +} + +static void husb2_ep_free_buffer(struct usb_ep *_ep, void *buf, + dma_addr_t dma, unsigned bytes) +{ + DBG(DBG_GADGET, "ep_free_buffer: %s, buf %p (size %u)\n", + _ep->name, buf, bytes); + kfree(buf); +} + +static int queue_dma(struct husb2_udc *udc, struct husb2_ep *ep, + struct husb2_request *req, unsigned int direction, + gfp_t gfp_flags) +{ + struct husb2_packet *pkt, *prev_pkt; + unsigned int pkt_size, nr_pkts, i; + unsigned int residue; + dma_addr_t addr; + unsigned long flags; + u32 ctrl; + + req->using_dma = 1; + + if (req->req.length == 0) { + if (!req->req.zero) + return -EINVAL; + req->send_zlp = 1; + + spin_lock_irqsave(&udc->lock, flags); + husb2_ep_writel(ep, CTL_ENB, HUSB2_BIT(TX_PK_RDY)); + list_add_tail(&req->queue, &ep->queue); + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; + } + + if (req->req.dma == DMA_ADDR_INVALID) + req->req.dma = dma_map_single(&udc->pdev->dev, + req->req.buf, + req->req.length, + direction); + else + dma_sync_single_for_device(&udc->pdev->dev, + req->req.dma, + req->req.length, + direction); + + pkt_size = ep->ep.maxpacket; + nr_pkts = req->req.length / pkt_size; + residue = req->req.length % pkt_size; + if (residue != 0) + nr_pkts++; + else if (req->req.zero && ep_is_in(ep)) + /* ensure last packet is short */ + req->send_zlp = 1; + + req->nr_pkts = nr_pkts; + + req->packet = kzalloc(sizeof(*req->packet) * nr_pkts, gfp_flags); + if (!req->packet) + goto out_of_memory; + + addr = req->req.dma; + ctrl = (HUSB2_BF(DMA_BUF_LEN, pkt_size) + | HUSB2_BIT(DMA_CH_EN) | HUSB2_BIT(DMA_LINK) + | HUSB2_BIT(DMA_END_TR_EN) | HUSB2_BIT(DMA_END_TR_IE)); + prev_pkt = NULL; + pkt = NULL; + DBG(DBG_DMA, "DMA descriptors:\n"); + for (i = 0; i < nr_pkts; i++) { + pkt = &req->packet[i]; + pkt->desc = dma_pool_alloc(udc->desc_pool, gfp_flags, + &pkt->desc_dma); + if (!pkt->desc) + goto out_of_memory; + + if (prev_pkt) { + prev_pkt->desc->next = pkt->desc_dma; + DBG(DBG_DMA, "[%d] n%08x a%08x c%08x\n", + i - 1, prev_pkt->desc->next, prev_pkt->desc->addr, + prev_pkt->desc->ctrl); + } + prev_pkt = pkt; + + pkt->desc->addr = addr; + pkt->desc->ctrl = ctrl; + addr += pkt_size; + } + + /* special care is needed for the last packet... */ + ctrl = (HUSB2_BIT(DMA_CH_EN) + | HUSB2_BIT(DMA_END_TR_EN) | HUSB2_BIT(DMA_END_TR_IE) + | HUSB2_BIT(DMA_END_BUF_IE)); + if (ep_is_in(ep)) + ctrl |= HUSB2_BIT(DMA_END_BUF_EN); + if (req->req.zero || residue) + ctrl |= HUSB2_BF(DMA_BUF_LEN, residue); + else + ctrl |= HUSB2_BF(DMA_BUF_LEN, pkt_size); + pkt->desc->ctrl = ctrl; + + DBG(DBG_DMA, "[%d] n%08x a%08x c%08x\n", + i - 1, prev_pkt->desc->next, prev_pkt->desc->addr, + prev_pkt->desc->ctrl); + + /* Add this request to the queue and try to chain the DMA descriptors */ + spin_lock_irqsave(&udc->lock, flags); + + /* If the DMA controller is idle, start it */ + if (list_empty(&ep->queue)) { + husb2_dma_writel(ep, NXT_DSC, req->packet[0].desc_dma); + husb2_dma_writel(ep, CONTROL, HUSB2_BIT(DMA_LINK)); + } + + list_add_tail(&req->queue, &ep->queue); + + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; + +out_of_memory: + printk(KERN_ERR "ERROR: Could not allocate DMA memory for endpoint %s\n", + ep_name(ep)); + if (req->packet) { + for (i = 0; i < nr_pkts; i++) + if (req->packet[i].desc) + dma_pool_free(udc->desc_pool, + req->packet[i].desc, + req->packet[i].desc_dma); + kfree(req->packet); + } + + return -ENOMEM; +} + +static int husb2_ep_queue(struct usb_ep *_ep, struct usb_request *_req, + gfp_t gfp_flags) +{ + struct husb2_request *req = to_husb2_req(_req); + struct husb2_ep *ep = to_husb2_ep(_ep); + struct husb2_udc *udc = ep->udc; + unsigned long flags; + int direction_in = 0; + + DBG(DBG_GADGET | DBG_QUEUE | DBG_REQ, + "%s: queue req %p, len %u\n", ep_name(ep), req, _req->length); + + if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN) + return -ESHUTDOWN; + if (!ep->desc) + return -ENODEV; + + req->nr_pkts = 0; + req->submitted = 0; + req->using_dma = 0; + req->last_transaction = 0; + req->send_zlp = 0; + + BUG_ON(req->packet); + + if (ep_is_in(ep) + || (ep_is_control(ep) && (ep->state == DATA_STAGE_IN + || ep->state == STATUS_STAGE_IN))) + direction_in = 1; + + _req->status = -EINPROGRESS; + _req->actual = 0; + + if (ep_can_dma(ep)) { + return queue_dma(udc, ep, req, (direction_in + ? DMA_TO_DEVICE + : DMA_FROM_DEVICE), + gfp_flags); + } else { + spin_lock_irqsave(&udc->lock, flags); + list_add_tail(&req->queue, &ep->queue); + + if (direction_in) + husb2_ep_writel(ep, CTL_ENB, HUSB2_BIT(TX_PK_RDY)); + else + husb2_ep_writel(ep, CTL_ENB, HUSB2_BIT(RX_BK_RDY)); + spin_unlock_irqrestore(&udc->lock, flags); + } + + return 0; +} + +static void husb2_update_req(struct husb2_ep *ep, struct husb2_request *req, + u32 status) +{ + struct husb2_dma_desc *desc; + dma_addr_t from; + dma_addr_t addr; + size_t size; + unsigned int i; + + addr = husb2_dma_readl(ep, ADDRESS); + req->req.actual = 0; + + for (i = 0; i < req->nr_pkts; i++) { + desc = req->packet[i].desc; + from = desc->addr; + size = HUSB2_BFEXT(DMA_BUF_LEN, desc->ctrl); + + req->req.actual += size; + + DBG(DBG_DMA, " from=%#08x, size=%#zx\n", from, size); + + if (from <= addr && (from + size) >= addr) + break; + } + + req->req.actual -= HUSB2_BFEXT(DMA_BUF_LEN, status); +} + +static int stop_dma(struct husb2_ep *ep, u32 *pstatus) +{ + unsigned int timeout; + u32 status; + + /* + * Stop the DMA controller. When writing both CH_EN + * and LINK to 0, the other bits are not affected. + */ + husb2_dma_writel(ep, CONTROL, 0); + + /* Wait for the FIFO to empty */ + for (timeout = 40; timeout; --timeout) { + status = husb2_dma_readl(ep, STATUS); + if (!(status & HUSB2_BIT(DMA_CH_EN))) + break; + udelay(1); + } + + if (pstatus) + *pstatus = status; + + if (timeout == 0) { + dev_err(&ep->udc->pdev->dev, + "%s: timed out waiting for DMA FIFO to empty\n", + ep_name(ep)); + return -ETIMEDOUT; + } + + return 0; +} + +static int husb2_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ + struct husb2_ep *ep = to_husb2_ep(_ep); + struct husb2_udc *udc = ep->udc; + struct husb2_request *req = to_husb2_req(_req); + unsigned long flags; + u32 status; + + DBG(DBG_GADGET | DBG_QUEUE, "ep_dequeue: %s, req %p\n", ep_name(ep), req); + + spin_lock_irqsave(&udc->lock, flags); + + if (req->using_dma) { + /* + * If this request is currently being transferred, + * stop the DMA controller and reset the FIFO. + */ + if (ep->queue.next == &req->queue) { + status = husb2_dma_readl(ep, STATUS); + if (status & HUSB2_BIT(DMA_CH_EN)) + stop_dma(ep, &status); + +#ifdef CONFIG_DEBUG_FS + ep->last_dma_status = status; +#endif + + husb2_writel(udc, EPT_RST, + 1 << ep_index(ep)); + + husb2_update_req(ep, req, status); + } + } + + /* + * Errors should stop the queue from advancing until the + * completion function returns. + */ + list_del_init(&req->queue); + spin_unlock_irqrestore(&udc->lock, flags); + + request_complete(ep, req, -ECONNRESET); + + /* Process the next request if any */ + spin_lock_irqsave(&udc->lock, flags); + submit_next_request(ep); + spin_unlock_irqrestore(&udc->lock, flags); + + return 0; +} + +static int husb2_ep_set_halt(struct usb_ep *_ep, int value) +{ + struct husb2_ep *ep = to_husb2_ep(_ep); + struct husb2_udc *udc = ep->udc; + unsigned long flags; + int ret = 0; + + DBG(DBG_GADGET, "endpoint %s: %s HALT\n", ep_name(ep), + value ? "set" : "clear"); + + if (!ep->desc) { + DBG(DBG_ERR, "Attempted to halt uninitialized ep %s\n", + ep_name(ep)); + return -ENODEV; + } + if (ep_is_isochronous(ep)) { + DBG(DBG_ERR, "Attempted to halt isochronous ep %s\n", + ep_name(ep)); + return -ENOTTY; + } + + spin_lock_irqsave(&udc->lock, flags); + + /* + * We can't halt IN endpoints while there are still data to be + * transferred + */ + if (!list_empty(&ep->queue) + || ((value && ep_is_in(ep) + && (husb2_ep_readl(ep, STA) + & HUSB2_BF(BUSY_BANKS, -1L))))) { + ret = -EAGAIN; + } else { + if (value) + husb2_ep_writel(ep, SET_STA, HUSB2_BIT(FORCE_STALL)); + else + husb2_ep_writel(ep, CLR_STA, (HUSB2_BIT(FORCE_STALL) + | HUSB2_BIT(TOGGLE_SEQ))); + husb2_ep_readl(ep, STA); + } + + spin_unlock_irqrestore(&udc->lock, flags); + + return ret; +} + +static int husb2_ep_fifo_status(struct usb_ep *_ep) +{ + struct husb2_ep *ep = to_husb2_ep(_ep); + + return HUSB2_BFEXT(BYTE_COUNT, husb2_ep_readl(ep, STA)); +} + +static void husb2_ep_fifo_flush(struct usb_ep *_ep) +{ + struct husb2_ep *ep = to_husb2_ep(_ep); + struct husb2_udc *udc = ep->udc; + + husb2_writel(udc, EPT_RST, 1 << ep->index); +} + +struct usb_ep_ops husb2_ep_ops = { + .enable = husb2_ep_enable, + .disable = husb2_ep_disable, + .alloc_request = husb2_ep_alloc_request, + .free_request = husb2_ep_free_request, + .alloc_buffer = husb2_ep_alloc_buffer, + .free_buffer = husb2_ep_free_buffer, + .queue = husb2_ep_queue, + .dequeue = husb2_ep_dequeue, + .set_halt = husb2_ep_set_halt, + .fifo_status = husb2_ep_fifo_status, + .fifo_flush = husb2_ep_fifo_flush, +}; + +static int husb2_udc_get_frame(struct usb_gadget *gadget) +{ + struct husb2_udc *udc = to_husb2_udc(gadget); + + return HUSB2_BFEXT(FRAME_NUMBER, husb2_readl(udc, FNUM)); +} + +struct usb_gadget_ops husb2_udc_ops = { + .get_frame = husb2_udc_get_frame, +}; + +#define EP(nam, type, idx, caps) { \ + .ep = { \ + .ops = &husb2_ep_ops, \ + .name = nam, \ + .maxpacket = type##_FIFO_SIZE, \ + }, \ + .udc = &the_udc, \ + .queue = LIST_HEAD_INIT(husb2_ep[idx].queue), \ + .fifo_size = type##_FIFO_SIZE, \ + .nr_banks = type##_NR_BANKS, \ + .index = idx, \ + .capabilities = caps, \ +} + +static struct husb2_ep husb2_ep[] = { + EP("ep0", EP0, 0, 0), + EP("ep1in-bulk", BULK, 1, HUSB2_EP_CAP_DMA), + EP("ep2out-bulk", BULK, 2, HUSB2_EP_CAP_DMA), + EP("ep3in-iso", ISO, 3, HUSB2_EP_CAP_DMA | HUSB2_EP_CAP_ISOC), + EP("ep4out-iso", ISO, 4, HUSB2_EP_CAP_DMA | HUSB2_EP_CAP_ISOC), + EP("ep5in-int", INT, 5, HUSB2_EP_CAP_DMA), + EP("ep6out-int", INT, 6, HUSB2_EP_CAP_DMA), +}; +#undef EP + +static struct usb_endpoint_descriptor husb2_ep0_desc = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 0, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, + .wMaxPacketSize = __constant_cpu_to_le16(64), + /* FIXME: I have no idea what to put here */ + .bInterval = 1, +}; + +static void nop_release(struct device *dev) +{ + +} + +static struct husb2_udc the_udc = { + .gadget = { + .ops = &husb2_udc_ops, + .ep0 = &husb2_ep[0].ep, + .ep_list = LIST_HEAD_INIT(the_udc.gadget.ep_list), + .is_dualspeed = 1, + .name = "husb2_udc", + .dev = { + .bus_id = "gadget", + .release = nop_release, + }, + }, + + .lock = SPIN_LOCK_UNLOCKED, +}; + +static void udc_enable(struct husb2_udc *udc) +{ + struct husb2_ep *ep0 = &husb2_ep[0]; + + /* Enable the controller */ + husb2_writel(udc, CTRL, HUSB2_BIT(EN_HUSB2)); + + /* Reset all endpoints and enable basic interrupts */ + husb2_writel(udc, EPT_RST, ~0UL); + husb2_writel(udc, INT_ENB, (HUSB2_BIT(DET_SUSPEND) + | HUSB2_BIT(END_OF_RESET) + | HUSB2_BIT(END_OF_RESUME))); + + /* Configure endpoint 0 */ + ep0->desc = &husb2_ep0_desc; + + husb2_writel(udc, EPT_RST, 1 << 0); + husb2_ep_writel(ep0, CTL_ENB, HUSB2_BIT(EPT_ENABLE)); + husb2_ep_writel(ep0, CFG, (HUSB2_BF(EPT_SIZE, EP0_EPT_SIZE) + | HUSB2_BF(EPT_TYPE, HUSB2_EPT_TYPE_CONTROL) + | HUSB2_BF(BK_NUMBER, HUSB2_BK_NUMBER_ONE))); + + husb2_ep_writel(ep0, CTL_ENB, HUSB2_BIT(RX_SETUP)); + husb2_writel(udc, INT_ENB, (husb2_readl(udc, INT_ENB) + | HUSB2_BF(EPT_INT, 1))); + + if (!(husb2_ep_readl(ep0, CFG) & HUSB2_BIT(EPT_MAPPED))) + dev_warn(&udc->pdev->dev, + "WARNING: EP0 configuration is invalid!\n"); +} + +static void udc_disable(struct husb2_udc *udc) +{ + udc->gadget.speed = USB_SPEED_UNKNOWN; + + husb2_writel(udc, CTRL, 0); +} + +/* + * Called with interrupts disabled and udc->lock held. + */ +static void reset_all_endpoints(struct husb2_udc *udc) +{ + struct husb2_ep *ep; + struct husb2_request *req, *tmp_req; + + husb2_writel(udc, EPT_RST, ~0UL); + + ep = to_husb2_ep(udc->gadget.ep0); + list_for_each_entry_safe(req, tmp_req, &ep->queue, queue) { + list_del_init(&req->queue); + request_complete(ep, req, -ECONNRESET); + } + BUG_ON(!list_empty(&ep->queue)); + + list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) { + if (ep->desc) + husb2_ep_disable(&ep->ep); + } +} + +static struct husb2_ep *get_ep_by_addr(struct husb2_udc *udc, u16 wIndex) +{ + struct husb2_ep *ep; + + if ((wIndex & USB_ENDPOINT_NUMBER_MASK) == 0) + return to_husb2_ep(udc->gadget.ep0); + + list_for_each_entry (ep, &udc->gadget.ep_list, ep.ep_list) { + u8 bEndpointAddress; + + if (!ep->desc) + continue; + bEndpointAddress = ep->desc->bEndpointAddress; + if ((wIndex ^ bEndpointAddress) & USB_DIR_IN) + continue; + if ((wIndex & USB_ENDPOINT_NUMBER_MASK) + == (bEndpointAddress & USB_ENDPOINT_NUMBER_MASK)) + return ep; + } + + return NULL; +} + +/* Called with interrupts disabled and udc->lock held */ +static inline void set_protocol_stall(struct husb2_udc *udc, + struct husb2_ep *ep) +{ + husb2_ep_writel(ep, SET_STA, HUSB2_BIT(FORCE_STALL)); + ep->state = WAIT_FOR_SETUP; +} + +static inline int is_stalled(struct husb2_udc *udc, struct husb2_ep *ep) +{ + if (husb2_ep_readl(ep, STA) & HUSB2_BIT(FORCE_STALL)) + return 1; + return 0; +} + +static inline void set_address(struct husb2_udc *udc, unsigned int addr) +{ + u32 regval; + + DBG(DBG_BUS, "setting address %u...\n", addr); + regval = husb2_readl(udc, CTRL); + regval = HUSB2_BFINS(DEV_ADDR, addr, regval); + husb2_writel(udc, CTRL, regval); +} + +static int handle_ep0_setup(struct husb2_udc *udc, struct husb2_ep *ep, + struct usb_ctrlrequest *crq) +{ + switch (crq->bRequest) { + case USB_REQ_GET_STATUS: { + u16 status; + + if (crq->bRequestType == (USB_DIR_IN | USB_RECIP_DEVICE)) { + /* Self-powered, no remote wakeup */ + status = __constant_cpu_to_le16(1 << 0); + } else if (crq->bRequestType + == (USB_DIR_IN | USB_RECIP_INTERFACE)) { + status = __constant_cpu_to_le16(0); + } else if (crq->bRequestType + == (USB_DIR_IN | USB_RECIP_ENDPOINT)) { + struct husb2_ep *target; + + target = get_ep_by_addr(udc, le16_to_cpu(crq->wIndex)); + if (!target) + goto stall; + + status = 0; + if (is_stalled(udc, target)) + status |= __constant_cpu_to_le16(1); + } else { + goto delegate; + } + + /* Write directly to the FIFO. No queueing is done. */ + if(crq->wLength != __constant_cpu_to_le16(sizeof(status))) + goto stall; + ep->state = DATA_STAGE_IN; + __raw_writew(status, ep->fifo); + husb2_ep_writel(ep, SET_STA, HUSB2_BIT(TX_PK_RDY)); + break; + } + + case USB_REQ_CLEAR_FEATURE: { + if (crq->bRequestType == USB_RECIP_DEVICE) { + /* We don't support TEST_MODE */ + goto stall; + } else if (crq->bRequestType == USB_RECIP_ENDPOINT) { + struct husb2_ep *target; + + if (crq->wValue != __constant_cpu_to_le16(USB_ENDPOINT_HALT) + || crq->wLength != __constant_cpu_to_le16(0)) + goto stall; + target = get_ep_by_addr(udc, le16_to_cpu(crq->wIndex)); + if (!target) + goto stall; + + husb2_ep_writel(target, CLR_STA, (HUSB2_BIT(FORCE_STALL) + | HUSB2_BIT(TOGGLE_SEQ))); + } else { + goto delegate; + } + + send_status(udc, ep); + break; + } + + case USB_REQ_SET_FEATURE: { + if (crq->bRequestType == USB_RECIP_DEVICE) { + /* We don't support TEST_MODE */ + goto stall; + } else if (crq->bRequestType == USB_RECIP_ENDPOINT) { + struct husb2_ep *target; + + if (crq->wValue != __constant_cpu_to_le16(USB_ENDPOINT_HALT) + || crq->wLength != __constant_cpu_to_le16(0)) + goto stall; + + target = get_ep_by_addr(udc, le16_to_cpu(crq->wIndex)); + if (!target) + goto stall; + + husb2_ep_writel(target, SET_STA, HUSB2_BIT(FORCE_STALL)); + } else + goto delegate; + + send_status(udc, ep); + break; + } + + case USB_REQ_SET_ADDRESS: + if (crq->bRequestType != (USB_DIR_OUT | USB_RECIP_DEVICE)) + goto delegate; + + set_address(udc, le16_to_cpu(crq->wValue)); + send_status(udc, ep); + ep->state = STATUS_STAGE_ADDR; + break; + + default: + delegate: + return udc->driver->setup(&udc->gadget, crq); + } + + return 0; + +stall: + printk(KERN_ERR + "udc: %s: Invalid setup request: %02x.%02x v%04x i%04x l%d, " + "halting endpoint...\n", + ep_name(ep), crq->bRequestType, crq->bRequest, + le16_to_cpu(crq->wValue), le16_to_cpu(crq->wIndex), + le16_to_cpu(crq->wLength)); + set_protocol_stall(udc, ep); + return -1; +} + +static void husb2_control_irq(struct husb2_udc *udc, struct husb2_ep *ep) +{ + struct husb2_request *req; + u32 epstatus; + u32 epctrl; + +restart: + epstatus = husb2_ep_readl(ep, STA); + epctrl = husb2_ep_readl(ep, CTL); + + DBG(DBG_INT, "%s: interrupt, status: 0x%08x\n", + ep_name(ep), epstatus); + + req = NULL; + if (!list_empty(&ep->queue)) + req = list_entry(ep->queue.next, + struct husb2_request, queue); + + if ((epctrl & HUSB2_BIT(TX_PK_RDY)) + && !(epstatus & HUSB2_BIT(TX_PK_RDY))) { + DBG(DBG_BUS, "tx pk rdy: %d\n", ep->state); + + if (req->submitted) + next_fifo_transaction(ep, req); + else + submit_request(ep, req); + + if (req->last_transaction) { + husb2_ep_writel(ep, CTL_DIS, HUSB2_BIT(TX_PK_RDY)); + husb2_ep_writel(ep, CTL_ENB, HUSB2_BIT(TX_COMPLETE)); + } + goto restart; + } + if ((epstatus & epctrl) & HUSB2_BIT(TX_COMPLETE)) { + husb2_ep_writel(ep, CLR_STA, HUSB2_BIT(TX_COMPLETE)); + DBG(DBG_BUS, "txc: %d\n", ep->state); + + switch (ep->state) { + case DATA_STAGE_IN: + husb2_ep_writel(ep, CTL_ENB, HUSB2_BIT(RX_BK_RDY)); + husb2_ep_writel(ep, CTL_DIS, + HUSB2_BIT(TX_COMPLETE)); + ep->state = STATUS_STAGE_OUT; + break; + case STATUS_STAGE_ADDR: + /* Activate our new address */ + husb2_writel(udc, CTRL, (husb2_readl(udc, CTRL) + | HUSB2_BIT(FADDR_EN))); + husb2_ep_writel(ep, CTL_DIS, + HUSB2_BIT(TX_COMPLETE)); + ep->state = WAIT_FOR_SETUP; + break; + case STATUS_STAGE_IN: + if (req) { + list_del_init(&req->queue); + request_complete(ep, req, 0); + submit_next_request(ep); + } + BUG_ON(!list_empty(&ep->queue)); + husb2_ep_writel(ep, CTL_DIS, + HUSB2_BIT(TX_COMPLETE)); + ep->state = WAIT_FOR_SETUP; + break; + default: + printk(KERN_ERR + "udc: %s: TXCOMP: Invalid endpoint state %d, " + "halting endpoint...\n", + ep_name(ep), ep->state); + set_protocol_stall(udc, ep); + break; + } + + goto restart; + } + if ((epstatus & epctrl) & HUSB2_BIT(RX_BK_RDY)) { + DBG(DBG_BUS, "rxc: %d\n", ep->state); + + switch (ep->state) { + case STATUS_STAGE_OUT: + husb2_ep_writel(ep, CLR_STA, HUSB2_BIT(RX_BK_RDY)); + + if (req) { + list_del_init(&req->queue); + request_complete(ep, req, 0); + } + husb2_ep_writel(ep, CTL_DIS, HUSB2_BIT(RX_BK_RDY)); + ep->state = WAIT_FOR_SETUP; + break; + + case DATA_STAGE_OUT: + receive_data(ep); + break; + + default: + husb2_ep_writel(ep, CLR_STA, HUSB2_BIT(RX_BK_RDY)); + set_protocol_stall(udc, ep); + printk(KERN_ERR + "udc: %s: RXRDY: Invalid endpoint state %d, " + "halting endpoint...\n", + ep_name(ep), ep->state); + break; + } + + goto restart; + } + if (epstatus & HUSB2_BIT(RX_SETUP)) { + union { + struct usb_ctrlrequest crq; + unsigned long data[2]; + } crq; + unsigned int pkt_len; + int ret; + + if (ep->state != WAIT_FOR_SETUP) { + /* + * Didn't expect a SETUP packet at this + * point. Clean up any pending requests (which + * may be successful). + */ + int status = -EPROTO; + + /* + * RXRDY is dropped when SETUP packets arrive. + * Just pretend we received the status packet. + */ + if (ep->state == STATUS_STAGE_OUT) + status = 0; + + if (req) { + list_del_init(&req->queue); + request_complete(ep, req, status); + } + BUG_ON(!list_empty(&ep->queue)); + } + + pkt_len = HUSB2_BFEXT(BYTE_COUNT, husb2_ep_readl(ep, STA)); + DBG(DBG_HW, "Packet length: %u\n", pkt_len); + BUG_ON(pkt_len != sizeof(crq)); + + DBG(DBG_FIFO, "Copying ctrl request from 0x%p:\n", ep->fifo); + copy_from_fifo(crq.data, ep->fifo, sizeof(crq)); + + /* Free up one bank in the FIFO so that we can + * generate or receive a reply right away. */ + husb2_ep_writel(ep, CLR_STA, HUSB2_BIT(RX_SETUP)); + + /* printk(KERN_DEBUG "setup: %d: %02x.%02x\n", + ep->state, crq.crq.bRequestType, + crq.crq.bRequest); */ + + if (crq.crq.bRequestType & USB_DIR_IN) { + /* + * The USB 2.0 spec states that "if wLength is + * zero, there is no data transfer phase." + * However, testusb #14 seems to actually + * expect a data phase even if wLength = 0... + */ + ep->state = DATA_STAGE_IN; + } else { + if (crq.crq.wLength != __constant_cpu_to_le16(0)) + ep->state = DATA_STAGE_OUT; + else + ep->state = STATUS_STAGE_IN; + } + + ret = -1; + if (ep->index == 0) + ret = handle_ep0_setup(udc, ep, &crq.crq); + else + ret = udc->driver->setup(&udc->gadget, &crq.crq); + + DBG(DBG_BUS, "req %02x.%02x, length %d, state %d, ret %d\n", + crq.crq.bRequestType, crq.crq.bRequest, + le16_to_cpu(crq.crq.wLength), ep->state, ret); + + if (ret < 0) { + /* Let the host know that we failed */ + set_protocol_stall(udc, ep); + } + } +} + +static void husb2_ep_irq(struct husb2_udc *udc, struct husb2_ep *ep) +{ + struct husb2_request *req; + u32 epstatus; + u32 epctrl; + + epstatus = husb2_ep_readl(ep, STA); + epctrl = husb2_ep_readl(ep, CTL); + + DBG(DBG_INT, "%s: interrupt, status: 0x%08x\n", + ep_name(ep), epstatus); + + while ((epctrl & HUSB2_BIT(TX_PK_RDY)) + && !(epstatus & HUSB2_BIT(TX_PK_RDY))) { + BUG_ON(!ep_is_in(ep)); + + DBG(DBG_BUS, "%s: TX PK ready\n", ep_name(ep)); + + if (list_empty(&ep->queue)) { + dev_warn(&udc->pdev->dev, "ep_irq: queue empty\n"); + husb2_ep_writel(ep, CTL_DIS, HUSB2_BIT(TX_PK_RDY)); + return; + } + + req = list_entry(ep->queue.next, struct husb2_request, queue); + + if (req->using_dma) { + BUG_ON(!req->send_zlp); + + /* Send a zero-length packet */ + husb2_ep_writel(ep, SET_STA, + HUSB2_BIT(TX_PK_RDY)); + husb2_ep_writel(ep, CTL_DIS, + HUSB2_BIT(TX_PK_RDY)); + list_del_init(&req->queue); + submit_next_request(ep); + request_complete(ep, req, 0); + } else { + if (req->submitted) + next_fifo_transaction(ep, req); + else + submit_request(ep, req); + + if (req->last_transaction) { + list_del_init(&req->queue); + submit_next_request(ep); + request_complete(ep, req, 0); + } + } + + epstatus = husb2_ep_readl(ep, STA); + epctrl = husb2_ep_readl(ep, CTL); + } + if ((epstatus & epctrl) & HUSB2_BIT(RX_BK_RDY)) { + BUG_ON(ep_is_in(ep)); + + DBG(DBG_BUS, "%s: RX data ready\n", ep_name(ep)); + receive_data(ep); + husb2_ep_writel(ep, CLR_STA, HUSB2_BIT(RX_BK_RDY)); + } +} + +static void husb2_dma_irq(struct husb2_udc *udc, struct husb2_ep *ep) +{ + struct husb2_request *req; + u32 status, control, pending; + + status = husb2_dma_readl(ep, STATUS); + control = husb2_dma_readl(ep, CONTROL); +#ifdef CONFIG_DEBUG_FS + ep->last_dma_status = status; +#endif + pending = status & control; + DBG(DBG_INT, "dma irq, status=%#08x, pending=%#08x, control=%#08x\n", + status, pending, control); + + BUG_ON(status & HUSB2_BIT(DMA_CH_EN)); + + if (list_empty(&ep->queue)) + /* Might happen if a reset comes along at the right moment */ + return; + + if (pending & (HUSB2_BIT(DMA_END_TR_ST) | HUSB2_BIT(DMA_END_BUF_ST))) { + req = list_entry(ep->queue.next, struct husb2_request, queue); + husb2_update_req(ep, req, status); + + if (req->send_zlp) { + husb2_ep_writel(ep, CTL_ENB, HUSB2_BIT(TX_PK_RDY)); + } else { + list_del_init(&req->queue); + submit_next_request(ep); + request_complete(ep, req, 0); + } + } +} + +static irqreturn_t husb2_udc_irq(int irq, void *devid) +{ + struct husb2_udc *udc = devid; + u32 status; + u32 dma_status; + u32 ep_status; + + spin_lock(&udc->lock); + + status = husb2_readl(udc, INT_STA); + DBG(DBG_INT, "irq, status=%#08x\n", status); + + if (status & HUSB2_BIT(DET_SUSPEND)) { + husb2_writel(udc, INT_CLR, HUSB2_BIT(DET_SUSPEND)); + //DBG(DBG_BUS, "Suspend detected\n"); + if (udc->gadget.speed != USB_SPEED_UNKNOWN + && udc->driver && udc->driver->suspend) + udc->driver->suspend(&udc->gadget); + } + + if (status & HUSB2_BIT(WAKE_UP)) { + husb2_writel(udc, INT_CLR, HUSB2_BIT(WAKE_UP)); + //DBG(DBG_BUS, "Wake Up CPU detected\n"); + } + + if (status & HUSB2_BIT(END_OF_RESUME)) { + husb2_writel(udc, INT_CLR, HUSB2_BIT(END_OF_RESUME)); + DBG(DBG_BUS, "Resume detected\n"); + if (udc->gadget.speed != USB_SPEED_UNKNOWN + && udc->driver && udc->driver->resume) + udc->driver->resume(&udc->gadget); + } + + dma_status = HUSB2_BFEXT(DMA_INT, status); + if (dma_status) { + int i; + + for (i = 1; i < HUSB2_NR_ENDPOINTS; i++) + if (dma_status & (1 << i)) + husb2_dma_irq(udc, &husb2_ep[i]); + } + + ep_status = HUSB2_BFEXT(EPT_INT, status); + if (ep_status) { + int i; + + for (i = 0; i < HUSB2_NR_ENDPOINTS; i++) + if (ep_status & (1 << i)) { + if (ep_is_control(&husb2_ep[i])) + husb2_control_irq(udc, &husb2_ep[i]); + else + husb2_ep_irq(udc, &husb2_ep[i]); + } + } + + if (status & HUSB2_BIT(END_OF_RESET)) { + husb2_writel(udc, INT_CLR, HUSB2_BIT(END_OF_RESET)); + if (status & HUSB2_BIT(HIGH_SPEED)) { + DBG(DBG_BUS, "High-speed bus reset detected\n"); + udc->gadget.speed = USB_SPEED_HIGH; + } else { + DBG(DBG_BUS, "Full-speed bus reset detected\n"); + udc->gadget.speed = USB_SPEED_FULL; + } + /* Better start from scratch... */ + reset_all_endpoints(udc); + husb2_ep[0].state = WAIT_FOR_SETUP; + udc_enable(udc); + } + + spin_unlock(&udc->lock); + + return IRQ_HANDLED; +} + +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + struct husb2_udc *udc = &the_udc; + int ret; + + spin_lock(&udc->lock); + + ret = -ENODEV; + if (!udc->pdev) + goto out; + ret = -EBUSY; + if (udc->driver) + goto out; + + udc->driver = driver; + udc->gadget.dev.driver = &driver->driver; + + device_add(&udc->gadget.dev); + ret = driver->bind(&udc->gadget); + if (ret) { + DBG(DBG_ERR, "Could not bind to driver %s: error %d\n", + driver->driver.name, ret); + device_del(&udc->gadget.dev); + + udc->driver = NULL; + udc->gadget.dev.driver = NULL; + goto out; + } + + /* TODO: Create sysfs files */ + + DBG(DBG_GADGET, "registered driver `%s'\n", driver->driver.name); + udc_enable(udc); + +out: + spin_unlock(&udc->lock); + return ret; +} +EXPORT_SYMBOL(usb_gadget_register_driver); + +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct husb2_udc *udc = &the_udc; + int ret; + + spin_lock(&udc->lock); + + ret = -ENODEV; + if (!udc->pdev) + goto out; + ret = -EINVAL; + if (driver != udc->driver) + goto out; + + local_irq_disable(); + udc_disable(udc); + local_irq_enable(); + + driver->unbind(&udc->gadget); + udc->driver = NULL; + + device_del(&udc->gadget.dev); + + /* TODO: Remove sysfs files */ + + DBG(DBG_GADGET, "unregistered driver `%s'\n", driver->driver.name); + +out: + spin_unlock(&udc->lock); + return ret; +} +EXPORT_SYMBOL(usb_gadget_unregister_driver); + +static int __devinit husb2_udc_probe(struct platform_device *pdev) +{ + struct resource *regs, *fifo; + struct clk *pclk, *hclk; + struct husb2_udc *udc = &the_udc; + int irq, ret, i; + + regs = platform_get_resource(pdev, IORESOURCE_MEM, CTRL_IOMEM_ID); + fifo = platform_get_resource(pdev, IORESOURCE_MEM, FIFO_IOMEM_ID); + if (!regs || !fifo) + return -ENXIO; + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + pclk = clk_get(&pdev->dev, "pclk"); + if (IS_ERR(pclk)) + return PTR_ERR(pclk); + hclk = clk_get(&pdev->dev, "hclk"); + if (IS_ERR(hclk)) { + ret = PTR_ERR(hclk); + goto out_put_pclk; + } + + clk_enable(pclk); + clk_enable(hclk); + + udc->pdev = pdev; + udc->pclk = pclk; + udc->hclk = hclk; + + ret = -ENOMEM; + udc->regs = ioremap(regs->start, regs->end - regs->start + 1); + if (!udc->regs) { + dev_err(&pdev->dev, "Unable to map I/O memory, aborting.\n"); + goto out_disable_clocks; + } + dev_info(&pdev->dev, "MMIO registers at 0x%08lx mapped at %p\n", + (unsigned long)regs->start, udc->regs); + udc->fifo = ioremap(fifo->start, fifo->end - fifo->start + 1); + if (!udc->fifo) { + dev_err(&pdev->dev, "Unable to map FIFO, aborting.\n"); + goto out_unmap_regs; + } + dev_info(&pdev->dev, "FIFO at 0x%08lx mapped at %p\n", + (unsigned long)fifo->start, udc->fifo); + + device_initialize(&udc->gadget.dev); + udc->gadget.dev.parent = &pdev->dev; + udc->gadget.dev.dma_mask = pdev->dev.dma_mask; + + /* The 3-word descriptors must be 4-word aligned... */ + udc->desc_pool = dma_pool_create("husb2-desc", &pdev->dev, + sizeof(struct husb2_dma_desc), + 16, 0); + if (!udc->desc_pool) { + dev_err(&pdev->dev, "Cannot create descriptor DMA pool\n"); + goto out_unmap_fifo; + } + + platform_set_drvdata(pdev, udc); + + udc_disable(udc); + + INIT_LIST_HEAD(&husb2_ep[0].ep.ep_list); + husb2_ep[0].ep_regs = udc->regs + HUSB2_EPT_BASE(0); + husb2_ep[0].dma_regs = udc->regs + HUSB2_DMA_BASE(0); + husb2_ep[0].fifo = udc->fifo + HUSB2_FIFO_BASE(0); + for (i = 1; i < ARRAY_SIZE(husb2_ep); i++) { + struct husb2_ep *ep = &husb2_ep[i]; + + ep->ep_regs = udc->regs + HUSB2_EPT_BASE(i); + ep->dma_regs = udc->regs + HUSB2_DMA_BASE(i); + ep->fifo = udc->fifo + HUSB2_FIFO_BASE(i); + + list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list); + } + + ret = request_irq(irq, husb2_udc_irq, SA_SAMPLE_RANDOM, + "husb2_udc", udc); + if (ret) { + dev_err(&pdev->dev, "Cannot request irq %d (error %d)\n", + irq, ret); + goto out_free_pool; + } + udc->irq = irq; + + husb2_init_debugfs(udc); + + return 0; + +out_free_pool: + dma_pool_destroy(udc->desc_pool); +out_unmap_fifo: + iounmap(udc->fifo); +out_unmap_regs: + iounmap(udc->regs); +out_disable_clocks: + clk_disable(hclk); + clk_disable(pclk); + clk_put(hclk); +out_put_pclk: + clk_put(pclk); + + platform_set_drvdata(pdev, NULL); + + return ret; +} + +static int __devexit husb2_udc_remove(struct platform_device *pdev) +{ + struct husb2_udc *udc; + + udc = platform_get_drvdata(pdev); + if (!udc) + return 0; + + husb2_cleanup_debugfs(udc); + + free_irq(udc->irq, udc); + dma_pool_destroy(udc->desc_pool); + iounmap(udc->fifo); + iounmap(udc->regs); + clk_disable(udc->hclk); + clk_disable(udc->pclk); + clk_put(udc->hclk); + clk_put(udc->pclk); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver udc_driver = { + .probe = husb2_udc_probe, + .remove = __devexit_p(husb2_udc_remove), + .driver = { + .name = "usba", + }, +}; + +static int __init udc_init(void) +{ + printk(KERN_INFO "husb2device: Driver version %s\n", 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("Atmel HUSB2 Device Controller driver"); +MODULE_AUTHOR("Haavard Skinnemoen <hskinnemoen@atmel.com>"); +MODULE_LICENSE("GPL"); diff -urN linux-2.6.20.4-0rig/drivers/usb/gadget/husb2_udc.h linux-2.6.20.4-atmel/drivers/usb/gadget/husb2_udc.h --- linux-2.6.20.4-0rig/drivers/usb/gadget/husb2_udc.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/usb/gadget/husb2_udc.h 2007-03-24 16:42:29.000000000 +0100 @@ -0,0 +1,406 @@ +/* + * Driver for the Atmel HUSB2device high speed USB device controller + * + * Copyright (C) 2005-2006 Atmel Corporation + * + * 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. + */ +#ifndef __LINUX_USB_GADGET_HUSB2_UDC_H__ +#define __LINUX_USB_GADGET_HUSB2_UDC_H__ + +/* USB register offsets */ +#define HUSB2_CTRL 0x0000 +#define HUSB2_FNUM 0x0004 +#define HUSB2_INT_ENB 0x0010 +#define HUSB2_INT_STA 0x0014 +#define HUSB2_INT_CLR 0x0018 +#define HUSB2_EPT_RST 0x001c +#define HUSB2_TST_SOF_CNT 0x00d0 +#define HUSB2_TST_CNT_A 0x00d4 +#define HUSB2_TST_CNT_B 0x00d8 +#define HUSB2_TST_MODE_REG 0x00dc +#define HUSB2_TST 0x00f0 + +/* USB endpoint register offsets */ +#define HUSB2_EPT_CFG 0x0000 +#define HUSB2_EPT_CTL_ENB 0x0004 +#define HUSB2_EPT_CTL_DIS 0x0008 +#define HUSB2_EPT_CTL 0x000c +#define HUSB2_EPT_SET_STA 0x0014 +#define HUSB2_EPT_CLR_STA 0x0018 +#define HUSB2_EPT_STA 0x001c + +/* USB DMA register offsets */ +#define HUSB2_DMA_NXT_DSC 0x0000 +#define HUSB2_DMA_ADDRESS 0x0004 +#define HUSB2_DMA_CONTROL 0x0008 +#define HUSB2_DMA_STATUS 0x000c + +/* Bitfields in CTRL */ +#define HUSB2_DEV_ADDR_OFFSET 0 +#define HUSB2_DEV_ADDR_SIZE 7 +#define HUSB2_FADDR_EN_OFFSET 7 +#define HUSB2_FADDR_EN_SIZE 1 +#define HUSB2_EN_HUSB2_OFFSET 8 +#define HUSB2_EN_HUSB2_SIZE 1 +#define HUSB2_DETACH_OFFSET 9 +#define HUSB2_DETACH_SIZE 1 +#define HUSB2_REMOTE_WAKE_UP_OFFSET 10 +#define HUSB2_REMOTE_WAKE_UP_SIZE 1 + +/* Bitfields in FNUM */ +#define HUSB2_MICRO_FRAME_NUM_OFFSET 0 +#define HUSB2_MICRO_FRAME_NUM_SIZE 3 +#define HUSB2_FRAME_NUMBER_OFFSET 3 +#define HUSB2_FRAME_NUMBER_SIZE 11 +#define HUSB2_FRAME_NUM_ERROR_OFFSET 31 +#define HUSB2_FRAME_NUM_ERROR_SIZE 1 + +/* Bitfields in INT_ENB/INT_STA/INT_CLR */ +#define HUSB2_HIGH_SPEED_OFFSET 0 +#define HUSB2_HIGH_SPEED_SIZE 1 +#define HUSB2_DET_SUSPEND_OFFSET 1 +#define HUSB2_DET_SUSPEND_SIZE 1 +#define HUSB2_MICRO_SOF_OFFSET 2 +#define HUSB2_MICRO_SOF_SIZE 1 +#define HUSB2_SOF_OFFSET 3 +#define HUSB2_SOF_SIZE 1 +#define HUSB2_END_OF_RESET_OFFSET 4 +#define HUSB2_END_OF_RESET_SIZE 1 +#define HUSB2_WAKE_UP_OFFSET 5 +#define HUSB2_WAKE_UP_SIZE 1 +#define HUSB2_END_OF_RESUME_OFFSET 6 +#define HUSB2_END_OF_RESUME_SIZE 1 +#define HUSB2_UPSTREAM_RESUME_OFFSET 7 +#define HUSB2_UPSTREAM_RESUME_SIZE 1 +#define HUSB2_EPT_INT_OFFSET 8 +#define HUSB2_EPT_INT_SIZE 16 +#define HUSB2_DMA_INT_OFFSET 24 +#define HUSB2_DMA_INT_SIZE 8 + +/* Bitfields in EPT_RST */ +#define HUSB2_RST_OFFSET 0 +#define HUSB2_RST_SIZE 16 + +/* Bitfields in TST_SOF_CNT */ +#define HUSB2_SOF_CNT_MAX_OFFSET 0 +#define HUSB2_SOF_CNT_MAX_SIZE 7 +#define HUSB2_SOF_CNT_LOAD_OFFSET 7 +#define HUSB2_SOF_CNT_LOAD_SIZE 1 + +/* Bitfields in TST_CNT_A */ +#define HUSB2_CNT_A_MAX_OFFSET 0 +#define HUSB2_CNT_A_MAX_SIZE 7 +#define HUSB2_CNT_A_LOAD_OFFSET 7 +#define HUSB2_CNT_A_LOAD_SIZE 1 + +/* Bitfields in TST_CNT_B */ +#define HUSB2_CNT_B_MAX_OFFSET 0 +#define HUSB2_CNT_B_MAX_SIZE 7 +#define HUSB2_CNT_B_LOAD_OFFSET 7 +#define HUSB2_CNT_B_LOAD_SIZE 1 + +/* Bitfields in TST_MODE_REG */ +#define HUSB2_TST_MODE_OFFSET 0 +#define HUSB2_TST_MODE_SIZE 6 + +/* Bitfields in HUSB2_TST */ +#define HUSB2_SPEED_CFG_OFFSET 0 +#define HUSB2_SPEED_CFG_SIZE 2 +#define HUSB2_TST_J_MODE_OFFSET 2 +#define HUSB2_TST_J_MODE_SIZE 1 +#define HUSB2_TST_K_MODE_OFFSET 3 +#define HUSB2_TST_K_MODE_SIZE 1 +#define HUSB2_TST_PKT_MODE_OFFSE 4 +#define HUSB2_TST_PKT_MODE_SIZE 1 +#define HUSB2_OPMODE2_OFFSET 5 +#define HUSB2_OPMODE2_SIZE 1 + +/* Bitfields in EPT_CFG */ +#define HUSB2_EPT_SIZE_OFFSET 0 +#define HUSB2_EPT_SIZE_SIZE 3 +#define HUSB2_EPT_DIR_OFFSET 3 +#define HUSB2_EPT_DIR_SIZE 1 +#define HUSB2_EPT_TYPE_OFFSET 4 +#define HUSB2_EPT_TYPE_SIZE 2 +#define HUSB2_BK_NUMBER_OFFSET 6 +#define HUSB2_BK_NUMBER_SIZE 2 +#define HUSB2_NB_TRANS_OFFSET 8 +#define HUSB2_NB_TRANS_SIZE 2 +#define HUSB2_EPT_MAPPED_OFFSET 31 +#define HUSB2_EPT_MAPPED_SIZE 1 + +/* Bitfields in EPT_CTL/EPT_CTL_ENB/EPT_CTL_DIS */ +#define HUSB2_EPT_ENABLE_OFFSET 0 +#define HUSB2_EPT_ENABLE_SIZE 1 +#define HUSB2_AUTO_VALID_OFFSET 1 +#define HUSB2_AUTO_VALID_SIZE 1 +#define HUSB2_INT_DIS_DMA_OFFSET 3 +#define HUSB2_INT_DIS_DMA_SIZE 1 +#define HUSB2_NYET_DIS_OFFSET 4 +#define HUSB2_NYET_DIS_SIZE 1 +#define HUSB2_DATAX_RX_OFFSET 6 +#define HUSB2_DATAX_RX_SIZE 1 +#define HUSB2_MDATA_RX_OFFSET 7 +#define HUSB2_MDATA_RX_SIZE 1 +/* Bits 8-15 and 31 enable interrupts for respective bits in EPT_STA */ +#define HUSB2_BUSY_BANK_IE_OFFSET 18 +#define HUSB2_BUSY_BANK_IE_SIZE 1 + +/* Bitfields in EPT_SET_STA/EPT_CLR_STA/EPT_STA */ +#define HUSB2_FORCE_STALL_OFFSET 5 +#define HUSB2_FORCE_STALL_SIZE 1 +#define HUSB2_TOGGLE_SEQ_OFFSET 6 +#define HUSB2_TOGGLE_SEQ_SIZE 2 +#define HUSB2_ERR_OVFLW_OFFSET 8 +#define HUSB2_ERR_OVFLW_SIZE 1 +#define HUSB2_RX_BK_RDY_OFFSET 9 +#define HUSB2_RX_BK_RDY_SIZE 1 +#define HUSB2_KILL_BANK_OFFSET 9 +#define HUSB2_KILL_BANK_SIZE 1 +#define HUSB2_TX_COMPLETE_OFFSET 10 +#define HUSB2_TX_COMPLETE_SIZE 1 +#define HUSB2_TX_PK_RDY_OFFSET 11 +#define HUSB2_TX_PK_RDY_SIZE 1 +#define HUSB2_ISO_ERR_TRANS_OFFSET 11 +#define HUSB2_ISO_ERR_TRANS_SIZE 1 +#define HUSB2_RX_SETUP_OFFSET 12 +#define HUSB2_RX_SETUP_SIZE 1 +#define HUSB2_ISO_ERR_FLOW_OFFSET 12 +#define HUSB2_ISO_ERR_FLOW_SIZE 1 +#define HUSB2_STALL_SENT_OFFSET 13 +#define HUSB2_STALL_SENT_SIZE 1 +#define HUSB2_ISO_ERR_CRC_OFFSET 13 +#define HUSB2_ISO_ERR_CRC_SIZE 1 +#define HUSB2_ISO_ERR_NBTRANS_OFFSET 13 +#define HUSB2_ISO_ERR_NBTRANS_SIZE 1 +#define HUSB2_NAK_IN_OFFSET 14 +#define HUSB2_NAK_IN_SIZE 1 +#define HUSB2_ISO_ERR_FLUSH_OFFSET 14 +#define HUSB2_ISO_ERR_FLUSH_SIZE 1 +#define HUSB2_NAK_OUT_OFFSET 15 +#define HUSB2_NAK_OUT_SIZE 1 +#define HUSB2_CURRENT_BANK_OFFSET 16 +#define HUSB2_CURRENT_BANK_SIZE 2 +#define HUSB2_BUSY_BANKS_OFFSET 18 +#define HUSB2_BUSY_BANKS_SIZE 2 +#define HUSB2_BYTE_COUNT_OFFSET 20 +#define HUSB2_BYTE_COUNT_SIZE 11 +#define HUSB2_SHORT_PACKET_OFFSET 31 +#define HUSB2_SHORT_PACKET_SIZE 1 + +/* Bitfields in DMA_CONTROL */ +#define HUSB2_DMA_CH_EN_OFFSET 0 +#define HUSB2_DMA_CH_EN_SIZE 1 +#define HUSB2_DMA_LINK_OFFSET 1 +#define HUSB2_DMA_LINK_SIZE 1 +#define HUSB2_DMA_END_TR_EN_OFFSET 2 +#define HUSB2_DMA_END_TR_EN_SIZE 1 +#define HUSB2_DMA_END_BUF_EN_OFFSET 3 +#define HUSB2_DMA_END_BUF_EN_SIZE 1 +#define HUSB2_DMA_END_TR_IE_OFFSET 4 +#define HUSB2_DMA_END_TR_IE_SIZE 1 +#define HUSB2_DMA_END_BUF_IE_OFFSET 5 +#define HUSB2_DMA_END_BUF_IE_SIZE 1 +#define HUSB2_DMA_DESC_LOAD_IE_OFFSET 6 +#define HUSB2_DMA_DESC_LOAD_IE_SIZE 1 +#define HUSB2_DMA_BURST_LOCK_OFFSET 7 +#define HUSB2_DMA_BURST_LOCK_SIZE 1 +#define HUSB2_DMA_BUF_LEN_OFFSET 16 +#define HUSB2_DMA_BUF_LEN_SIZE 16 + +/* Bitfields in DMA_STATUS */ +#define HUSB2_DMA_CH_ACTIVE_OFFSET 1 +#define HUSB2_DMA_CH_ACTIVE_SIZE 1 +#define HUSB2_DMA_END_TR_ST_OFFSET 4 +#define HUSB2_DMA_END_TR_ST_SIZE 1 +#define HUSB2_DMA_END_BUF_ST_OFFSET 5 +#define HUSB2_DMA_END_BUF_ST_SIZE 1 +#define HUSB2_DMA_DESC_LOAD_ST_OFFSET 6 +#define HUSB2_DMA_DESC_LOAD_ST_SIZE 1 + +/* Constants for SPEED_CFG */ +#define HUSB2_SPEED_CFG_NORMAL 0 +#define HUSB2_SPEED_CFG_FORCE_HIGH 2 +#define HUSB2_SPEED_CFG_FORCE_FULL 3 + +/* Constants for EPT_SIZE */ +#define HUSB2_EPT_SIZE_8 0 +#define HUSB2_EPT_SIZE_16 1 +#define HUSB2_EPT_SIZE_32 2 +#define HUSB2_EPT_SIZE_64 3 +#define HUSB2_EPT_SIZE_128 4 +#define HUSB2_EPT_SIZE_256 5 +#define HUSB2_EPT_SIZE_512 6 +#define HUSB2_EPT_SIZE_1024 7 + +/* Constants for EPT_TYPE */ +#define HUSB2_EPT_TYPE_CONTROL 0 +#define HUSB2_EPT_TYPE_ISO 1 +#define HUSB2_EPT_TYPE_BULK 2 +#define HUSB2_EPT_TYPE_INT 3 + +/* Constants for BK_NUMBER */ +#define HUSB2_BK_NUMBER_ZERO 0 +#define HUSB2_BK_NUMBER_ONE 1 +#define HUSB2_BK_NUMBER_DOUBLE 2 +#define HUSB2_BK_NUMBER_TRIPLE 3 + +/* Bit manipulation macros */ +#define HUSB2_BIT(name) \ + (1 << HUSB2_##name##_OFFSET) +#define HUSB2_BF(name,value) \ + (((value) & ((1 << HUSB2_##name##_SIZE) - 1)) \ + << HUSB2_##name##_OFFSET) +#define HUSB2_BFEXT(name,value) \ + (((value) >> HUSB2_##name##_OFFSET) \ + & ((1 << HUSB2_##name##_SIZE) - 1)) +#define HUSB2_BFINS(name,value,old) \ + (((old) & ~(((1 << HUSB2_##name##_SIZE) - 1) \ + << HUSB2_##name##_OFFSET)) \ + | HUSB2_BF(name,value)) + +/* Register access macros */ +#define husb2_readl(udc,reg) \ + __raw_readl((udc)->regs + HUSB2_##reg) +#define husb2_writel(udc,reg,value) \ + __raw_writel((value), (udc)->regs + HUSB2_##reg) +#define husb2_ep_readl(ep,reg) \ + __raw_readl((ep)->ep_regs + HUSB2_EPT_##reg) +#define husb2_ep_writel(ep,reg,value) \ + __raw_writel((value), (ep)->ep_regs + HUSB2_EPT_##reg) +#define husb2_dma_readl(ep,reg) \ + __raw_readl((ep)->dma_regs + HUSB2_DMA_##reg) +#define husb2_dma_writel(ep,reg,value) \ + __raw_writel((value), (ep)->dma_regs + HUSB2_DMA_##reg) + +/* Calculate base address for a given endpoint or DMA controller */ +#define HUSB2_EPT_BASE(x) (0x100 + (x) * 0x20) +#define HUSB2_DMA_BASE(x) (0x300 + (x) * 0x10) +#define HUSB2_FIFO_BASE(x) ((x) << 16) + +/* Synth parameters */ +#define HUSB2_NR_ENDPOINTS 7 + +#define EP0_FIFO_SIZE 64 +#define EP0_EPT_SIZE HUSB2_EPT_SIZE_64 +#define EP0_NR_BANKS 1 +#define BULK_FIFO_SIZE 512 +#define BULK_EPT_SIZE HUSB2_EPT_SIZE_512 +#define BULK_NR_BANKS 2 +#define ISO_FIFO_SIZE 1024 +#define ISO_EPT_SIZE HUSB2_EPT_SIZE_1024 +#define ISO_NR_BANKS 3 +#define INT_FIFO_SIZE 64 +#define INT_EPT_SIZE HUSB2_EPT_SIZE_64 +#define INT_NR_BANKS 3 + +enum husb2_ctrl_state { + WAIT_FOR_SETUP, + DATA_STAGE_IN, + DATA_STAGE_OUT, + STATUS_STAGE_IN, + STATUS_STAGE_OUT, + STATUS_STAGE_ADDR, +}; +/* + EP_STATE_IDLE, + EP_STATE_SETUP, + EP_STATE_IN_DATA, + EP_STATE_OUT_DATA, + EP_STATE_SET_ADDR_STATUS, + EP_STATE_RX_STATUS, + EP_STATE_TX_STATUS, + EP_STATE_HALT, +*/ + +struct husb2_dma_desc { + dma_addr_t next; + dma_addr_t addr; + u32 ctrl; +}; + +struct husb2_ep { + int state; + void __iomem *ep_regs; + void __iomem *dma_regs; + void __iomem *fifo; + struct usb_ep ep; + struct husb2_udc *udc; + + struct list_head queue; + const struct usb_endpoint_descriptor *desc; + + u16 fifo_size; + u8 nr_banks; + u8 index; + u8 capabilities; + +#ifdef CONFIG_DEBUG_FS + u32 last_dma_status; + struct dentry *debugfs_dir; + struct dentry *debugfs_queue; + struct dentry *debugfs_dma_status; +#endif +}; +#define HUSB2_EP_CAP_ISOC 0x0001 +#define HUSB2_EP_CAP_DMA 0x0002 + +struct husb2_packet { + struct husb2_dma_desc *desc; + dma_addr_t desc_dma; +}; + +struct husb2_request { + struct usb_request req; + struct list_head queue; + + struct husb2_packet *packet; + unsigned int nr_pkts; + + unsigned int submitted:1; + unsigned int using_dma:1; + unsigned int last_transaction:1; + unsigned int send_zlp:1; +}; + +struct husb2_udc { + spinlock_t lock; + + void __iomem *regs; + void __iomem *fifo; + + struct dma_pool *desc_pool; + + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + struct platform_device *pdev; + int irq; + struct clk *pclk; + struct clk *hclk; + +#ifdef CONFIG_DEBUG_FS + struct dentry *debugfs_root; + struct dentry *debugfs_regs; +#endif +}; + +#define to_husb2_ep(x) container_of((x), struct husb2_ep, ep) +#define to_husb2_req(x) container_of((x), struct husb2_request, req) +#define to_husb2_udc(x) container_of((x), struct husb2_udc, gadget) + +#define ep_index(ep) ((ep)->index) +#define ep_can_dma(ep) ((ep)->capabilities & HUSB2_EP_CAP_DMA) +#define ep_is_in(ep) (((ep)->desc->bEndpointAddress \ + & USB_ENDPOINT_DIR_MASK) \ + == USB_DIR_IN) +#define ep_is_isochronous(ep) \ + (((ep)->desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) \ + == USB_ENDPOINT_XFER_ISOC) +#define ep_is_control(ep) (ep_index(ep) == 0) +#define ep_name(ep) ((ep)->ep.name) +#define ep_is_idle(ep) ((ep)->state == EP_STATE_IDLE) + +#endif /* __LINUX_USB_GADGET_HUSB2_H */ diff -urN linux-2.6.20.4-0rig/drivers/usb/gadget/Kconfig linux-2.6.20.4-atmel/drivers/usb/gadget/Kconfig --- linux-2.6.20.4-0rig/drivers/usb/gadget/Kconfig 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/usb/gadget/Kconfig 2007-03-24 16:42:29.000000000 +0100 @@ -154,6 +154,16 @@ default USB_GADGET select USB_GADGET_SELECTED +config USB_GADGET_HUSB2DEV + boolean "Atmel HUSB2DEVICE" + select USB_GADGET_DUALSPEED + depends on AVR32 + +config USB_HUSB2DEV + tristate + depends on USB_GADGET_HUSB2DEV + default USB_GADGET + select USB_GADGET_SELECTED config USB_GADGET_OMAP boolean "OMAP USB Device Controller" diff -urN linux-2.6.20.4-0rig/drivers/usb/gadget/Makefile linux-2.6.20.4-atmel/drivers/usb/gadget/Makefile --- linux-2.6.20.4-0rig/drivers/usb/gadget/Makefile 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/usb/gadget/Makefile 2007-03-24 16:42:29.000000000 +0100 @@ -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_HUSB2DEV) += husb2_udc.o # # USB gadget drivers diff -urN linux-2.6.20.4-0rig/drivers/usb/gadget/serial.c linux-2.6.20.4-atmel/drivers/usb/gadget/serial.c --- linux-2.6.20.4-0rig/drivers/usb/gadget/serial.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/usb/gadget/serial.c 2007-03-24 16:42:28.000000000 +0100 @@ -333,7 +333,7 @@ .strings = gs_strings, }; -static struct usb_device_descriptor gs_device_desc = { +static struct usb_device_descriptor __attribute__((aligned(2))) gs_device_desc = { .bLength = USB_DT_DEVICE_SIZE, .bDescriptorType = USB_DT_DEVICE, .bcdUSB = __constant_cpu_to_le16(0x0200), @@ -353,7 +353,7 @@ .bmAttributes = USB_OTG_SRP, }; -static struct usb_config_descriptor gs_bulk_config_desc = { +static struct usb_config_descriptor __attribute__((aligned(2))) gs_bulk_config_desc = { .bLength = USB_DT_CONFIG_SIZE, .bDescriptorType = USB_DT_CONFIG, /* .wTotalLength computed dynamically */ @@ -364,7 +364,7 @@ .bMaxPower = 1, }; -static struct usb_config_descriptor gs_acm_config_desc = { +static struct usb_config_descriptor __attribute__((aligned(2))) gs_acm_config_desc = { .bLength = USB_DT_CONFIG_SIZE, .bDescriptorType = USB_DT_CONFIG, /* .wTotalLength computed dynamically */ @@ -375,7 +375,7 @@ .bMaxPower = 1, }; -static const struct usb_interface_descriptor gs_bulk_interface_desc = { +static const struct usb_interface_descriptor __attribute__((aligned(2))) gs_bulk_interface_desc = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = GS_BULK_INTERFACE_ID, @@ -386,7 +386,7 @@ .iInterface = GS_DATA_STR_ID, }; -static const struct usb_interface_descriptor gs_control_interface_desc = { +static const struct usb_interface_descriptor __attribute__((aligned(2))) gs_control_interface_desc = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = GS_CONTROL_INTERFACE_ID, @@ -397,7 +397,7 @@ .iInterface = GS_CONTROL_STR_ID, }; -static const struct usb_interface_descriptor gs_data_interface_desc = { +static const struct usb_interface_descriptor __attribute__((aligned(2))) gs_data_interface_desc = { .bLength = USB_DT_INTERFACE_SIZE, .bDescriptorType = USB_DT_INTERFACE, .bInterfaceNumber = GS_DATA_INTERFACE_ID, @@ -408,7 +408,7 @@ .iInterface = GS_DATA_STR_ID, }; -static const struct usb_cdc_header_desc gs_header_desc = { +static const struct usb_cdc_header_desc __attribute__((aligned(2))) gs_header_desc = { .bLength = sizeof(gs_header_desc), .bDescriptorType = USB_DT_CS_INTERFACE, .bDescriptorSubType = USB_CDC_HEADER_TYPE, @@ -438,7 +438,7 @@ .bSlaveInterface0 = 1, /* index of data interface */ }; -static struct usb_endpoint_descriptor gs_fullspeed_notify_desc = { +static struct usb_endpoint_descriptor __attribute__((aligned(2))) gs_fullspeed_notify_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, @@ -447,14 +447,14 @@ .bInterval = 1 << GS_LOG2_NOTIFY_INTERVAL, }; -static struct usb_endpoint_descriptor gs_fullspeed_in_desc = { +static struct usb_endpoint_descriptor __attribute__((aligned(2))) gs_fullspeed_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 gs_fullspeed_out_desc = { +static struct usb_endpoint_descriptor __attribute__((aligned(2))) gs_fullspeed_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_OUT, @@ -484,7 +484,7 @@ }; #ifdef CONFIG_USB_GADGET_DUALSPEED -static struct usb_endpoint_descriptor gs_highspeed_notify_desc = { +static struct usb_endpoint_descriptor __attribute__((aligned(2))) gs_highspeed_notify_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = USB_DIR_IN, @@ -493,21 +493,21 @@ .bInterval = GS_LOG2_NOTIFY_INTERVAL+4, }; -static struct usb_endpoint_descriptor gs_highspeed_in_desc = { +static struct usb_endpoint_descriptor __attribute__((aligned(2))) gs_highspeed_in_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = __constant_cpu_to_le16(512), }; -static struct usb_endpoint_descriptor gs_highspeed_out_desc = { +static struct usb_endpoint_descriptor __attribute__((aligned(2))) gs_highspeed_out_desc = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bmAttributes = USB_ENDPOINT_XFER_BULK, .wMaxPacketSize = __constant_cpu_to_le16(512), }; -static struct usb_qualifier_descriptor gs_qualifier_desc = { +static struct usb_qualifier_descriptor __attribute__((aligned(2))) gs_qualifier_desc = { .bLength = sizeof(struct usb_qualifier_descriptor), .bDescriptorType = USB_DT_DEVICE_QUALIFIER, .bcdUSB = __constant_cpu_to_le16 (0x0200), diff -urN linux-2.6.20.4-0rig/drivers/usb/gadget/zero.c linux-2.6.20.4-atmel/drivers/usb/gadget/zero.c --- linux-2.6.20.4-0rig/drivers/usb/gadget/zero.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/usb/gadget/zero.c 2007-03-24 16:42:28.000000000 +0100 @@ -221,7 +221,7 @@ #define CONFIG_LOOPBACK 2 static struct usb_device_descriptor -device_desc = { +device_desc __attribute__((aligned(2))) = { .bLength = sizeof device_desc, .bDescriptorType = USB_DT_DEVICE, @@ -237,7 +237,7 @@ }; static struct usb_config_descriptor -source_sink_config = { +source_sink_config __attribute__((aligned(2))) = { .bLength = sizeof source_sink_config, .bDescriptorType = USB_DT_CONFIG, @@ -250,7 +250,7 @@ }; static struct usb_config_descriptor -loopback_config = { +loopback_config __attribute__((aligned(2))) = { .bLength = sizeof loopback_config, .bDescriptorType = USB_DT_CONFIG, @@ -273,7 +273,7 @@ /* one interface in each configuration */ static const struct usb_interface_descriptor -source_sink_intf = { +source_sink_intf __attribute__((aligned(2))) = { .bLength = sizeof source_sink_intf, .bDescriptorType = USB_DT_INTERFACE, @@ -283,7 +283,7 @@ }; static const struct usb_interface_descriptor -loopback_intf = { +loopback_intf __attribute__((aligned(2))) = { .bLength = sizeof loopback_intf, .bDescriptorType = USB_DT_INTERFACE, @@ -295,7 +295,7 @@ /* two full speed bulk endpoints; their use is config-dependent */ static struct usb_endpoint_descriptor -fs_source_desc = { +fs_source_desc __attribute__((aligned(2))) = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -304,7 +304,7 @@ }; static struct usb_endpoint_descriptor -fs_sink_desc = { +fs_sink_desc __attribute__((aligned(2))) = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -340,7 +340,7 @@ */ static struct usb_endpoint_descriptor -hs_source_desc = { +hs_source_desc __attribute__((aligned(2))) = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -349,7 +349,7 @@ }; static struct usb_endpoint_descriptor -hs_sink_desc = { +hs_sink_desc __attribute__((aligned(2))) = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, @@ -358,7 +358,7 @@ }; static struct usb_qualifier_descriptor -dev_qualifier = { +dev_qualifier __attribute__((aligned(2))) = { .bLength = sizeof dev_qualifier, .bDescriptorType = USB_DT_DEVICE_QUALIFIER, diff -urN linux-2.6.20.4-0rig/drivers/usb/host/ohci-at91.c linux-2.6.20.4-atmel/drivers/usb/host/ohci-at91.c --- linux-2.6.20.4-0rig/drivers/usb/host/ohci-at91.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/usb/host/ohci-at91.c 2007-03-24 16:39:15.000000000 +0100 @@ -18,19 +18,38 @@ #include <asm/mach-types.h> #include <asm/hardware.h> #include <asm/arch/board.h> +#include <asm/arch/cpu.h> #ifndef CONFIG_ARCH_AT91 #error "CONFIG_ARCH_AT91 must be defined." #endif /* interface and function clocks */ -static struct clk *iclk, *fclk; +static struct clk *iclk, *fclk, *hclock; static int clocked; extern int usb_disabled(void); /*-------------------------------------------------------------------------*/ +static void at91_start_clock(void) +{ + if (cpu_is_at91sam9261()) + clk_enable(hclock); + clk_enable(iclk); + clk_enable(fclk); + clocked = 1; +} + +static void at91_stop_clock(void) +{ + clk_disable(fclk); + clk_disable(iclk); + if (cpu_is_at91sam9261()) + clk_disable(hclock); + clocked = 0; +} + static void at91_start_hc(struct platform_device *pdev) { struct usb_hcd *hcd = platform_get_drvdata(pdev); @@ -41,9 +60,7 @@ /* * Start the USB clocks. */ - clk_enable(iclk); - clk_enable(fclk); - clocked = 1; + at91_start_clock(); /* * The USB host controller must remain in reset. @@ -66,9 +83,7 @@ /* * Stop the USB clocks. */ - clk_disable(fclk); - clk_disable(iclk); - clocked = 0; + at91_stop_clock(); } @@ -126,6 +141,8 @@ iclk = clk_get(&pdev->dev, "ohci_clk"); fclk = clk_get(&pdev->dev, "uhpck"); + if (cpu_is_at91sam9261()) + hclock = clk_get(&pdev->dev, "hck0"); at91_start_hc(pdev); ohci_hcd_init(hcd_to_ohci(hcd)); @@ -137,6 +154,8 @@ /* Error handling */ at91_stop_hc(pdev); + if (cpu_is_at91sam9261()) + clk_put(hclock); clk_put(fclk); clk_put(iclk); @@ -170,11 +189,12 @@ at91_stop_hc(pdev); iounmap(hcd->regs); release_mem_region(hcd->rsrc_start, hcd->rsrc_len); - disable_irq_wake(hcd->irq); + if (cpu_is_at91sam9261()) + clk_put(hclock); clk_put(fclk); clk_put(iclk); - fclk = iclk = NULL; + fclk = iclk = hclock = NULL; dev_set_drvdata(&pdev->dev, NULL); return 0; @@ -271,8 +291,6 @@ if (device_may_wakeup(&pdev->dev)) enable_irq_wake(hcd->irq); - else - disable_irq_wake(hcd->irq); /* * The integrated transceivers seem unable to notice disconnect, @@ -283,9 +301,7 @@ */ if (at91_suspend_entering_slow_clock()) { ohci_usb_reset (ohci); - clk_disable(fclk); - clk_disable(iclk); - clocked = 0; + at91_stop_clock(); } return 0; @@ -293,11 +309,13 @@ static int ohci_hcd_at91_drv_resume(struct platform_device *pdev) { - if (!clocked) { - clk_enable(iclk); - clk_enable(fclk); - clocked = 1; - } + struct usb_hcd *hcd = platform_get_drvdata(pdev); + + if (device_may_wakeup(&pdev->dev)) + disable_irq_wake(hcd->irq); + + if (!clocked) + at91_start_clock(); return 0; } diff -urN linux-2.6.20.4-0rig/drivers/video/atmel_lcdfb.c linux-2.6.20.4-atmel/drivers/video/atmel_lcdfb.c --- linux-2.6.20.4-0rig/drivers/video/atmel_lcdfb.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/video/atmel_lcdfb.c 2007-03-24 16:39:15.000000000 +0100 @@ -0,0 +1,781 @@ +/* + * drivers/video/atmel_lcdfb.c + * + * Driver for AT91/AVR32 LCD Controller + * + * Copyright (C) 2007 Atmel 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 + */ + +#include <linux/kernel.h> +#include <linux/platform_device.h> +#include <linux/dma-mapping.h> +#include <linux/interrupt.h> +#include <linux/clk.h> +#include <linux/fb.h> +#include <linux/init.h> +#include <linux/delay.h> + +#include <asm/arch/board.h> +#include <asm/arch/cpu.h> +#include <asm/arch/gpio.h> + +#include <video/atmel_lcdc.h> + +#define lcdc_readl(sinfo, reg) __raw_readl((sinfo)->mmio+(reg)) +#define lcdc_writel(sinfo, reg, val) __raw_writel((val), (sinfo)->mmio+(reg)) + +/* More or less configurable parameters */ +#define ATMEL_LCDC_FIFO_SIZE 512 +#define ATMEL_LCDC_CRST_VAL_DEFAULT 0xc8 +#define ATMEL_LCDC_DMA_BURST_LEN 16 + +#define LCD_POWER_ON 0 +#define LCD_POWER_OFF 1 + +#if defined(CONFIG_ARCH_AT91) +static inline void atmel_lcdfb_update_dma2d(struct fb_info *info, + struct fb_var_screeninfo *var) { } +static inline void atmel_lcdfb_set_2dcfg(struct fb_info *info) { } +#elif defined(CONFIG_AVR32) +static void atmel_lcdfb_update_dma2d(struct fb_info *info, + struct fb_var_screeninfo *var) +{ + dma2dcfg = lcdc_readl(sinfo, ATMEL_LCDC_DMA2DCFG); + dma2dcfg = LCDC_INSBF(DMA2DCFG_PIXELOFF, pixeloff, dma2dcfg); + lcdc_writel(sinfo, ATMEL_LCDC_DMA2DCFG, dma2dcfg); + + /* Update configuration */ + lcdc_writel(sinfo, ATMEL_LCDC_DMACON, + lcdc_readl(sinfo, ATMEL_LCDC_DMACON) | LCDC_BIT(DMACON_DMAUPDT)); +} + +static void atmel_lcdfb_set_2dcfg(struct fb_info *info) +{ + /* ...set 2D configuration (necessary for xres_virtual != xres) */ + value = LCDC_MKBF(DMA2DCFG_ADDRINC, + info->var.xres_virtual - info->var.xres); + lcdc_writel(sinfo, DMA2DCFG, value); + + /* ...wait for DMA engine to become idle... */ + while (lcdc_readl(sinfo, ATMEL_LCDC_DMACON) & LCDC_BIT(DMACON_DMABUSY)) + msleep(10); +} +#endif + + +static struct fb_fix_screeninfo atmel_lcdfb_fix __initdata = { + .type = FB_TYPE_PACKED_PIXELS, + .visual = FB_VISUAL_TRUECOLOR, + .xpanstep = 0, + .ypanstep = 0, + .ywrapstep = 0, + .accel = FB_ACCEL_NONE, +}; + +static u32 pseudo_palette[16] = { + 0x000000, + 0xaa0000, + 0x00aa00, + 0xaa5500, + 0x0000aa, + 0xaa00aa, + 0x00aaaa, + 0xaaaaaa, + 0x555555, + 0xff5555, + 0x55ff55, + 0xffff55, + 0x5555ff, + 0xff55ff, + 0x55ffff, + 0xffffff +}; + +static void atmel_lcdfb_update_dma(struct fb_info *info, + struct fb_var_screeninfo *var) +{ + struct atmel_lcdfb_info *sinfo = info->par; + struct fb_fix_screeninfo *fix = &info->fix; + unsigned long dma_addr; + + dma_addr = (fix->smem_start + var->yoffset * fix->line_length + + var->xoffset * var->bits_per_pixel / 8); + + dma_addr &= ~3UL; + + /* Set framebuffer DMA base address and pixel offset */ + lcdc_writel(sinfo, ATMEL_LCDC_DMABADDR1, dma_addr); + + atmel_lcdfb_update_dma2d(info, var); +} + +static inline void atmel_lcdfb_unmap_video_memory(struct atmel_lcdfb_info *sinfo) +{ + struct fb_info *info = sinfo->info; + + dma_free_writecombine(info->device, sinfo->map_size, + sinfo->map_cpu, sinfo->map_dma); +} + +/** + * atmel_lcdfb_alloc_framebuffer - Allocate framebuffer memory + * @sinfo: the frame buffer to allocate memory for + */ +static int atmel_lcdfb_map_video_memory(struct atmel_lcdfb_info *sinfo) +{ + struct fb_info *info = sinfo->info; + struct fb_var_screeninfo *var = &info->var; + + sinfo->map_size = (var->xres_virtual * var->yres_virtual + * ((var->bits_per_pixel + 7) / 8)); + + sinfo->map_cpu = dma_alloc_writecombine(info->device, sinfo->map_size, + &sinfo->map_dma, GFP_KERNEL); + + if (!sinfo->map_cpu) { + return -ENOMEM; + } + + return 0; +} + +/** + * atmel_lcdfb_check_var - Validates a var passed in. + * @var: frame buffer variable screen structure + * @info: frame buffer structure that represents a single frame buffer + * + * Checks to see if the hardware supports the state requested by + * var passed in. This function does not alter the hardware + * state!!! This means the data stored in struct fb_info and + * struct atmel_lcdfb_info do not change. This includes the var + * inside of struct fb_info. Do NOT change these. This function + * can be called on its own if we intent to only test a mode and + * not actually set it. The stuff in modedb.c is a example of + * this. If the var passed in is slightly off by what the + * hardware can support then we alter the var PASSED in to what + * we can do. If the hardware doesn't support mode change a + * -EINVAL will be returned by the upper layers. You don't need + * to implement this function then. If you hardware doesn't + * support changing the resolution then this function is not + * needed. In this case the driver would just provide a var that + * represents the static state the screen is in. + * + * Returns negative errno on error, or zero on success. + */ +static int atmel_lcdfb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct device *dev = info->device; + struct atmel_lcdfb_info *sinfo = info->par; + unsigned long clk_value_khz = 0; + + clk_value_khz = clk_get_rate(sinfo->lcdc_clk) / 1000; + + dev_dbg(dev, "%s:\n", __func__); + dev_dbg(dev, " resolution: %ux%u\n", var->xres, var->yres); + dev_dbg(dev, " pixclk: %lu KHz\n", PICOS2KHZ(var->pixclock)); + dev_dbg(dev, " bpp: %u\n", var->bits_per_pixel); + dev_dbg(dev, " clk: %lu KHz\n", clk_value_khz); + + if ((PICOS2KHZ(var->pixclock) * var->bits_per_pixel / 8) > clk_value_khz) { + dev_err(dev, "%lu KHz pixel clock is too fast\n", PICOS2KHZ(var->pixclock)); + return -EINVAL; + } + + /* Force same alignment for each line */ + var->xres = (var->xres + 3) & ~3UL; + var->xres_virtual = (var->xres_virtual + 3) & ~3UL; + + var->red.msb_right = var->green.msb_right = var->blue.msb_right = 0; + var->transp.offset = var->transp.length = 0; + + switch (var->bits_per_pixel) { + case 2: + case 4: + case 8: + var->red.offset = var->green.offset = var->blue.offset = 0; + var->red.length = var->green.length = var->blue.length + = var->bits_per_pixel; + break; + case 15: + case 16: + var->red.offset = 0; + var->green.offset = 5; + var->blue.offset = 10; + var->red.length = var->green.length = var->blue.length = 5; + break; + case 24: + case 32: + var->red.offset = 16; + var->green.offset = 8; + var->blue.offset = 0; + var->red.length = var->green.length = var->blue.length = 8; + break; + default: + dev_err(dev, "color depth %d not supported\n", + var->bits_per_pixel); + return -EINVAL; + } + + var->xoffset = var->yoffset = 0; + var->red.msb_right = var->green.msb_right = var->blue.msb_right = + var->transp.msb_right = 0; + + return 0; +} + +/** + * atmel_lcdfb_set_par - Alters the hardware state. + * @info: frame buffer structure that represents a single frame buffer + * + * Using the fb_var_screeninfo in fb_info we set the resolution + * of the this particular framebuffer. This function alters the + * par AND the fb_fix_screeninfo stored in fb_info. It doesn't + * not alter var in fb_info since we are using that data. This + * means we depend on the data in var inside fb_info to be + * supported by the hardware. atmel_lcdfb_check_var is always called + * before atmel_lcdfb_set_par to ensure this. Again if you can't + * change the resolution you don't need this function. + * + */ +static int atmel_lcdfb_set_par(struct fb_info *info) +{ + struct atmel_lcdfb_info *sinfo = info->par; + unsigned long value; + unsigned long clk_value_khz = 0; + + dev_dbg(info->device, "%s:\n", __func__); + dev_dbg(info->device, " * resolution: %ux%u (%ux%u virtual)\n", + info->var.xres, info->var.yres, + info->var.xres_virtual, info->var.yres_virtual); + + /* Turn off the LCD controller and the DMA controller */ + lcdc_writel(sinfo, ATMEL_LCDC_PWRCON, sinfo->guard_time << ATMEL_LCDC_GUARDT_OFFSET); + + lcdc_writel(sinfo, ATMEL_LCDC_DMACON, 0); + + /* Reset LCDC DMA*/ + lcdc_writel(sinfo, ATMEL_LCDC_DMACON, ATMEL_LCDC_DMARST); + + if (info->var.bits_per_pixel <= 8) + info->fix.visual = FB_VISUAL_PSEUDOCOLOR; + else + info->fix.visual = FB_VISUAL_TRUECOLOR; + + info->fix.line_length = info->var.xres_virtual * (info->var.bits_per_pixel / 8); + + /* Re-initialize the DMA engine... */ + dev_dbg(info->device, " * update DMA engine\n"); + atmel_lcdfb_update_dma(info, &info->var); + + /* ...set frame size and burst length = 8 words (?) */ + value = (info->var.yres * info->var.xres * info->var.bits_per_pixel) / 32; + value |= ((ATMEL_LCDC_DMA_BURST_LEN - 1) << ATMEL_LCDC_BLENGTH_OFFSET); + lcdc_writel(sinfo, ATMEL_LCDC_DMAFRMCFG, value); + + atmel_lcdfb_set_2dcfg(info); + + /* Now, the LCDC core... */ + + /* Set pixel clock */ + clk_value_khz = clk_get_rate(sinfo->lcdc_clk) / 1000; + + value = clk_value_khz / PICOS2KHZ(info->var.pixclock); + + if (clk_value_khz % PICOS2KHZ(info->var.pixclock)) + value++; + + value = (value / 2) - 1; + + if (value == 0) { + dev_notice(info->device, "Bypassing pixel clock divider\n"); + lcdc_writel(sinfo, ATMEL_LCDC_LCDCON1, ATMEL_LCDC_BYPASS); + } else + lcdc_writel(sinfo, ATMEL_LCDC_LCDCON1, value << ATMEL_LCDC_CLKVAL_OFFSET); + + /* Initialize control register 2 */ + value = sinfo->default_lcdcon2; + + if (!(info->var.sync & FB_SYNC_HOR_HIGH_ACT)) + value |= ATMEL_LCDC_INVLINE_INVERTED; + if (!(info->var.sync & FB_SYNC_VERT_HIGH_ACT)) + value |= ATMEL_LCDC_INVFRAME_INVERTED; + + switch (info->var.bits_per_pixel) { + case 1: value |= ATMEL_LCDC_PIXELSIZE_1; break; + case 2: value |= ATMEL_LCDC_PIXELSIZE_2; break; + case 4: value |= ATMEL_LCDC_PIXELSIZE_4; break; + case 8: value |= ATMEL_LCDC_PIXELSIZE_8; break; + case 15: /* fall through */ + case 16: value |= ATMEL_LCDC_PIXELSIZE_16; break; + case 24: value |= ATMEL_LCDC_PIXELSIZE_24; break; + case 32: value |= ATMEL_LCDC_PIXELSIZE_32; break; + default: BUG(); break; + } + dev_dbg(info->device, " * LCDCON2 = %08lx\n", value); + lcdc_writel(sinfo, ATMEL_LCDC_LCDCON2, value); + + /* Vertical timing */ + value = (info->var.vsync_len - 1) << ATMEL_LCDC_VPW_OFFSET; + value |= info->var.upper_margin << ATMEL_LCDC_VBP_OFFSET; + value |= info->var.lower_margin; + dev_dbg(info->device, " * LCDTIM1 = %08lx\n", value); + lcdc_writel(sinfo, ATMEL_LCDC_TIM1, value); + + /* Horizontal timing */ + value = (info->var.right_margin - 1) << ATMEL_LCDC_HFP_OFFSET; + value |= (info->var.hsync_len - 1) << ATMEL_LCDC_HPW_OFFSET; + value |= (info->var.left_margin - 1); + dev_dbg(info->device, " * LCDTIM2 = %08lx\n", value); + lcdc_writel(sinfo, ATMEL_LCDC_TIM2, value); + + /* Display size */ + value = (info->var.xres - 1) << ATMEL_LCDC_HOZVAL_OFFSET; + value |= info->var.yres - 1; + lcdc_writel(sinfo, ATMEL_LCDC_LCDFRMCFG, value); + + /* FIFO Threshold: Use formula from data sheet */ + value = ATMEL_LCDC_FIFO_SIZE - (2 * ATMEL_LCDC_DMA_BURST_LEN + 3); + lcdc_writel(sinfo, ATMEL_LCDC_FIFO, value); + + /* Toggle LCD_MODE every frame */ + lcdc_writel(sinfo, ATMEL_LCDC_MVAL, 0); + + /* Disable all interrupts */ + lcdc_writel(sinfo, ATMEL_LCDC_IDR, ~0UL); + + // Set contrast + value = ATMEL_LCDC_PS_DIV8 | ATMEL_LCDC_POL_POSITIVE | ATMEL_LCDC_ENA_PWMENABLE; + lcdc_writel(sinfo, ATMEL_LCDC_CONTRAST_CTR, value); + lcdc_writel(sinfo, ATMEL_LCDC_CONTRAST_VAL, ATMEL_LCDC_CRST_VAL_DEFAULT); + /* ...wait for DMA engine to become idle... */ + while (lcdc_readl(sinfo, ATMEL_LCDC_DMACON) & ATMEL_LCDC_DMABUSY) + msleep(10); + + dev_dbg(info->device, " * re-enable DMA engine\n"); + /* ...and enable it with updated configuration */ + lcdc_writel(sinfo, ATMEL_LCDC_DMACON, sinfo->default_dmacon); + + dev_dbg(info->device, " * re-enable LCDC core\n"); + lcdc_writel(sinfo, ATMEL_LCDC_PWRCON, + (sinfo->guard_time << ATMEL_LCDC_GUARDT_OFFSET) | ATMEL_LCDC_PWR); + + dev_dbg(info->device, " * DONE\n"); + + return 0; +} + +static inline u_int chan_to_field(u_int chan, const struct fb_bitfield *bf) +{ + chan &= 0xffff; + chan >>= 16 - bf->length; + return chan << bf->offset; +} + +/** + * atmel_lcdfb_setcolreg - Optional function. Sets a color register. + * @regno: Which register in the CLUT we are programming + * @red: The red value which can be up to 16 bits wide + * @green: The green value which can be up to 16 bits wide + * @blue: The blue value which can be up to 16 bits wide. + * @transp: If supported the alpha value which can be up to 16 bits wide. + * @info: frame buffer info structure + * + * Set a single color register. The values supplied have a 16 bit + * magnitude which needs to be scaled in this function for the hardware. + * Things to take into consideration are how many color registers, if + * any, are supported with the current color visual. With truecolor mode + * no color palettes are supported. Here a psuedo palette is created + * which we store the value in pseudo_palette in struct fb_info. For + * pseudocolor mode we have a limited color palette. To deal with this + * we can program what color is displayed for a particular pixel value. + * DirectColor is similar in that we can program each color field. If + * we have a static colormap we don't need to implement this function. + * + * Returns negative errno on error, or zero on success. In an + * ideal world, this would have been the case, but as it turns + * out, the other drivers return 1 on failure, so that's what + * we're going to do. + */ +static int atmel_lcdfb_setcolreg(unsigned int regno, unsigned int red, + unsigned int green, unsigned int blue, + unsigned int transp, struct fb_info *info) +{ + struct atmel_lcdfb_info *sinfo = info->par; + unsigned int val; + u32 *pal; + int ret = 1; + + if (info->var.grayscale) + red = green = blue = (19595 * red + 38470 * green + + 7471 * blue) >> 16; + + switch (info->fix.visual) { + case FB_VISUAL_TRUECOLOR: + if (regno < 16) { + pal = info->pseudo_palette; + + val = chan_to_field(red, &info->var.red); + val |= chan_to_field(green, &info->var.green); + val |= chan_to_field(blue, &info->var.blue); + + pal[regno] = val; + ret = 0; + } + break; + + case FB_VISUAL_PSEUDOCOLOR: + if (regno < 256) { + val = ((red >> 11) & 0x001f); + val |= ((green >> 6) & 0x03e0); + val |= ((blue >> 1) & 0x7c00); + + /* + * TODO: intensity bit. Maybe something like + * ~(red[10] ^ green[10] ^ blue[10]) & 1 + */ + + lcdc_writel(sinfo, ATMEL_LCDC_LUT_(regno), val); + ret = 0; + } + break; + } + + return ret; +} + +static int atmel_lcdfb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + dev_dbg(info->device, "%s\n", __func__); + + atmel_lcdfb_update_dma(info, var); + + return 0; +} + +static struct fb_ops atmel_lcdfb_ops = { + .owner = THIS_MODULE, + .fb_check_var = atmel_lcdfb_check_var, + .fb_set_par = atmel_lcdfb_set_par, + .fb_setcolreg = atmel_lcdfb_setcolreg, + .fb_pan_display = atmel_lcdfb_pan_display, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, +}; + +static irqreturn_t atmel_lcdfb_interrupt(int irq, void *dev_id) +{ + struct fb_info *info = dev_id; + struct atmel_lcdfb_info *sinfo = info->par; + u32 status; + + status = lcdc_readl(sinfo, ATMEL_LCDC_ISR); + lcdc_writel(sinfo, ATMEL_LCDC_IDR, status); + return IRQ_HANDLED; +} + +static int atmel_lcdfb_init_fbinfo(struct atmel_lcdfb_info *sinfo) +{ + struct fb_info *info = sinfo->info; + int ret = 0; + + info->screen_base = sinfo->map_cpu; + info->fix.smem_start = sinfo->map_dma; + info->fix.smem_len = sinfo->map_size; + + memset(info->screen_base, 0, info->fix.smem_len); + info->var.activate |= FB_ACTIVATE_FORCE | FB_ACTIVATE_NOW; + + dev_info(info->device, + "%luKiB frame buffer at %08lx (mapped at %p)\n", + (unsigned long)info->fix.smem_len / 1024, + (unsigned long)info->fix.smem_start, + info->screen_base); + + /* Allocate colormap */ + ret = fb_alloc_cmap(&info->cmap, 256, 0); + if (ret < 0) + dev_err(info->device, "Alloc color map failed\n"); + + return ret; +} + +static int atmel_lcdfb_start_clock(struct atmel_lcdfb_info *sinfo) +{ + int ret = 0; + + if (sinfo->bus_clk && !IS_ERR(sinfo->bus_clk)) + clk_enable(sinfo->bus_clk); + else { + if (cpu_is_at91sam9261()) { + /* not an error for other cpus */ + ret = -ENXIO; + } + } + + if (sinfo->lcdc_clk && !IS_ERR(sinfo->lcdc_clk)) + clk_enable(sinfo->lcdc_clk); + else + ret = -ENXIO; + + return ret; +} + +static void atmel_lcdfb_stop_clock(struct atmel_lcdfb_info *sinfo) +{ + if (sinfo->bus_clk && !IS_ERR(sinfo->bus_clk)) + clk_disable(sinfo->bus_clk); + if (sinfo->lcdc_clk && !IS_ERR(sinfo->lcdc_clk)) + clk_disable(sinfo->lcdc_clk); +} + + +static int atmel_lcdfb_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct fb_info *info; + struct atmel_lcdfb_info *sinfo; + struct resource *regs = NULL; + struct resource *map = NULL; + int ret; + + dev_dbg(dev, "%s BEGIN\n", __func__); + + ret = -ENOMEM; + info = framebuffer_alloc(sizeof(struct atmel_lcdfb_info), dev); + if (!info) { + dev_err(dev, "cannot allocate memory\n"); + goto out; + } + + sinfo = info->par; + memcpy(sinfo, dev->platform_data, sizeof(struct atmel_lcdfb_info)); + sinfo->info = info; + sinfo->pdev = pdev; + + strcpy(info->fix.id, sinfo->pdev->name); + info->flags = sinfo->default_flags; + info->pseudo_palette = pseudo_palette; + info->fbops = &atmel_lcdfb_ops; + + memcpy(&info->monspecs, sinfo->default_monspecs, sizeof(info->monspecs)); + info->fix = atmel_lcdfb_fix; + + /* Enable LCDC Clocks */ + if (cpu_is_at91sam9261()) + sinfo->bus_clk = clk_get(dev, "hck1"); + sinfo->lcdc_clk = clk_get(dev, "lcdc_clk"); + if (atmel_lcdfb_start_clock(sinfo)) { + dev_err(dev, "unable to clock LCDC\n"); + goto free_info; + } + + ret = fb_find_mode(&info->var, info, NULL, info->monspecs.modedb, + info->monspecs.modedb_len, info->monspecs.modedb, + sinfo->default_bpp); + if (!ret) { + dev_err(dev, "no suitable video mode found\n"); + goto stop_clk; + } + + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) { + dev_err(dev, "resources unusable\n"); + ret = -ENXIO; + goto stop_clk; + } + + sinfo->irq_base = platform_get_irq(pdev, 0); + if (sinfo->irq_base < 0) { + dev_err(dev, "unable to get irq\n"); + ret = sinfo->irq_base; + goto stop_clk; + } + + /* Initialize video memory */ + map = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (map) { + /* use a pre-allocated memory buffer */ + sinfo->map_dma = map->start; + sinfo->map_size = map->end - map->start + 1; + if (!request_mem_region(sinfo->map_dma, + sinfo->map_size, pdev->name)) { + ret = -EBUSY; + goto stop_clk; + } + + sinfo->map_cpu = ioremap(sinfo->map_dma, sinfo->map_size); + if (!sinfo->map_cpu) + goto release_intmem; + } else { + /* alocate memory buffer */ + ret = atmel_lcdfb_map_video_memory(sinfo); + if (ret < 0) { + dev_err(dev, "cannot allocate framebuffer: %d\n", ret); + goto stop_clk; + } + } + + /* LCDC registers */ + info->fix.mmio_start = regs->start; + info->fix.mmio_len = regs->end - regs->start + 1; + + if (!request_mem_region(info->fix.mmio_start, + info->fix.mmio_len, pdev->name)) { + ret = -EBUSY; + goto free_fb; + } + + sinfo->mmio = ioremap(info->fix.mmio_start, info->fix.mmio_len); + if (!sinfo->mmio) { + dev_err(dev, "cannot map LCDC registers\n"); + goto release_mem; + } + + /* interrupt */ + ret = request_irq(sinfo->irq_base, atmel_lcdfb_interrupt, 0, pdev->name, info); + if (ret) { + dev_err(dev, "request_irq failed: %d\n", ret); + goto unmap_mmio; + } + + ret = atmel_lcdfb_init_fbinfo(sinfo); + if (ret < 0) { + dev_err(dev, "init fbinfo failed: %d\n", ret); + goto unregister_irqs; + } + + /* + * This makes sure that our colour bitfield + * descriptors are correctly initialised. + */ + atmel_lcdfb_check_var(&info->var, info); + atmel_lcdfb_set_par(info); + + ret = fb_set_var(info, &info->var); + if (ret) { + dev_warn(dev, "unable to set display parameters\n"); + goto free_cmap; + } + + dev_set_drvdata(dev, info); + + /* + * Tell the world that we're ready to go + */ + ret = register_framebuffer(info); + if (ret < 0) { + dev_err(dev, "failed to register framebuffer device: %d\n", ret); + goto free_cmap; + } + + /* Power up the LCDC screen */ + if (sinfo->power_control_pin) + at91_set_gpio_value(sinfo->power_control_pin, LCD_POWER_ON); + + dev_info(dev, "fb%d: Atmel LCDC at 0x%08lx (mapped at %p), irq %lu\n", + info->node, info->fix.mmio_start, sinfo->mmio, sinfo->irq_base); + + return 0; + + +free_cmap: + fb_dealloc_cmap(&info->cmap); +unregister_irqs: + free_irq(sinfo->irq_base, info); +unmap_mmio: + iounmap(sinfo->mmio); +release_mem: + release_mem_region(info->fix.mmio_start, info->fix.mmio_len); +free_fb: + if (map) { + iounmap(sinfo->map_cpu); + } else { + atmel_lcdfb_unmap_video_memory(sinfo); + } + +release_intmem: + if (map) { + release_mem_region(sinfo->map_dma, sinfo->map_size); + } +stop_clk: + atmel_lcdfb_stop_clock(sinfo); +free_info: + framebuffer_release(info); +out: + dev_dbg(dev, "%s FAILED\n", __func__); + return ret; +} + +static int atmel_lcdfb_remove(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct fb_info *info = dev_get_drvdata(dev); + struct atmel_lcdfb_info *sinfo = info->par; + + if (!sinfo) + return 0; + + if (sinfo->power_control_pin) + at91_set_gpio_value(sinfo->power_control_pin, LCD_POWER_OFF); + unregister_framebuffer(info); + atmel_lcdfb_stop_clock(sinfo); + fb_dealloc_cmap(&info->cmap); + free_irq(sinfo->irq_base, info); + iounmap(sinfo->mmio); + release_mem_region(info->fix.mmio_start, info->fix.mmio_len); + if (platform_get_resource(pdev, IORESOURCE_MEM, 1)) { + iounmap(sinfo->map_cpu); + release_mem_region(sinfo->map_dma, sinfo->map_size); + } else { + atmel_lcdfb_unmap_video_memory(sinfo); + } + + dev_set_drvdata(dev, NULL); + framebuffer_release(info); + + return 0; +} + +static struct platform_driver atmel_lcdfb_driver = { + .probe = atmel_lcdfb_probe, + .remove = atmel_lcdfb_remove, + .driver = { + .name = "atmel_lcdfb", + .owner = THIS_MODULE, + }, +}; + +static int __init atmel_lcdfb_init(void) +{ + return platform_driver_register(&atmel_lcdfb_driver); +} + +static void __exit atmel_lcdfb_exit(void) +{ + platform_driver_unregister(&atmel_lcdfb_driver); +} + +module_init(atmel_lcdfb_init); +module_exit(atmel_lcdfb_exit); + +MODULE_AUTHOR("Atmel Corporation"); +MODULE_DESCRIPTION("AT91/AT32 LCD Controller framebuffer driver"); +MODULE_LICENSE("GPL"); diff -urN linux-2.6.20.4-0rig/drivers/video/backlight/Kconfig linux-2.6.20.4-atmel/drivers/video/backlight/Kconfig --- linux-2.6.20.4-0rig/drivers/video/backlight/Kconfig 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/video/backlight/Kconfig 2007-03-24 16:42:29.000000000 +0100 @@ -42,6 +42,18 @@ depends on LCD_CLASS_DEVICE default y +config LCD_LTV350QV + tristate "Samsung LTV350QV LCD Panel" + depends on LCD_DEVICE && SPI + default n + help + If you have a Samsung LTV350QV LCD panel, say y to include a + power control driver for it. The panel starts up in power + off state, so you need this driver in order to see any + output. + + The LTV350QV panel is present on most ATSTK1000 boards. + config BACKLIGHT_CORGI tristate "Sharp Corgi Backlight Driver (SL Series)" depends on BACKLIGHT_DEVICE && PXA_SHARPSL diff -urN linux-2.6.20.4-0rig/drivers/video/backlight/ltv350qv.c linux-2.6.20.4-atmel/drivers/video/backlight/ltv350qv.c --- linux-2.6.20.4-0rig/drivers/video/backlight/ltv350qv.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/video/backlight/ltv350qv.c 2007-03-24 16:42:29.000000000 +0100 @@ -0,0 +1,300 @@ +/* + * Power control for Samsung LTV350QV Quarter VGA LCD Panel + * + * Copyright (C) 2006 Atmel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/fb.h> +#include <linux/init.h> +#include <linux/lcd.h> +#include <linux/module.h> +#include <linux/reboot.h> +#include <linux/spi/spi.h> + +#define POWER_IS_ON(pwr) ((pwr) <= FB_BLANK_NORMAL) + +struct ltv350qv { + struct spi_device *spi; + u8 *buffer; + int power; + struct semaphore lock; + struct lcd_device *ld; + struct list_head list; + int halt_done; +}; + +static LIST_HEAD(lcd_list); + +static int ltv350qv_write_reg(struct ltv350qv *lcd, u8 reg, u16 val) +{ + struct spi_message msg; + struct spi_transfer index_xfer = { + .len = 3, + .cs_change = 1, + }; + struct spi_transfer value_xfer = { + .len = 3, + .cs_change = 1, + }; + + spi_message_init(&msg); + + /* register index */ + lcd->buffer[0] = 0x74; + lcd->buffer[1] = 0x00; + lcd->buffer[2] = reg & 0x7f; + index_xfer.tx_buf = lcd->buffer; + spi_message_add_tail(&index_xfer, &msg); + + /* register value */ + lcd->buffer[4] = 0x76; + lcd->buffer[5] = val >> 8; + lcd->buffer[6] = val; + value_xfer.tx_buf = lcd->buffer + 4; + spi_message_add_tail(&value_xfer, &msg); + + return spi_sync(lcd->spi, &msg); +} + +#define write_reg(_spi, reg, val) \ + do { \ + ret = ltv350qv_write_reg(_spi, reg, val); \ + if (ret) \ + goto out; \ + } while (0) + +static int ltv350qv_power_on(struct ltv350qv *lcd) +{ + int ret; + + write_reg(lcd, 9, 0x0000); + msleep(15); + write_reg(lcd, 9, 0x4000); + write_reg(lcd, 10, 0x2000); + write_reg(lcd, 9, 0x4055); + msleep(55); + write_reg(lcd, 1, 0x409d); + write_reg(lcd, 2, 0x0204); + write_reg(lcd, 3, 0x0100); + write_reg(lcd, 4, 0x3000); + write_reg(lcd, 5, 0x4003); + write_reg(lcd, 6, 0x000a); + write_reg(lcd, 7, 0x0021); + write_reg(lcd, 8, 0x0c00); + write_reg(lcd, 10, 0x0103); + write_reg(lcd, 11, 0x0301); + write_reg(lcd, 12, 0x1f0f); + write_reg(lcd, 13, 0x1f0f); + write_reg(lcd, 14, 0x0707); + write_reg(lcd, 15, 0x0307); + write_reg(lcd, 16, 0x0707); + write_reg(lcd, 17, 0x0000); + write_reg(lcd, 18, 0x0004); + write_reg(lcd, 19, 0x0000); + + msleep(20); + write_reg(lcd, 9, 0x4a55); + write_reg(lcd, 5, 0x5003); + +out: + return ret; +} + +static int ltv350qv_power_off(struct ltv350qv *lcd) +{ + int ret; + + /* GON -> 0, POC -> 0 */ + write_reg(lcd, 9, 0x4055); + /* DSC -> 0 */ + write_reg(lcd, 5, 0x4003); + /* VCOMG -> 0 */ + write_reg(lcd, 10, 0x2103); + + msleep(1); + + /* AP[2:0] -> 000 */ + write_reg(lcd, 9, 0x4050); + +out: + return ret; +} + +static int ltv350qv_power(struct ltv350qv *lcd, int power) +{ + int ret = 0; + + down(&lcd->lock); + + if (POWER_IS_ON(power) && !POWER_IS_ON(lcd->power)) + ret = ltv350qv_power_on(lcd); + else if (!POWER_IS_ON(power) && POWER_IS_ON(lcd->power)) + ret = ltv350qv_power_off(lcd); + + if (!ret) + lcd->power = power; + + up(&lcd->lock); + + return ret; +} + +static int ltv350qv_set_power(struct lcd_device *ld, int power) +{ + struct ltv350qv *lcd; + + lcd = class_get_devdata(&ld->class_dev); + return ltv350qv_power(lcd, power); +} + +static int ltv350qv_get_power(struct lcd_device *ld) +{ + struct ltv350qv *lcd; + + lcd = class_get_devdata(&ld->class_dev); + return lcd->power; +} + +static struct lcd_properties lcd_properties = { + .owner = THIS_MODULE, + .get_power = ltv350qv_get_power, + .set_power = ltv350qv_set_power, +}; + +static int __devinit ltv350qv_probe(struct spi_device *spi) +{ + struct ltv350qv *lcd; + struct lcd_device *ld; + int ret; + + lcd = kzalloc(sizeof(struct ltv350qv), GFP_KERNEL); + if (!lcd) + return -ENOMEM; + + lcd->spi = spi; + lcd->power = FB_BLANK_POWERDOWN; + init_MUTEX(&lcd->lock); + lcd->buffer = kzalloc(8, GFP_KERNEL); + + spi->mode = SPI_MODE_3; + spi->bits_per_word = 8; + ret = spi_setup(spi); + if (ret) + goto out_free_lcd; + + ld = lcd_device_register("ltv350qv", lcd, &lcd_properties); + if (IS_ERR(ld)) { + ret = PTR_ERR(ld); + goto out_free_lcd; + } + lcd->ld = ld; + + list_add(&lcd->list, &lcd_list); + + ret = ltv350qv_power(lcd, FB_BLANK_UNBLANK); + if (ret) + goto out_unregister; + + dev_set_drvdata(&spi->dev, lcd); + + return 0; + +out_unregister: + lcd_device_unregister(ld); +out_free_lcd: + kfree(lcd); + return ret; +} + +static int __devexit ltv350qv_remove(struct spi_device *spi) +{ + struct ltv350qv *lcd = dev_get_drvdata(&spi->dev); + + ltv350qv_power(lcd, FB_BLANK_POWERDOWN); + list_del(&lcd->list); + lcd_device_unregister(lcd->ld); + kfree(lcd); + + return 0; +} + +#ifdef CONFIG_PM +static int ltv350qv_suspend(struct spi_device *spi, + pm_message_t state, u32 level) +{ + struct ltv350qv *lcd = dev_get_drvdata(&spi->dev); + + if (level == SUSPEND_POWER_DOWN) + return ltv350qv_power(lcd, FB_BLANK_POWERDOWN); + + return 0; +} + +static int ltv350qv_resume(struct spi_device *spi, u32 level) +{ + struct ltv350qv *lcd = dev_get_drvdata(&spi->dev); + + if (level == RESUME_POWER_ON) + return ltv350qv_power(lcd, FB_BLANK_UNBLANK); + + return 0; +} +#else +#define ltv350qv_suspend NULL +#define ltv350qv_resume NULL +#endif + +/* Power down all displays on reboot, poweroff or halt */ +static int ltv350qv_halt(struct notifier_block *nb, unsigned long event, + void *p) +{ + struct ltv350qv *lcd; + + list_for_each_entry(lcd, &lcd_list, list) { + if (!lcd->halt_done) + ltv350qv_power(lcd, FB_BLANK_POWERDOWN); + lcd->halt_done = 1; + } + + return NOTIFY_OK; +} + +static struct spi_driver ltv350qv_driver = { + .driver = { + .name = "ltv350qv", + .bus = &spi_bus_type, + .owner = THIS_MODULE, + }, + + .probe = ltv350qv_probe, + .remove = __devexit_p(ltv350qv_remove), + .suspend = ltv350qv_suspend, + .resume = ltv350qv_resume, +}; + +static struct notifier_block ltv350qv_notifier = { + .notifier_call = ltv350qv_halt, +}; + +static int __init ltv350qv_init(void) +{ + register_reboot_notifier(<v350qv_notifier); + return spi_register_driver(<v350qv_driver); +} + +static void __exit ltv350qv_exit(void) +{ + unregister_reboot_notifier(<v350qv_notifier); + spi_unregister_driver(<v350qv_driver); +} +module_init(ltv350qv_init); +module_exit(ltv350qv_exit); + +MODULE_AUTHOR("Atmel Norway"); +MODULE_DESCRIPTION("Samsung LTV350QV LCD Driver"); +MODULE_LICENSE("GPL"); diff -urN linux-2.6.20.4-0rig/drivers/video/backlight/Makefile linux-2.6.20.4-atmel/drivers/video/backlight/Makefile --- linux-2.6.20.4-0rig/drivers/video/backlight/Makefile 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/video/backlight/Makefile 2007-03-24 16:42:29.000000000 +0100 @@ -5,3 +5,4 @@ obj-$(CONFIG_BACKLIGHT_CORGI) += corgi_bl.o obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o +obj-$(CONFIG_LCD_LTV350QV) += ltv350qv.o diff -urN linux-2.6.20.4-0rig/drivers/video/fbmem.c linux-2.6.20.4-atmel/drivers/video/fbmem.c --- linux-2.6.20.4-0rig/drivers/video/fbmem.c 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/video/fbmem.c 2007-03-24 16:42:29.000000000 +0100 @@ -1199,6 +1199,10 @@ pgprot_val(vma->vm_page_prot) |= _PAGE_NO_CACHE; #elif defined(__arm__) || defined(__sh__) || defined(__m32r__) vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); +#elif defined(__avr32__) + vma->vm_page_prot = __pgprot((pgprot_val(vma->vm_page_prot) + & ~_PAGE_CACHABLE) + | (_PAGE_BUFFER | _PAGE_DIRTY)); #elif defined(__ia64__) if (efi_range_is_wc(vma->vm_start, vma->vm_end - vma->vm_start)) vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); diff -urN linux-2.6.20.4-0rig/drivers/video/Kconfig linux-2.6.20.4-atmel/drivers/video/Kconfig --- linux-2.6.20.4-0rig/drivers/video/Kconfig 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/video/Kconfig 2007-03-24 16:42:29.000000000 +0100 @@ -276,6 +276,28 @@ If you plan to use the LCD display with your SA-1100 system, say Y here. +config FB_SIDSA + tristate "SIDSA LCDC support" + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + depends on FB && AVR32 + help + This enables support for the SIDSA LCD Controller. + +config FB_SIDSA_DEFAULT_BPP + int "SIDSA LCDC default color depth" + default 24 + depends on FB_SIDSA + help + Specify the maximum color depth you want to be able to + support. This, together with the resolution of the LCD + panel, determines the amount of framebuffer memory allocated + when the driver is initialized. + + Allowable values are 1, 2, 4, 8, 16, 24 and 32. If unsure, + say 24. + config FB_IMX tristate "Motorola i.MX LCD support" depends on FB && ARM && ARCH_IMX @@ -698,6 +720,22 @@ working with S1D13806). Product specs at <http://www.erd.epson.com/vdc/html/legacy_13xxx.htm> +config FB_ATMEL + tristate "AT91/AT32 LCD Controller support" + depends on FB && (ARCH_AT91SAM9261 || ARCH_AT91SAM9263 || AVR32) + select FB_CFB_FILLRECT + select FB_CFB_COPYAREA + select FB_CFB_IMAGEBLIT + help + This enables support for the AT91/AT32 LCD Controller. + +config FB_INTSRAM + bool "Frame Buffer in internal SRAM" + depends on FB_ATMEL && ARCH_AT91SAM9261 + help + Say Y if you want to map Frame Buffer in internal SRAM. Say N if you want + to let frame buffer in external SDRAM. + config FB_NVIDIA tristate "nVidia Framebuffer Support" depends on FB && PCI diff -urN linux-2.6.20.4-0rig/drivers/video/Makefile linux-2.6.20.4-atmel/drivers/video/Makefile --- linux-2.6.20.4-0rig/drivers/video/Makefile 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/video/Makefile 2007-03-24 16:42:29.000000000 +0100 @@ -78,6 +78,7 @@ obj-$(CONFIG_FB_SUN3) += sun3fb.o obj-$(CONFIG_FB_HIT) += hitfb.o obj-$(CONFIG_FB_EPSON1355) += epson1355fb.o +obj-$(CONFIG_FB_ATMEL) += atmel_lcdfb.o obj-$(CONFIG_FB_PVR2) += pvr2fb.o obj-$(CONFIG_FB_VOODOO1) += sstfb.o obj-$(CONFIG_FB_ARMCLCD) += amba-clcd.o @@ -100,6 +101,7 @@ obj-$(CONFIG_FB_PNX4008_DUM) += pnx4008/ obj-$(CONFIG_FB_PNX4008_DUM_RGB) += pnx4008/ obj-$(CONFIG_FB_IBM_GXT4500) += gxt4500.o +obj-$(CONFIG_FB_SIDSA) += sidsafb.o # Platform or fallback drivers go here obj-$(CONFIG_FB_VESA) += vesafb.o diff -urN linux-2.6.20.4-0rig/drivers/video/sidsafb.c linux-2.6.20.4-atmel/drivers/video/sidsafb.c --- linux-2.6.20.4-0rig/drivers/video/sidsafb.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/drivers/video/sidsafb.c 2007-03-24 16:42:29.000000000 +0100 @@ -0,0 +1,860 @@ +/* + * Framebuffer Driver for Atmel/SIDSA LCD Controller + * + * Copyright (C) 2004-2006 Atmel Corporation + * + * 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. + */ +#undef DEBUG + +#include <linux/clk.h> +#include <linux/completion.h> +#include <linux/kernel.h> +#include <linux/dma-mapping.h> +#include <linux/interrupt.h> +#include <linux/fb.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/platform_device.h> + +#include <asm/arch/board.h> + +#include <asm/periph/lcdc.h> + +/* More or less configurable parameters */ +#define SIDSAFB_FIFO_SIZE 512 +#define SIDSAFB_DMA_BURST_LEN 8 + +/* TODO: These should be autogenerated from part description file */ +#define LCDC_DISTYPE_STN_MONO 0 +#define LCDC_DISTYPE_STN_COLOR 1 +#define LCDC_DISTYPE_TFT 2 +#define LCDC_LUT 0xc00 + +struct sidsafb_info { + spinlock_t lock; + struct fb_info * info; + void __iomem * regs; + unsigned long irq_base; + int wait_for_vsync; + struct completion vsync_complete; + unsigned int guard_time; + struct clk *hclk; + struct clk *pixclk; + struct platform_device *pdev; + u32 pseudo_palette[16]; +}; + +/* + * How large framebuffer to allocate if none was provided by the + * platform. This default is the smallest we can possibly get away + * with. + */ +static unsigned long fb_size = (320 * 240); + +#if 0 +static struct fb_videomode sony_modes[] = { + { + .refresh = 48, + .xres = 240, .yres = 160, + .pixclock = 520833, + + .left_margin = 7, .right_margin = 9, + .upper_margin = 19, .lower_margin = 20, + .hsync_len = 9, .vsync_len = 2, + + .sync = 0, + .vmode = FB_VMODE_NONINTERLACED, + }, +}; +#endif + +#if 0 +static struct fb_videomode vga_modes[] = { + { + .refresh = 122, + .xres = 320, .yres = 240, + .pixclock = 80000, + + .left_margin = 10, .right_margin = 20, + .upper_margin = 30, .lower_margin = 5, + .hsync_len = 20, .vsync_len = 3, + + .sync = 0, + .vmode = FB_VMODE_NONINTERLACED, + }, + { + .refresh = 70, + .xres = 640, .yres = 480, + .pixclock = 40000, + + .left_margin = 10, .right_margin = 20, + .upper_margin = 30, .lower_margin = 5, + .hsync_len = 20, .vsync_len = 3, + + .sync = 0, + .vmode = FB_VMODE_NONINTERLACED, + }, +}; +#else +static struct fb_videomode samsung_modes[] = { + { + .refresh = 75, + .xres = 320, .yres = 240, + .pixclock = 145111, + + .left_margin = 17, .right_margin = 33, + .upper_margin = 10, .lower_margin = 10, + .hsync_len = 16, .vsync_len = 1, + + .sync = FB_SYNC_PCLK_RISING, + .vmode = FB_VMODE_NONINTERLACED, + }, +}; +#endif + +#if 1 +static struct fb_monspecs default_monspecs = { + .modedb = samsung_modes, + .manufacturer = "SNG", + .monitor = "LCD panel", + .serial_no = "xxxx", + .ascii = "yyyy", + .modedb_len = ARRAY_SIZE(samsung_modes), + .hfmin = 14820, + .hfmax = 22230, + .vfmin = 60, + .vfmax = 90, + .dclkmax = 30000000, +}; +#endif + +#if 0 +static struct fb_monspecs default_monspecs = { + .modedb = sony_modes, + .manufacturer = "SNY", /* 4 chars?!? */ + .monitor = "LCD panel", + .serial_no = "xxxx", + .ascii = "yyyy", + .modedb_len = ARRAY_SIZE(sony_modes), + .hfmin = 7000, + .hfmax = 8000, + .vfmin = 45, + .vfmax = 50, +}; +// #else +static struct fb_monspecs default_monspecs = { + .modedb = vga_modes, + .manufacturer = "VGA", + .monitor = "Generic VGA", + .serial_no = "xxxx", + .ascii = "yyyy", + .modedb_len = ARRAY_SIZE(vga_modes), + .hfmin = 30000, + .hfmax = 64000, + .vfmin = 50, + .vfmax = 150, +}; +#endif + +/* Driver defaults */ +static struct fb_fix_screeninfo sidsafb_fix __devinitdata = { + .id = "sidsafb", + .type = FB_TYPE_PACKED_PIXELS, + .visual = FB_VISUAL_TRUECOLOR, + .xpanstep = 1, + .ypanstep = 1, + .ywrapstep = 0, + .accel = FB_ACCEL_NONE, +}; + +/* + * Let the user decide whether FBIOPAN_DISPLAY waits for the next + * vsync or not. + */ +static ssize_t +vsync_pan_show(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct fb_info *info = dev_get_drvdata(dev); + struct sidsafb_info *sinfo = info->par; + + return sprintf(buf, "%d\n", sinfo->wait_for_vsync); +} + +static ssize_t +vsync_pan_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct fb_info *info = dev_get_drvdata(dev); + struct sidsafb_info *sinfo = info->par; + unsigned long val; + + val = simple_strtoul(buf, NULL, 0); + if (val) + sinfo->wait_for_vsync = 1; + else + sinfo->wait_for_vsync = 0; + + return count; +} + +static DEVICE_ATTR(vsync_pan, 0644, vsync_pan_show, vsync_pan_store); + +static void sidsafb_update_dma(struct fb_info *info, + struct fb_var_screeninfo *var) +{ + struct sidsafb_info *sinfo = info->par; + struct fb_fix_screeninfo *fix = &info->fix; + unsigned long dma_addr; + unsigned long pixeloff; + unsigned long dma2dcfg; + + dma_addr = (fix->smem_start + var->yoffset * fix->line_length + + var->xoffset * var->bits_per_pixel / 8); + + dma_addr &= ~3UL; + pixeloff = LCDC_MKBF(DMA2DCFG_PIXELOFF, var->xoffset * var->bits_per_pixel); + + /* Set framebuffer DMA base address and pixel offset */ + lcdc_writel(sinfo, DMABADDR1, dma_addr); + dma2dcfg = lcdc_readl(sinfo, DMA2DCFG); + dma2dcfg = LCDC_INSBF(DMA2DCFG_PIXELOFF, pixeloff, dma2dcfg); + lcdc_writel(sinfo, DMA2DCFG, dma2dcfg); + + /* Update configuration */ + lcdc_writel(sinfo, DMACON, (lcdc_readl(sinfo, DMACON) + | LCDC_BIT(DMACON_DMAUPDT))); +} + +/** + * sidsafb_check_var - Validates a var passed in. + * @var: frame buffer variable screen structure + * @info: frame buffer structure that represents a single frame buffer + * + * Checks to see if the hardware supports the state requested by + * var passed in. This function does not alter the hardware + * state!!! This means the data stored in struct fb_info and + * struct sidsafb_info do not change. This includes the var + * inside of struct fb_info. Do NOT change these. This function + * can be called on its own if we intent to only test a mode and + * not actually set it. The stuff in modedb.c is a example of + * this. If the var passed in is slightly off by what the + * hardware can support then we alter the var PASSED in to what + * we can do. If the hardware doesn't support mode change a + * -EINVAL will be returned by the upper layers. You don't need + * to implement this function then. If you hardware doesn't + * support changing the resolution then this function is not + * needed. In this case the driver would just provide a var that + * represents the static state the screen is in. + * + * Returns negative errno on error, or zero on success. + */ +static int sidsafb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + unsigned long new_fb_size; + + pr_debug("sidsafb_check_var:\n"); + pr_debug(" resolution: %ux%u\n", var->xres, var->yres); + pr_debug(" pixclk: %llu Hz\n", 1000000000000ULL / var->pixclock); + pr_debug(" bpp: %u\n", var->bits_per_pixel); + + new_fb_size = (var->xres_virtual * var->yres_virtual + * ((var->bits_per_pixel + 7) / 8)); + if (new_fb_size > info->fix.smem_len) { + printk(KERN_NOTICE + "sidsafb: %uB framebuffer too small for %ux%ux%u\n", + info->fix.smem_len, var->xres_virtual, + var->yres_virtual, var->bits_per_pixel); + return -EINVAL; + } + + /* Force same alignment for each line */ + var->xres = (var->xres + 3) & ~3UL; + var->xres_virtual = (var->xres_virtual + 3) & ~3UL; + + var->red.msb_right = var->green.msb_right = var->blue.msb_right = 0; + var->transp.offset = var->transp.length = 0; + + switch (var->bits_per_pixel) { + case 2: + case 4: + case 8: + var->red.offset = var->green.offset = var->blue.offset = 0; + var->red.length = var->green.length = var->blue.length + = var->bits_per_pixel; + break; + case 15: + case 16: + /* + * Bit 16 is the "intensity" bit, I think. Not sure + * what we're going to use that for... + */ + var->red.offset = 0; + var->green.offset = 5; + var->blue.offset = 10; + var->red.length = 5; + var->green.length = 5; + var->blue.length = 5; + break; + case 32: + var->transp.offset = 24; + var->transp.length = 8; + /* fall through */ + case 24: + var->red.offset = 16; + var->green.offset = 8; + var->blue.offset = 0; + var->red.length = var->green.length = var->blue.length = 8; + break; + default: + printk(KERN_NOTICE "sidsafb: color depth %d not supported\n", + var->bits_per_pixel); + return -EINVAL; + } + + var->xoffset = var->yoffset = 0; + var->red.msb_right = var->green.msb_right = var->blue.msb_right = + var->transp.msb_right = 0; + + return 0; +} + +/** + * sidsafb_set_par - Alters the hardware state. + * @info: frame buffer structure that represents a single frame buffer + * + * Using the fb_var_screeninfo in fb_info we set the resolution + * of the this particular framebuffer. This function alters the + * par AND the fb_fix_screeninfo stored in fb_info. It doesn't + * not alter var in fb_info since we are using that data. This + * means we depend on the data in var inside fb_info to be + * supported by the hardware. sidsafb_check_var is always called + * before sidsafb_set_par to ensure this. Again if you can't + * change the resolution you don't need this function. + * + */ +static int sidsafb_set_par(struct fb_info *info) +{ + struct sidsafb_info *sinfo = info->par; + unsigned long value; + + pr_debug("sidsafb_set_par:\n"); + pr_debug(" * resolution: %ux%u (%ux%u virtual)\n", + info->var.xres, info->var.yres, + info->var.xres_virtual, info->var.yres_virtual); + + /* Turn off the LCD controller and the DMA controller */ + pr_debug("writing 0x%08x to %p\n", + LCDC_MKBF(PWRCON_GUARD_TIME, sinfo->guard_time), + sinfo->regs + LCDC_PWRCON); + lcdc_writel(sinfo, PWRCON, + LCDC_MKBF(PWRCON_GUARD_TIME, sinfo->guard_time)); + pr_debug("writing 0 to %p\n", sinfo->regs + LCDC_DMACON); + lcdc_writel(sinfo, DMACON, 0); + + info->fix.line_length = (info->var.xres_virtual + * (info->var.bits_per_pixel / 8)); + + if (info->var.bits_per_pixel <= 8) + info->fix.visual = FB_VISUAL_PSEUDOCOLOR; + else + info->fix.visual = FB_VISUAL_TRUECOLOR; + + /* Re-initialize the DMA engine... */ + pr_debug(" * update DMA engine\n"); + sidsafb_update_dma(info, &info->var); + + /* ...set frame size and burst length = 8 words (?) */ + value = LCDC_MKBF(DMAFRMCFG_FRMSIZE, + (info->var.yres * info->fix.line_length + 3) / 4); + value |= LCDC_MKBF(DMAFRMCFG_BRSTLEN, (SIDSAFB_DMA_BURST_LEN - 1)); + lcdc_writel(sinfo, DMAFRMCFG, value); + + /* ...set 2D configuration (necessary for xres_virtual != xres) */ + value = LCDC_MKBF(DMA2DCFG_ADDRINC, + info->var.xres_virtual - info->var.xres); + lcdc_writel(sinfo, DMA2DCFG, value); + + /* ...wait for DMA engine to become idle... */ + while (lcdc_readl(sinfo, DMACON) & LCDC_BIT(DMACON_DMABUSY)) + msleep(10); + + pr_debug(" * re-enable DMA engine\n"); + /* ...and enable it with updated configuration */ + lcdc_writel(sinfo, DMACON, (LCDC_BIT(DMACON_DMAEN) + | LCDC_BIT(DMACON_DMAUPDT) + | LCDC_BIT(DMACON_DMA2DEN))); + + /* Now, the LCD core... */ + + /* Set pixel clock. */ + value = (clk_get_rate(sinfo->pixclk) / 100000) * info->var.pixclock; + value /= 10000000; + value = (value + 1) / 2; + if (value == 0) { + printk("sidsafb: Bypassing lcdc_pclk divider\n"); + lcdc_writel(sinfo, LCDCON1, LCDC_BIT(LCDCON1_BYPASS)); + } else { + lcdc_writel(sinfo, LCDCON1, LCDC_MKBF(LCDCON1_CLKVAL, value - 1)); + } + + /* Initialize control register 2 */ + value = (LCDC_BIT(LCDCON2_CLKMOD) + | LCDC_MKBF(LCDCON2_DISTYPE, LCDC_DISTYPE_TFT)); + if (!(info->var.sync & FB_SYNC_HOR_HIGH_ACT)) + value |= LCDC_BIT(LCDCON2_INVLINE); + if (!(info->var.sync & FB_SYNC_VERT_HIGH_ACT)) + value |= LCDC_BIT(LCDCON2_INVFRAME); + if (info->var.sync & FB_SYNC_PCLK_RISING) + value |= LCDC_BIT(LCDCON2_INVCLK); + + switch (info->var.bits_per_pixel) { + case 1: value |= LCDC_MKBF(LCDCON2_PIXELSIZE, 0); break; + case 2: value |= LCDC_MKBF(LCDCON2_PIXELSIZE, 1); break; + case 4: value |= LCDC_MKBF(LCDCON2_PIXELSIZE, 2); break; + case 8: value |= LCDC_MKBF(LCDCON2_PIXELSIZE, 3); break; + case 15: /* fall through */ + case 16: value |= LCDC_MKBF(LCDCON2_PIXELSIZE, 4); break; + case 24: value |= LCDC_MKBF(LCDCON2_PIXELSIZE, 5); break; + case 32: value |= LCDC_MKBF(LCDCON2_PIXELSIZE, 6); break; + default: BUG(); break; + } + pr_debug(" * LCDCON2 = %08lx\n", value); + lcdc_writel(sinfo, LCDCON2, value); + + /* Vertical timing */ + value = LCDC_MKBF(LCDTIM1_VPW, info->var.vsync_len - 1); + value |= LCDC_MKBF(LCDTIM1_VBP, info->var.upper_margin); + value |= LCDC_MKBF(LCDTIM1_VFP, info->var.lower_margin); + pr_debug(" * LCDTIM1 = %08lx\n", value); + lcdc_writel(sinfo, LCDTIM1, value); + + /* Horizontal timing */ + value = LCDC_MKBF(LCDTIM2_HFP, info->var.right_margin - 1); + value |= LCDC_MKBF(LCDTIM2_HPW, info->var.hsync_len - 1); + value |= LCDC_MKBF(LCDTIM2_HBP, info->var.left_margin - 1); + pr_debug(" * LCDTIM2 = %08lx\n", value); + lcdc_writel(sinfo, LCDTIM2, value); + + /* Display size */ + value = LCDC_MKBF(LCDFRMCFG_LINESIZE, info->var.xres - 1); + value |= LCDC_MKBF(LCDFRMCFG_LINEVAL, info->var.yres - 1); + lcdc_writel(sinfo, LCDFRMCFG, value); + + /* FIFO Threshold: Use formula from data sheet */ + value = SIDSAFB_FIFO_SIZE - (2 * SIDSAFB_DMA_BURST_LEN + 3); + lcdc_writel(sinfo, LCDFIFO, value); + + /* Toggle LCD_MODE every frame */ + lcdc_writel(sinfo, LCDMVAL, 0); + + /* Disable all interrupts */ + lcdc_writel(sinfo, LCD_IDR, ~0UL); + + /* Wait for the LCDC core to become idle and enable it */ + while(lcdc_readl(sinfo, PWRCON) & LCDC_BIT(PWRCON_LCD_BUSY)) + msleep(10); + + pr_debug(" * re-enable LCD core\n"); + lcdc_writel(sinfo, PWRCON, + LCDC_MKBF(PWRCON_GUARD_TIME, sinfo->guard_time) + | LCDC_BIT(PWRCON_LCD_PWR)); + + pr_debug(" * DONE\n"); + return 0; +} + +static inline u_int chan_to_field(u_int chan, const struct fb_bitfield *bf) +{ + chan &= 0xffff; + chan >>= 16 - bf->length; + return chan << bf->offset; +} + +/** + * sidsafb_setcolreg - Optional function. Sets a color register. + * @regno: Which register in the CLUT we are programming + * @red: The red value which can be up to 16 bits wide + * @green: The green value which can be up to 16 bits wide + * @blue: The blue value which can be up to 16 bits wide. + * @transp: If supported the alpha value which can be up to 16 bits wide. + * @info: frame buffer info structure + * + * Set a single color register. The values supplied have a 16 bit + * magnitude which needs to be scaled in this function for the hardware. + * Things to take into consideration are how many color registers, if + * any, are supported with the current color visual. With truecolor mode + * no color palettes are supported. Here a psuedo palette is created + * which we store the value in pseudo_palette in struct fb_info. For + * pseudocolor mode we have a limited color palette. To deal with this + * we can program what color is displayed for a particular pixel value. + * DirectColor is similar in that we can program each color field. If + * we have a static colormap we don't need to implement this function. + * + * Returns negative errno on error, or zero on success. In an + * ideal world, this would have been the case, but as it turns + * out, the other drivers return 1 on failure, so that's what + * we're going to do. + */ +static int sidsafb_setcolreg(unsigned int regno, unsigned int red, + unsigned int green, unsigned int blue, + unsigned int transp, struct fb_info *info) +{ + struct sidsafb_info *sinfo = info->par; + unsigned int val; + u32 *pal; + int ret = 1; + + if (info->var.grayscale) + red = green = blue = (19595 * red + 38470 * green + + 7471 * blue) >> 16; + + switch (info->fix.visual) { + case FB_VISUAL_TRUECOLOR: + if (regno < 16) { + pal = info->pseudo_palette; + + val = chan_to_field(red, &info->var.red); + val |= chan_to_field(green, &info->var.green); + val |= chan_to_field(blue, &info->var.blue); + + pal[regno] = val; + ret = 0; + } + break; + + case FB_VISUAL_PSEUDOCOLOR: + if (regno < 256) { + val = ((red >> 11) & 0x001f); + val |= ((green >> 6) & 0x03e0); + val |= ((blue >> 1) & 0x7c00); + + /* + * TODO: intensity bit. Maybe something like + * ~(red[10] ^ green[10] ^ blue[10]) & 1 + */ + + lcdc_writel(sinfo, LUT + regno * 4, val); + ret = 0; + } + break; + } + + return ret; +} + +static int sidsafb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct sidsafb_info *sinfo = info->par; + + pr_debug("sidsafb_pan_display\n"); + + sidsafb_update_dma(info, var); + + if (sinfo->wait_for_vsync) { + spin_lock_irq(&sinfo->lock); + lcdc_writel(sinfo, LCD_ICR, LCDC_BIT(LCD_ICR_EOFIC)); + lcdc_writel(sinfo, LCD_IER, LCDC_BIT(LCD_IER_EOFIE)); + init_completion(&sinfo->vsync_complete); + lcdc_readl(sinfo, LCD_IMR); + spin_unlock_irq(&sinfo->lock); + + wait_for_completion(&sinfo->vsync_complete); + + lcdc_writel(sinfo, LCD_IDR, LCDC_BIT(LCD_IDR_EOFID)); + } + + return 0; +} + +static struct fb_ops sidsafb_ops = { + .owner = THIS_MODULE, + .fb_check_var = sidsafb_check_var, + .fb_set_par = sidsafb_set_par, + .fb_setcolreg = sidsafb_setcolreg, + .fb_pan_display = sidsafb_pan_display, + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, +}; + +static irqreturn_t sidsafb_interrupt(int irq, void *dev_id) +{ + struct fb_info *info = dev_id; + struct sidsafb_info *sinfo = info->par; + u32 status; + + status = lcdc_readl(sinfo, LCD_ISR); + while (status) { + if (status & LCDC_BIT(LCD_ISR_EOFIS)) { + pr_debug("sidsafb: DMA End Of Frame interrupt\n"); + + lcdc_writel(sinfo, LCD_ICR, LCDC_BIT(LCD_ICR_EOFIC)); + status &= ~LCDC_BIT(LCD_ISR_EOFIS); + complete(&sinfo->vsync_complete); + } + + if (status) { + printk(KERN_ERR + "LCDC: Interrupts still pending: 0x%x\n", + status); + lcdc_writel(sinfo, LCD_IDR, status); + } + + status = lcdc_readl(sinfo, LCD_ISR); + } + + return IRQ_HANDLED; +} + +static void __devinit init_pseudo_palette(u32 *palette) +{ + static const u32 init_palette[16] = { + 0x000000, + 0xaa0000, + 0x00aa00, + 0xaa5500, + 0x0000aa, + 0xaa00aa, + 0x00aaaa, + 0xaaaaaa, + 0x555555, + 0xff5555, + 0x55ff55, + 0xffff55, + 0x5555ff, + 0xff55ff, + 0x55ffff, + 0xffffff + }; + + memcpy(palette, init_palette, sizeof(init_palette)); +} + +static int __devinit sidsafb_set_fbinfo(struct sidsafb_info *sinfo) +{ + struct fb_info *info = sinfo->info; + + init_pseudo_palette(sinfo->pseudo_palette); + + info->flags = (FBINFO_DEFAULT + | FBINFO_PARTIAL_PAN_OK + | FBINFO_HWACCEL_XPAN + | FBINFO_HWACCEL_YPAN); + memcpy(&info->fix, &sidsafb_fix, sizeof(info->fix)); + memcpy(&info->monspecs, &default_monspecs, sizeof(info->monspecs)); + info->fbops = &sidsafb_ops; + info->pseudo_palette = sinfo->pseudo_palette; + + return 0; +} + +static int __devinit sidsafb_probe(struct platform_device *pdev) +{ + struct lcdc_platform_data *fb_data = pdev->dev.platform_data; + struct fb_info *info; + struct sidsafb_info *sinfo; + const struct resource *mmio_resource; + int ret; + + pr_debug("sidsafb_probe BEGIN\n"); + + mmio_resource = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!mmio_resource) { + dev_err(&pdev->dev, "no MMIO resource found\n"); + return -ENXIO; + } + + ret = -ENOMEM; + info = framebuffer_alloc(sizeof(struct sidsafb_info), &pdev->dev); + if (!info) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + goto out; + } + + sinfo = info->par; + sinfo->info = info; + sinfo->pdev = pdev; + sinfo->guard_time = 1; + + spin_lock_init(&sinfo->lock); + sidsafb_set_fbinfo(sinfo); + info->fix.mmio_start = mmio_resource->start; + info->fix.mmio_len = mmio_resource->end - mmio_resource->start + 1; + sinfo->irq_base = platform_get_irq(pdev, 0); + + sinfo->hclk = clk_get(&pdev->dev, "hclk"); + if (IS_ERR(sinfo->hclk)) { + dev_err(&pdev->dev, "failed to get hclk\n"); + ret = PTR_ERR(sinfo->hclk); + goto free_info; + } + sinfo->pixclk = clk_get(&pdev->dev, "pixclk"); + if (IS_ERR(sinfo->pixclk)) { + dev_err(&pdev->dev, "failed to get pixel clock\n"); + ret = PTR_ERR(sinfo->hclk); + goto put_hclk; + } + + clk_enable(sinfo->hclk); + clk_enable(sinfo->pixclk); + + /* Use platform-supplied framebuffer memory if available */ + if (fb_data && fb_data->fbmem_size != 0) { + info->fix.smem_start = fb_data->fbmem_start; + info->fix.smem_len = fb_data->fbmem_size; + info->screen_base = ioremap(info->fix.smem_start, + info->fix.smem_len); + } else { + dma_addr_t paddr; + + info->fix.smem_len = fb_size; + info->screen_base = dma_alloc_coherent(&pdev->dev, fb_size, + &paddr, GFP_KERNEL); + info->fix.smem_start = paddr; + } + + if (!info->screen_base) { + printk(KERN_ERR "sidsafb: Could not allocate framebuffer\n"); + goto disable_clocks; + } + + sinfo->regs = ioremap(info->fix.mmio_start, info->fix.mmio_len); + if (!sinfo->regs) { + printk(KERN_ERR "sidsafb: Could not map LCDC registers\n"); + goto free_fb; + } + + ret = fb_find_mode(&info->var, info, NULL, info->monspecs.modedb, + info->monspecs.modedb_len, info->monspecs.modedb, + CONFIG_FB_SIDSA_DEFAULT_BPP); + if (!ret) { + printk(KERN_ERR "sidsafb: No suitable video mode found\n"); + goto unmap_regs; + } + + ret = request_irq(sinfo->irq_base, sidsafb_interrupt, 0, + "sidsafb", info); + if (ret) + goto unmap_regs; + + /* Allocate colormap */ + if (fb_alloc_cmap(&info->cmap, 256, 0)) { + ret = -ENOMEM; + goto unregister_irqs; + } + + platform_set_drvdata(pdev, info); + ret = device_create_file(&pdev->dev, &dev_attr_vsync_pan); + if (ret) + goto free_cmap; + + /* + * Tell the world that we're ready to go + */ + ret = register_framebuffer(info); + if (ret) + goto remove_attrs; + + printk("fb%d: Atmel LCDC at 0x%08lx (mapped at %p), irq %lu\n", + info->node, info->fix.mmio_start, sinfo->regs, sinfo->irq_base); + + memset_io(info->screen_base, 0, info->fix.smem_len); + info->var.activate |= FB_ACTIVATE_FORCE | FB_ACTIVATE_NOW; + ret = fb_set_var(info, &info->var); + if (ret) + printk(KERN_WARNING + "sidsafb: Unable to set display parameters\n"); + info->var.activate &= ~(FB_ACTIVATE_FORCE | FB_ACTIVATE_NOW); + + pr_debug("sidsafb_probe SUCCESS\n"); + return 0; + + +remove_attrs: + device_remove_file(&pdev->dev, &dev_attr_vsync_pan); +free_cmap: + fb_dealloc_cmap(&info->cmap); +unregister_irqs: + free_irq(sinfo->irq_base, info); +unmap_regs: + iounmap(sinfo->regs); +free_fb: + if (!fb_data || fb_data->fbmem_size == 0) + dma_free_coherent(&pdev->dev, info->fix.smem_len, + (void __force *)info->screen_base, + info->fix.smem_start); +disable_clocks: + clk_disable(sinfo->pixclk); + clk_disable(sinfo->hclk); + clk_put(sinfo->pixclk); +put_hclk: + clk_put(sinfo->hclk); +free_info: + framebuffer_release(info); +out: + pr_debug("sidsafb_probe FAILED\n"); + return ret; +} + +static int __devexit sidsafb_remove(struct platform_device *pdev) +{ + struct lcdc_platform_data *fb_data = pdev->dev.platform_data; + struct fb_info *info = platform_get_drvdata(pdev); + struct sidsafb_info *sinfo; + + if (!info) + return 0; + sinfo = info->par; + + /* TODO: Restore original state */ + unregister_framebuffer(info); + + device_remove_file(&pdev->dev, &dev_attr_vsync_pan); + + fb_dealloc_cmap(&info->cmap); + free_irq(sinfo->irq_base, info); + iounmap(sinfo->regs); + if (!fb_data || fb_data->fbmem_size == 0) + dma_free_coherent(&pdev->dev, info->fix.smem_len, + (void __force *)info->screen_base, + info->fix.smem_start); + clk_disable(sinfo->hclk); + clk_put(sinfo->hclk); + platform_set_drvdata(pdev, NULL); + framebuffer_release(info); + + return 0; +} + +static struct platform_driver sidsafb_driver = { + .probe = sidsafb_probe, + .remove = __devexit_p(sidsafb_remove), + .driver = { + .name = "lcdc", + }, +}; + +int __init sidsafb_init(void) +{ + return platform_driver_register(&sidsafb_driver); +} + +static void __exit sidsafb_exit(void) +{ + platform_driver_unregister(&sidsafb_driver); +} + +module_init(sidsafb_init); +module_exit(sidsafb_exit); + +module_param(fb_size, ulong, 0644); +MODULE_PARM_DESC(fb_size, "Minimum framebuffer size to allocate"); + +MODULE_AUTHOR("Haavard Skinnemoen <hskinnemoen@atmel.com>"); +MODULE_DESCRIPTION("Atmel/SIDSA LCD Controller framebuffer driver"); +MODULE_LICENSE("GPL"); diff -urN linux-2.6.20.4-0rig/include/asm-arm/arch-at91rm9200/at91_dbgu.h linux-2.6.20.4-atmel/include/asm-arm/arch-at91rm9200/at91_dbgu.h --- linux-2.6.20.4-0rig/include/asm-arm/arch-at91rm9200/at91_dbgu.h 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/include/asm-arm/arch-at91rm9200/at91_dbgu.h 2007-03-24 16:39:15.000000000 +0100 @@ -35,6 +35,20 @@ #define AT91_CIDR_NVPSIZ (0xf << 8) /* Nonvolatile Program Memory Size */ #define AT91_CIDR_NVPSIZ2 (0xf << 12) /* Second Nonvolatile Program Memory Size */ #define AT91_CIDR_SRAMSIZ (0xf << 16) /* Internal SRAM Size */ +#define AT91_CIDR_SRAMSIZ_1K (1 << 16) +#define AT91_CIDR_SRAMSIZ_2K (2 << 16) +#define AT91_CIDR_SRAMSIZ_112K (4 << 16) +#define AT91_CIDR_SRAMSIZ_4K (5 << 16) +#define AT91_CIDR_SRAMSIZ_80K (6 << 16) +#define AT91_CIDR_SRAMSIZ_160K (7 << 16) +#define AT91_CIDR_SRAMSIZ_8K (8 << 16) +#define AT91_CIDR_SRAMSIZ_16K (9 << 16) +#define AT91_CIDR_SRAMSIZ_32K (10 << 16) +#define AT91_CIDR_SRAMSIZ_64K (11 << 16) +#define AT91_CIDR_SRAMSIZ_128K (12 << 16) +#define AT91_CIDR_SRAMSIZ_256K (13 << 16) +#define AT91_CIDR_SRAMSIZ_96K (14 << 16) +#define AT91_CIDR_SRAMSIZ_512K (15 << 16) #define AT91_CIDR_ARCH (0xff << 20) /* Architecture Identifier */ #define AT91_CIDR_NVPTYP (7 << 28) /* Nonvolatile Program Memory Type */ #define AT91_CIDR_EXT (1 << 31) /* Extension Flag */ diff -urN linux-2.6.20.4-0rig/include/asm-arm/arch-at91rm9200/at91_mci.h linux-2.6.20.4-atmel/include/asm-arm/arch-at91rm9200/at91_mci.h --- linux-2.6.20.4-0rig/include/asm-arm/arch-at91rm9200/at91_mci.h 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/include/asm-arm/arch-at91rm9200/at91_mci.h 2007-03-24 16:39:15.000000000 +0100 @@ -26,6 +26,9 @@ #define AT91_MCI_MR 0x04 /* Mode Register */ #define AT91_MCI_CLKDIV (0xff << 0) /* Clock Divider */ #define AT91_MCI_PWSDIV (7 << 8) /* Power Saving Divider */ +#define AT91_MCI_RDPROOF (1 << 11) /* Read Proof Enable [SAM926[03] only] */ +#define AT91_MCI_WRPROOF (1 << 12) /* Write Proof Enable [SAM926[03] only] */ +#define AT91_MCI_PDCFBYTE (1 << 13) /* PDC Force Byte Transfer [SAM926[03] only] */ #define AT91_MCI_PDCPADV (1 << 14) /* PDC Padding Value */ #define AT91_MCI_PDCMODE (1 << 15) /* PDC-orientated Mode */ #define AT91_MCI_BLKLEN (0xfff << 18) /* Data Block Length */ diff -urN linux-2.6.20.4-0rig/include/asm-arm/arch-at91rm9200/at91_pdc.h linux-2.6.20.4-atmel/include/asm-arm/arch-at91rm9200/at91_pdc.h --- linux-2.6.20.4-0rig/include/asm-arm/arch-at91rm9200/at91_pdc.h 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/include/asm-arm/arch-at91rm9200/at91_pdc.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,36 +0,0 @@ -/* - * include/asm-arm/arch-at91rm9200/at91_pdc.h - * - * Copyright (C) 2005 Ivan Kokshaysky - * Copyright (C) SAN People - * - * Peripheral Data Controller (PDC) registers. - * Based on AT91RM9200 datasheet revision E. - * - * 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. - */ - -#ifndef AT91_PDC_H -#define AT91_PDC_H - -#define AT91_PDC_RPR 0x100 /* Receive Pointer Register */ -#define AT91_PDC_RCR 0x104 /* Receive Counter Register */ -#define AT91_PDC_TPR 0x108 /* Transmit Pointer Register */ -#define AT91_PDC_TCR 0x10c /* Transmit Counter Register */ -#define AT91_PDC_RNPR 0x110 /* Receive Next Pointer Register */ -#define AT91_PDC_RNCR 0x114 /* Receive Next Counter Register */ -#define AT91_PDC_TNPR 0x118 /* Transmit Next Pointer Register */ -#define AT91_PDC_TNCR 0x11c /* Transmit Next Counter Register */ - -#define AT91_PDC_PTCR 0x120 /* Transfer Control Register */ -#define AT91_PDC_RXTEN (1 << 0) /* Receiver Transfer Enable */ -#define AT91_PDC_RXTDIS (1 << 1) /* Receiver Transfer Disable */ -#define AT91_PDC_TXTEN (1 << 8) /* Transmitter Transfer Enable */ -#define AT91_PDC_TXTDIS (1 << 9) /* Transmitter Transfer Disable */ - -#define AT91_PDC_PTSR 0x124 /* Transfer Status Register */ - -#endif diff -urN linux-2.6.20.4-0rig/include/asm-arm/arch-at91rm9200/at91_rstc.h linux-2.6.20.4-atmel/include/asm-arm/arch-at91rm9200/at91_rstc.h --- linux-2.6.20.4-0rig/include/asm-arm/arch-at91rm9200/at91_rstc.h 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/include/asm-arm/arch-at91rm9200/at91_rstc.h 2007-03-24 16:39:16.000000000 +0100 @@ -17,7 +17,7 @@ #define AT91_RSTC_PROCRST (1 << 0) /* Processor Reset */ #define AT91_RSTC_PERRST (1 << 2) /* Peripheral Reset */ #define AT91_RSTC_EXTRST (1 << 3) /* External Reset */ -#define AT91_RSTC_KEY (0xff << 24) /* KEY Password */ +#define AT91_RSTC_KEY (0xa5 << 24) /* KEY Password */ #define AT91_RSTC_SR (AT91_RSTC + 0x04) /* Reset Controller Status Register */ #define AT91_RSTC_URSTS (1 << 0) /* User Reset Status */ @@ -34,6 +34,5 @@ #define AT91_RSTC_URSTEN (1 << 0) /* User Reset Enable */ #define AT91_RSTC_URSTIEN (1 << 4) /* User Reset Interrupt Enable */ #define AT91_RSTC_ERSTL (0xf << 8) /* External Reset Length */ -#define AT91_RSTC_KEY (0xff << 24) /* KEY Password */ #endif diff -urN linux-2.6.20.4-0rig/include/asm-arm/arch-at91rm9200/at91sam9260.h linux-2.6.20.4-atmel/include/asm-arm/arch-at91rm9200/at91sam9260.h --- linux-2.6.20.4-0rig/include/asm-arm/arch-at91rm9200/at91sam9260.h 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/include/asm-arm/arch-at91rm9200/at91sam9260.h 2007-03-24 16:39:16.000000000 +0100 @@ -113,6 +113,10 @@ #define AT91SAM9260_UHP_BASE 0x00500000 /* USB Host controller */ +#define AT91SAM9XE_FLASH_BASE 0x00200000 /* Internal FLASH base address */ +#define AT91SAM9XE_SRAM_BASE 0x00300000 /* Internal SRAM base address */ + + #if 0 /* * PIO pin definitions (peripheral A/B multiplexing). diff -urN linux-2.6.20.4-0rig/include/asm-arm/arch-at91rm9200/at91sam9260_matrix.h linux-2.6.20.4-atmel/include/asm-arm/arch-at91rm9200/at91sam9260_matrix.h --- linux-2.6.20.4-0rig/include/asm-arm/arch-at91rm9200/at91sam9260_matrix.h 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/include/asm-arm/arch-at91rm9200/at91sam9260_matrix.h 2007-03-24 16:39:16.000000000 +0100 @@ -18,7 +18,7 @@ #define AT91_MATRIX_MCFG2 (AT91_MATRIX + 0x08) /* Master Configuration Register 2 */ #define AT91_MATRIX_MCFG3 (AT91_MATRIX + 0x0C) /* Master Configuration Register 3 */ #define AT91_MATRIX_MCFG4 (AT91_MATRIX + 0x10) /* Master Configuration Register 4 */ -#define AT91_MATRIX_MCFG5 (AT91_MATRIX + 0x04) /* Master Configuration Register 5 */ +#define AT91_MATRIX_MCFG5 (AT91_MATRIX + 0x14) /* Master Configuration Register 5 */ #define AT91_MATRIX_ULBT (7 << 0) /* Undefined Length Burst Type */ #define AT91_MATRIX_ULBT_INFINITE (0 << 0) #define AT91_MATRIX_ULBT_SINGLE (1 << 0) diff -urN linux-2.6.20.4-0rig/include/asm-arm/arch-at91rm9200/at91sam9263.h linux-2.6.20.4-atmel/include/asm-arm/arch-at91rm9200/at91sam9263.h --- linux-2.6.20.4-0rig/include/asm-arm/arch-at91rm9200/at91sam9263.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/include/asm-arm/arch-at91rm9200/at91sam9263.h 2007-03-24 16:39:16.000000000 +0100 @@ -0,0 +1,131 @@ +/* + * include/asm-arm/arch-at91rm9200/at91sam9263.h + * + * (C) 2007 Atmel Corporation. + * + * Common definitions. + * Based on AT91SAM9263 datasheet revision B (Preliminary). + * + * 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. + */ + +#ifndef AT91SAM9263_H +#define AT91SAM9263_H + +/* + * Peripheral identifiers/interrupts. + */ +#define AT91_ID_FIQ 0 /* Advanced Interrupt Controller (FIQ) */ +#define AT91_ID_SYS 1 /* System Peripherals */ +#define AT91SAM9263_ID_PIOA 2 /* Parallel IO Controller A */ +#define AT91SAM9263_ID_PIOB 3 /* Parallel IO Controller B */ +#define AT91SAM9263_ID_PIOCDE 4 /* Parallel IO Controller C, D and E */ +#define AT91SAM9263_ID_US0 7 /* USART 0 */ +#define AT91SAM9263_ID_US1 8 /* USART 1 */ +#define AT91SAM9263_ID_US2 9 /* USART 2 */ +#define AT91SAM9263_ID_MCI0 10 /* Multimedia Card Interface 0 */ +#define AT91SAM9263_ID_MCI1 11 /* Multimedia Card Interface 1 */ +#define AT91SAM9263_ID_CAN 12 /* CAN */ +#define AT91SAM9263_ID_TWI 13 /* Two-Wire Interface */ +#define AT91SAM9263_ID_SPI0 14 /* Serial Peripheral Interface 0 */ +#define AT91SAM9263_ID_SPI1 15 /* Serial Peripheral Interface 1 */ +#define AT91SAM9263_ID_SSC0 16 /* Serial Synchronous Controller 0 */ +#define AT91SAM9263_ID_SSC1 17 /* Serial Synchronous Controller 1 */ +#define AT91SAM9263_ID_AC97C 18 /* AC97 Controller */ +#define AT91SAM9263_ID_TCB 19 /* Timer Counter 0, 1 and 2 */ +#define AT91SAM9263_ID_PWMC 20 /* Pulse Width Modulation Controller */ +#define AT91SAM9263_ID_EMAC 21 /* Ethernet */ +#define AT91SAM9263_ID_2DGE 23 /* 2D Graphic Engine */ +#define AT91SAM9263_ID_UDP 24 /* USB Device Port */ +#define AT91SAM9263_ID_ISI 25 /* Image Sensor Interface */ +#define AT91SAM9263_ID_LCDC 26 /* LCD Controller */ +#define AT91SAM9263_ID_DMA 27 /* DMA Controller */ +#define AT91SAM9263_ID_UHP 29 /* USB Host port */ +#define AT91SAM9263_ID_IRQ0 30 /* Advanced Interrupt Controller (IRQ0) */ +#define AT91SAM9263_ID_IRQ1 31 /* Advanced Interrupt Controller (IRQ1) */ + + +/* + * User Peripheral physical base addresses. + */ +#define AT91SAM9263_BASE_UDP 0xfff78000 +#define AT91SAM9263_BASE_TCB0 0xfff7c000 +#define AT91SAM9263_BASE_TC0 0xfff7c000 +#define AT91SAM9263_BASE_TC1 0xfff7c040 +#define AT91SAM9263_BASE_TC2 0xfff7c080 +#define AT91SAM9263_BASE_MCI0 0xfff80000 +#define AT91SAM9263_BASE_MCI1 0xfff84000 +#define AT91SAM9263_BASE_TWI 0xfff88000 +#define AT91SAM9263_BASE_US0 0xfff8c000 +#define AT91SAM9263_BASE_US1 0xfff90000 +#define AT91SAM9263_BASE_US2 0xfff94000 +#define AT91SAM9263_BASE_SSC0 0xfff98000 +#define AT91SAM9263_BASE_SSC1 0xfff9c000 +#define AT91SAM9263_BASE_AC97C 0xfffa0000 +#define AT91SAM9263_BASE_SPI0 0xfffa4000 +#define AT91SAM9263_BASE_SPI1 0xfffa8000 +#define AT91SAM9263_BASE_CAN 0xfffac000 +#define AT91SAM9263_BASE_PWMC 0xfffb8000 +#define AT91SAM9263_BASE_EMAC 0xfffbc000 +#define AT91SAM9263_BASE_ISI 0xfffc4000 +#define AT91SAM9263_BASE_2DGE 0xfffc8000 +#define AT91_BASE_SYS 0xffffe000 + +/* + * System Peripherals (offset from AT91_BASE_SYS) + */ +#define AT91_ECC0 (0xffffe000 - AT91_BASE_SYS) +#define AT91_SDRAMC0 (0xffffe200 - AT91_BASE_SYS) +#define AT91_SMC0 (0xffffe400 - AT91_BASE_SYS) +#define AT91_ECC1 (0xffffe600 - AT91_BASE_SYS) +#define AT91_SDRAMC1 (0xffffe800 - AT91_BASE_SYS) +#define AT91_SMC1 (0xffffea00 - AT91_BASE_SYS) +#define AT91_MATRIX (0xffffec00 - AT91_BASE_SYS) +#define AT91_CCFG (0xffffed10 - AT91_BASE_SYS) +#define AT91_DBGU (0xffffee00 - AT91_BASE_SYS) +#define AT91_AIC (0xfffff000 - AT91_BASE_SYS) +#define AT91_PIOA (0xfffff200 - AT91_BASE_SYS) +#define AT91_PIOB (0xfffff400 - AT91_BASE_SYS) +#define AT91_PIOC (0xfffff600 - AT91_BASE_SYS) +#define AT91_PIOD (0xfffff800 - AT91_BASE_SYS) +#define AT91_PIOE (0xfffffa00 - AT91_BASE_SYS) +#define AT91_PMC (0xfffffc00 - AT91_BASE_SYS) +#define AT91_RSTC (0xfffffd00 - AT91_BASE_SYS) +#define AT91_SHDWC (0xfffffd10 - AT91_BASE_SYS) +#define AT91_RTT0 (0xfffffd20 - AT91_BASE_SYS) +#define AT91_PIT (0xfffffd30 - AT91_BASE_SYS) +#define AT91_WDT (0xfffffd40 - AT91_BASE_SYS) +#define AT91_RTT1 (0xfffffd50 - AT91_BASE_SYS) +#define AT91_GPBR (0xfffffd60 - AT91_BASE_SYS) + +#define AT91_SMC AT91_SMC0 + +/* + * Internal Memory. + */ +#define AT91SAM9263_SRAM0_BASE 0x00300000 /* Internal SRAM 0 base address */ +#define AT91SAM9263_SRAM0_SIZE (80 * SZ_1K) /* Internal SRAM 0 size (80Kb) */ + +#define AT91SAM9263_ROM_BASE 0x00400000 /* Internal ROM base address */ +#define AT91SAM9263_ROM_SIZE SZ_128K /* Internal ROM size (128Kb) */ + +#define AT91SAM9263_SRAM1_BASE 0x00500000 /* Internal SRAM 1 base address */ +#define AT91SAM9263_SRAM1_SIZE SZ_16K /* Internal SRAM 1 size (16Kb) */ + +#define AT91SAM9263_LCDC_BASE 0x00700000 /* LCD Controller */ +#define AT91SAM9263_DMAC_BASE 0x00800000 /* DMA Controller */ +#define AT91SAM9263_UHP_BASE 0x00a00000 /* USB Host controller */ + +#if 0 +/* + * PIO pin definitions (peripheral A/B multiplexing). + */ + +// TODO: Add + +#endif + +#endif diff -urN linux-2.6.20.4-0rig/include/asm-arm/arch-at91rm9200/at91sam9263_matrix.h linux-2.6.20.4-atmel/include/asm-arm/arch-at91rm9200/at91sam9263_matrix.h --- linux-2.6.20.4-0rig/include/asm-arm/arch-at91rm9200/at91sam9263_matrix.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/include/asm-arm/arch-at91rm9200/at91sam9263_matrix.h 2007-03-24 16:39:16.000000000 +0100 @@ -0,0 +1,129 @@ +/* + * include/asm-arm/arch-at91rm9200/at91sam9263_matrix.h + * + * Copyright (C) 2006 Atmel Corporation. + * + * Memory Controllers (MATRIX, EBI) - System peripherals registers. + * Based on AT91SAM9263 datasheet revision B (Preliminary). + * + * 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. + */ + +#ifndef AT91SAM9263_MATRIX_H +#define AT91SAM9263_MATRIX_H + +#define AT91_MATRIX_MCFG0 (AT91_MATRIX + 0x00) /* Master Configuration Register 0 */ +#define AT91_MATRIX_MCFG1 (AT91_MATRIX + 0x04) /* Master Configuration Register 1 */ +#define AT91_MATRIX_MCFG2 (AT91_MATRIX + 0x08) /* Master Configuration Register 2 */ +#define AT91_MATRIX_MCFG3 (AT91_MATRIX + 0x0C) /* Master Configuration Register 3 */ +#define AT91_MATRIX_MCFG4 (AT91_MATRIX + 0x10) /* Master Configuration Register 4 */ +#define AT91_MATRIX_MCFG5 (AT91_MATRIX + 0x14) /* Master Configuration Register 5 */ +#define AT91_MATRIX_MCFG6 (AT91_MATRIX + 0x18) /* Master Configuration Register 6 */ +#define AT91_MATRIX_MCFG7 (AT91_MATRIX + 0x1C) /* Master Configuration Register 7 */ +#define AT91_MATRIX_MCFG8 (AT91_MATRIX + 0x20) /* Master Configuration Register 8 */ +#define AT91_MATRIX_ULBT (7 << 0) /* Undefined Length Burst Type */ +#define AT91_MATRIX_ULBT_INFINITE (0 << 0) +#define AT91_MATRIX_ULBT_SINGLE (1 << 0) +#define AT91_MATRIX_ULBT_FOUR (2 << 0) +#define AT91_MATRIX_ULBT_EIGHT (3 << 0) +#define AT91_MATRIX_ULBT_SIXTEEN (4 << 0) + +#define AT91_MATRIX_SCFG0 (AT91_MATRIX + 0x40) /* Slave Configuration Register 0 */ +#define AT91_MATRIX_SCFG1 (AT91_MATRIX + 0x44) /* Slave Configuration Register 1 */ +#define AT91_MATRIX_SCFG2 (AT91_MATRIX + 0x48) /* Slave Configuration Register 2 */ +#define AT91_MATRIX_SCFG3 (AT91_MATRIX + 0x4C) /* Slave Configuration Register 3 */ +#define AT91_MATRIX_SCFG4 (AT91_MATRIX + 0x50) /* Slave Configuration Register 4 */ +#define AT91_MATRIX_SCFG5 (AT91_MATRIX + 0x54) /* Slave Configuration Register 5 */ +#define AT91_MATRIX_SCFG6 (AT91_MATRIX + 0x58) /* Slave Configuration Register 6 */ +#define AT91_MATRIX_SCFG7 (AT91_MATRIX + 0x5C) /* Slave Configuration Register 7 */ +#define AT91_MATRIX_SLOT_CYCLE (0xff << 0) /* Maximum Number of Allowed Cycles for a Burst */ +#define AT91_MATRIX_DEFMSTR_TYPE (3 << 16) /* Default Master Type */ +#define AT91_MATRIX_DEFMSTR_TYPE_NONE (0 << 16) +#define AT91_MATRIX_DEFMSTR_TYPE_LAST (1 << 16) +#define AT91_MATRIX_DEFMSTR_TYPE_FIXED (2 << 16) +#define AT91_MATRIX_FIXED_DEFMSTR (7 << 18) /* Fixed Index of Default Master */ +#define AT91_MATRIX_ARBT (3 << 24) /* Arbitration Type */ +#define AT91_MATRIX_ARBT_ROUND_ROBIN (0 << 24) +#define AT91_MATRIX_ARBT_FIXED_PRIORITY (1 << 24) + +#define AT91_MATRIX_PRAS0 (AT91_MATRIX + 0x80) /* Priority Register A for Slave 0 */ +#define AT91_MATRIX_PRBS0 (AT91_MATRIX + 0x84) /* Priority Register B for Slave 0 */ +#define AT91_MATRIX_PRAS1 (AT91_MATRIX + 0x88) /* Priority Register A for Slave 1 */ +#define AT91_MATRIX_PRBS1 (AT91_MATRIX + 0x8C) /* Priority Register B for Slave 1 */ +#define AT91_MATRIX_PRAS2 (AT91_MATRIX + 0x90) /* Priority Register A for Slave 2 */ +#define AT91_MATRIX_PRBS2 (AT91_MATRIX + 0x94) /* Priority Register B for Slave 2 */ +#define AT91_MATRIX_PRAS3 (AT91_MATRIX + 0x98) /* Priority Register A for Slave 3 */ +#define AT91_MATRIX_PRBS3 (AT91_MATRIX + 0x9C) /* Priority Register B for Slave 3 */ +#define AT91_MATRIX_PRAS4 (AT91_MATRIX + 0xA0) /* Priority Register A for Slave 4 */ +#define AT91_MATRIX_PRBS4 (AT91_MATRIX + 0xA4) /* Priority Register B for Slave 4 */ +#define AT91_MATRIX_PRAS5 (AT91_MATRIX + 0xA8) /* Priority Register A for Slave 5 */ +#define AT91_MATRIX_PRBS5 (AT91_MATRIX + 0xAC) /* Priority Register B for Slave 5 */ +#define AT91_MATRIX_PRAS6 (AT91_MATRIX + 0xB0) /* Priority Register A for Slave 6 */ +#define AT91_MATRIX_PRBS6 (AT91_MATRIX + 0xB4) /* Priority Register B for Slave 6 */ +#define AT91_MATRIX_PRAS7 (AT91_MATRIX + 0xB8) /* Priority Register A for Slave 7 */ +#define AT91_MATRIX_PRBS7 (AT91_MATRIX + 0xBC) /* Priority Register B for Slave 7 */ +#define AT91_MATRIX_M0PR (3 << 0) /* Master 0 Priority */ +#define AT91_MATRIX_M1PR (3 << 4) /* Master 1 Priority */ +#define AT91_MATRIX_M2PR (3 << 8) /* Master 2 Priority */ +#define AT91_MATRIX_M3PR (3 << 12) /* Master 3 Priority */ +#define AT91_MATRIX_M4PR (3 << 16) /* Master 4 Priority */ +#define AT91_MATRIX_M5PR (3 << 20) /* Master 5 Priority */ +#define AT91_MATRIX_M6PR (3 << 24) /* Master 6 Priority */ +#define AT91_MATRIX_M7PR (3 << 28) /* Master 7 Priority */ +#define AT91_MATRIX_M8PR (3 << 0) /* Master 8 Priority (in Register B) */ + +#define AT91_MATRIX_MRCR (AT91_MATRIX + 0x100) /* Master Remap Control Register */ +#define AT91_MATRIX_RCB0 (1 << 0) /* Remap Command for AHB Master 0 (ARM926EJ-S Instruction Master) */ +#define AT91_MATRIX_RCB1 (1 << 1) /* Remap Command for AHB Master 1 (ARM926EJ-S Data Master) */ +#define AT91_MATRIX_RCB2 (1 << 2) +#define AT91_MATRIX_RCB3 (1 << 3) +#define AT91_MATRIX_RCB4 (1 << 4) +#define AT91_MATRIX_RCB5 (1 << 5) +#define AT91_MATRIX_RCB6 (1 << 6) +#define AT91_MATRIX_RCB7 (1 << 7) +#define AT91_MATRIX_RCB8 (1 << 8) + +#define AT91_MATRIX_TCMR (AT91_MATRIX + 0x114) /* TCM Configuration Register */ +#define AT91_MATRIX_ITCM_SIZE (0xf << 0) /* Size of ITCM enabled memory block */ +#define AT91_MATRIX_ITCM_0 (0 << 0) +#define AT91_MATRIX_ITCM_16 (5 << 0) +#define AT91_MATRIX_ITCM_32 (6 << 0) +#define AT91_MATRIX_DTCM_SIZE (0xf << 4) /* Size of DTCM enabled memory block */ +#define AT91_MATRIX_DTCM_0 (0 << 4) +#define AT91_MATRIX_DTCM_16 (5 << 4) +#define AT91_MATRIX_DTCM_32 (6 << 4) + +#define AT91_MATRIX_EBI0CSA (AT91_MATRIX + 0x120) /* EBI0 Chip Select Assignment Register */ +#define AT91_MATRIX_EBI0_CS1A (1 << 1) /* Chip Select 1 Assignment */ +#define AT91_MATRIX_EBI0_CS1A_SMC (0 << 1) +#define AT91_MATRIX_EBI0_CS1A_SDRAMC (1 << 1) +#define AT91_MATRIX_EBI0_CS3A (1 << 3) /* Chip Select 3 Assignment */ +#define AT91_MATRIX_EBI0_CS3A_SMC (0 << 3) +#define AT91_MATRIX_EBI0_CS3A_SMC_SMARTMEDIA (1 << 3) +#define AT91_MATRIX_EBI0_CS4A (1 << 4) /* Chip Select 4 Assignment */ +#define AT91_MATRIX_EBI0_CS4A_SMC (0 << 4) +#define AT91_MATRIX_EBI0_CS4A_SMC_CF1 (1 << 4) +#define AT91_MATRIX_EBI0_CS5A (1 << 5) /* Chip Select 5 Assignment */ +#define AT91_MATRIX_EBI0_CS5A_SMC (0 << 5) +#define AT91_MATRIX_EBI0_CS5A_SMC_CF2 (1 << 5) +#define AT91_MATRIX_EBI0_DBPUC (1 << 8) /* Data Bus Pull-up Configuration */ +#define AT91_MATRIX_EBI0_VDDIOMSEL (1 << 16) /* Memory voltage selection */ +#define AT91_MATRIX_EBI0_VDDIOMSEL_1_8V (0 << 16) +#define AT91_MATRIX_EBI0_VDDIOMSEL_3_3V (1 << 16) + +#define AT91_MATRIX_EBI1CSA (AT91_MATRIX + 0x124) /* EBI1 Chip Select Assignment Register */ +#define AT91_MATRIX_EBI1_CS1A (1 << 1) /* Chip Select 1 Assignment */ +#define AT91_MATRIX_EBI1_CS1A_SMC (0 << 1) +#define AT91_MATRIX_EBI1_CS1A_SDRAMC (1 << 1) +#define AT91_MATRIX_EBI1_CS2A (1 << 3) /* Chip Select 3 Assignment */ +#define AT91_MATRIX_EBI1_CS2A_SMC (0 << 3) +#define AT91_MATRIX_EBI1_CS2A_SMC_SMARTMEDIA (1 << 3) +#define AT91_MATRIX_EBI1_DBPUC (1 << 8) /* Data Bus Pull-up Configuration */ +#define AT91_MATRIX_EBI1_VDDIOMSEL (1 << 16) /* Memory voltage selection */ +#define AT91_MATRIX_EBI1_VDDIOMSEL_1_8V (0 << 16) +#define AT91_MATRIX_EBI1_VDDIOMSEL_3_3V (1 << 16) + +#endif diff -urN linux-2.6.20.4-0rig/include/asm-arm/arch-at91rm9200/at91sam926x_mc.h linux-2.6.20.4-atmel/include/asm-arm/arch-at91rm9200/at91sam926x_mc.h --- linux-2.6.20.4-0rig/include/asm-arm/arch-at91rm9200/at91sam926x_mc.h 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/include/asm-arm/arch-at91rm9200/at91sam926x_mc.h 2007-03-24 16:39:16.000000000 +0100 @@ -131,4 +131,11 @@ #define AT91_SMC_PS_16 (2 << 28) #define AT91_SMC_PS_32 (3 << 28) +#if defined(AT91_SMC1) /* The AT91SAM9263 has 2 Static Memory contollers */ +#define AT91_SMC1_SETUP(n) (AT91_SMC1 + 0x00 + ((n)*0x10)) /* Setup Register for CS n */ +#define AT91_SMC1_PULSE(n) (AT91_SMC1 + 0x04 + ((n)*0x10)) /* Pulse Register for CS n */ +#define AT91_SMC1_CYCLE(n) (AT91_SMC1 + 0x08 + ((n)*0x10)) /* Cycle Register for CS n */ +#define AT91_SMC1_MODE(n) (AT91_SMC1 + 0x0c + ((n)*0x10)) /* Mode Register for CS n */ +#endif + #endif diff -urN linux-2.6.20.4-0rig/include/asm-arm/arch-at91rm9200/board.h linux-2.6.20.4-atmel/include/asm-arm/arch-at91rm9200/board.h --- linux-2.6.20.4-0rig/include/asm-arm/arch-at91rm9200/board.h 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/include/asm-arm/arch-at91rm9200/board.h 2007-03-24 16:39:16.000000000 +0100 @@ -34,6 +34,7 @@ #include <linux/mtd/partitions.h> #include <linux/device.h> #include <linux/spi/spi.h> +#include <video/atmel_lcdc.h> /* USB Device */ struct at91_udc_data { @@ -60,18 +61,23 @@ u8 wp_pin; /* (SD) writeprotect detect */ u8 vcc_pin; /* power switching (high == on) */ }; -extern void __init at91_add_device_mmc(struct at91_mmc_data *data); +extern void __init at91_add_device_mmc(short mmc_id, struct at91_mmc_data *data); - /* Ethernet */ +/* Ethernet (EMAC & MACB) */ struct at91_eth_data { u8 phy_irq_pin; /* PHY IRQ */ u8 is_rmii; /* using RMII interface? */ }; extern void __init at91_add_device_eth(struct at91_eth_data *data); +#if defined(CONFIG_ARCH_AT91SAM9260) || defined(CONFIG_ARCH_AT91SAM9263) +#define eth_platform_data at91_eth_data +#endif + /* USB Host */ struct at91_usbh_data { u8 ports; /* number of ports on root hub */ + u8 vbus_pin[]; /* port power switch */ }; extern void __init at91_add_device_usbh(struct at91_usbh_data *data); @@ -93,6 +99,9 @@ /* SPI */ extern void __init at91_add_device_spi(struct spi_board_info *devices, int nr_devices); + /* LCD Controler */ +extern void __init at91_add_device_lcdc(struct atmel_lcdfb_info *data); + /* Serial */ struct at91_uart_config { unsigned short console_tty; /* tty number of serial console */ @@ -114,4 +123,13 @@ extern u8 at91_leds_timer; extern void __init at91_init_leds(u8 cpu_led, u8 timer_led); +struct at91_gpio_led { + u8 index; /* index of LED */ + char* name; /* name of LED */ + u8 gpio; /* AT91_PIN_xx */ + u8 flags; /* 1=active-high */ + char* trigger; /* default trigger */ +}; +extern void __init at91_gpio_leds(struct at91_gpio_led *leds, int nr); + #endif diff -urN linux-2.6.20.4-0rig/include/asm-arm/arch-at91rm9200/cpu.h linux-2.6.20.4-atmel/include/asm-arm/arch-at91rm9200/cpu.h --- linux-2.6.20.4-0rig/include/asm-arm/arch-at91rm9200/cpu.h 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/include/asm-arm/arch-at91rm9200/cpu.h 2007-03-24 16:39:16.000000000 +0100 @@ -20,7 +20,11 @@ #define ARCH_ID_AT91RM9200 0x09290780 #define ARCH_ID_AT91SAM9260 0x019803a0 #define ARCH_ID_AT91SAM9261 0x019703a0 +#define ARCH_ID_AT91SAM9263 0x019607a0 +#define ARCH_ID_AT91SAM9XE128 0x329973a0 +#define ARCH_ID_AT91SAM9XE256 0x329a93a0 +#define ARCH_ID_AT91SAM9XE512 0x329aa3a0 static inline unsigned long at91_cpu_identify(void) { @@ -28,6 +32,16 @@ } +#define ARCH_FAMILY_AT91X92 0x09200000 +#define ARCH_FAMILY_AT91SAM9 0x01900000 +#define ARCH_FAMILY_AT91SAM9XE 0x02900000 + +static inline unsigned long at91_arch_identify(void) +{ + return (at91_sys_read(AT91_DBGU_CIDR) & AT91_CIDR_ARCH); +} + + #ifdef CONFIG_ARCH_AT91RM9200 #define cpu_is_at91rm9200() (at91_cpu_identify() == ARCH_ID_AT91RM9200) #else @@ -35,8 +49,10 @@ #endif #ifdef CONFIG_ARCH_AT91SAM9260 -#define cpu_is_at91sam9260() (at91_cpu_identify() == ARCH_ID_AT91SAM9260) +#define cpu_is_at91sam9xe() (at91_arch_identify() == ARCH_FAMILY_AT91SAM9XE) +#define cpu_is_at91sam9260() ((at91_cpu_identify() == ARCH_ID_AT91SAM9260) || cpu_is_at91sam9xe()) #else +#define cpu_is_at91sam9xe() (0) #define cpu_is_at91sam9260() (0) #endif @@ -46,4 +62,10 @@ #define cpu_is_at91sam9261() (0) #endif +#ifdef CONFIG_ARCH_AT91SAM9263 +#define cpu_is_at91sam9263() (at91_cpu_identify() == ARCH_ID_AT91SAM9263) +#else +#define cpu_is_at91sam9263() (0) +#endif + #endif diff -urN linux-2.6.20.4-0rig/include/asm-arm/arch-at91rm9200/debug-macro.S linux-2.6.20.4-atmel/include/asm-arm/arch-at91rm9200/debug-macro.S --- linux-2.6.20.4-0rig/include/asm-arm/arch-at91rm9200/debug-macro.S 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/include/asm-arm/arch-at91rm9200/debug-macro.S 2007-03-24 16:39:16.000000000 +0100 @@ -16,24 +16,24 @@ .macro addruart,rx mrc p15, 0, \rx, c1, c0 - tst \rx, #1 @ MMU enabled? - ldreq \rx, =AT91_BASE_SYS @ System peripherals (phys address) - ldrne \rx, =AT91_VA_BASE_SYS @ System peripherals (virt address) + tst \rx, #1 @ MMU enabled? + ldreq \rx, =(AT91_BASE_SYS + AT91_DBGU) @ System peripherals (phys address) + ldrne \rx, =(AT91_VA_BASE_SYS + AT91_DBGU) @ System peripherals (virt address) .endm .macro senduart,rd,rx - strb \rd, [\rx, #AT91_DBGU_THR] @ Write to Transmitter Holding Register + strb \rd, [\rx, #(AT91_DBGU_THR - AT91_DBGU)] @ Write to Transmitter Holding Register .endm .macro waituart,rd,rx -1001: ldr \rd, [\rx, #AT91_DBGU_SR] @ Read Status Register - tst \rd, #AT91_DBGU_TXRDY @ DBGU_TXRDY = 1 when ready to transmit +1001: ldr \rd, [\rx, #(AT91_DBGU_SR - AT91_DBGU)] @ Read Status Register + tst \rd, #AT91_DBGU_TXRDY @ DBGU_TXRDY = 1 when ready to transmit beq 1001b .endm .macro busyuart,rd,rx -1001: ldr \rd, [\rx, #AT91_DBGU_SR] @ Read Status Register - tst \rd, #AT91_DBGU_TXEMPTY @ DBGU_TXEMPTY = 1 when transmission complete +1001: ldr \rd, [\rx, #(AT91_DBGU_SR - AT91_DBGU)] @ Read Status Register + tst \rd, #AT91_DBGU_TXEMPTY @ DBGU_TXEMPTY = 1 when transmission complete beq 1001b .endm diff -urN linux-2.6.20.4-0rig/include/asm-arm/arch-at91rm9200/entry-macro.S linux-2.6.20.4-atmel/include/asm-arm/arch-at91rm9200/entry-macro.S --- linux-2.6.20.4-0rig/include/asm-arm/arch-at91rm9200/entry-macro.S 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/include/asm-arm/arch-at91rm9200/entry-macro.S 2007-03-24 16:39:16.000000000 +0100 @@ -17,10 +17,10 @@ .endm .macro get_irqnr_and_base, irqnr, irqstat, base, tmp - ldr \base, =(AT91_VA_BASE_SYS) @ base virtual address of SYS peripherals - ldr \irqnr, [\base, #AT91_AIC_IVR] @ read IRQ vector register: de-asserts nIRQ to processor (and clears interrupt) - ldr \irqstat, [\base, #AT91_AIC_ISR] @ read interrupt source number - teq \irqstat, #0 @ ISR is 0 when no current interrupt, or spurious interrupt - streq \tmp, [\base, #AT91_AIC_EOICR] @ not going to be handled further, then ACK it now. + ldr \base, =(AT91_VA_BASE_SYS + AT91_AIC) @ base virtual address of AIC peripheral + ldr \irqnr, [\base, #(AT91_AIC_IVR - AT91_AIC)] @ read IRQ vector register: de-asserts nIRQ to processor (and clears interrupt) + ldr \irqstat, [\base, #(AT91_AIC_ISR - AT91_AIC)] @ read interrupt source number + teq \irqstat, #0 @ ISR is 0 when no current interrupt, or spurious interrupt + streq \tmp, [\base, #(AT91_AIC_EOICR - AT91_AIC)] @ not going to be handled further, then ACK it now. .endm diff -urN linux-2.6.20.4-0rig/include/asm-arm/arch-at91rm9200/gpio.h linux-2.6.20.4-atmel/include/asm-arm/arch-at91rm9200/gpio.h --- linux-2.6.20.4-0rig/include/asm-arm/arch-at91rm9200/gpio.h 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/include/asm-arm/arch-at91rm9200/gpio.h 2007-03-24 16:39:16.000000000 +0100 @@ -17,7 +17,7 @@ #define PIN_BASE NR_AIC_IRQS -#define MAX_GPIO_BANKS 4 +#define MAX_GPIO_BANKS 5 /* these pin numbers double as IRQ numbers, like AT91xxx_ID_* values */ @@ -26,37 +26,31 @@ #define AT91_PIN_PA2 (PIN_BASE + 0x00 + 2) #define AT91_PIN_PA3 (PIN_BASE + 0x00 + 3) #define AT91_PIN_PA4 (PIN_BASE + 0x00 + 4) - #define AT91_PIN_PA5 (PIN_BASE + 0x00 + 5) #define AT91_PIN_PA6 (PIN_BASE + 0x00 + 6) #define AT91_PIN_PA7 (PIN_BASE + 0x00 + 7) #define AT91_PIN_PA8 (PIN_BASE + 0x00 + 8) #define AT91_PIN_PA9 (PIN_BASE + 0x00 + 9) - #define AT91_PIN_PA10 (PIN_BASE + 0x00 + 10) #define AT91_PIN_PA11 (PIN_BASE + 0x00 + 11) #define AT91_PIN_PA12 (PIN_BASE + 0x00 + 12) #define AT91_PIN_PA13 (PIN_BASE + 0x00 + 13) #define AT91_PIN_PA14 (PIN_BASE + 0x00 + 14) - #define AT91_PIN_PA15 (PIN_BASE + 0x00 + 15) #define AT91_PIN_PA16 (PIN_BASE + 0x00 + 16) #define AT91_PIN_PA17 (PIN_BASE + 0x00 + 17) #define AT91_PIN_PA18 (PIN_BASE + 0x00 + 18) #define AT91_PIN_PA19 (PIN_BASE + 0x00 + 19) - #define AT91_PIN_PA20 (PIN_BASE + 0x00 + 20) #define AT91_PIN_PA21 (PIN_BASE + 0x00 + 21) #define AT91_PIN_PA22 (PIN_BASE + 0x00 + 22) #define AT91_PIN_PA23 (PIN_BASE + 0x00 + 23) #define AT91_PIN_PA24 (PIN_BASE + 0x00 + 24) - #define AT91_PIN_PA25 (PIN_BASE + 0x00 + 25) #define AT91_PIN_PA26 (PIN_BASE + 0x00 + 26) #define AT91_PIN_PA27 (PIN_BASE + 0x00 + 27) #define AT91_PIN_PA28 (PIN_BASE + 0x00 + 28) #define AT91_PIN_PA29 (PIN_BASE + 0x00 + 29) - #define AT91_PIN_PA30 (PIN_BASE + 0x00 + 30) #define AT91_PIN_PA31 (PIN_BASE + 0x00 + 31) @@ -65,37 +59,31 @@ #define AT91_PIN_PB2 (PIN_BASE + 0x20 + 2) #define AT91_PIN_PB3 (PIN_BASE + 0x20 + 3) #define AT91_PIN_PB4 (PIN_BASE + 0x20 + 4) - #define AT91_PIN_PB5 (PIN_BASE + 0x20 + 5) #define AT91_PIN_PB6 (PIN_BASE + 0x20 + 6) #define AT91_PIN_PB7 (PIN_BASE + 0x20 + 7) #define AT91_PIN_PB8 (PIN_BASE + 0x20 + 8) #define AT91_PIN_PB9 (PIN_BASE + 0x20 + 9) - #define AT91_PIN_PB10 (PIN_BASE + 0x20 + 10) #define AT91_PIN_PB11 (PIN_BASE + 0x20 + 11) #define AT91_PIN_PB12 (PIN_BASE + 0x20 + 12) #define AT91_PIN_PB13 (PIN_BASE + 0x20 + 13) #define AT91_PIN_PB14 (PIN_BASE + 0x20 + 14) - #define AT91_PIN_PB15 (PIN_BASE + 0x20 + 15) #define AT91_PIN_PB16 (PIN_BASE + 0x20 + 16) #define AT91_PIN_PB17 (PIN_BASE + 0x20 + 17) #define AT91_PIN_PB18 (PIN_BASE + 0x20 + 18) #define AT91_PIN_PB19 (PIN_BASE + 0x20 + 19) - #define AT91_PIN_PB20 (PIN_BASE + 0x20 + 20) #define AT91_PIN_PB21 (PIN_BASE + 0x20 + 21) #define AT91_PIN_PB22 (PIN_BASE + 0x20 + 22) #define AT91_PIN_PB23 (PIN_BASE + 0x20 + 23) #define AT91_PIN_PB24 (PIN_BASE + 0x20 + 24) - #define AT91_PIN_PB25 (PIN_BASE + 0x20 + 25) #define AT91_PIN_PB26 (PIN_BASE + 0x20 + 26) #define AT91_PIN_PB27 (PIN_BASE + 0x20 + 27) #define AT91_PIN_PB28 (PIN_BASE + 0x20 + 28) #define AT91_PIN_PB29 (PIN_BASE + 0x20 + 29) - #define AT91_PIN_PB30 (PIN_BASE + 0x20 + 30) #define AT91_PIN_PB31 (PIN_BASE + 0x20 + 31) @@ -104,37 +92,31 @@ #define AT91_PIN_PC2 (PIN_BASE + 0x40 + 2) #define AT91_PIN_PC3 (PIN_BASE + 0x40 + 3) #define AT91_PIN_PC4 (PIN_BASE + 0x40 + 4) - #define AT91_PIN_PC5 (PIN_BASE + 0x40 + 5) #define AT91_PIN_PC6 (PIN_BASE + 0x40 + 6) #define AT91_PIN_PC7 (PIN_BASE + 0x40 + 7) #define AT91_PIN_PC8 (PIN_BASE + 0x40 + 8) #define AT91_PIN_PC9 (PIN_BASE + 0x40 + 9) - #define AT91_PIN_PC10 (PIN_BASE + 0x40 + 10) #define AT91_PIN_PC11 (PIN_BASE + 0x40 + 11) #define AT91_PIN_PC12 (PIN_BASE + 0x40 + 12) #define AT91_PIN_PC13 (PIN_BASE + 0x40 + 13) #define AT91_PIN_PC14 (PIN_BASE + 0x40 + 14) - #define AT91_PIN_PC15 (PIN_BASE + 0x40 + 15) #define AT91_PIN_PC16 (PIN_BASE + 0x40 + 16) #define AT91_PIN_PC17 (PIN_BASE + 0x40 + 17) #define AT91_PIN_PC18 (PIN_BASE + 0x40 + 18) #define AT91_PIN_PC19 (PIN_BASE + 0x40 + 19) - #define AT91_PIN_PC20 (PIN_BASE + 0x40 + 20) #define AT91_PIN_PC21 (PIN_BASE + 0x40 + 21) #define AT91_PIN_PC22 (PIN_BASE + 0x40 + 22) #define AT91_PIN_PC23 (PIN_BASE + 0x40 + 23) #define AT91_PIN_PC24 (PIN_BASE + 0x40 + 24) - #define AT91_PIN_PC25 (PIN_BASE + 0x40 + 25) #define AT91_PIN_PC26 (PIN_BASE + 0x40 + 26) #define AT91_PIN_PC27 (PIN_BASE + 0x40 + 27) #define AT91_PIN_PC28 (PIN_BASE + 0x40 + 28) #define AT91_PIN_PC29 (PIN_BASE + 0x40 + 29) - #define AT91_PIN_PC30 (PIN_BASE + 0x40 + 30) #define AT91_PIN_PC31 (PIN_BASE + 0x40 + 31) @@ -143,40 +125,67 @@ #define AT91_PIN_PD2 (PIN_BASE + 0x60 + 2) #define AT91_PIN_PD3 (PIN_BASE + 0x60 + 3) #define AT91_PIN_PD4 (PIN_BASE + 0x60 + 4) - #define AT91_PIN_PD5 (PIN_BASE + 0x60 + 5) #define AT91_PIN_PD6 (PIN_BASE + 0x60 + 6) #define AT91_PIN_PD7 (PIN_BASE + 0x60 + 7) #define AT91_PIN_PD8 (PIN_BASE + 0x60 + 8) #define AT91_PIN_PD9 (PIN_BASE + 0x60 + 9) - #define AT91_PIN_PD10 (PIN_BASE + 0x60 + 10) #define AT91_PIN_PD11 (PIN_BASE + 0x60 + 11) #define AT91_PIN_PD12 (PIN_BASE + 0x60 + 12) #define AT91_PIN_PD13 (PIN_BASE + 0x60 + 13) #define AT91_PIN_PD14 (PIN_BASE + 0x60 + 14) - #define AT91_PIN_PD15 (PIN_BASE + 0x60 + 15) #define AT91_PIN_PD16 (PIN_BASE + 0x60 + 16) #define AT91_PIN_PD17 (PIN_BASE + 0x60 + 17) #define AT91_PIN_PD18 (PIN_BASE + 0x60 + 18) #define AT91_PIN_PD19 (PIN_BASE + 0x60 + 19) - #define AT91_PIN_PD20 (PIN_BASE + 0x60 + 20) #define AT91_PIN_PD21 (PIN_BASE + 0x60 + 21) #define AT91_PIN_PD22 (PIN_BASE + 0x60 + 22) #define AT91_PIN_PD23 (PIN_BASE + 0x60 + 23) #define AT91_PIN_PD24 (PIN_BASE + 0x60 + 24) - #define AT91_PIN_PD25 (PIN_BASE + 0x60 + 25) #define AT91_PIN_PD26 (PIN_BASE + 0x60 + 26) #define AT91_PIN_PD27 (PIN_BASE + 0x60 + 27) #define AT91_PIN_PD28 (PIN_BASE + 0x60 + 28) #define AT91_PIN_PD29 (PIN_BASE + 0x60 + 29) - #define AT91_PIN_PD30 (PIN_BASE + 0x60 + 30) #define AT91_PIN_PD31 (PIN_BASE + 0x60 + 31) +#define AT91_PIN_PE0 (PIN_BASE + 0x80 + 0) +#define AT91_PIN_PE1 (PIN_BASE + 0x80 + 1) +#define AT91_PIN_PE2 (PIN_BASE + 0x80 + 2) +#define AT91_PIN_PE3 (PIN_BASE + 0x80 + 3) +#define AT91_PIN_PE4 (PIN_BASE + 0x80 + 4) +#define AT91_PIN_PE5 (PIN_BASE + 0x80 + 5) +#define AT91_PIN_PE6 (PIN_BASE + 0x80 + 6) +#define AT91_PIN_PE7 (PIN_BASE + 0x80 + 7) +#define AT91_PIN_PE8 (PIN_BASE + 0x80 + 8) +#define AT91_PIN_PE9 (PIN_BASE + 0x80 + 9) +#define AT91_PIN_PE10 (PIN_BASE + 0x80 + 10) +#define AT91_PIN_PE11 (PIN_BASE + 0x80 + 11) +#define AT91_PIN_PE12 (PIN_BASE + 0x80 + 12) +#define AT91_PIN_PE13 (PIN_BASE + 0x80 + 13) +#define AT91_PIN_PE14 (PIN_BASE + 0x80 + 14) +#define AT91_PIN_PE15 (PIN_BASE + 0x80 + 15) +#define AT91_PIN_PE16 (PIN_BASE + 0x80 + 16) +#define AT91_PIN_PE17 (PIN_BASE + 0x80 + 17) +#define AT91_PIN_PE18 (PIN_BASE + 0x80 + 18) +#define AT91_PIN_PE19 (PIN_BASE + 0x80 + 19) +#define AT91_PIN_PE20 (PIN_BASE + 0x80 + 20) +#define AT91_PIN_PE21 (PIN_BASE + 0x80 + 21) +#define AT91_PIN_PE22 (PIN_BASE + 0x80 + 22) +#define AT91_PIN_PE23 (PIN_BASE + 0x80 + 23) +#define AT91_PIN_PE24 (PIN_BASE + 0x80 + 24) +#define AT91_PIN_PE25 (PIN_BASE + 0x80 + 25) +#define AT91_PIN_PE26 (PIN_BASE + 0x80 + 26) +#define AT91_PIN_PE27 (PIN_BASE + 0x80 + 27) +#define AT91_PIN_PE28 (PIN_BASE + 0x80 + 28) +#define AT91_PIN_PE29 (PIN_BASE + 0x80 + 29) +#define AT91_PIN_PE30 (PIN_BASE + 0x80 + 30) +#define AT91_PIN_PE31 (PIN_BASE + 0x80 + 31) + #ifndef __ASSEMBLY__ /* setup setup routines, called from board init or driver probe() */ extern int __init_or_module at91_set_A_periph(unsigned pin, int use_pullup); diff -urN linux-2.6.20.4-0rig/include/asm-arm/arch-at91rm9200/hardware.h linux-2.6.20.4-atmel/include/asm-arm/arch-at91rm9200/hardware.h --- linux-2.6.20.4-0rig/include/asm-arm/arch-at91rm9200/hardware.h 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/include/asm-arm/arch-at91rm9200/hardware.h 2007-03-24 16:39:16.000000000 +0100 @@ -22,21 +22,23 @@ #include <asm/arch/at91sam9260.h> #elif defined(CONFIG_ARCH_AT91SAM9261) #include <asm/arch/at91sam9261.h> +#elif defined(CONFIG_ARCH_AT91SAM9263) +#include <asm/arch/at91sam9263.h> #else #error "Unsupported AT91 processor" #endif /* - * Remap the peripherals from address 0xFFFA0000 .. 0xFFFFFFFF - * to 0xFEFA0000 .. 0xFF000000. (384Kb) + * Remap the peripherals from address 0xFFF78000 .. 0xFFFFFFFF + * to 0xFEF78000 .. 0xFF000000. (544Kb) */ -#define AT91_IO_PHYS_BASE 0xFFFA0000 +#define AT91_IO_PHYS_BASE 0xFFF78000 #define AT91_IO_SIZE (0xFFFFFFFF - AT91_IO_PHYS_BASE + 1) #define AT91_IO_VIRT_BASE (0xFF000000 - AT91_IO_SIZE) /* Convert a physical IO address to virtual IO address */ -#define AT91_IO_P2V(x) ((x) - AT91_IO_PHYS_BASE + AT91_IO_VIRT_BASE) +#define AT91_IO_P2V(x) ((x) - AT91_IO_PHYS_BASE + AT91_IO_VIRT_BASE) /* * Virtual to Physical Address mapping for IO devices. diff -urN linux-2.6.20.4-0rig/include/asm-arm/arch-at91rm9200/ics1523.h linux-2.6.20.4-atmel/include/asm-arm/arch-at91rm9200/ics1523.h --- linux-2.6.20.4-0rig/include/asm-arm/arch-at91rm9200/ics1523.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/include/asm-arm/arch-at91rm9200/ics1523.h 2007-03-24 16:39:16.000000000 +0100 @@ -0,0 +1,154 @@ +//*---------------------------------------------------------------------------- +//* ATMEL Microcontroller Software Support - ROUSSET - +//*---------------------------------------------------------------------------- +//* The software is delivered "AS IS" without warranty or condition of any +//* kind, either express, implied or statutory. This includes without +//* limitation any warranty or condition with respect to merchantability or +//* fitness for any particular purpose, or against the infringements of +//* intellectual property rights of others. +//*---------------------------------------------------------------------------- +//* File Name : ics1523.h +//* Object : Clock Generator Prototyping File. +//* +//* 1.0 08/28/02 ED : Creation +//* 1.2 13/01/03 FB : Update on lib V3 +//*---------------------------------------------------------------------------- + +#ifndef ics1523_h +#define ics1523_h + +/*-------------------------------------------*/ +/* ICS1523 TWI Serial Clock Definition */ +/*-------------------------------------------*/ + +#define ICS_MIN_CLOCK 100 /* Min Frequency Access Clock KHz */ +#define ICS_MAX_CLOCK 400 /* Max Frequency Access Clock KHz */ +#define ICS_TRANSFER_RATE ICS_MAX_CLOCK /* Transfer speed to apply */ + +#define ICS_WRITE_CLK_PNB 30 /* TWCK Clock Periods required to write */ +#define ICS_READ_CLK_PNB 40 /* TWCK Clock Periods required to read */ + +/*-------------------------------------------*/ +/* ICS1523 Write Operation Definition */ +/*-------------------------------------------*/ + +#define ICS1523_ACCESS_OK 0 /* OK */ +#define ICS1523_ACCESS_ERROR -1 /* NOK */ + +/*-------------------------------------------*/ +/* ICS1523 Device Addresses Definition */ +/*-------------------------------------------*/ + +#define ICS_ADDR 0x26 /* Device Address */ + +/*--------------------------------------------------*/ +/* ICS1523 Registers Internal Addresses Definition */ +/*--------------------------------------------------*/ + +#define ICS_ICR 0x0 /* Input Control Register */ +#define ICS_LCR 0x1 /* Loop Control Register */ +#define ICS_FD0 0x2 /* PLL FeedBack Divider LSBs */ +#define ICS_FD1 0x3 /* PLL FeedBack Divider MSBs */ +#define ICS_DPAO 0x4 /* Dynamic Phase Aligner Offset */ +#define ICS_DPAC 0x5 /* Dynamic Phase Aligner Resolution */ +#define ICS_OE 0x6 /* Output Enables Register */ +#define ICS_OD 0x7 /* Osc Divider Register */ +#define ICS_SWRST 0x8 /* DPA & PLL Reset Register */ +#define ICS_VID 0x10 /* Chip Version Register */ +#define ICS_RID 0x11 /* Chip Revision Register */ +#define ICS_SR 0x12 /* Status Register */ + +/*------------------------------------------------------*/ +/* ICS1523 Input Control Register Bits Definition */ +/*------------------------------------------------------*/ + +#define ICS_PDEN 0x1 /* Phase Detector Enable */ +#define ICS_PDPOL 0x2 /* Phase Detector Enable Polarity */ +#define ICS_REFPOL 0x4 /* External Reference Polarity */ +#define ICS_FBKPOL 0x8 /* External Feedback Polarity */ +#define ICS_FBKSEL 0x10 /* External Feedback Select */ +#define ICS_FUNCSEL 0x20 /* Function Out Select */ +#define ICS_ENPLS 0x40 /* Enable PLL Lock/Ref Status Output */ +#define ICS_ENDLS 0x80 /* Enable DPA Lock/Ref Status Output */ + +/*-----------------------------------------------------*/ +/* ICS1523 Loop Control Register Bits Definition */ +/*-----------------------------------------------------*/ + +#define ICS_PFD 0x7 /* Phase Detector Gain */ +#define ICS_PSD 0x30 /* Post-Scaler Divider */ + +/*----------------------------------------------------*/ +/* ICS1523 PLL FeedBack Divider LSBs Definition */ +/*----------------------------------------------------*/ + +#define ICS_FBDL 0xFF /* PLL FeedBack Divider LSBs */ + +/*----------------------------------------------------*/ +/* ICS1523 PLL FeedBack Divider MSBs Definition */ +/*----------------------------------------------------*/ + +#define ICS_FBDM 0xF /* PLL FeedBack Divider MSBs */ + +/*------------------------------------------------------------*/ +/* ICS1523 Dynamic Phase Aligner Offset Bits Definition */ +/*------------------------------------------------------------*/ + +#define ICS_DPAOS 0x2F /* Dynamic Phase Aligner Offset */ +#define ICS_FILSEL 0x80 /* Loop Filter Select */ + +/*----------------------------------------------------------------*/ +/* ICS1523 Dynamic Phase Aligner Resolution Bits Definition */ +/*----------------------------------------------------------------*/ + +#define ICS_DPARES 0x3 /* Dynamic Phase Aligner Resolution */ +#define ICS_MMREV 0xFC /* Metal Mask Revision Number */ + +/*-------------------------------------------------------*/ +/* ICS1523 Output Enables Register Bits Definition */ +/*-------------------------------------------------------*/ + +#define ICS_OEPCK 0x1 /* Output Enable for PECL PCLK Outputs */ +#define ICS_OETCK 0x2 /* Output Enable for STTL CLK Output */ +#define ICS_OEP2 0x4 /* Output Enable for PECL CLK/2 Outputs */ +#define ICS_OET2 0x8 /* Output Enable for STTL CLK/2 Output */ +#define ICS_OEF 0x10 /* Output Enable for STTL FUNC Output */ +#define ICS_CLK2INV 0x20 /* CLK/2 Invert */ +#define ICS_OSCL 0xC0 /* SSTL Clock Scaler */ + +/*----------------------------------------------------*/ +/* ICS1523 Osc Divider Register Bits Definition */ +/*----------------------------------------------------*/ + +#define ICS_OSCDIV 0x7F /* Oscillator Divider Modulus */ +#define ICS_INSEL 0x80 /* Input Select */ + +/*---------------------------------------------------*/ +/* ICS1523 DPA & PLL Reset Register Definition */ +/*---------------------------------------------------*/ + +#define ICS_DPAR 0x0A /* DPA Reset Command */ +#define ICS_PLLR 0x50 /* PLL Reset Command */ + +/*------------------------------------------------*/ +/* ICS1523 Chip Version Register Definition */ +/*------------------------------------------------*/ + +#define ICS_CHIPV 0xFF /* Chip Version */ + +/*-------------------------------------------------*/ +/* ICS1523 Chip Revision Register Definition */ +/*-------------------------------------------------*/ + +#define ICS_CHIPR 0xFF /* Chip Revision */ + +/*------------------------------------------*/ +/* ICS1523 Status Register Definition */ +/*------------------------------------------*/ + +#define ICS_DPALOCK 0x1 /* DPA Lock Status */ +#define ICS_PLLLOCK 0x2 /* PLL Lock Status */ + +int at91_ics1523_init(void); + +#endif /* ics1523_h */ diff -urN linux-2.6.20.4-0rig/include/asm-arm/arch-at91rm9200/irqs.h linux-2.6.20.4-atmel/include/asm-arm/arch-at91rm9200/irqs.h --- linux-2.6.20.4-0rig/include/asm-arm/arch-at91rm9200/irqs.h 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/include/asm-arm/arch-at91rm9200/irqs.h 2007-03-24 16:39:16.000000000 +0100 @@ -37,8 +37,8 @@ * IRQ interrupt symbols are the AT91xxx_ID_* symbols * for IRQs handled directly through the AIC, or else the AT91_PIN_* * symbols in gpio.h for ones handled indirectly as GPIOs. - * We make provision for 4 banks of GPIO. + * We make provision for 5 banks of GPIO. */ -#define NR_IRQS (NR_AIC_IRQS + (4 * 32)) +#define NR_IRQS (NR_AIC_IRQS + (5 * 32)) #endif diff -urN linux-2.6.20.4-0rig/include/asm-arm/arch-at91rm9200/spi.h linux-2.6.20.4-atmel/include/asm-arm/arch-at91rm9200/spi.h --- linux-2.6.20.4-0rig/include/asm-arm/arch-at91rm9200/spi.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/include/asm-arm/arch-at91rm9200/spi.h 2007-03-24 16:39:16.000000000 +0100 @@ -0,0 +1,54 @@ +/* + * Serial Peripheral Interface (SPI) driver for the Atmel AT91RM9200 + * + * (c) SAN People (Pty) Ltd + * + * 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. + */ + +#ifndef AT91_LEGACY_SPI_H +#define AT91_LEGACY_SPI_H + +#define SPI_MAJOR 153 /* registered device number */ + +#define DEFAULT_SPI_CLK 6000000 + + +/* Maximum number of buffers in a single SPI transfer. + * DataFlash uses maximum of 2 + * spidev interface supports up to 8. + */ +#define MAX_SPI_TRANSFERS 8 +#define NR_SPI_DEVICES 4 /* number of devices on SPI bus */ + +/* + * Describes the buffers for a SPI transfer. + * A transmit & receive buffer must be specified for each transfer + */ +struct spi_transfer_list { + void* tx[MAX_SPI_TRANSFERS]; /* transmit */ + int txlen[MAX_SPI_TRANSFERS]; + void* rx[MAX_SPI_TRANSFERS]; /* receive */ + int rxlen[MAX_SPI_TRANSFERS]; + int nr_transfers; /* number of transfers */ + int curr; /* current transfer */ +}; + +struct spi_local { + unsigned int pcs; /* Peripheral Chip Select value */ + + struct spi_transfer_list *xfers; /* current transfer list */ + dma_addr_t tx, rx; /* DMA address for current transfer */ + dma_addr_t txnext, rxnext; /* DMA address for next transfer */ +}; + + +/* Exported functions */ +extern void spi_access_bus(short device); +extern void spi_release_bus(short device); +extern int spi_transfer(struct spi_transfer_list* list); + +#endif diff -urN linux-2.6.20.4-0rig/include/asm-arm/arch-at91rm9200/timex.h linux-2.6.20.4-atmel/include/asm-arm/arch-at91rm9200/timex.h --- linux-2.6.20.4-0rig/include/asm-arm/arch-at91rm9200/timex.h 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/include/asm-arm/arch-at91rm9200/timex.h 2007-03-24 16:39:16.000000000 +0100 @@ -32,6 +32,11 @@ #define AT91SAM9_MASTER_CLOCK 99300000 #define CLOCK_TICK_RATE (AT91SAM9_MASTER_CLOCK/16) +#elif defined(CONFIG_ARCH_AT91SAM9263) + +#define AT91SAM9_MASTER_CLOCK 99959500 +#define CLOCK_TICK_RATE (AT91SAM9_MASTER_CLOCK/16) + #endif #endif diff -urN linux-2.6.20.4-0rig/include/asm-avr32/arch-at32ap/at32ap7000.h linux-2.6.20.4-atmel/include/asm-avr32/arch-at32ap/at32ap7000.h --- linux-2.6.20.4-0rig/include/asm-avr32/arch-at32ap/at32ap7000.h 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/include/asm-avr32/arch-at32ap/at32ap7000.h 2007-03-24 16:42:28.000000000 +0100 @@ -24,10 +24,12 @@ #define GPIO_PIOB_BASE (GPIO_PIOA_BASE + 32) #define GPIO_PIOC_BASE (GPIO_PIOB_BASE + 32) #define GPIO_PIOD_BASE (GPIO_PIOC_BASE + 32) +#define GPIO_PIOE_BASE (GPIO_PIOD_BASE + 32) #define GPIO_PIN_PA(N) (GPIO_PIOA_BASE + (N)) #define GPIO_PIN_PB(N) (GPIO_PIOB_BASE + (N)) #define GPIO_PIN_PC(N) (GPIO_PIOC_BASE + (N)) #define GPIO_PIN_PD(N) (GPIO_PIOD_BASE + (N)) +#define GPIO_PIN_PE(N) (GPIO_PIOE_BASE + (N)) #endif /* __ASM_ARCH_AT32AP7000_H__ */ diff -urN linux-2.6.20.4-0rig/include/asm-avr32/arch-at32ap/at91_pdc.h linux-2.6.20.4-atmel/include/asm-avr32/arch-at32ap/at91_pdc.h --- linux-2.6.20.4-0rig/include/asm-avr32/arch-at32ap/at91_pdc.h 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/include/asm-avr32/arch-at32ap/at91_pdc.h 1970-01-01 01:00:00.000000000 +0100 @@ -1,36 +0,0 @@ -/* - * include/asm-arm/arch-at91rm9200/at91_pdc.h - * - * Copyright (C) 2005 Ivan Kokshaysky - * Copyright (C) SAN People - * - * Peripheral Data Controller (PDC) registers. - * Based on AT91RM9200 datasheet revision E. - * - * 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. - */ - -#ifndef AT91_PDC_H -#define AT91_PDC_H - -#define AT91_PDC_RPR 0x100 /* Receive Pointer Register */ -#define AT91_PDC_RCR 0x104 /* Receive Counter Register */ -#define AT91_PDC_TPR 0x108 /* Transmit Pointer Register */ -#define AT91_PDC_TCR 0x10c /* Transmit Counter Register */ -#define AT91_PDC_RNPR 0x110 /* Receive Next Pointer Register */ -#define AT91_PDC_RNCR 0x114 /* Receive Next Counter Register */ -#define AT91_PDC_TNPR 0x118 /* Transmit Next Pointer Register */ -#define AT91_PDC_TNCR 0x11c /* Transmit Next Counter Register */ - -#define AT91_PDC_PTCR 0x120 /* Transfer Control Register */ -#define AT91_PDC_RXTEN (1 << 0) /* Receiver Transfer Enable */ -#define AT91_PDC_RXTDIS (1 << 1) /* Receiver Transfer Disable */ -#define AT91_PDC_TXTEN (1 << 8) /* Transmitter Transfer Enable */ -#define AT91_PDC_TXTDIS (1 << 9) /* Transmitter Transfer Disable */ - -#define AT91_PDC_PTSR 0x124 /* Transfer Status Register */ - -#endif diff -urN linux-2.6.20.4-0rig/include/asm-avr32/arch-at32ap/board.h linux-2.6.20.4-atmel/include/asm-avr32/arch-at32ap/board.h --- linux-2.6.20.4-0rig/include/asm-avr32/arch-at32ap/board.h 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/include/asm-avr32/arch-at32ap/board.h 2007-03-24 16:42:29.000000000 +0100 @@ -6,6 +6,8 @@ #include <linux/types.h> +#define GPIO_PIN_NONE (-1) + /* Add basic devices: system manager, interrupt controller, portmuxes, etc. */ void at32_add_system_devices(void); @@ -26,7 +28,19 @@ struct platform_device * at32_add_device_eth(unsigned int id, struct eth_platform_data *data); -struct platform_device *at32_add_device_spi(unsigned int id); +struct spi_board_info; +struct platform_device * +at32_add_device_spi(unsigned int id, struct spi_board_info *b, unsigned int n); + +struct platform_device *at32_add_device_twi(unsigned int id); + +struct mci_platform_data { + int detect_pin; + int wp_pin; +}; +struct platform_device * +at32_add_device_mci(unsigned int id, struct mci_platform_data *data); +struct platform_device *at32_add_device_usba(unsigned int id); struct lcdc_platform_data { unsigned long fbmem_start; @@ -35,4 +49,8 @@ struct platform_device * at32_add_device_lcdc(unsigned int id, struct lcdc_platform_data *data); +struct platform_device *at32_add_device_ac97c(unsigned int id); +struct platform_device *at32_add_device_abdac(unsigned int id); +struct platform_device *at32_add_device_isi(unsigned int id); + #endif /* __ASM_ARCH_BOARD_H */ diff -urN linux-2.6.20.4-0rig/include/asm-avr32/arch-at32ap/gpio.h linux-2.6.20.4-atmel/include/asm-avr32/arch-at32ap/gpio.h --- linux-2.6.20.4-0rig/include/asm-avr32/arch-at32ap/gpio.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/include/asm-avr32/arch-at32ap/gpio.h 2007-03-24 16:42:28.000000000 +0100 @@ -0,0 +1,27 @@ +#ifndef __ASM_AVR32_ARCH_GPIO_H +#define __ASM_AVR32_ARCH_GPIO_H + +#include <linux/compiler.h> +#include <asm/irq.h> + + +/* Arch-neutral GPIO API */ +int __must_check gpio_request(unsigned int gpio, const char *label); +void gpio_free(unsigned int gpio); + +int gpio_direction_input(unsigned int gpio); +int gpio_direction_output(unsigned int gpio); +int gpio_get_value(unsigned int gpio); +void gpio_set_value(unsigned int gpio, int value); + +static inline int gpio_to_irq(unsigned int gpio) +{ + return gpio + GPIO_IRQ_BASE; +} + +static inline int irq_to_gpio(unsigned int irq) +{ + return irq - GPIO_IRQ_BASE; +} + +#endif /* __ASM_AVR32_ARCH_GPIO_H */ diff -urN linux-2.6.20.4-0rig/include/asm-avr32/arch-at32ap/irq.h linux-2.6.20.4-atmel/include/asm-avr32/arch-at32ap/irq.h --- linux-2.6.20.4-0rig/include/asm-avr32/arch-at32ap/irq.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/include/asm-avr32/arch-at32ap/irq.h 2007-03-24 16:42:28.000000000 +0100 @@ -0,0 +1,14 @@ +#ifndef __ASM_AVR32_ARCH_IRQ_H +#define __ASM_AVR32_ARCH_IRQ_H + +#define EIM_IRQ_BASE NR_INTERNAL_IRQS +#define NR_EIM_IRQS 32 + +#define AT32_EXTINT(n) (EIM_IRQ_BASE + (n)) + +#define GPIO_IRQ_BASE (EIM_IRQ_BASE + NR_EIM_IRQS) +#define NR_GPIO_IRQS (5 * 32) + +#define NR_IRQS (GPIO_IRQ_BASE + NR_GPIO_IRQS) + +#endif /* __ASM_AVR32_ARCH_IRQ_H */ diff -urN linux-2.6.20.4-0rig/include/asm-avr32/arch-at32ap/portmux.h linux-2.6.20.4-atmel/include/asm-avr32/arch-at32ap/portmux.h --- linux-2.6.20.4-0rig/include/asm-avr32/arch-at32ap/portmux.h 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/include/asm-avr32/arch-at32ap/portmux.h 2007-03-24 16:42:28.000000000 +0100 @@ -15,12 +15,14 @@ * * The following flags determine the initial state of the pin. */ -#define AT32_GPIOF_PULLUP 0x00000001 /* Enable pull-up */ -#define AT32_GPIOF_OUTPUT 0x00000002 /* Enable output driver */ -#define AT32_GPIOF_HIGH 0x00000004 /* Set output high */ +#define AT32_GPIOF_PULLUP 0x00000001 /* (not-OUT) Enable pull-up */ +#define AT32_GPIOF_OUTPUT 0x00000002 /* (OUT) Enable output driver */ +#define AT32_GPIOF_HIGH 0x00000004 /* (OUT) Set output high */ +#define AT32_GPIOF_DEGLITCH 0x00000008 /* (IN) Filter glitches */ void at32_select_periph(unsigned int pin, unsigned int periph, unsigned long flags); void at32_select_gpio(unsigned int pin, unsigned long flags); +void at32_reserve_pin(unsigned int pin); #endif /* __ASM_ARCH_PORTMUX_H__ */ diff -urN linux-2.6.20.4-0rig/include/asm-avr32/arch-at32ap/smc.h linux-2.6.20.4-atmel/include/asm-avr32/arch-at32ap/smc.h --- linux-2.6.20.4-0rig/include/asm-avr32/arch-at32ap/smc.h 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/include/asm-avr32/arch-at32ap/smc.h 2007-03-24 16:42:29.000000000 +0100 @@ -48,10 +48,32 @@ unsigned int nwe_controlled:1; /* + * 0: NWAIT is disabled + * 1: Reserved + * 2: NWAIT is frozen mode + * 3: NWAIT in ready mode + */ + unsigned int nwait_mode:2; + + /* * 0: Byte select access type * 1: Byte write access type */ unsigned int byte_write:1; + + /* + * Number of clock cycles before data is released after + * the rising edge of the read controlling signal + * + * Total cycles from SMC is tdf_cycles + 1 + */ + unsigned int tdf_cycles:4; + + /* + * 0: TDF optimization disabled + * 1: TDF optimization enabled + */ + unsigned int tdf_mode:1; }; extern int smc_set_configuration(int cs, const struct smc_config *config); diff -urN linux-2.6.20.4-0rig/include/asm-avr32/checksum.h linux-2.6.20.4-atmel/include/asm-avr32/checksum.h --- linux-2.6.20.4-0rig/include/asm-avr32/checksum.h 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/include/asm-avr32/checksum.h 2007-03-24 16:42:28.000000000 +0100 @@ -38,7 +38,7 @@ * passed in an incorrect kernel address to one of these functions. * * If you use these functions directly please don't forget the - * verify_area(). + * access_ok(). */ static inline __wsum csum_partial_copy_nocheck(const void *src, void *dst, diff -urN linux-2.6.20.4-0rig/include/asm-avr32/dma-controller.h linux-2.6.20.4-atmel/include/asm-avr32/dma-controller.h --- linux-2.6.20.4-0rig/include/asm-avr32/dma-controller.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/include/asm-avr32/dma-controller.h 2007-03-24 16:42:29.000000000 +0100 @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2005-2006 Atmel Corporation + * + * 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. + */ +#ifndef __ASM_AVR32_DMA_CONTROLLER_H +#define __ASM_AVR32_DMA_CONTROLLER_H + +#include <linux/device.h> + +#define DMA_DIR_MEM_TO_MEM 0x0000 +#define DMA_DIR_MEM_TO_PERIPH 0x0001 +#define DMA_DIR_PERIPH_TO_MEM 0x0002 +#define DMA_DIR_PERIPH_TO_PERIPH 0x0003 + +#define DMA_WIDTH_8BIT 0 +#define DMA_WIDTH_16BIT 1 +#define DMA_WIDTH_32BIT 2 + +struct dma_request { + struct dma_controller *dmac; + struct list_head list; + + unsigned short channel; + + void (*xfer_complete)(struct dma_request *req); + void (*block_complete)(struct dma_request *req); + void (*error)(struct dma_request *req); +}; + +struct dma_request_sg { + struct dma_request req; + + int nr_sg; + struct scatterlist *sg; + unsigned long block_size; + unsigned int nr_blocks; + + dma_addr_t data_reg; + unsigned short periph_id; + + unsigned char direction; + unsigned char width; +}; +#define to_dma_request_sg(_req) \ + container_of(_req, struct dma_request_sg, req) + +struct dma_request_cyclic { + struct dma_request req; + + int periods; + unsigned long buffer_size; + + dma_addr_t buffer_start; + dma_addr_t data_reg; + + unsigned short periph_id; + unsigned char direction; + unsigned char width; + + void *dev_id; +}; +#define to_dma_request_cyclic(_req) \ + container_of(_req, struct dma_request_cyclic, req) + +struct dma_request_memcpy { + struct dma_request req; + + dma_addr_t src_addr; + unsigned int src_width; + unsigned int src_stride; + + dma_addr_t dst_addr; + unsigned int dst_width; + unsigned int dst_stride; + + size_t length; + + unsigned short src_reverse:1; + unsigned short dst_reverse:1; +}; +#define to_dma_request_memcpy(_req) \ + container_of(_req, struct dma_request_memcpy, req) + +struct dma_controller { + struct list_head list; + int id; + struct device *dev; + + int (*alloc_channel)(struct dma_controller *dmac); + void (*release_channel)(struct dma_controller *dmac, + int channel); + int (*prepare_request_sg)(struct dma_controller *dmac, + struct dma_request_sg *req); + int (*prepare_request_cyclic)(struct dma_controller *dmac, + struct dma_request_cyclic *req); + int (*prepare_request_memcpy)(struct dma_controller *dmac, + struct dma_request_memcpy *req); + int (*start_request)(struct dma_controller *dmac, + unsigned int channel); + int (*stop_request)(struct dma_controller *dmac, + unsigned int channel); + dma_addr_t (*get_current_pos)(struct dma_controller *dmac, + unsigned int channel); +}; + +static inline int +dma_alloc_channel(struct dma_controller *dmac) +{ + return dmac->alloc_channel(dmac); +} + +static inline void +dma_release_channel(struct dma_controller *dmac, int chan) +{ + dmac->release_channel(dmac, chan); +} + +static inline int +dma_prepare_request_sg(struct dma_controller *dmac, + struct dma_request_sg *req) +{ + return dmac->prepare_request_sg(dmac, req); +} + +static inline int +dma_prepare_request_cyclic(struct dma_controller *dmac, + struct dma_request_cyclic *req) +{ + return dmac->prepare_request_cyclic(dmac, req); +} + +static inline int +dma_prepare_request_memcpy(struct dma_controller *dmac, + struct dma_request_memcpy *req) +{ + return dmac->prepare_request_memcpy(dmac, req); +} + +static inline int +dma_start_request(struct dma_controller *dmac, + unsigned int channel) +{ + return dmac->start_request(dmac, channel); +} + +static inline int +dma_stop_request(struct dma_controller *dmac, + unsigned int channel) +{ + return dmac->stop_request(dmac, channel); +} + +static inline dma_addr_t +dma_get_current_pos(struct dma_controller *dmac, + unsigned int channel) +{ + return dmac->get_current_pos(dmac, channel); +} + +extern int register_dma_controller(struct dma_controller *dmac); +extern struct dma_controller *find_dma_controller(int id); + +#endif /* __ASM_AVR32_DMA_CONTROLLER_H */ diff -urN linux-2.6.20.4-0rig/include/asm-avr32/dma-mapping.h linux-2.6.20.4-atmel/include/asm-avr32/dma-mapping.h --- linux-2.6.20.4-0rig/include/asm-avr32/dma-mapping.h 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/include/asm-avr32/dma-mapping.h 2007-03-24 16:42:28.000000000 +0100 @@ -32,6 +32,14 @@ return 0; } +/* + * dma_map_single can't fail as it is implemented now. + */ +static inline int dma_mapping_error(dma_addr_t addr) +{ + return 0; +} + /** * dma_alloc_coherent - allocate consistent memory for DMA * @dev: valid struct device pointer, or NULL for ISA and EISA-like devices diff -urN linux-2.6.20.4-0rig/include/asm-avr32/gpio.h linux-2.6.20.4-atmel/include/asm-avr32/gpio.h --- linux-2.6.20.4-0rig/include/asm-avr32/gpio.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/include/asm-avr32/gpio.h 2007-03-24 16:42:28.000000000 +0100 @@ -0,0 +1,6 @@ +#ifndef __ASM_AVR32_GPIO_H +#define __ASM_AVR32_GPIO_H + +#include <asm/arch/gpio.h> + +#endif /* __ASM_AVR32_GPIO_H */ diff -urN linux-2.6.20.4-0rig/include/asm-avr32/ide.h linux-2.6.20.4-atmel/include/asm-avr32/ide.h --- linux-2.6.20.4-0rig/include/asm-avr32/ide.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/include/asm-avr32/ide.h 2007-03-24 16:42:29.000000000 +0100 @@ -0,0 +1,36 @@ +/* + * linux/include/asm-arm/ide.h + * + * Copyright (C) 1994-1996 Linus Torvalds & authors + */ + +/* + * This file contains the ARM architecture specific IDE code. + */ + +#ifndef __ASMAVR32_IDE_H +#define __ASMAVR32_IDE_H + +#ifdef __KERNEL__ + +#ifndef MAX_HWIFS +#define MAX_HWIFS 4 +#endif + +#if !defined(CONFIG_ARCH_L7200) +# define IDE_ARCH_OBSOLETE_INIT +# ifdef CONFIG_ARCH_CLPS7500 +# define ide_default_io_ctl(base) ((base) + 0x206) /* obsolete */ +# else +# define ide_default_io_ctl(base) (0) +# endif +#endif /* !ARCH_L7200 */ + +#define __ide_mm_insw(port,addr,len) readsw(port,addr,len) +#define __ide_mm_insl(port,addr,len) readsl(port,addr,len) +#define __ide_mm_outsw(port,addr,len) writesw(port,addr,len) +#define __ide_mm_outsl(port,addr,len) writesl(port,addr,len) + +#endif /* __KERNEL__ */ + +#endif /* __ASMAVR32_IDE_H */ diff -urN linux-2.6.20.4-0rig/include/asm-avr32/io.h linux-2.6.20.4-atmel/include/asm-avr32/io.h --- linux-2.6.20.4-0rig/include/asm-avr32/io.h 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/include/asm-avr32/io.h 2007-03-24 16:42:29.000000000 +0100 @@ -5,6 +5,8 @@ #ifdef __KERNEL__ +#include <linux/types.h> + #include <asm/addrspace.h> #include <asm/byteorder.h> @@ -28,45 +30,72 @@ * Generic IO read/write. These perform native-endian accesses. Note * that some architectures will want to re-define __raw_{read,write}w. */ -extern void __raw_writesb(unsigned int addr, const void *data, int bytelen); -extern void __raw_writesw(unsigned int addr, const void *data, int wordlen); -extern void __raw_writesl(unsigned int addr, const void *data, int longlen); +extern void __raw_writesb(void __iomem *addr, const void *data, int bytelen); +extern void __raw_writesw(void __iomem *addr, const void *data, int wordlen); +extern void __raw_writesl(void __iomem *addr, const void *data, int longlen); + +extern void __raw_readsb(const void __iomem *addr, void *data, int bytelen); +extern void __raw_readsw(const void __iomem *addr, void *data, int wordlen); +extern void __raw_readsl(const void __iomem *addr, void *data, int longlen); + +static inline void __raw_writeb(u8 v, volatile void __iomem *addr) +{ + *(volatile u8 __force *)addr = v; +} +static inline void __raw_writew(u16 v, volatile void __iomem *addr) +{ + *(volatile u16 __force *)addr = v; +} +static inline void __raw_writel(u32 v, volatile void __iomem *addr) +{ + *(volatile u32 __force *)addr = v; +} + +static inline u8 __raw_readb(const volatile void __iomem *addr) +{ + return *(const volatile u8 __force *)addr; +} +static inline u16 __raw_readw(const volatile void __iomem *addr) +{ + return *(const volatile u16 __force *)addr; +} +static inline u32 __raw_readl(const volatile void __iomem *addr) +{ + return *(const volatile u32 __force *)addr; +} -extern void __raw_readsb(unsigned int addr, void *data, int bytelen); -extern void __raw_readsw(unsigned int addr, void *data, int wordlen); -extern void __raw_readsl(unsigned int addr, void *data, int longlen); +#define __swizzle_addr_b(addr) \ + ((typeof(addr))((unsigned long)(addr) ^ 3UL)) +#define __swizzle_addr_w(addr) \ + ((typeof(addr))((unsigned long)(addr) ^ 2UL)) +#define __swizzle_addr_l(addr) \ + (addr) -static inline void writeb(unsigned char b, volatile void __iomem *addr) +static inline void writeb(u8 v, volatile void __iomem *addr) { - *(volatile unsigned char __force *)addr = b; + __raw_writeb(v, __swizzle_addr_b(addr)); } -static inline void writew(unsigned short b, volatile void __iomem *addr) +static inline void writew(u16 v, volatile void __iomem *addr) { - *(volatile unsigned short __force *)addr = b; + __raw_writew(v, __swizzle_addr_w(addr)); } -static inline void writel(unsigned int b, volatile void __iomem *addr) +static inline void writel(u32 v, volatile void __iomem *addr) { - *(volatile unsigned int __force *)addr = b; + __raw_writel(v, __swizzle_addr_l(addr)); } -#define __raw_writeb writeb -#define __raw_writew writew -#define __raw_writel writel -static inline unsigned char readb(const volatile void __iomem *addr) +static inline u8 readb(const volatile void __iomem *addr) { - return *(const volatile unsigned char __force *)addr; + return __raw_readb(__swizzle_addr_b(addr)); } -static inline unsigned short readw(const volatile void __iomem *addr) +static inline u16 readw(const volatile void __iomem *addr) { - return *(const volatile unsigned short __force *)addr; + return __raw_readw(__swizzle_addr_w(addr)); } -static inline unsigned int readl(const volatile void __iomem *addr) +static inline u32 readl(const volatile void __iomem *addr) { - return *(const volatile unsigned int __force *)addr; + return __raw_readl(__swizzle_addr_l(addr)); } -#define __raw_readb readb -#define __raw_readw readw -#define __raw_readl readl #define writesb(p, d, l) __raw_writesb((unsigned int)p, d, l) #define writesw(p, d, l) __raw_writesw((unsigned int)p, d, l) @@ -108,17 +137,13 @@ #endif - -/* - * These two are only here because ALSA _thinks_ it needs them... - */ static inline void memcpy_fromio(void * to, const volatile void __iomem *from, unsigned long count) { char *p = to; while (count) { count--; - *p = readb(from); + *p = __raw_readb(from); p++; from++; } @@ -130,7 +155,7 @@ const char *p = from; while (count) { count--; - writeb(*p, to); + __raw_writeb(*p, to); p++; to++; } @@ -252,6 +277,9 @@ #define ioremap(offset, size) \ __ioremap((offset), (size), 0) +#define ioremap_nocache(offset, size) \ + __ioremap((offset), (size), 0) + #define iounmap(addr) \ __iounmap(addr) @@ -263,6 +291,14 @@ #define page_to_bus page_to_phys #define bus_to_page phys_to_page +/* + * Create a virtual mapping cookie for an IO port range. There exists + * no such thing as port-based I/O on AVR32, so a regular ioremap() + * should do what we need. + */ +#define ioport_map(port, nr) ioremap(port, nr) +#define ioport_unmap(port) iounmap(port) + #define dma_cache_wback_inv(_start, _size) \ flush_dcache_region(_start, _size) #define dma_cache_inv(_start, _size) \ diff -urN linux-2.6.20.4-0rig/include/asm-avr32/irq.h linux-2.6.20.4-atmel/include/asm-avr32/irq.h --- linux-2.6.20.4-0rig/include/asm-avr32/irq.h 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/include/asm-avr32/irq.h 2007-03-24 16:42:28.000000000 +0100 @@ -2,8 +2,12 @@ #define __ASM_AVR32_IRQ_H #define NR_INTERNAL_IRQS 64 -#define NR_EXTERNAL_IRQS 64 -#define NR_IRQS (NR_INTERNAL_IRQS + NR_EXTERNAL_IRQS) + +#include <asm/arch/irq.h> + +#ifndef NR_IRQS +#define NR_IRQS (NR_INTERNAL_IRQS) +#endif #define irq_canonicalize(i) (i) diff -urN linux-2.6.20.4-0rig/include/asm-avr32/Kbuild linux-2.6.20.4-atmel/include/asm-avr32/Kbuild --- linux-2.6.20.4-0rig/include/asm-avr32/Kbuild 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/include/asm-avr32/Kbuild 2007-03-24 16:42:28.000000000 +0100 @@ -1,3 +1,3 @@ include include/asm-generic/Kbuild.asm -headers-y += cachectl.h +header-y += cachectl.h diff -urN linux-2.6.20.4-0rig/include/asm-avr32/periph/lcdc.h linux-2.6.20.4-atmel/include/asm-avr32/periph/lcdc.h --- linux-2.6.20.4-0rig/include/asm-avr32/periph/lcdc.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/include/asm-avr32/periph/lcdc.h 2007-03-24 16:42:29.000000000 +0100 @@ -0,0 +1,271 @@ +/* + * Register definitions for Atmel/SIDSA LCD Controller + * + * Copyright (C) 2004-2006 Atmel Corporation + * + * 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. + */ +#ifndef __ASM_AVR32_PERIPH_LCDC_H__ +#define __ASM_AVR32_PERIPH_LCDC_H__ + +#define LCDC_CONTRAST_CTR 0x00000840 +# define LCDC_CONTRAST_CTR_ENA_OFFSET 3 +# define LCDC_CONTRAST_CTR_ENA_SIZE 1 +# define LCDC_CONTRAST_CTR_POL_OFFSET 2 +# define LCDC_CONTRAST_CTR_POL_SIZE 1 +# define LCDC_CONTRAST_CTR_PS_OFFSET 0 +# define LCDC_CONTRAST_CTR_PS_SIZE 2 +#define LCDC_CONTRAST_VAL 0x00000844 +# define LCDC_CONTRAST_VAL_CVAL_OFFSET 0 +# define LCDC_CONTRAST_VAL_CVAL_SIZE 8 +#define LCDC_DMABADDR1 0x00000000 +# define LCDC_DMABADDR1_BADDR_U_OFFSET 0 +# define LCDC_DMABADDR1_BADDR_U_SIZE 32 +#define LCDC_DMABADDR2 0x00000004 +# define LCDC_DMABADDR2_BADDR_L_OFFSET 0 +# define LCDC_DMABADDR2_BADDR_L_SIZE 32 +#define LCDC_DMACON 0x0000001C +# define LCDC_DMACON_DMABUSY_OFFSET 2 +# define LCDC_DMACON_DMABUSY_SIZE 1 +# define LCDC_DMACON_DMAEN_OFFSET 0 +# define LCDC_DMACON_DMAEN_SIZE 1 +# define LCDC_DMACON_DMARST_OFFSET 1 +# define LCDC_DMACON_DMARST_SIZE 1 +# define LCDC_DMACON_DMAUPDT_OFFSET 3 +# define LCDC_DMACON_DMAUPDT_SIZE 1 +# define LCDC_DMACON_DMA2DEN_OFFSET 4 +# define LCDC_DMACON_DMA2DEN_SIZE 1 +#define LCDC_DMAFRMADD1 0x00000010 +# define LCDC_DMAFRMADD1_FRMADD_U_OFFSET 0 +# define LCDC_DMAFRMADD1_FRMADD_U_SIZE 32 +#define LCDC_DMAFRMADD2 0x00000014 +# define LCDC_DMAFRMADD2_FRMADD_L_OFFSET 0 +# define LCDC_DMAFRMADD2_FRMADD_L_SIZE 32 +#define LCDC_DMAFRMCFG 0x00000018 +# define LCDC_DMAFRMCFG_BRSTLEN_OFFSET 24 +# define LCDC_DMAFRMCFG_BRSTLEN_SIZE 7 +# define LCDC_DMAFRMCFG_FRMSIZE_OFFSET 0 +# define LCDC_DMAFRMCFG_FRMSIZE_SIZE 23 +#define LCDC_DMAFRMPT1 0x00000008 +# define LCDC_DMAFRMPT1_FRMPT_U_OFFSET 0 +# define LCDC_DMAFRMPT1_FRMPT_U_SIZE 23 +#define LCDC_DMAFRMPT2 0x0000000C +# define LCDC_DMAFRMPT2_FRMPT_L_OFFSET 0 +# define LCDC_DMAFRMPT2_FRMPT_L_SIZE 23 +#define LCDC_DMA2DCFG 0x00000020 +# define LCDC_DMA2DCFG_ADDRINC_OFFSET 0 +# define LCDC_DMA2DCFG_ADDRINC_SIZE 16 +# define LCDC_DMA2DCFG_PIXELOFF_OFFSET 24 +# define LCDC_DMA2DCFG_PIXELOFF_SIZE 5 +#define LCDC_DP1_2 0x0000081C +# define LCDC_DP1_2_DP1_2_OFFSET 0 +# define LCDC_DP1_2_DP1_2_SIZE 8 +#define LCDC_DP2_3 0x00000828 +# define LCDC_DP2_3_DP2_3_OFFSET 0 +# define LCDC_DP2_3_DP2_3_SIZE 12 +#define LCDC_DP3_4 0x00000830 +# define LCDC_DP3_4_DP3_4_OFFSET 0 +# define LCDC_DP3_4_DP3_4_SIZE 16 +#define LCDC_DP3_5 0x00000824 +# define LCDC_DP3_5_DP3_5_OFFSET 0 +# define LCDC_DP3_5_DP3_5_SIZE 20 +#define LCDC_DP4_5 0x00000834 +# define LCDC_DP4_5_DP4_5_OFFSET 0 +# define LCDC_DP4_5_DP4_5_SIZE 20 +#define LCDC_DP4_7 0x00000820 +# define LCDC_DP4_7_DP4_7_OFFSET 0 +# define LCDC_DP4_7_DP4_7_SIZE 28 +#define LCDC_DP5_7 0x0000082C +# define LCDC_DP5_7_DP5_7_OFFSET 0 +# define LCDC_DP5_7_DP5_7_SIZE 28 +#define LCDC_DP6_7 0x00000838 +# define LCDC_DP6_7_DP6_7_OFFSET 0 +# define LCDC_DP6_7_DP6_7_SIZE 28 +#define LCDC_LCDCON1 0x00000800 +# define LCDC_LCDCON1_BYPASS_OFFSET 0 +# define LCDC_LCDCON1_BYPASS_SIZE 1 +# define LCDC_LCDCON1_CLKVAL_OFFSET 12 +# define LCDC_LCDCON1_CLKVAL_SIZE 9 +# define LCDC_LCDCON1_LINECNT_OFFSET 21 +# define LCDC_LCDCON1_LINECNT_SIZE 11 +#define LCDC_LCDCON2 0x00000804 +# define LCDC_LCDCON2_CLKMOD_OFFSET 15 +# define LCDC_LCDCON2_CLKMOD_SIZE 1 +# define LCDC_LCDCON2_DISTYPE_OFFSET 0 +# define LCDC_LCDCON2_DISTYPE_SIZE 2 +# define LCDC_LCDCON2_IFWIDTH_OFFSET 3 +# define LCDC_LCDCON2_IFWIDTH_SIZE 2 +# define LCDC_LCDCON2_INVCLK_OFFSET 11 +# define LCDC_LCDCON2_INVCLK_SIZE 1 +# define LCDC_LCDCON2_INVDVAL_OFFSET 12 +# define LCDC_LCDCON2_INVDVAL_SIZE 1 +# define LCDC_LCDCON2_INVFRAME_OFFSET 9 +# define LCDC_LCDCON2_INVFRAME_SIZE 1 +# define LCDC_LCDCON2_INVLINE_OFFSET 10 +# define LCDC_LCDCON2_INVLINE_SIZE 1 +# define LCDC_LCDCON2_INVVD_OFFSET 8 +# define LCDC_LCDCON2_INVVD_SIZE 1 +# define LCDC_LCDCON2_MEMOR_OFFSET 30 +# define LCDC_LCDCON2_MEMOR_SIZE 2 +# define LCDC_LCDCON2_PIXELSIZE_OFFSET 5 +# define LCDC_LCDCON2_PIXELSIZE_SIZE 3 +# define LCDC_LCDCON2_SCANMOD_OFFSET 2 +# define LCDC_LCDCON2_SCANMOD_SIZE 1 +#define LCDC_LCDFIFO 0x00000814 +# define LCDC_LCDFIFO_FIFOTH_OFFSET 0 +# define LCDC_LCDFIFO_FIFOTH_SIZE 16 +#define LCDC_LCDFRMCFG 0x00000810 +# define LCDC_LCDFRMCFG_LINESIZE_OFFSET 21 +# define LCDC_LCDFRMCFG_LINESIZE_SIZE 11 +# define LCDC_LCDFRMCFG_LINEVAL_OFFSET 0 +# define LCDC_LCDFRMCFG_LINEVAL_SIZE 11 +#define LCDC_LCDMVAL 0x00000818 +# define LCDC_LCDMVAL_MMODE_OFFSET 31 +# define LCDC_LCDMVAL_MMODE_SIZE 1 +# define LCDC_LCDMVAL_MVAL_OFFSET 0 +# define LCDC_LCDMVAL_MVAL_SIZE 8 +#define LCDC_LCDTIM1 0x00000808 +# define LCDC_LCDTIM1_VBP_OFFSET 8 +# define LCDC_LCDTIM1_VBP_SIZE 8 +# define LCDC_LCDTIM1_VFP_OFFSET 0 +# define LCDC_LCDTIM1_VFP_SIZE 8 +# define LCDC_LCDTIM1_VHDLY_OFFSET 24 +# define LCDC_LCDTIM1_VHDLY_SIZE 4 +# define LCDC_LCDTIM1_VPW_OFFSET 16 +# define LCDC_LCDTIM1_VPW_SIZE 6 +#define LCDC_LCDTIM2 0x0000080C +# define LCDC_LCDTIM2_HBP_OFFSET 0 +# define LCDC_LCDTIM2_HBP_SIZE 8 +# define LCDC_LCDTIM2_HFP_OFFSET 21 +# define LCDC_LCDTIM2_HFP_SIZE 11 +# define LCDC_LCDTIM2_HPW_OFFSET 8 +# define LCDC_LCDTIM2_HPW_SIZE 6 +#define LCDC_LCD_GPR 0x0000085C +# define LCDC_LCD_GPR_GPRB0_OFFSET 0 +# define LCDC_LCD_GPR_GPRB0_SIZE 1 +# define LCDC_LCD_GPR_GPRB1_OFFSET 1 +# define LCDC_LCD_GPR_GPRB1_SIZE 1 +# define LCDC_LCD_GPR_GPRB2_OFFSET 2 +# define LCDC_LCD_GPR_GPRB2_SIZE 1 +# define LCDC_LCD_GPR_GPRB3_OFFSET 3 +# define LCDC_LCD_GPR_GPRB3_SIZE 1 +# define LCDC_LCD_GPR_GPRB4_OFFSET 4 +# define LCDC_LCD_GPR_GPRB4_SIZE 1 +# define LCDC_LCD_GPR_GPRB5_OFFSET 5 +# define LCDC_LCD_GPR_GPRB5_SIZE 1 +# define LCDC_LCD_GPR_GPRB6_OFFSET 6 +# define LCDC_LCD_GPR_GPRB6_SIZE 1 +# define LCDC_LCD_GPR_GPRB7_OFFSET 7 +# define LCDC_LCD_GPR_GPRB7_SIZE 1 +#define LCDC_LCD_ICR 0x00000858 +# define LCDC_LCD_ICR_EOFIC_OFFSET 2 +# define LCDC_LCD_ICR_EOFIC_SIZE 1 +# define LCDC_LCD_ICR_LNIC_OFFSET 0 +# define LCDC_LCD_ICR_LNIC_SIZE 1 +# define LCDC_LCD_ICR_LSTLNIC_OFFSET 1 +# define LCDC_LCD_ICR_LSTLNIC_SIZE 1 +# define LCDC_LCD_ICR_MERIC_OFFSET 6 +# define LCDC_LCD_ICR_MERIC_SIZE 1 +# define LCDC_LCD_ICR_OWRIC_OFFSET 5 +# define LCDC_LCD_ICR_OWRIC_SIZE 1 +# define LCDC_LCD_ICR_UFLWIC_OFFSET 4 +# define LCDC_LCD_ICR_UFLWIC_SIZE 1 +#define LCDC_LCD_IDR 0x0000084C +# define LCDC_LCD_IDR_EOFID_OFFSET 2 +# define LCDC_LCD_IDR_EOFID_SIZE 1 +# define LCDC_LCD_IDR_LNID_OFFSET 0 +# define LCDC_LCD_IDR_LNID_SIZE 1 +# define LCDC_LCD_IDR_LSTLNID_OFFSET 1 +# define LCDC_LCD_IDR_LSTLNID_SIZE 1 +# define LCDC_LCD_IDR_MERID_OFFSET 6 +# define LCDC_LCD_IDR_MERID_SIZE 1 +# define LCDC_LCD_IDR_OWRID_OFFSET 5 +# define LCDC_LCD_IDR_OWRID_SIZE 1 +# define LCDC_LCD_IDR_UFLWID_OFFSET 4 +# define LCDC_LCD_IDR_UFLWID_SIZE 1 +#define LCDC_LCD_IER 0x00000848 +# define LCDC_LCD_IER_EOFIE_OFFSET 2 +# define LCDC_LCD_IER_EOFIE_SIZE 1 +# define LCDC_LCD_IER_LNIE_OFFSET 0 +# define LCDC_LCD_IER_LNIE_SIZE 1 +# define LCDC_LCD_IER_LSTLNIE_OFFSET 1 +# define LCDC_LCD_IER_LSTLNIE_SIZE 1 +# define LCDC_LCD_IER_MERIE_OFFSET 6 +# define LCDC_LCD_IER_MERIE_SIZE 1 +# define LCDC_LCD_IER_OWRIE_OFFSET 5 +# define LCDC_LCD_IER_OWRIE_SIZE 1 +# define LCDC_LCD_IER_UFLWIE_OFFSET 4 +# define LCDC_LCD_IER_UFLWIE_SIZE 1 +#define LCDC_LCD_IMR 0x00000850 +# define LCDC_LCD_IMR_EOFIM_OFFSET 2 +# define LCDC_LCD_IMR_EOFIM_SIZE 1 +# define LCDC_LCD_IMR_LNIM_OFFSET 0 +# define LCDC_LCD_IMR_LNIM_SIZE 1 +# define LCDC_LCD_IMR_LSTLNIM_OFFSET 1 +# define LCDC_LCD_IMR_LSTLNIM_SIZE 1 +# define LCDC_LCD_IMR_MERIM_OFFSET 6 +# define LCDC_LCD_IMR_MERIM_SIZE 1 +# define LCDC_LCD_IMR_OWRIM_OFFSET 5 +# define LCDC_LCD_IMR_OWRIM_SIZE 1 +# define LCDC_LCD_IMR_UFLWIM_OFFSET 4 +# define LCDC_LCD_IMR_UFLWIM_SIZE 1 +#define LCDC_LCD_IRR 0x00000864 +# define LCDC_LCD_IRR_EOFIR_OFFSET 2 +# define LCDC_LCD_IRR_EOFIR_SIZE 1 +# define LCDC_LCD_IRR_LNIR_OFFSET 0 +# define LCDC_LCD_IRR_LNIR_SIZE 1 +# define LCDC_LCD_IRR_LSTLNIR_OFFSET 1 +# define LCDC_LCD_IRR_LSTLNIR_SIZE 1 +# define LCDC_LCD_IRR_MERIR_OFFSET 6 +# define LCDC_LCD_IRR_MERIR_SIZE 1 +# define LCDC_LCD_IRR_OWRIR_OFFSET 5 +# define LCDC_LCD_IRR_OWRIR_SIZE 1 +# define LCDC_LCD_IRR_UFLWIR_OFFSET 4 +# define LCDC_LCD_IRR_UFLWIR_SIZE 1 +#define LCDC_LCD_ISR 0x00000854 +# define LCDC_LCD_ISR_EOFIS_OFFSET 2 +# define LCDC_LCD_ISR_EOFIS_SIZE 1 +# define LCDC_LCD_ISR_LNIS_OFFSET 0 +# define LCDC_LCD_ISR_LNIS_SIZE 1 +# define LCDC_LCD_ISR_LSTLNIS_OFFSET 1 +# define LCDC_LCD_ISR_LSTLNIS_SIZE 1 +# define LCDC_LCD_ISR_MERIS_OFFSET 6 +# define LCDC_LCD_ISR_MERIS_SIZE 1 +# define LCDC_LCD_ISR_OWRIS_OFFSET 5 +# define LCDC_LCD_ISR_OWRIS_SIZE 1 +# define LCDC_LCD_ISR_UFLWIS_OFFSET 4 +# define LCDC_LCD_ISR_UFLWIS_SIZE 1 +#define LCDC_LCD_ITR 0x00000860 +# define LCDC_LCD_ITR_EOFIT_OFFSET 2 +# define LCDC_LCD_ITR_EOFIT_SIZE 1 +# define LCDC_LCD_ITR_LNIT_OFFSET 0 +# define LCDC_LCD_ITR_LNIT_SIZE 1 +# define LCDC_LCD_ITR_LSTLNIT_OFFSET 1 +# define LCDC_LCD_ITR_LSTLNIT_SIZE 1 +# define LCDC_LCD_ITR_MERIT_OFFSET 6 +# define LCDC_LCD_ITR_MERIT_SIZE 1 +# define LCDC_LCD_ITR_OWRIT_OFFSET 5 +# define LCDC_LCD_ITR_OWRIT_SIZE 1 +# define LCDC_LCD_ITR_UFLWIT_OFFSET 4 +# define LCDC_LCD_ITR_UFLWIT_SIZE 1 +#define LCDC_PWRCON 0x0000083C +# define LCDC_PWRCON_GUARD_TIME_OFFSET 1 +# define LCDC_PWRCON_GUARD_TIME_SIZE 7 +# define LCDC_PWRCON_LCD_BUSY_OFFSET 31 +# define LCDC_PWRCON_LCD_BUSY_SIZE 1 +# define LCDC_PWRCON_LCD_PWR_OFFSET 0 +# define LCDC_PWRCON_LCD_PWR_SIZE 1 + +#define LCDC_BIT(name) (1 << LCDC_##name##_OFFSET) +#define LCDC_MKBF(name,value) (((value) & ((1 << LCDC_##name##_SIZE) - 1)) << LCDC_##name##_OFFSET) +#define LCDC_GETBF(name,value) (((value) >> LCDC_##name##_OFFSET) & ((1 << LCDC_##name##_SIZE) - 1)) +#define LCDC_INSBF(name,value,old) (((old) & ~(((1 << LCDC_##name##_SIZE) - 1) << LCDC_##name##_OFFSET)) | LCDC_MKBF(name, value)) + +#define lcdc_readl(port,reg) \ + __raw_readl((port)->regs + LCDC_##reg) +#define lcdc_writel(port,reg,value) \ + __raw_writel((value), (port)->regs + LCDC_##reg) + +#endif /* __ASM_AVR32_PERIPH_LCDC_H__ */ diff -urN linux-2.6.20.4-0rig/include/asm-avr32/posix_types.h linux-2.6.20.4-atmel/include/asm-avr32/posix_types.h --- linux-2.6.20.4-0rig/include/asm-avr32/posix_types.h 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/include/asm-avr32/posix_types.h 2007-03-24 16:42:28.000000000 +0100 @@ -23,7 +23,7 @@ typedef unsigned int __kernel_uid_t; typedef unsigned int __kernel_gid_t; typedef unsigned long __kernel_size_t; -typedef int __kernel_ssize_t; +typedef long __kernel_ssize_t; typedef int __kernel_ptrdiff_t; typedef long __kernel_time_t; typedef long __kernel_suseconds_t; diff -urN linux-2.6.20.4-0rig/include/asm-avr32/uaccess.h linux-2.6.20.4-atmel/include/asm-avr32/uaccess.h --- linux-2.6.20.4-0rig/include/asm-avr32/uaccess.h 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/include/asm-avr32/uaccess.h 2007-03-24 16:42:28.000000000 +0100 @@ -68,12 +68,6 @@ #define access_ok(type, addr, size) (likely(__range_ok(addr, size) == 0)) -static inline int -verify_area(int type, const void __user *addr, unsigned long size) -{ - return access_ok(type, addr, size) ? 0 : -EFAULT; -} - /* Generic arbitrary sized copy. Return the number of bytes NOT copied */ extern __kernel_size_t __copy_user(void *to, const void *from, __kernel_size_t n); diff -urN linux-2.6.20.4-0rig/include/asm-avr32/unistd.h linux-2.6.20.4-atmel/include/asm-avr32/unistd.h --- linux-2.6.20.4-0rig/include/asm-avr32/unistd.h 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/include/asm-avr32/unistd.h 2007-03-24 16:42:28.000000000 +0100 @@ -120,7 +120,7 @@ #define __NR_getitimer 105 #define __NR_swapoff 106 #define __NR_sysinfo 107 -#define __NR_ipc 108 +/* 108 was __NR_ipc for a little while */ #define __NR_sendfile 109 #define __NR_setdomainname 110 #define __NR_uname 111 @@ -282,8 +282,21 @@ #define __NR_vmsplice 264 #define __NR_epoll_pwait 265 +#define __NR_msgget 266 +#define __NR_msgsnd 267 +#define __NR_msgrcv 268 +#define __NR_msgctl 269 +#define __NR_semget 270 +#define __NR_semop 271 +#define __NR_semctl 272 +#define __NR_semtimedop 273 +#define __NR_shmat 274 +#define __NR_shmget 275 +#define __NR_shmdt 276 +#define __NR_shmctl 277 + #ifdef __KERNEL__ -#define NR_syscalls 266 +#define NR_syscalls 278 #define __ARCH_WANT_IPC_PARSE_VERSION diff -urN linux-2.6.20.4-0rig/include/linux/atmel_pdc.h linux-2.6.20.4-atmel/include/linux/atmel_pdc.h --- linux-2.6.20.4-0rig/include/linux/atmel_pdc.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/include/linux/atmel_pdc.h 2007-03-24 16:39:16.000000000 +0100 @@ -0,0 +1,36 @@ +/* + * include/linux/atmel_pdc.h + * + * Copyright (C) 2005 Ivan Kokshaysky + * Copyright (C) SAN People + * + * Peripheral Data Controller (PDC) registers. + * Based on AT91RM9200 datasheet revision E. + * + * 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. + */ + +#ifndef ATMEL_PDC_H +#define ATMEL_PDC_H + +#define ATMEL_PDC_RPR 0x100 /* Receive Pointer Register */ +#define ATMEL_PDC_RCR 0x104 /* Receive Counter Register */ +#define ATMEL_PDC_TPR 0x108 /* Transmit Pointer Register */ +#define ATMEL_PDC_TCR 0x10c /* Transmit Counter Register */ +#define ATMEL_PDC_RNPR 0x110 /* Receive Next Pointer Register */ +#define ATMEL_PDC_RNCR 0x114 /* Receive Next Counter Register */ +#define ATMEL_PDC_TNPR 0x118 /* Transmit Next Pointer Register */ +#define ATMEL_PDC_TNCR 0x11c /* Transmit Next Counter Register */ + +#define ATMEL_PDC_PTCR 0x120 /* Transfer Control Register */ +#define ATMEL_PDC_RXTEN (1 << 0) /* Receiver Transfer Enable */ +#define ATMEL_PDC_RXTDIS (1 << 1) /* Receiver Transfer Disable */ +#define ATMEL_PDC_TXTEN (1 << 8) /* Transmitter Transfer Enable */ +#define ATMEL_PDC_TXTDIS (1 << 9) /* Transmitter Transfer Disable */ + +#define ATMEL_PDC_PTSR 0x124 /* Transfer Status Register */ + +#endif diff -urN linux-2.6.20.4-0rig/include/linux/fb.h linux-2.6.20.4-atmel/include/linux/fb.h --- linux-2.6.20.4-0rig/include/linux/fb.h 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/include/linux/fb.h 2007-03-24 16:42:29.000000000 +0100 @@ -192,6 +192,7 @@ /* vtotal = 144d/288n/576i => PAL */ /* vtotal = 121d/242n/484i => NTSC */ #define FB_SYNC_ON_GREEN 32 /* sync on green */ +#define FB_SYNC_PCLK_RISING 64 /* pixel data sampled on rising pclk */ #define FB_VMODE_NONINTERLACED 0 /* non interlaced */ #define FB_VMODE_INTERLACED 1 /* interlaced */ @@ -827,7 +828,7 @@ #define fb_writeq sbus_writeq #define fb_memset sbus_memset_io -#elif defined(__i386__) || defined(__alpha__) || defined(__x86_64__) || defined(__hppa__) || (defined(__sh__) && !defined(__SH5__)) || defined(__powerpc__) +#elif defined(__i386__) || defined(__alpha__) || defined(__x86_64__) || defined(__hppa__) || (defined(__sh__) && !defined(__SH5__)) || defined(__powerpc__) || defined(__avr32__) #define fb_readb __raw_readb #define fb_readw __raw_readw diff -urN linux-2.6.20.4-0rig/include/linux/mtd/physmap.h linux-2.6.20.4-atmel/include/linux/mtd/physmap.h --- linux-2.6.20.4-0rig/include/linux/mtd/physmap.h 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/include/linux/mtd/physmap.h 2007-03-24 16:42:28.000000000 +0100 @@ -18,9 +18,10 @@ #define __LINUX_MTD_PHYSMAP__ #include <linux/mtd/mtd.h> -#include <linux/mtd/map.h> #include <linux/mtd/partitions.h> +struct map_info; + struct physmap_flash_data { unsigned int width; void (*set_vpp)(struct map_info *, int); diff -urN linux-2.6.20.4-0rig/include/linux/spi/ads7846.h linux-2.6.20.4-atmel/include/linux/spi/ads7846.h --- linux-2.6.20.4-0rig/include/linux/spi/ads7846.h 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/include/linux/spi/ads7846.h 2007-03-24 16:39:16.000000000 +0100 @@ -5,9 +5,17 @@ * * It's OK if the min/max values are zero. */ +enum ads7846_filter { + ADS7846_FILTER_OK, + ADS7846_FILTER_REPEAT, + ADS7846_FILTER_IGNORE, +}; + struct ads7846_platform_data { u16 model; /* 7843, 7845, 7846. */ u16 vref_delay_usecs; /* 0 for external vref; etc */ + int keep_vref_on:1; /* set to keep vref on for differential + * measurements as well */ u16 x_plate_ohms; u16 y_plate_ohms; @@ -21,5 +29,9 @@ u16 debounce_rep; /* additional consecutive good readings * required after the first two */ int (*get_pendown_state)(void); + int (*filter_init) (struct ads7846_platform_data *pdata, + void **filter_data); + int (*filter) (void *filter_data, int data_idx, int *val); + void (*filter_cleanup)(void *filter_data); }; diff -urN linux-2.6.20.4-0rig/include/video/atmel_lcdc.h linux-2.6.20.4-atmel/include/video/atmel_lcdc.h --- linux-2.6.20.4-0rig/include/video/atmel_lcdc.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/include/video/atmel_lcdc.h 2007-03-24 16:39:16.000000000 +0100 @@ -0,0 +1,193 @@ +/* + * include/video/atmel_lcdc.h + * + * Header file for AT91/AT32 LCD Controller + * + * Data structure and register user interface + * + * Copyright (C) 2007 Atmel 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 __ATMEL_LCDC_H__ +#define __ATMEL_LCDC_H__ + + /* LCD Controller info data structure */ +struct atmel_lcdfb_info { + spinlock_t lock; + struct fb_info *info; + void __iomem *mmio; + unsigned long irq_base; + dma_addr_t map_dma; + void *map_cpu; + size_t map_size; + + unsigned int guard_time; + struct platform_device *pdev; + struct clk *bus_clk; + struct clk *lcdc_clk; + unsigned int default_bpp; + unsigned int default_lcdcon2; + unsigned int default_dmacon; + int default_flags; + u8 power_control_pin; + struct fb_monspecs *default_monspecs; +}; + +#define ATMEL_LCDC_DMABADDR1 0x00 /* DMA Base Address Register 1 */ +#define ATMEL_LCDC_DMABADDR2 0x04 /* DMA Base Address Register 2 */ +#define ATMEL_LCDC_DMAFRMPT1 0x08 /* DMA Frame Pointer Register 1 */ +#define ATMEL_LCDC_DMAFRMPT2 0x0c /* DMA Frame Pointer Register 2 */ +#define ATMEL_LCDC_DMAFRMADD1 0x10 /* DMA Frame Address Register 1 */ +#define ATMEL_LCDC_DMAFRMADD2 0x14 /* DMA Frame Address Register 2 */ + +#define ATMEL_LCDC_DMAFRMCFG 0x18 /* DMA Frame Configuration Register */ +#define ATMEL_LCDC_FRSIZE (0x7fffff << 0) /* Frame Size */ +#define ATMEL_LCDC_BLENGTH_OFFSET 24 /* Burst Length */ +#define ATMEL_LCDC_BLENGTH (0x7f << ATMEL_LCDC_BLENGTH_OFFSET) + +#define ATMEL_LCDC_DMACON 0x1c /* DMA Control Register */ +#define ATMEL_LCDC_DMAEN (0x1 << 0) /* DMA Enable */ +#define ATMEL_LCDC_DMARST (0x1 << 1) /* DMA Reset */ +#define ATMEL_LCDC_DMABUSY (0x1 << 2) /* DMA Busy */ + +#define ATMEL_LCDC_LCDCON1 0x0800 /* LCD Control Register 1 */ +#define ATMEL_LCDC_BYPASS (1 << 0) /* Bypass lcd_dotck divider */ +#define ATMEL_LCDC_CLKVAL_OFFSET 12 /* Clock Divider */ +#define ATMEL_LCDC_CLKVAL (0x1ff << ATMEL_LCDC_CLKVAL_OFFSET) +#define ATMEL_LCDC_LINCNT (0x7ff << 21) /* Line Counter */ + +#define ATMEL_LCDC_LCDCON2 0x0804 /* LCD Control Register 2 */ +#define ATMEL_LCDC_DISTYPE (3 << 0) /* Display Type */ +#define ATMEL_LCDC_DISTYPE_STNMONO (0 << 0) +#define ATMEL_LCDC_DISTYPE_STNCOLOR (1 << 0) +#define ATMEL_LCDC_DISTYPE_TFT (2 << 0) +#define ATMEL_LCDC_SCANMOD (1 << 2) /* Scan Mode */ +#define ATMEL_LCDC_SCANMOD_SINGLE (0 << 2) +#define ATMEL_LCDC_SCANMOD_DUAL (1 << 2) +#define ATMEL_LCDC_IFWIDTH (3 << 3) /*Interface Width */ +#define ATMEL_LCDC_IFWIDTH_4 (0 << 3) +#define ATMEL_LCDC_IFWIDTH_8 (1 << 3) +#define ATMEL_LCDC_IFWIDTH_16 (2 << 3) +#define ATMEL_LCDC_PIXELSIZE (7 << 5) /* Bits per pixel */ +#define ATMEL_LCDC_PIXELSIZE_1 (0 << 5) +#define ATMEL_LCDC_PIXELSIZE_2 (1 << 5) +#define ATMEL_LCDC_PIXELSIZE_4 (2 << 5) +#define ATMEL_LCDC_PIXELSIZE_8 (3 << 5) +#define ATMEL_LCDC_PIXELSIZE_16 (4 << 5) +#define ATMEL_LCDC_PIXELSIZE_24 (5 << 5) +#define ATMEL_LCDC_PIXELSIZE_32 (6 << 5) +#define ATMEL_LCDC_INVVD (1 << 8) /* LCD Data polarity */ +#define ATMEL_LCDC_INVVD_NORMAL (0 << 8) +#define ATMEL_LCDC_INVVD_INVERTED (1 << 8) +#define ATMEL_LCDC_INVFRAME (1 << 9 ) /* LCD VSync polarity */ +#define ATMEL_LCDC_INVFRAME_NORMAL (0 << 9) +#define ATMEL_LCDC_INVFRAME_INVERTED (1 << 9) +#define ATMEL_LCDC_INVLINE (1 << 10) /* LCD HSync polarity */ +#define ATMEL_LCDC_INVLINE_NORMAL (0 << 10) +#define ATMEL_LCDC_INVLINE_INVERTED (1 << 10) +#define ATMEL_LCDC_INVCLK (1 << 11) /* LCD dotclk polarity */ +#define ATMEL_LCDC_INVCLK_NORMAL (0 << 11) +#define ATMEL_LCDC_INVCLK_INVERTED (1 << 11) +#define ATMEL_LCDC_INVDVAL (1 << 12) /* LCD dval polarity */ +#define ATMEL_LCDC_INVDVAL_NORMAL (0 << 12) +#define ATMEL_LCDC_INVDVAL_INVERTED (1 << 12) +#define ATMEL_LCDC_CLKMOD (1 << 15) /* LCD dotclk mode */ +#define ATMEL_LCDC_CLKMOD_ACTIVEDISPLAY (0 << 15) +#define ATMEL_LCDC_CLKMOD_ALWAYSACTIVE (1 << 15) +#define ATMEL_LCDC_MEMOR (1 << 31) /* Memory Ordering Format */ +#define ATMEL_LCDC_MEMOR_BIG (0 << 31) +#define ATMEL_LCDC_MEMOR_LITTLE (1 << 31) + +#define ATMEL_LCDC_TIM1 0x0808 /* LCD Timing Register 1 */ +#define ATMEL_LCDC_VFP (0xff << 0) /* Vertical Front Porch */ +#define ATMEL_LCDC_VBP_OFFSET 8 /* Vertical Back Porch */ +#define ATMEL_LCDC_VBP (0xff << ATMEL_LCDC_VBP_OFFSET) +#define ATMEL_LCDC_VPW_OFFSET 16 /* Vertical Synchronization Pulse Width */ +#define ATMEL_LCDC_VPW (0x3f << ATMEL_LCDC_VPW_OFFSET) +#define ATMEL_LCDC_VHDLY_OFFSET 24 /* Vertical to Horizontal Delay */ +#define ATMEL_LCDC_VHDLY (0xf << ATMEL_LCDC_VHDLY_OFFSET) + +#define ATMEL_LCDC_TIM2 0x080c /* LCD Timing Register 2 */ +#define ATMEL_LCDC_HBP (0xff << 0) /* Horizontal Back Porch */ +#define ATMEL_LCDC_HPW_OFFSET 8 /* Horizontal Synchronization Pulse Width */ +#define ATMEL_LCDC_HPW (0x3f << ATMEL_LCDC_HPW_OFFSET) +#define ATMEL_LCDC_HFP_OFFSET 21 /* Horizontal Front Porch */ +#define ATMEL_LCDC_HFP (0x7ff << ATMEL_LCDC_HFP_OFFSET) + +#define ATMEL_LCDC_LCDFRMCFG 0x0810 /* LCD Frame Configuration Register */ +#define ATMEL_LCDC_LINEVAL (0x7ff << 0) /* Vertical Size of LCD Module */ +#define ATMEL_LCDC_HOZVAL_OFFSET 21 /* Horizontal Size of LCD Module */ +#define ATMEL_LCDC_HOZVAL (0x7ff << ATMEL_LCDC_HOZVAL_OFFSET) + +#define ATMEL_LCDC_FIFO 0x0814 /* LCD FIFO Register */ +#define ATMEL_LCDC_FIFOTH (0xffff) /* FIFO Threshold */ + +#define ATMEL_LCDC_MVAL 0x0818 /* LCD Mode Toggle Rate Value Register */ + +#define ATMEL_LCDC_DP1_2 0x081c /* Dithering Pattern DP1_2 Register */ +#define ATMEL_LCDC_DP4_7 0x0820 /* Dithering Pattern DP4_7 Register */ +#define ATMEL_LCDC_DP3_5 0x0824 /* Dithering Pattern DP3_5 Register */ +#define ATMEL_LCDC_DP2_3 0x0828 /* Dithering Pattern DP2_3 Register */ +#define ATMEL_LCDC_DP5_7 0x082c /* Dithering Pattern DP5_7 Register */ +#define ATMEL_LCDC_DP3_4 0x0830 /* Dithering Pattern DP3_4 Register */ +#define ATMEL_LCDC_DP4_5 0x0834 /* Dithering Pattern DP4_5 Register */ +#define ATMEL_LCDC_DP6_7 0x0838 /* Dithering Pattern DP6_7 Register */ +#define ATMEL_LCDC_DP1_2_VAL (0xff) +#define ATMEL_LCDC_DP4_7_VAL (0xfffffff) +#define ATMEL_LCDC_DP3_5_VAL (0xfffff) +#define ATMEL_LCDC_DP2_3_VAL (0xfff) +#define ATMEL_LCDC_DP5_7_VAL (0xfffffff) +#define ATMEL_LCDC_DP3_4_VAL (0xffff) +#define ATMEL_LCDC_DP4_5_VAL (0xfffff) +#define ATMEL_LCDC_DP6_7_VAL (0xfffffff) + +#define ATMEL_LCDC_PWRCON 0x083c /* Power Control Register */ +#define ATMEL_LCDC_PWR (1 << 0) /* LCD Module Power Control */ +#define ATMEL_LCDC_GUARDT_OFFSET 1 /* Delay in Frame Period */ +#define ATMEL_LCDC_GUARDT (0x7f << ATMEL_LCDC_GUARDT_OFFSET) +#define ATMEL_LCDC_BUSY (1 << 31) /* LCD Busy */ + +#define ATMEL_LCDC_CONTRAST_CTR 0x0840 /* Contrast Control Register */ +#define ATMEL_LCDC_PS (3 << 0) /* Contrast Counter Prescaler */ +#define ATMEL_LCDC_PS_DIV1 (0 << 0) +#define ATMEL_LCDC_PS_DIV2 (1 << 0) +#define ATMEL_LCDC_PS_DIV4 (2 << 0) +#define ATMEL_LCDC_PS_DIV8 (3 << 0) +#define ATMEL_LCDC_POL (1 << 2) /* Polarity of output Pulse */ +#define ATMEL_LCDC_POL_NEGATIVE (0 << 2) +#define ATMEL_LCDC_POL_POSITIVE (1 << 2) +#define ATMEL_LCDC_ENA (1 << 3) /* PWM generator Control */ +#define ATMEL_LCDC_ENA_PWMDISABLE (0 << 3) +#define ATMEL_LCDC_ENA_PWMENABLE (1 << 3) + +#define ATMEL_LCDC_CONTRAST_VAL 0x0844 /* Contrast Value Register */ +#define ATMEL_LCDC_CVAL (0xff) /* PWM compare value */ + +#define ATMEL_LCDC_IER 0x0848 /* Interrupt Enable Register */ +#define ATMEL_LCDC_IDR 0x084c /* Interrupt Disable Register */ +#define ATMEL_LCDC_IMR 0x0850 /* Interrupt Mask Register */ +#define ATMEL_LCDC_ISR 0x0854 /* Interrupt Status Register */ +#define ATMEL_LCDC_ICR 0x0858 /* Interrupt Clear Register */ +#define ATMEL_LCDC_LNI (1 << 0) /* Line Interrupt */ +#define ATMEL_LCDC_LSTLNI (1 << 1) /* Last Line Interrupt */ +#define ATMEL_LCDC_EOFI (1 << 2) /* DMA End Of Frame Interrupt */ +#define ATMEL_LCDC_UFLWI (1 << 4) /* FIFO Underflow Interrupt */ +#define ATMEL_LCDC_OWRI (1 << 5) /* FIFO Overwrite Interrupt */ +#define ATMEL_LCDC_MERI (1 << 6) /* DMA Memory Error Interrupt */ + +#define ATMEL_LCDC_LUT_(n) (0x0c00 + ((n)*4)) /* Palette Entry 0..255 */ + +#endif /* __ATMEL_LCDC_H__ */ diff -urN linux-2.6.20.4-0rig/MAINTAINERS linux-2.6.20.4-atmel/MAINTAINERS --- linux-2.6.20.4-0rig/MAINTAINERS 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/MAINTAINERS 2007-03-24 16:43:49.000000000 +0100 @@ -602,6 +602,11 @@ M: hskinnemoen@atmel.com S: Supported +ATMEL SPI DRIVER +P: Haavard Skinnemoen +M: hskinnemoen@atmel.com +S: Supported + ATMEL WIRELESS DRIVER P: Simon Kelley M: simon@thekelleys.org.uk diff -urN linux-2.6.20.4-0rig/scripts/checkstack.pl linux-2.6.20.4-atmel/scripts/checkstack.pl --- linux-2.6.20.4-0rig/scripts/checkstack.pl 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/scripts/checkstack.pl 2007-03-24 16:42:29.000000000 +0100 @@ -12,6 +12,7 @@ # sh64 port by Paul Mundt # Random bits by Matt Mackall <mpm@selenic.com> # M68k port by Geert Uytterhoeven and Andreas Schwab +# AVR32 port by Haavard Skinnemoen <hskinnemoen@atmel.com> # # Usage: # objdump -d vmlinux | stackcheck.pl [arch] @@ -37,6 +38,10 @@ if ($arch eq 'arm') { #c0008ffc: e24dd064 sub sp, sp, #100 ; 0x64 $re = qr/.*sub.*sp, sp, #(([0-9]{2}|[3-9])[0-9]{2})/o; + } elsif ($arch eq 'avr32') { + #8000008a: 20 1d sub sp,4 + #80000ca8: fa cd 05 b0 sub sp,sp,1456 + $re = qr/^.*sub.*sp.*,([0-9]{1,8})/o; } elsif ($arch =~ /^i[3456]86$/) { #c0105234: 81 ec ac 05 00 00 sub $0x5ac,%esp $re = qr/^.*[as][du][db] \$(0x$x{1,8}),\%esp$/o; diff -urN linux-2.6.20.4-0rig/sound/avr32/ac97c.c linux-2.6.20.4-atmel/sound/avr32/ac97c.c --- linux-2.6.20.4-0rig/sound/avr32/ac97c.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/sound/avr32/ac97c.c 2007-03-24 16:42:29.000000000 +0100 @@ -0,0 +1,1250 @@ +/* + * Driver for the Atmel AC97 Controller + * + * Copyright (C) 2005-2006 Atmel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/dma-mapping.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/mutex.h> + +#include <sound/driver.h> +#include <sound/core.h> +#include <sound/initval.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/ac97_codec.h> +#ifndef SND_ATMEL_AC97_USE_ALSA_MALLOC_CALLS +#include <sound/memalloc.h> +#endif + +#include <asm/io.h> + +#include "ac97c.h" + +static DEFINE_MUTEX(opened_mutex); + +/* module parameters */ +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; + +module_param_array(index, int, NULL, 0444); +MODULE_PARM_DESC(index, "Index value for AC97 controller"); +module_param_array(id, charp, NULL, 0444); +MODULE_PARM_DESC(id, "ID string for AC97 controller"); +module_param_array(enable, bool, NULL, 0444); +MODULE_PARM_DESC(enable, "Enable AC97 controller"); + +#ifndef CONFIG_SND_ATMEL_AC97C_USE_PDC +#include <asm/dma-controller.h> + +struct atmel_ac97_dma_info { + struct dma_request_cyclic req_tx; + struct dma_request_cyclic req_rx; + unsigned short rx_periph_id; + unsigned short tx_periph_id; +}; +#endif + + +typedef struct atmel_ac97 { + spinlock_t lock; + void __iomem *regs; + int period; + + snd_pcm_substream_t *playback_substream; + snd_pcm_substream_t *capture_substream; + snd_card_t *card; + snd_pcm_t *pcm; + ac97_t *ac97; + ac97_bus_t *ac97_bus; + int irq; + int opened; + u64 cur_format; + unsigned int cur_rate; + struct clk *mck; + struct platform_device *pdev; + struct atmel_ac97_dma_info dma; +} atmel_ac97_t; +#define get_chip(card) ((atmel_ac97_t *)(card)->private_data) + +#define ac97c_writel(chip, reg, val) \ + __raw_writel((val), (chip)->regs + AC97C_##reg) +#define ac97c_readl(chip, reg) \ + __raw_readl((chip)->regs + AC97C_##reg) + +/* PCM part */ + +static snd_pcm_hardware_t snd_atmel_ac97_playback_hw = { + .info = (SNDRV_PCM_INFO_INTERLEAVED + |SNDRV_PCM_INFO_MMAP + |SNDRV_PCM_INFO_MMAP_VALID + |SNDRV_PCM_INFO_BLOCK_TRANSFER + |SNDRV_PCM_INFO_JOINT_DUPLEX), + .formats = (SNDRV_PCM_FMTBIT_S16_BE|SNDRV_PCM_FMTBIT_S16_LE), + .rates = (SNDRV_PCM_RATE_CONTINUOUS), + .rate_min = 4000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 6, + .buffer_bytes_max = 64*1024, + .period_bytes_min = 512, +#ifdef CONFIG_SND_ATMEL_AC97C_USE_PDC + .period_bytes_max = 64*1024, +#else + .period_bytes_max = 4095, +#endif + .periods_min = 8, + .periods_max = 1024, +}; + +static snd_pcm_hardware_t snd_atmel_ac97_capture_hw = { + .info = (SNDRV_PCM_INFO_INTERLEAVED + |SNDRV_PCM_INFO_MMAP + |SNDRV_PCM_INFO_MMAP_VALID + |SNDRV_PCM_INFO_BLOCK_TRANSFER + |SNDRV_PCM_INFO_JOINT_DUPLEX), + .formats = (SNDRV_PCM_FMTBIT_S16_BE|SNDRV_PCM_FMTBIT_S16_LE), + .rates = (SNDRV_PCM_RATE_CONTINUOUS), + .rate_min = 4000, + .rate_max = 48000, + .channels_min = 1, + .channels_max = 2, + .buffer_bytes_max = 64*1024, + .period_bytes_min = 512, +#ifdef CONFIG_SND_ATMEL_AC97C_USE_PDC + .period_bytes_max = 64*1024, +#else + .period_bytes_max = 4095, +#endif + .periods_min = 8, + .periods_max = 1024, +}; + +/* Joint full duplex variables */ +unsigned int hw_rates[1]; +unsigned int hw_formats[1]; +struct snd_pcm_hw_constraint_list hw_constraint_rates; +struct snd_pcm_hw_constraint_list hw_constraint_formats; + +/* + * PCM functions + */ +static int +snd_atmel_ac97_playback_open(snd_pcm_substream_t *substream) +{ + atmel_ac97_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + mutex_lock(&opened_mutex); + chip->opened++; + runtime->hw = snd_atmel_ac97_playback_hw; + if (chip->cur_rate) { + runtime->hw.rate_min = chip->cur_rate; + runtime->hw.rate_max = chip->cur_rate; + } + if (chip->cur_format) + runtime->hw.formats = (1ULL<<chip->cur_format); + mutex_unlock(&opened_mutex); + chip->playback_substream = substream; + chip->period = 0; + return 0; +} + +static int +snd_atmel_ac97_capture_open(snd_pcm_substream_t *substream) +{ + atmel_ac97_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + mutex_lock(&opened_mutex); + chip->opened++; + runtime->hw = snd_atmel_ac97_capture_hw; + if (chip->cur_rate) { + runtime->hw.rate_min = chip->cur_rate; + runtime->hw.rate_max = chip->cur_rate; + } + if (chip->cur_format) + runtime->hw.formats = (1ULL<<chip->cur_format); + mutex_unlock(&opened_mutex); + chip->capture_substream = substream; + chip->period = 0; + return 0; +} + +static int snd_atmel_ac97_playback_close(snd_pcm_substream_t *substream) +{ + atmel_ac97_t *chip = snd_pcm_substream_chip(substream); + mutex_lock(&opened_mutex); + chip->opened--; + if (!chip->opened) { + chip->cur_rate = 0; + chip->cur_format = 0; + } + mutex_unlock(&opened_mutex); + return 0; +} + +static int snd_atmel_ac97_capture_close(snd_pcm_substream_t *substream) +{ + atmel_ac97_t *chip = snd_pcm_substream_chip(substream); + mutex_lock(&opened_mutex); + chip->opened--; + if (!chip->opened) { + chip->cur_rate = 0; + chip->cur_format = 0; + } + mutex_unlock(&opened_mutex); + return 0; +} + +static int snd_atmel_ac97_playback_hw_params(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *hw_params) +{ + atmel_ac97_t *chip = snd_pcm_substream_chip(substream); +#ifdef SND_ATMEL_AC97_USE_ALSA_MALLOC_CALLS + int err; + err = snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); + + if (err < 0) + return err; + + /* Set restrictions to params */ + mutex_lock(&opened_mutex); + chip->cur_rate = params_rate(hw_params); + chip->cur_format = params_format(hw_params); + mutex_unlock(&opened_mutex); + + return err; +#else + int pg; + size_t size = params_buffer_bytes(hw_params); + struct snd_pcm_runtime *runtime; + struct snd_dma_buffer *dmab = NULL; + + substream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV; + snd_assert(substream != NULL, return -EINVAL); + runtime = substream->runtime; + snd_assert(runtime != NULL, return -EINVAL); + + /* Set restrictions to params */ + mutex_lock(&opened_mutex); + chip->cur_rate = params_rate(hw_params); + chip->cur_format = params_format(hw_params); + mutex_unlock(&opened_mutex); + + /* check if buffer is already allocated */ + if (runtime->dma_buffer_p) { + size_t size_previouse; + int pg_previouse; + + /* new buffer is smaler than previouse allocated buffer */ + if (runtime->dma_buffer_p->bytes >= size) { + runtime->dma_bytes = size; + return 0; /* don't change buffer size */ + } + + size_previouse = runtime->dma_buffer_p->bytes; + pg_previouse = get_order(size_previouse); + + dma_free_coherent(runtime->dma_buffer_p->dev.dev, + PAGE_SIZE << pg_previouse, + runtime->dma_buffer_p->area, + runtime->dma_buffer_p->addr); + + kfree(runtime->dma_buffer_p); + } + + dmab = kzalloc(sizeof(*dmab), GFP_KERNEL); + if (!dmab) + return -ENOMEM; + + dmab->dev = substream->dma_buffer.dev; + dmab->bytes = 0; + + pg = get_order(size); + + dmab->area = dma_alloc_coherent( + substream->dma_buffer.dev.dev, + PAGE_SIZE << pg, + (dma_addr_t *)&dmab->addr, + GFP_KERNEL); + + if (!dmab->area) { + kfree(dmab); + return -ENOMEM; + } + + dmab->bytes = size; + + snd_pcm_set_runtime_buffer(substream, dmab); + runtime->dma_bytes = size; + return 1; +#endif +} + +static int snd_atmel_ac97_capture_hw_params(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *hw_params) +{ + atmel_ac97_t *chip = snd_pcm_substream_chip(substream); +#ifdef SND_ATMEL_AC97_USE_ALSA_MALLOC_CALLS + int err; + err = snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); + + if (err < 0) + return err; + + /* Set restrictions to params */ + mutex_lock(&opened_mutex); + chip->cur_rate = params_rate(hw_params); + chip->cur_format = params_format(hw_params); + mutex_unlock(&opened_mutex); + + return err; +#else + int pg; + size_t size = params_buffer_bytes(hw_params); + struct snd_pcm_runtime *runtime; + struct snd_dma_buffer *dmab = NULL; + + substream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV; + snd_assert(substream != NULL, return -EINVAL); + runtime = substream->runtime; + snd_assert(runtime != NULL, return -EINVAL); + + /* Set restrictions to params */ + mutex_lock(&opened_mutex); + chip->cur_rate = params_rate(hw_params); + chip->cur_format = params_format(hw_params); + mutex_unlock(&opened_mutex); + + /* check if buffer is already allocated */ + if (runtime->dma_buffer_p) { + size_t size_previouse; + int pg_previouse; + + /* new buffer is smaler than previouse allocated buffer */ + if (runtime->dma_buffer_p->bytes >= size) { + runtime->dma_bytes = size; + return 0; /* don't change buffer size */ + } + + size_previouse = runtime->dma_buffer_p->bytes; + pg_previouse = get_order(size_previouse); + + dma_free_coherent(runtime->dma_buffer_p->dev.dev, + PAGE_SIZE << pg_previouse, + runtime->dma_buffer_p->area, + runtime->dma_buffer_p->addr); + + kfree(runtime->dma_buffer_p); + } + + dmab = kzalloc(sizeof(*dmab), GFP_KERNEL); + if (!dmab) + return -ENOMEM; + + dmab->dev = substream->dma_buffer.dev; + dmab->bytes = 0; + + pg = get_order(size); + + dmab->area = dma_alloc_coherent( + substream->dma_buffer.dev.dev, + PAGE_SIZE << pg, + (dma_addr_t *)&dmab->addr, + GFP_KERNEL); + + if (!dmab->area) { + kfree(dmab); + return -ENOMEM; + } + + dmab->bytes = size; + + snd_pcm_set_runtime_buffer(substream, dmab); + runtime->dma_bytes = size; + return 1; +#endif +} + +static int snd_atmel_ac97_playback_hw_free(snd_pcm_substream_t *substream) +{ +#ifdef SND_ATMEL_AC97_USE_ALSA_MALLOC_CALLS + return snd_pcm_lib_free_pages(substream); +#else + int pg; + struct snd_pcm_runtime *runtime; + struct snd_dma_buffer *dmab = NULL; + + snd_assert(substream != NULL, return -EINVAL); + runtime = substream->runtime; + snd_assert(runtime != NULL, return -EINVAL); + dmab = runtime->dma_buffer_p; + + if (!dmab) + return 0; + + if (!dmab->area) + return 0; + + pg = get_order(dmab->bytes); + dma_free_coherent(dmab->dev.dev, PAGE_SIZE << pg, dmab->area, dmab->addr); + kfree(runtime->dma_buffer_p); + snd_pcm_set_runtime_buffer(substream, NULL); + return 0; +#endif +} + +static int snd_atmel_ac97_capture_hw_free(snd_pcm_substream_t *substream) +{ + +#ifdef SND_ATMEL_AC97_USE_ALSA_MALLOC_CALLS + return snd_pcm_lib_free_pages(substream); +#else + int pg; + struct snd_pcm_runtime *runtime; + struct snd_dma_buffer *dmab = NULL; + + snd_assert(substream != NULL, return -EINVAL); + runtime = substream->runtime; + snd_assert(runtime != NULL, return -EINVAL); + dmab = runtime->dma_buffer_p; + + if (!dmab) + return 0; + + if (!dmab->area) + return 0; + + pg = get_order(dmab->bytes); + dma_free_coherent(dmab->dev.dev, PAGE_SIZE << pg, dmab->area, dmab->addr); + kfree(runtime->dma_buffer_p); + snd_pcm_set_runtime_buffer(substream, NULL); + return 0; +#endif +} + +static int snd_atmel_ac97_playback_prepare(snd_pcm_substream_t *substream) +{ + atmel_ac97_t *chip = snd_pcm_substream_chip(substream); + struct platform_device *pdev = chip->pdev; + snd_pcm_runtime_t *runtime = substream->runtime; + int block_size = frames_to_bytes(runtime, runtime->period_size); + unsigned long word = 0; + unsigned long buffer_size = 0; + + dma_sync_single_for_device(&pdev->dev, runtime->dma_addr, + block_size * 2, DMA_TO_DEVICE); + + /* Assign slots to channels */ + switch (substream->runtime->channels) { + case 1: + word |= AC97C_CH_ASSIGN(PCM_LEFT, A); + break; + case 2: + /* Assign Left and Right slot to Channel A */ + word |= AC97C_CH_ASSIGN(PCM_LEFT, A) + | AC97C_CH_ASSIGN(PCM_RIGHT, A); + break; + default: + /* TODO: support more than two channels */ + return -EINVAL; + break; + } + ac97c_writel(chip, OCA, word); + + /* Configure sample format and size */ + word = AC97C_CMR_PDCEN | AC97C_CMR_SIZE_16; + + switch (runtime->format){ + case SNDRV_PCM_FORMAT_S16_LE: + word |= AC97C_CMR_CEM_LITTLE; + break; + case SNDRV_PCM_FORMAT_S16_BE: + default: + word &= ~AC97C_CMR_CEM_LITTLE; + break; + } + + ac97c_writel(chip, CAMR, word); + + /* Set variable rate if needed */ + if (runtime->rate != 48000) { + word = ac97c_readl(chip, MR); + word |= AC97C_MR_VRA; + ac97c_writel(chip, MR, word); + } else { + /* Clear Variable Rate Bit */ + word = ac97c_readl(chip, MR); + word &= ~AC97C_MR_VRA; + ac97c_writel(chip, MR, word); + } + + /* Set rate */ + snd_ac97_set_rate(chip->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate); + +#ifdef CONFIG_SND_ATMEL_AC97C_USE_PDC + /* Initialize and start the PDC */ + ac97c_writel(chip, CATPR, runtime->dma_addr); + ac97c_writel(chip, CATCR, block_size / 4); + ac97c_writel(chip, CATNPR, runtime->dma_addr + block_size); + ac97c_writel(chip, CATNCR, block_size / 4); + ac97c_writel(chip, PTCR, PDC_PTCR_TXTEN); + /* Enable Channel A interrupts */ + ac97c_writel(chip, IER, AC97C_SR_CAEVT); +#else + buffer_size = frames_to_bytes(runtime, runtime->period_size) * + runtime->periods; + + chip->dma.req_tx.buffer_size = buffer_size; + chip->dma.req_tx.periods = runtime->periods; + + BUG_ON(chip->dma.req_tx.buffer_size != + (chip->dma.req_tx.periods * + frames_to_bytes(runtime, runtime->period_size))); + + chip->dma.req_tx.buffer_start = runtime->dma_addr; + chip->dma.req_tx.data_reg = (dma_addr_t)(chip->regs + AC97C_CATHR + 2); + chip->dma.req_tx.periph_id = chip->dma.tx_periph_id; + chip->dma.req_tx.direction = DMA_DIR_MEM_TO_PERIPH; + chip->dma.req_tx.width = DMA_WIDTH_16BIT; + chip->dma.req_tx.dev_id = chip; +#endif + + return 0; +} + +static int snd_atmel_ac97_capture_prepare(snd_pcm_substream_t *substream) +{ + atmel_ac97_t *chip = snd_pcm_substream_chip(substream); + struct platform_device *pdev = chip->pdev; + snd_pcm_runtime_t *runtime = substream->runtime; + int block_size = frames_to_bytes(runtime, runtime->period_size); + unsigned long word = 0; + unsigned long buffer_size = 0; + + dma_sync_single_for_device(&pdev->dev, runtime->dma_addr, + block_size * 2, DMA_FROM_DEVICE); + + /* Assign slots to channels */ + switch (substream->runtime->channels) { + case 1: + word |= AC97C_CH_ASSIGN(PCM_LEFT, A); + break; + case 2: + /* Assign Left and Right slot to Channel A */ + word |= AC97C_CH_ASSIGN(PCM_LEFT, A) + | AC97C_CH_ASSIGN(PCM_RIGHT, A); + break; + default: + /* TODO: support more than two channels */ + return -EINVAL; + break; + } + ac97c_writel(chip, ICA, word); + + /* Configure sample format and size */ + word = AC97C_CMR_PDCEN | AC97C_CMR_SIZE_16; + + switch (runtime->format) { + case SNDRV_PCM_FORMAT_S16_LE: + word |= AC97C_CMR_CEM_LITTLE; + break; + case SNDRV_PCM_FORMAT_S16_BE: + default: + word &= ~(AC97C_CMR_CEM_LITTLE); + break; + } + + ac97c_writel(chip, CAMR, word); + + /* Set variable rate if needed */ + if (runtime->rate != 48000) { + word = ac97c_readl(chip, MR); + word |= AC97C_MR_VRA; + ac97c_writel(chip, MR, word); + } else { + /* Clear Variable Rate Bit */ + word = ac97c_readl(chip, MR); + word &= ~(AC97C_MR_VRA); + ac97c_writel(chip, MR, word); + } + + /* Set rate */ + snd_ac97_set_rate(chip->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate); + +#ifdef CONFIG_SND_ATMEL_AC97C_USE_PDC + /* Initialize and start the PDC */ + ac97c_writel(chip, CARPR, runtime->dma_addr); + ac97c_writel(chip, CARCR, block_size / 4); + ac97c_writel(chip, CARNPR, runtime->dma_addr + block_size); + ac97c_writel(chip, CARNCR, block_size / 4); + ac97c_writel(chip, PTCR, PDC_PTCR_RXEN); + /* Enable Channel A interrupts */ + ac97c_writel(chip, IER, AC97C_SR_CAEVT); +#else + buffer_size = frames_to_bytes(runtime, runtime->period_size) * + runtime->periods; + + chip->dma.req_rx.buffer_size = buffer_size; + chip->dma.req_rx.periods = runtime->periods; + + BUG_ON(chip->dma.req_rx.buffer_size != + (chip->dma.req_rx.periods * + frames_to_bytes(runtime, runtime->period_size))); + + chip->dma.req_rx.buffer_start = runtime->dma_addr; + chip->dma.req_rx.data_reg = (dma_addr_t)(chip->regs + AC97C_CARHR + 2); + chip->dma.req_rx.periph_id = chip->dma.rx_periph_id; + chip->dma.req_rx.direction = DMA_DIR_PERIPH_TO_MEM; + chip->dma.req_rx.width = DMA_WIDTH_16BIT; + chip->dma.req_rx.dev_id = chip; +#endif + + return 0; +} + +static int snd_atmel_ac97_playback_trigger(snd_pcm_substream_t *substream, int cmd) +{ + atmel_ac97_t *chip = snd_pcm_substream_chip(substream); + unsigned long camr; + int flags, err = 0; + + spin_lock_irqsave(&chip->lock, flags); + camr = ac97c_readl(chip, CAMR); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + err = dma_prepare_request_cyclic(chip->dma.req_tx.req.dmac, + &chip->dma.req_tx); + dma_start_request(chip->dma.req_tx.req.dmac, + chip->dma.req_tx.req.channel); + camr |= (AC97C_CMR_CENA +#ifdef CONFIG_SND_ATMEL_AC97C_USE_PDC + |AC97C_CMR_TXRDY +#endif + ); + break; + case SNDRV_PCM_TRIGGER_STOP: + err = dma_stop_request(chip->dma.req_tx.req.dmac, + chip->dma.req_tx.req.channel); + if (chip->opened <= 1) { + camr &= ~(AC97C_CMR_CENA +#ifdef CONFIG_SND_ATMEL_AC97C_USE_PDC + |AC97C_CMR_TXRDY +#endif + ); + } +#ifdef CONFIG_SND_ATMEL_AC97C_USE_PDC + else { + camr &= ~(AC97C_CMR_TXRDY); + } +#endif + break; + default: + err = -EINVAL; + break; + } + + ac97c_writel(chip, CAMR, camr); + + spin_unlock_irqrestore(&chip->lock, flags); + return err; +} + +static int snd_atmel_ac97_capture_trigger(snd_pcm_substream_t *substream, int cmd) +{ + atmel_ac97_t *chip = snd_pcm_substream_chip(substream); + unsigned long camr; + int flags, err = 0; + + spin_lock_irqsave(&chip->lock, flags); + camr = ac97c_readl(chip, CAMR); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + err = dma_prepare_request_cyclic(chip->dma.req_rx.req.dmac, + &chip->dma.req_rx); + dma_start_request(chip->dma.req_rx.req.dmac, + chip->dma.req_rx.req.channel); + camr |= (AC97C_CMR_CENA +#ifdef CONFIG_SND_ATMEL_AC97C_USE_PDC + | AC97C_CMR_RXRDY +#endif + ); + break; + case SNDRV_PCM_TRIGGER_STOP: + err = dma_stop_request(chip->dma.req_rx.req.dmac, + chip->dma.req_rx.req.channel); + mutex_lock(&opened_mutex); + if (chip->opened <= 1) { + camr &= ~(AC97C_CMR_CENA +#ifdef CONFIG_SND_ATMEL_AC97C_USE_PDC + | AC97C_CMR_RXRDY +#endif + ); + } +#ifdef CONFIG_SND_ATMEL_AC97C_USE_PDC + else { + camr &= ~(AC97C_CSR_RXRDY); + } +#endif + mutex_unlock(&opened_mutex); + break; + default: + err = -EINVAL; + break; + } + + ac97c_writel(chip, CAMR, camr); + + spin_unlock_irqrestore(&chip->lock, flags); + return err; +} + +static snd_pcm_uframes_t snd_atmel_ac97_playback_pointer(snd_pcm_substream_t *substream) +{ + atmel_ac97_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_uframes_t pos; + unsigned long bytes; + +#ifdef CONFIG_SND_ATMEL_AC97C_USE_PDC + bytes = ac97c_readl(chip, CATPR) - runtime->dma_addr; +#else + bytes = (dma_get_current_pos + (chip->dma.req_tx.req.dmac, + chip->dma.req_tx.req.channel) - runtime->dma_addr); +#endif + pos = bytes_to_frames(runtime, bytes); + if (pos >= runtime->buffer_size) + pos -= runtime->buffer_size; + + return pos; +} + +static snd_pcm_uframes_t snd_atmel_ac97_capture_pointer(snd_pcm_substream_t *substream) +{ + atmel_ac97_t *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_uframes_t pos; + unsigned long bytes; + +#ifdef CONFIG_SND_ATMEL_AC97C_USE_PDC + bytes = ac97c_readl(chip, CARPR) - runtime->dma_addr; +#else + bytes = (dma_get_current_pos + (chip->dma.req_rx.req.dmac,chip->dma.req_rx.req.channel) - + runtime->dma_addr); +#endif + pos = bytes_to_frames(runtime, bytes); + if (pos >= runtime->buffer_size) + pos -= runtime->buffer_size; + + + return pos; +} + +static snd_pcm_ops_t atmel_ac97_playback_ops = { + .open = snd_atmel_ac97_playback_open, + .close = snd_atmel_ac97_playback_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_atmel_ac97_playback_hw_params, + .hw_free = snd_atmel_ac97_playback_hw_free, + .prepare = snd_atmel_ac97_playback_prepare, + .trigger = snd_atmel_ac97_playback_trigger, + .pointer = snd_atmel_ac97_playback_pointer, +}; + +static snd_pcm_ops_t atmel_ac97_capture_ops = { + .open = snd_atmel_ac97_capture_open, + .close = snd_atmel_ac97_capture_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_atmel_ac97_capture_hw_params, + .hw_free = snd_atmel_ac97_capture_hw_free, + .prepare = snd_atmel_ac97_capture_prepare, + .trigger = snd_atmel_ac97_capture_trigger, + .pointer = snd_atmel_ac97_capture_pointer, +}; + +static struct ac97_pcm atmel_ac97_pcm_defs[] __devinitdata = { + /* Playback */ + { + .exclusive = 1, + .r = { { + .slots = ((1 << AC97_SLOT_PCM_LEFT) + | (1 << AC97_SLOT_PCM_RIGHT) + | (1 << AC97_SLOT_PCM_CENTER) + | (1 << AC97_SLOT_PCM_SLEFT) + | (1 << AC97_SLOT_PCM_SRIGHT) + | (1 << AC97_SLOT_LFE)), + } } + }, + /* PCM in */ + { + .stream = 1, + .exclusive = 1, + .r = { { + .slots = ((1 << AC97_SLOT_PCM_LEFT) + | (1 << AC97_SLOT_PCM_RIGHT)), + } } + }, + /* Mic in */ + { + .stream = 1, + .exclusive = 1, + .r = { { + .slots = (1<<AC97_SLOT_MIC), + } } + }, +}; + +static int __devinit snd_atmel_ac97_pcm_new(atmel_ac97_t *chip) +{ + snd_pcm_t *pcm; + int err; + + err = snd_ac97_pcm_assign(chip->ac97_bus, + ARRAY_SIZE(atmel_ac97_pcm_defs), + atmel_ac97_pcm_defs); + if (err) + return err; + + err = snd_pcm_new(chip->card, "Atmel-AC97", 0, 1, 1, &pcm); + if (err) + return err; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, + &atmel_ac97_playback_ops); + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, + &atmel_ac97_capture_ops); + +#ifdef SND_ATMEL_AC97_USE_ALSA_MALLOC_CALLS + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + &chip->pdev->dev, + 128 * 1024, 128 * 1024); +#endif + + pcm->private_data = chip; + pcm->info_flags = 0; + strcpy(pcm->name, "Atmel-AC97"); + chip->pcm = pcm; + + return 0; +} + +/* Mixer part */ +static int snd_atmel_ac97_mixer_new(atmel_ac97_t *chip) +{ + int err; + ac97_template_t template; + + memset(&template, 0, sizeof(template)); + template.private_data = chip; + err = snd_ac97_mixer(chip->ac97_bus, &template, &chip->ac97); + + return err; +} + +#ifdef CONFIG_SND_ATMEL_AC97C_USE_PDC +static irqreturn_t snd_atmel_ac97_interrupt(int irq, void *dev_id, + struct pt_regs *regs) +{ + atmel_ac97_t *chip = dev_id; + unsigned long status; + + status = ac97c_readl(chip, SR); + + if (status & AC97C_SR_CAEVT) { + snd_pcm_runtime_t *runtime; + int offset, next_period, block_size; + unsigned long casr; + + /* FIXME: separate playback from capture */ + runtime = chip->playback_substream->runtime; + block_size = frames_to_bytes(runtime, runtime->period_size); + + casr = ac97c_readl(chip, CASR); + + if (casr & AC97C_CSR_ENDTX) { + chip->period++; + if (chip->period == runtime->periods) + chip->period = 0; + next_period = chip->period + 1; + if (next_period == runtime->periods) + next_period = 0; + + offset = block_size * next_period; + + ac97c_writel(chip, CATNPR, + runtime->dma_addr + offset); + ac97c_writel(chip, CATNCR, block_size / 4); + + snd_pcm_period_elapsed(chip->playback_substream); + } + else if (casr & AC97C_CSR_ENDRX) { + chip->period++; + if (chip->period == runtime->periods) + chip->period = 0; + next_period = chip->period + 1; + if (next_period == runtime->periods) + next_period = 0; + + offset = block_size * next_period; + + ac97c_writel(chip, CARNPR, + runtime->dma_addr + offset); + ac97c_writel(chip, CARNCR, block_size / 4); + + snd_pcm_period_elapsed(chip->capture_substream); + } else { + snd_printk(KERN_INFO + "atmel-ac97: spurious interrupt, status = 0x%08lx\n", + (unsigned long)casr); + } + } else { + snd_printk(KERN_INFO + "atmel-ac97: spurious interrupt, status = 0x%08lx\n", + status); + } + + (volatile int)ac97c_readl(chip, SR); + + return IRQ_HANDLED; +} + +#else + +static void atmel_ac97_error(struct dma_request *_req) +{ + struct dma_request_cyclic *req = to_dma_request_cyclic(_req); + + printk(KERN_WARNING + "DMA Controller error, channel %d (AC97C)\n", + req->req.channel); +} + +static void atmel_ac97_block_complete(struct dma_request *_req) +{ + struct dma_request_cyclic *req = to_dma_request_cyclic(_req); + atmel_ac97_t *chip = req->dev_id; + if (req->periph_id == chip->dma.tx_periph_id) + snd_pcm_period_elapsed(chip->playback_substream); + else + snd_pcm_period_elapsed(chip->capture_substream); +} + +#endif + +/* CODEC part */ + +static void snd_atmel_ac97_write(ac97_t *ac97, unsigned short reg, + unsigned short val) +{ + atmel_ac97_t *chip = ac97->private_data; + unsigned long word; + int timeout = 40; + + word = (reg & 0x7f) << 16 | val; + + do { + if (ac97c_readl(chip, COSR) & AC97C_CSR_TXRDY) { + ac97c_writel(chip, COTHR, word); + return; + } + udelay(1); + } while (--timeout); + + snd_printk(KERN_WARNING "atmel-ac97: codec write timeout\n"); +} + +static unsigned short snd_atmel_ac97_read(ac97_t *ac97, + unsigned short reg) +{ + atmel_ac97_t *chip = ac97->private_data; + unsigned long word; + int timeout = 40; + int write = 10; + + word = (0x80 | (reg & 0x7f)) << 16; + + if ((ac97c_readl(chip, COSR) & AC97C_CSR_RXRDY) != 0) + ac97c_readl(chip, CORHR); + +retry_write: + timeout = 40; + + do { + if ((ac97c_readl(chip, COSR) & AC97C_CSR_TXRDY) != 0) { + ac97c_writel(chip, COTHR, word); + goto read_reg; + } + mdelay(10); + } while (--timeout); + + if (!--write) + goto timed_out; + goto retry_write; + +read_reg: + do { + if ((ac97c_readl(chip, COSR) & AC97C_CSR_RXRDY) != 0){ + unsigned short val = ac97c_readl(chip, CORHR); + return val; + } + mdelay(10); + } while (--timeout); + + if (!--write) + goto timed_out; + goto retry_write; + +timed_out: + snd_printk(KERN_INFO "atmel-ac97: codec read timeout\n"); + return 0xffff; +} + +static void snd_atmel_ac97_reset(atmel_ac97_t *chip) +{ + /* TODO: Perform hard reset of codec as well */ + ac97c_writel(chip, MR, AC97C_MR_WRST); + mdelay(1); + ac97c_writel(chip, MR, AC97C_MR_ENA); +} + +static void snd_atmel_ac97_destroy(snd_card_t *card) +{ + atmel_ac97_t *chip = get_chip(card); + +#ifdef CONFIG_SND_ATMEL_AC97C_USE_PDC + if (chip->irq != -1) + free_irq(chip->irq, chip); +#endif + if (chip->regs) + iounmap(chip->regs); + + if (chip->mck) { + clk_disable(chip->mck); + clk_put(chip->mck); + } + +#ifndef CONFIG_SND_ATMEL_AC97C_USE_PDC + if (chip->dma.req_tx.req.dmac){ + dma_release_channel(chip->dma.req_tx.req.dmac, + chip->dma.req_tx.req.channel); + } + if (chip->dma.req_rx.req.dmac) { + dma_release_channel(chip->dma.req_rx.req.dmac, + chip->dma.req_rx.req.channel); + } +#endif +} + +static int __devinit snd_atmel_ac97_create(snd_card_t *card, + struct platform_device *pdev) +{ + static ac97_bus_ops_t ops = { + .write = snd_atmel_ac97_write, + .read = snd_atmel_ac97_read, + }; + atmel_ac97_t *chip = get_chip(card); + struct resource *regs; + struct clk *mck; +#ifdef CONFIG_SND_ATMEL_AC97C_USE_PDC + int irq; +#endif + int err; + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) + return -ENXIO; +#ifdef CONFIG_SND_ATMEL_AC97C_USE_PDC + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; +#endif + + mck = clk_get(&pdev->dev, "mck"); + if (IS_ERR(mck)) + return PTR_ERR(mck); + clk_enable(mck); + chip->mck = mck; + + card->private_free = snd_atmel_ac97_destroy; + + spin_lock_init(&chip->lock); + chip->card = card; + chip->pdev = pdev; + chip->irq = -1; + +#ifdef CONFIG_SND_ATMEL_AC97C_USE_PDC + err = request_irq(irq, snd_atmel_ac97_interrupt, 0, + "ac97", chip); + if (err) { + snd_printk("unable to request IRQ%d\n", irq); + return err; + } + chip->irq = irq; +#endif + + chip->regs = ioremap(regs->start, regs->end - regs->start + 1); + if (!chip->regs) + return -ENOMEM; + + snd_card_set_dev(card, &pdev->dev); + + err = snd_ac97_bus(card, 0, &ops, chip, &chip->ac97_bus); + + return err; +} + +static int __devinit snd_atmel_ac97_probe(struct platform_device *pdev) +{ + static int dev; + snd_card_t *card; + atmel_ac97_t *chip; + int err; + int ch; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + err = -ENOMEM; + + mutex_init(&opened_mutex); + + card = snd_card_new(index[dev], id[dev], THIS_MODULE, + sizeof(atmel_ac97_t)); + if (!card) + goto out; + chip = get_chip(card); + + err = snd_atmel_ac97_create(card, pdev); + if (err) + goto out_free_card; + + snd_atmel_ac97_reset(chip); + + err = snd_atmel_ac97_mixer_new(chip); + if (err) + goto out_free_card; + + err = snd_atmel_ac97_pcm_new(chip); + if (err) + goto out_free_card; + +#ifndef CONFIG_SND_ATMEL_AC97C_USE_PDC + /* TODO: Get this information from the platform device */ + chip->dma.req_tx.req.dmac = find_dma_controller(0); + if (!chip->dma.req_tx.req.dmac) { + printk(KERN_ERR + "atmel-ac97c: No DMA controller for TX, aborting\n"); + goto out_free_card; + } + chip->dma.req_rx.req.dmac = find_dma_controller(0); + if (!chip->dma.req_rx.req.dmac) { + snd_printk(KERN_ERR + "atmel-ac97c: No DMA controller available for RX, aborting\n"); + goto out_free_card; + } + + chip->dma.rx_periph_id = 3; + chip->dma.tx_periph_id = 4; + + ch = dma_alloc_channel(chip->dma.req_tx.req.dmac); + if (ch < 0) { + printk(KERN_ERR + "atmel-ac97c: Unable to allocate TX DMA channel, aborting\n"); + goto out_free_card; + } + chip->dma.req_tx.req.channel = ch; + chip->dma.req_tx.width = DMA_WIDTH_16BIT; + chip->dma.req_tx.req.block_complete = atmel_ac97_block_complete; + chip->dma.req_tx.req.error = atmel_ac97_error; + + ch = dma_alloc_channel(chip->dma.req_rx.req.dmac); + if (ch < 0) { + snd_printk(KERN_ERR + "atmel-ac97c: Unable to allocate RX DMA channel, aborting\n"); + goto out_free_card; + } + chip->dma.req_rx.req.channel = ch; + chip->dma.req_rx.width = DMA_WIDTH_16BIT; + chip->dma.req_rx.req.block_complete = atmel_ac97_block_complete; + chip->dma.req_rx.req.error = atmel_ac97_error; +#endif + + strcpy(card->driver, "ac97c"); + strcpy(card->shortname, "Atmel-AC97"); +#ifdef CONFIG_SND_ATMEL_AC97C_USE_PDC + sprintf(card->longname, "Atmel AVR32 AC97 Controller at 0x%p, irq %i", + chip->regs, chip->irq); +#else + sprintf(card->longname, "Atmel AVR32 AC97 Controller at 0x%p, dma rx %i and tx %i", + chip->regs, chip->dma.rx_periph_id, chip->dma.tx_periph_id); +#endif + + err = snd_card_register(card); + if (err) + goto out_free_card; + + platform_set_drvdata(pdev, card); + dev++; + return 0; + +out_free_card: + snd_card_free(card); +out: + return err; +} + +static int __devexit snd_atmel_ac97_remove(struct platform_device *pdev) +{ + snd_card_t *card = platform_get_drvdata(pdev); + + snd_card_free(card); + platform_set_drvdata(pdev, NULL); + return 0; +} + +static struct platform_driver atmel_ac97_driver = { + .probe = snd_atmel_ac97_probe, + .remove = __devexit_p(snd_atmel_ac97_remove), + .driver = { + .name = "ac97c", + }, +}; + +static int __init atmel_ac97_init(void) +{ + return platform_driver_register(&atmel_ac97_driver); +} + +static void __exit atmel_ac97_exit(void) +{ + platform_driver_unregister(&atmel_ac97_driver); +} + +module_init(atmel_ac97_init); +module_exit(atmel_ac97_exit); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Driver for Atmel AC97 Controller"); +MODULE_AUTHOR("Haavard Skinnemoen <hskinnemoen@atmel.com>"); diff -urN linux-2.6.20.4-0rig/sound/avr32/ac97c.h linux-2.6.20.4-atmel/sound/avr32/ac97c.h --- linux-2.6.20.4-0rig/sound/avr32/ac97c.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/sound/avr32/ac97c.h 2007-03-24 16:42:29.000000000 +0100 @@ -0,0 +1,71 @@ +/* + * Register definitions for the Atmel AC97 Controller. + * + * Copyright (C) 2005-2006 Atmel Corporation + * + * 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. + */ +#ifndef __SOUND_AVR32_AC97C_H +#define __SOUND_AVR32_AC97C_H + +#define AC97C_MR 0x08 +#define AC97C_ICA 0x10 +#define AC97C_OCA 0x14 +#define AC97C_CARHR 0x20 +#define AC97C_CATHR 0x24 +#define AC97C_CASR 0x28 +#define AC97C_CAMR 0x2c +#define AC97C_CBRHR 0x30 +#define AC97C_CBTHR 0x34 +#define AC97C_CBSR 0x38 +#define AC97C_CBMR 0x3c +#define AC97C_CORHR 0x40 +#define AC97C_COTHR 0x44 +#define AC97C_COSR 0x48 +#define AC97C_COMR 0x4c +#define AC97C_SR 0x50 +#define AC97C_IER 0x54 +#define AC97C_IDR 0x58 +#define AC97C_IMR 0x5c +#define AC97C_VERSION 0xfc + +#define AC97C_CATPR PDC_TPR +#define AC97C_CATCR PDC_TCR +#define AC97C_CATNPR PDC_TNPR +#define AC97C_CATNCR PDC_TNCR +#define AC97C_CARPR PDC_RPR +#define AC97C_CARCR PDC_RCR +#define AC97C_CARNPR PDC_RNPR +#define AC97C_CARNCR PDC_RNCR +#define AC97C_PTCR PDC_PTCR + +#define AC97C_MR_ENA (1 << 0) +#define AC97C_MR_WRST (1 << 1) +#define AC97C_MR_VRA (1 << 2) + +#define AC97C_CSR_TXRDY (1 << 0) +#define AC97C_CSR_UNRUN (1 << 2) +#define AC97C_CSR_RXRDY (1 << 4) +#define AC97C_CSR_ENDTX (1 << 10) +#define AC97C_CSR_ENDRX (1 << 14) + +#define AC97C_CMR_SIZE_20 (0 << 16) +#define AC97C_CMR_SIZE_18 (1 << 16) +#define AC97C_CMR_SIZE_16 (2 << 16) +#define AC97C_CMR_SIZE_10 (3 << 16) +#define AC97C_CMR_CEM_LITTLE (1 << 18) +#define AC97C_CMR_CEM_BIG (0 << 18) +#define AC97C_CMR_CENA (1 << 21) +#define AC97C_CMR_PDCEN (1 << 22) + +#define AC97C_SR_CAEVT (1 << 3) + +#define AC97C_CH_ASSIGN(slot, channel) \ + (AC97C_CHANNEL_##channel << (3 * (AC97_SLOT_##slot - 3))) +#define AC97C_CHANNEL_NONE 0x0 +#define AC97C_CHANNEL_A 0x1 +#define AC97C_CHANNEL_B 0x2 + +#endif /* __SOUND_AVR32_AC97C_H */ diff -urN linux-2.6.20.4-0rig/sound/avr32/at73c213.c linux-2.6.20.4-atmel/sound/avr32/at73c213.c --- linux-2.6.20.4-0rig/sound/avr32/at73c213.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/sound/avr32/at73c213.c 2007-03-24 16:42:29.000000000 +0100 @@ -0,0 +1,1295 @@ +/* + * Driver for the at73c213 16-bit stereo DAC on Atmel ATSTK1000 + * + * Copyright (C) 2006 Atmel Norway + * + * 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. + * + * The full GNU General Public License is included in this + * distribution in the file called COPYING. + */ +#undef DEBUG +#include <linux/clk.h> +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/dma-mapping.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kmod.h> +#include <linux/module.h> +#include <linux/platform_device.h> + +#include <sound/initval.h> +#include <sound/driver.h> +#include <sound/control.h> +#include <sound/core.h> +#include <sound/pcm.h> +#ifndef SND_AT73C213_USE_ALSA_MALLOC_CALLS +#include <sound/memalloc.h> +#endif + +#include <linux/spi/spi.h> + +#include <asm/io.h> +#include <asm/processor.h> + +#include "at73c213.h" + +/* module parameters */ +static int index[SNDRV_CARDS] = SNDRV_DEFAULT_IDX; +static char *id[SNDRV_CARDS] = SNDRV_DEFAULT_STR; +static int enable[SNDRV_CARDS] = SNDRV_DEFAULT_ENABLE_PNP; + +/* Register defines */ +#define PIOA_BASE 0xFFE02800 +#define SSC0_BASE 0xFFE01C00 +#define PM_BASE 0xFFF00000 + +#define PM_CKSEL 0x04 +#define PM_APBAMASK 0x10 +#define PM_GCCTRL 0x60 + +#define PIO_PER 0x00 +#define PIO_PDR 0x04 +#define PIO_PUER 0x64 +#define PIO_ASR 0x70 +#define PIO_BSR 0x74 + +#define SSC_CMR 0x04 +#define SSC_CR 0x00 +#define SSC_TCMR 0x18 +#define SSC_TFMR 0x1C + +/* SSC register definitions */ +#define SSC_CR 0x00 +#define SSC_CMR 0x04 +#define SSC_TCMR 0x18 +#define SSC_TFMR 0x1C +#define SSC_THR 0x24 +#define SSC_SR 0x40 +#define SSC_IER 0x44 +#define SSC_IDR 0x48 +#define SSC_IMR 0x4C + +/* SSC fields definitions */ +#define SSC_CR_TXEN 0x00000100 +#define SSC_CR_TXDIS 0x00000200 +#define SSC_CR_SWRST 0x00008000 + +/* SSC interrupt definitions */ +#define SSC0_IRQ 10 +#define SSC_INT_ENDTX 0x00000004 +#define SSC_INT_TXBUFE 0x00000008 + +/* PDC register definitions */ +#define PDC_RPR 0x100 +#define PDC_RCR 0x104 +#define PDC_TPR 0x108 +#define PDC_TCR 0x10c +#define PDC_RNPR 0x110 +#define PDC_RNCR 0x114 +#define PDC_TNPR 0x118 +#define PDC_TNCR 0x11c +#define PDC_PTCR 0x120 +#define PDC_PTSR 0x124 + +/* PDC fields definitions */ +#define PDC_PTCR_RXTEN 0x0001 +#define PDC_PTCR_RXTDIS 0x0002 +#define PDC_PTCR_TXTEN 0x0100 +#define PDC_PTCR_TXTDIS 0x0200 + +static int bitrate; +static int gclk_div; +static int ssc_div; +static int spi = 0; +static int ssc = 1; + +module_param(spi, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); +MODULE_PARM_DESC(spi, "Which SPI interface to use to communicate with the at73c213"); +module_param(ssc, int, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); +MODULE_PARM_DESC(ssc, "Which SSC interface to use to communicate with the at73c213"); + +/* Initial AT73C213 register values */ +static unsigned char snd_at73c213_original_image[18] = +{ + 0x00, /* 00 - CTRL */ + 0x05, /* 01 - LLIG */ + 0x05, /* 02 - RLIG */ + 0x08, /* 03 - LPMG */ + 0x08, /* 04 - RPMG */ + 0x00, /* 05 - LLOG */ + 0x00, /* 06 - RLOG */ + 0x22, /* 07 - OLC */ + 0x09, /* 08 - MC */ + 0x00, /* 09 - CSFC */ + 0x00, /* 0A - MISC */ + 0x00, /* 0B - */ + 0x00, /* 0C - PRECH */ + 0x05, /* 0D - AUXG */ + 0x00, /* 0E - */ + 0x00, /* 0F - */ + 0x00, /* 10 - RST */ + 0x00, /* 11 - PA_CTRL */ +}; + +/* chip-specific data */ +struct snd_at73c213 { + snd_card_t *card; + snd_pcm_t *pcm; + snd_pcm_substream_t *substream; + int irq; + int period; + void __iomem *regs; + struct clk *ssc_clk; + struct spi_device *spi; + u8 spi_wbuffer[2]; + u8 spi_rbuffer[2]; + /* image of the SPI registers in AT73C213 */ + u8 image[18]; + spinlock_t lock; + struct platform_device *pdev; +}; + +#define get_chip(card) ((struct snd_at73c213 *)card->private_data) + +static int +snd_at73c213_write_reg(struct snd_at73c213 *chip, u8 reg, u8 val) +{ + struct spi_message msg; + struct spi_transfer msg_xfer = { + .len = 2, + .cs_change = 0, + }; + + spi_message_init(&msg); + + chip->spi_wbuffer[0] = reg; + chip->spi_wbuffer[1] = val; + + msg_xfer.tx_buf = chip->spi_wbuffer; + msg_xfer.rx_buf = chip->spi_rbuffer; + spi_message_add_tail(&msg_xfer, &msg); + + return spi_sync(chip->spi, &msg); +} + +#define write_reg(_spi, reg, val) \ + do { \ + retval = snd_at73c213_write_reg(_spi, reg, val); \ + if (retval) \ + goto out; \ + } while (0) + +static snd_pcm_hardware_t snd_at73c213_playback_hw = { + .info = SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER, + .formats = SNDRV_PCM_FMTBIT_S16_BE, + .rates = SNDRV_PCM_RATE_CONTINUOUS, + .rate_min = 8000, /* This will be overwritten with bitrate */ + .rate_max = 50000, /* This will be overwritten with bitrate */ + .channels_min = 2, + .channels_max = 2, + .buffer_bytes_max = 64 * 1024 - 1, + .period_bytes_min = 512, + .period_bytes_max = 64 * 1024 - 1, + .periods_min = 4, + .periods_max = 1024, +}; + +/* calculate and set bitrate and divisions */ +static int snd_at73c213_set_bitrate_and_div(void) +{ + extern struct avr32_cpuinfo boot_cpu_data; + unsigned long pll0_hz, apba_hz; + unsigned long apba_realdiv, gclk_realdiv, ssc_realdiv, wanted_bitrate; + char cpusel, ahbsel, apbasel; + int regval; + + regval = __raw_readl((void __iomem *)PM_BASE + PM_CKSEL); + wanted_bitrate = 48000; + + cpusel = regval & 0x07; + ahbsel = (regval>>8) & 0x07; + apbasel = (regval>>16) & 0x07; + + /* FIXME: Use the clk framework for this */ + if ((regval&(1<<7)) != 0) { + pll0_hz = clk_get_rate(boot_cpu_data.clk)/(1<<(cpusel+1)); + } else { + pll0_hz = clk_get_rate(boot_cpu_data.clk); + } + + if ((regval&(1<<23)) != 0) { + apba_hz = pll0_hz/(1<<(apbasel+1)); + apba_realdiv = (1<<(apbasel+1)); + } else { + apba_hz = pll0_hz; + apba_realdiv = 1; + } + +calculate: + /* Adjust bitrate as close as possible to 48000 Hz */ + gclk_realdiv = pll0_hz/(wanted_bitrate*256); + ssc_realdiv = 2 * apba_realdiv * gclk_realdiv; + + if ((gclk_realdiv % 2) == 0) + goto setbitrates; + + if(wanted_bitrate >= 22050 && wanted_bitrate <= 48000) + wanted_bitrate -= 50; + else if (wanted_bitrate < 22050) + wanted_bitrate = 48050; + else if (wanted_bitrate <= 50000) + wanted_bitrate += 50; + else { + printk(KERN_ERR "at73c213 could not set dividers for a valid bitrate\n"); + return -EINVAL; + } + + goto calculate; + +setbitrates: + bitrate = pll0_hz/(gclk_realdiv*256); + gclk_div = (gclk_realdiv/2)-1; + ssc_realdiv = 2*apba_realdiv*gclk_realdiv; + ssc_div = ssc_realdiv/(2*apba_realdiv); + + printk(KERN_INFO "at73c213: bitrate is %d Hz\n", bitrate); + + return 0; +} + +/* open callback */ +static int snd_at73c213_pcm_open(snd_pcm_substream_t *substream) +{ + struct snd_at73c213 *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + + snd_at73c213_playback_hw.rate_min = bitrate; + snd_at73c213_playback_hw.rate_max = bitrate; + runtime->hw = snd_at73c213_playback_hw; + chip->substream = substream; + + return 0; +} + +/* close callback */ +static int snd_at73c213_pcm_close(snd_pcm_substream_t *substream) +{ + struct snd_at73c213 *chip = snd_pcm_substream_chip(substream); + chip->substream = NULL; + return 0; +} + +/* hw_params callback */ +static int snd_at73c213_pcm_hw_params(snd_pcm_substream_t *substream, + snd_pcm_hw_params_t *hw_params) +{ +#ifdef SND_AT73C213_USE_ALSA_MALLOC_CALLS + return snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); +#else + int pg; + size_t size = params_buffer_bytes(hw_params); + struct snd_pcm_runtime *runtime; + struct snd_dma_buffer *dmab = NULL; + + substream->dma_buffer.dev.type = SNDRV_DMA_TYPE_DEV; + snd_assert(substream != NULL, return -EINVAL); + runtime = substream->runtime; + snd_assert(runtime != NULL, return -EINVAL); + + /* check if buffer is already allocated */ + if (runtime->dma_buffer_p) { + size_t size_previouse; + int pg_previouse; + + /* new buffer is smaler than previouse allocated buffer */ + if (runtime->dma_buffer_p->bytes >= size) { + runtime->dma_bytes = size; + return 0; /* don't change buffer size */ + } + + size_previouse = runtime->dma_buffer_p->bytes; + pg_previouse = get_order(size_previouse); + + dma_free_coherent(runtime->dma_buffer_p->dev.dev, + PAGE_SIZE << pg_previouse, + runtime->dma_buffer_p->area, + runtime->dma_buffer_p->addr); + + kfree(runtime->dma_buffer_p); + } + + dmab = kzalloc(sizeof(*dmab), GFP_KERNEL); + if (!dmab) + return -ENOMEM; + + dmab->dev = substream->dma_buffer.dev; + dmab->bytes = 0; + + pg = get_order(size); + + dmab->area = dma_alloc_coherent( + substream->dma_buffer.dev.dev, + PAGE_SIZE << pg, + (dma_addr_t *)&dmab->addr, + GFP_KERNEL); + + if (!dmab->area) { + kfree(dmab); + return -ENOMEM; + } + + dmab->bytes = size; + snd_pcm_set_runtime_buffer(substream, dmab); + runtime->dma_bytes = size; + return 1; +#endif +} + +/* hw_free callback */ +static int snd_at73c213_pcm_hw_free(snd_pcm_substream_t *substream) +{ +#ifdef SND_AT73C213_USE_ALSA_MALLOC_CALLS + return snd_pcm_lib_free_pages(substream); +#else + int pg; + struct snd_pcm_runtime *runtime; + struct snd_dma_buffer *dmab = NULL; + + snd_assert(substream != NULL, return -EINVAL); + runtime = substream->runtime; + snd_assert(runtime != NULL, return -EINVAL); + dmab = runtime->dma_buffer_p; + + if (!dmab) + return 0; + + if (!dmab->area) + return 0; + + pg = get_order(dmab->bytes); + dma_free_coherent(dmab->dev.dev, PAGE_SIZE << pg, dmab->area, dmab->addr); + kfree(runtime->dma_buffer_p); + snd_pcm_set_runtime_buffer(substream, NULL); + return 0; +#endif +} + +/* prepare callback */ +static int snd_at73c213_pcm_prepare(snd_pcm_substream_t *substream) +{ + struct snd_at73c213 *chip = snd_pcm_substream_chip(substream); + struct platform_device *pdev = chip->pdev; + snd_pcm_runtime_t *runtime = substream->runtime; + int block_size; + + block_size = frames_to_bytes(runtime, runtime->period_size); + + chip->period = 0; + + /* Make sure that our data are actually readable by the SSC */ + dma_sync_single_for_device(&pdev->dev, runtime->dma_addr, + block_size, DMA_TO_DEVICE); + dma_sync_single_for_device(&pdev->dev, runtime->dma_addr + block_size, + block_size, DMA_TO_DEVICE); + + __raw_writel(runtime->dma_addr, chip->regs + PDC_TPR); + __raw_writel(runtime->period_size * 2, chip->regs + PDC_TCR); + __raw_writel(runtime->dma_addr + block_size, chip->regs + PDC_TNPR); + __raw_writel(runtime->period_size * 2, chip->regs + PDC_TNCR); + + return 0; +} + +/* trigger callback */ +static int snd_at73c213_pcm_trigger(snd_pcm_substream_t *substream, + int cmd) +{ + struct snd_at73c213 *chip = snd_pcm_substream_chip(substream); + int retval = 0; + int flags = 0; + + spin_lock_irqsave(&chip->lock, flags); + + switch (cmd) { + case SNDRV_PCM_TRIGGER_START: + __raw_writel(SSC_INT_ENDTX, chip->regs + SSC_IER); + __raw_writel(PDC_PTCR_TXTEN, chip->regs + PDC_PTCR); + break; + case SNDRV_PCM_TRIGGER_STOP: + __raw_writel(PDC_PTCR_TXTDIS, chip->regs + PDC_PTCR); + __raw_writel(SSC_INT_ENDTX, chip->regs + SSC_IDR); + break; + default: + printk(KERN_WARNING "at73c213: spuriouse command %x\n", cmd); + retval = -EINVAL; + break; + } + + spin_unlock_irqrestore(&chip->lock, flags); + + return retval; +} + +/* pointer callback */ +static snd_pcm_uframes_t snd_at73c213_pcm_pointer(snd_pcm_substream_t *substream) +{ + struct snd_at73c213 *chip = snd_pcm_substream_chip(substream); + snd_pcm_runtime_t *runtime = substream->runtime; + snd_pcm_uframes_t pos; + unsigned long bytes; + + bytes = __raw_readl(chip->regs + PDC_TPR) - runtime->dma_addr; + + pos = bytes_to_frames(runtime, bytes); + if (pos >= runtime->buffer_size) + pos -= runtime->buffer_size; + + return pos; +} + +/* operators */ +static snd_pcm_ops_t at73c213_playback_ops = { + .open = snd_at73c213_pcm_open, + .close = snd_at73c213_pcm_close, + .ioctl = snd_pcm_lib_ioctl, + .hw_params = snd_at73c213_pcm_hw_params, + .hw_free = snd_at73c213_pcm_hw_free, + .prepare = snd_at73c213_pcm_prepare, + .trigger = snd_at73c213_pcm_trigger, + .pointer = snd_at73c213_pcm_pointer, +}; + +/* free a pcm device */ +static void snd_at73c213_pcm_free(snd_pcm_t *pcm) +{ + struct snd_at73c213 *chip = snd_pcm_chip(pcm); + if (chip->pcm != 0 ) { +#ifdef SND_AT73C213_USE_ALSA_MALLOC_CALLS + snd_pcm_lib_preallocate_free_for_all(chip->pcm); +#endif + chip->pcm = NULL; + } +} + +/* create a new pcm device */ +static int __devinit snd_at73c213_new_pcm(struct snd_at73c213 *chip, int device) +{ + snd_pcm_t *pcm; + int retval; + + retval = snd_pcm_new(chip->card, chip->card->shortname, device, 1, 0, &pcm); + if (retval < 0) + return retval; + + pcm->private_data = chip; + pcm->private_free = snd_at73c213_pcm_free; + pcm->info_flags = SNDRV_PCM_INFO_BLOCK_TRANSFER; + strcpy(pcm->name, "at73c213"); + chip->pcm = pcm; + + snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &at73c213_playback_ops); + +#ifdef SND_AT73C213_USE_ALSA_MALLOC_CALLS + snd_pcm_lib_preallocate_pages_for_all(chip->pcm, SNDRV_DMA_TYPE_DEV, + &chip->pdev->dev, 64 * 1024, 64 * 1024); +#endif + + return 0; +} + +static irqreturn_t snd_at73c213_interrupt(int irq, void *dev_id) +{ + struct snd_at73c213 *chip = dev_id; + struct platform_device *pdev = chip->pdev; + snd_pcm_runtime_t *runtime = chip->substream->runtime; + u32 status; + int offset, next_period, block_size; + + spin_lock(&chip->lock); + + block_size = frames_to_bytes(runtime, runtime->period_size); + + status = __raw_readl(chip->regs + SSC_IMR); + + if (status & SSC_INT_ENDTX) { + chip->period++; + if (chip->period == runtime->periods) + chip->period = 0; + next_period = chip->period + 1; + if (next_period == runtime->periods) + next_period = 0; + + offset = block_size * next_period; + + /* Make sure that our data are actually readable by the SSC */ + dma_sync_single_for_device(&pdev->dev, runtime->dma_addr + offset, + block_size, DMA_TO_DEVICE); + __raw_writel(runtime->dma_addr + offset, chip->regs + PDC_TNPR); + __raw_writel(runtime->period_size * 2, chip->regs + PDC_TNCR); + + if (next_period == 0) { + (void)__raw_readl(chip->regs + PDC_TPR); + (void)__raw_readl(chip->regs + PDC_TCR); + } + } else { + printk(KERN_WARNING + "Spurious SSC interrupt, status = 0x%08lx\n", + (unsigned long)status); + __raw_writel(status, chip->regs + SSC_IDR); + } + + (void)__raw_readl(chip->regs + SSC_IMR); + spin_unlock(&chip->lock); + + if (status & SSC_INT_ENDTX) + snd_pcm_period_elapsed(chip->substream); + + return IRQ_HANDLED; +} + +/* + * Mixer functions + */ +#if 0 /* Function not in use */ +static int snd_at73c213_mono_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + unsigned long mask = (kcontrol->private_value >> 16) & 0xff; + + uinfo->type = (mask == 1) ? + SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + + return 0; +} +#endif + +static int snd_at73c213_mono_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_at73c213 *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + spin_lock_irqsave(&chip->lock, flags); + + ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & mask; + + if (invert) + ucontrol->value.integer.value[0] = + (mask - ucontrol->value.integer.value[0]); + + spin_unlock_irqrestore(&chip->lock, flags); + + return 0; +} + +static int snd_at73c213_mono_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_at73c213 *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + int change, retval; + unsigned short val; + + val = (ucontrol->value.integer.value[0] & mask); + if (invert) + val = mask - val; + val <<= shift; + + spin_lock_irqsave(&chip->lock, flags); + + val = (chip->image[reg] & ~(mask << shift)) | val; + change = val != chip->image[reg]; + write_reg(chip, reg, val); + + chip->image[reg] = val; + + spin_unlock_irqrestore(&chip->lock, flags); + + return change; + +out: + return retval; +} + +static int snd_at73c213_stereo_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + int mask = (kcontrol->private_value >> 24) & 0xFF; + + uinfo->type = mask == 1 ? + SNDRV_CTL_ELEM_TYPE_BOOLEAN : SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = mask; + + return 0; +} + +static int snd_at73c213_stereo_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_at73c213 *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + + spin_lock_irqsave(&chip->lock, flags); + + ucontrol->value.integer.value[0] = + (chip->image[left_reg] >> shift_left) & mask; + ucontrol->value.integer.value[1] = + (chip->image[right_reg] >> shift_right) & mask; + + if (invert) { + ucontrol->value.integer.value[0] = + (mask - ucontrol->value.integer.value[0]); + ucontrol->value.integer.value[1] = + (mask - ucontrol->value.integer.value[1]); + } + + spin_unlock_irqrestore(&chip->lock, flags); + + return 0; +} + +static int snd_at73c213_stereo_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_at73c213 *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int left_reg = kcontrol->private_value & 0xff; + int right_reg = (kcontrol->private_value >> 8) & 0xff; + int shift_left = (kcontrol->private_value >> 16) & 0x07; + int shift_right = (kcontrol->private_value >> 19) & 0x07; + int mask = (kcontrol->private_value >> 24) & 0xff; + int invert = (kcontrol->private_value >> 22) & 1; + int change, retval; + unsigned short val1, val2; + + val1 = ucontrol->value.integer.value[0] & mask; + val2 = ucontrol->value.integer.value[1] & mask; + if (invert) { + val1 = mask - val1; + val2 = mask - val2; + } + val1 <<= shift_left; + val2 <<= shift_right; + + spin_lock_irqsave(&chip->lock, flags); + + val1 = (chip->image[left_reg] & ~(mask << shift_left)) | val1; + val2 = (chip->image[right_reg] & ~(mask << shift_right)) | val2; + change = val1 != chip->image[left_reg] || val2 != chip->image[right_reg]; + write_reg(chip, left_reg, val1); + write_reg(chip, right_reg, val2); + + chip->image[left_reg] = val1; + chip->image[right_reg] = val2; + + spin_unlock_irqrestore(&chip->lock, flags); + + return change; + +out: + return retval; +} + +static int snd_at73c213_mono_switch_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = 1; + + return 0; +} + +static int snd_at73c213_mono_switch_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_at73c213 *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + + spin_lock_irqsave(&chip->lock, flags); + + ucontrol->value.integer.value[0] = (chip->image[reg] >> shift) & 0x01; + + if (invert) + ucontrol->value.integer.value[0] = + (0x01 - ucontrol->value.integer.value[0]); + + spin_unlock_irqrestore(&chip->lock, flags); + + return 0; +} + +static int snd_at73c213_mono_switch_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_at73c213 *chip = snd_kcontrol_chip(kcontrol); + unsigned long flags; + int reg = kcontrol->private_value & 0xff; + int shift = (kcontrol->private_value >> 8) & 0xff; + int mask = (kcontrol->private_value >> 16) & 0xff; + int invert = (kcontrol->private_value >> 24) & 0xff; + int change, retval; + unsigned short val; + + if (ucontrol->value.integer.value[0]) + val = mask; + else + val = 0; + + if (invert) + val = mask - val; + val <<= shift; + + spin_lock_irqsave(&chip->lock, flags); + + val |= (chip->image[reg] & ~(mask << shift)); + change = val != chip->image[reg]; + + write_reg(chip, reg, val); + + chip->image[reg] = val; + + spin_unlock_irqrestore(&chip->lock, flags); + + return change; + +out: + return retval; +} + +static int snd_at73c213_pa_volume_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 0; + uinfo->value.integer.max = ((kcontrol->private_value >> 16) & 0xFF) - 1; + + return 0; +} + +static int snd_at73c213_line_capture_volume_info( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 2; + uinfo->value.integer.min = 14; + uinfo->value.integer.max = 31; + + return 0; +} + +static int snd_at73c213_aux_capture_volume_info( + struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; + uinfo->count = 1; + uinfo->value.integer.min = 14; + uinfo->value.integer.max = 31; + + return 0; +} + +#define AT73C213_MONO(xname, xindex, reg, shift, mask, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_at73c213_mono_info, \ + .get = snd_at73c213_mono_get, .put = snd_at73c213_mono_put, \ + .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } + +#define AT73C213_MONO_SWITCH(xname, xindex, reg, shift, mask, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_at73c213_mono_switch_info, \ + .get = snd_at73c213_mono_switch_get, .put = snd_at73c213_mono_switch_put, \ + .private_value = reg | (shift << 8) | (mask << 16) | (invert << 24) } + +#define AT73C213_STEREO(xname, xindex, left_reg, right_reg, shift_left, shift_right, mask, invert) \ +{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, .index = xindex, \ + .info = snd_at73c213_stereo_info, \ + .get = snd_at73c213_stereo_get, .put = snd_at73c213_stereo_put, \ + .private_value = left_reg | (right_reg << 8) | (shift_left << 16) | (shift_right << 19) | (mask << 24) | (invert << 22) } + +static struct snd_kcontrol_new snd_at73c213_controls[] __devinitdata = { +AT73C213_STEREO("Master Playback Volume", 0, DAC_LMPG, DAC_RMPG, 0, 0, 0x1F, 1), +AT73C213_STEREO("Master Playback Switch", 0, DAC_LMPG, DAC_RMPG, 5, 5, 1, 1), +AT73C213_STEREO("PCM Playback Volume", 0, DAC_LLOG, DAC_RLOG, 0, 0, 0x1F, 1), +AT73C213_STEREO("PCM Playback Switch", 0, DAC_LLOG, DAC_RLOG, 5, 5, 1, 1), +AT73C213_MONO_SWITCH("Mono PA Playback Switch", 0, DAC_CTRL, DAC_CTRL_ONPADRV, 0x01, 0), +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "PA Playback Volume", + .index = 0, + .info = snd_at73c213_pa_volume_info, + .get = snd_at73c213_mono_get, + .put = snd_at73c213_mono_put, + .private_value = PA_CTRL|(PA_CTRL_APAGAIN<<8)|(0x0F<<16)|(1<<24), +}, +AT73C213_MONO_SWITCH("PA High Gain Playback Switch", 0, PA_CTRL, PA_CTRL_APALP, 0x01, 1), +AT73C213_MONO_SWITCH("PA Playback Switch", 0, PA_CTRL, PA_CTRL_APAON, 0x01, 0), +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Aux Capture Volume", + .index = 0, + .info = snd_at73c213_aux_capture_volume_info, + .get = snd_at73c213_mono_get, + .put = snd_at73c213_mono_put, + .private_value = DAC_AUXG|(0<<8)|(0x1F<<16)|(1<<24), +}, +AT73C213_MONO_SWITCH("Aux Capture Switch", 0, DAC_CTRL, DAC_CTRL_ONAUXIN, 0x01, 0), +{ + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "Line Capture Volume", + .index = 0, + .info = snd_at73c213_line_capture_volume_info, + .get = snd_at73c213_stereo_get, + .put = snd_at73c213_stereo_put, + .private_value = DAC_LLIG|(DAC_RLIG<<8)|(0<<16)|(0<<19)|(0x1F<<24)|(1<<22), +}, +AT73C213_MONO_SWITCH("Line Capture Switch", 0, DAC_CTRL, 0, 0x03, 0), +}; + +static int __devinit snd_at73c213_mixer(struct snd_at73c213 *chip) +{ + struct snd_card *card; + int errval, idx; + + if (chip == NULL || chip->pcm == NULL) + return -EINVAL; + + card = chip->card; + + strcpy(card->mixername, chip->pcm->name); + + for (idx = 0; idx < ARRAY_SIZE(snd_at73c213_controls); idx++) { + if ((errval = snd_ctl_add(card, + snd_ctl_new1(&snd_at73c213_controls[idx], + chip))) < 0) + return errval; + } + + return 0; +} + +/* + * Device functions + */ +static int snd_at73c213_chip_init(struct snd_at73c213 *chip) +{ + int retval; + unsigned char dac_ctrl = 0; + + /* XXX: Unmask the APB clock for SSC0 */ + __raw_writel(__raw_readl((void __iomem *)PM_BASE + PM_APBAMASK)|(1<<7), + (void __iomem *)PM_BASE + PM_APBAMASK); + + /* Wait for clock to be stable */ + msleep(10); + + retval = snd_at73c213_set_bitrate_and_div(); + if (retval) + goto out; + + /* Reset the SSC */ + __raw_writel(SSC_CR_SWRST, chip->regs + SSC_CR); + + /* Enable GCLK0 */ + __raw_writel((1<<30), (void __iomem *)(PIOA_BASE + PIO_PDR)); + __raw_writel((1<<30), (void __iomem *)(PIOA_BASE + PIO_ASR)); + __raw_writel(((gclk_div<<8)|0x10|0x04|0x02), (void __iomem *)(PM_BASE + PM_GCCTRL)); + + /* Enable SSC and setup for I2S */ + __raw_writel(ssc_div, chip->regs + SSC_CMR); + + /* CKO, START, STTDLY, PERIOD */ + __raw_writel((1<<2)|(4<<8)|(1<<16)|(15<<24), chip->regs + SSC_TCMR); + + /* DATLEN, MSBF, DATNB, FSLEN, FSOS */ + __raw_writel((15<<0)|(1<<7)|(1<<8)|(15<<16)|(1<<20), chip->regs + SSC_TFMR); + + /* Initialize at73c213 on SPI bus */ + /* Reset the device */ + write_reg(chip, DAC_RST, 0x04); + msleep(1); + write_reg(chip, DAC_RST, 0x03); + + /* Turn on precharge */ + write_reg(chip, DAC_PRECH, 0xFF); + write_reg(chip, PA_CTRL, (1<<PA_CTRL_APAPRECH)); + write_reg(chip, DAC_CTRL, (1<<DAC_CTRL_ONLNOL)|(1<<DAC_CTRL_ONLNOR)); + + msleep(50); + + /* Stop precharging PA */ + write_reg(chip, PA_CTRL, (1<<PA_CTRL_APALP)|0x0F); + chip->image[PA_CTRL] = (1<<PA_CTRL_APALP)|0x0F; + + msleep(450); + + /* Stop precharging, turn on master power */ + write_reg(chip, DAC_PRECH, (1<<DAC_PRECH_ONMSTR)); + chip->image[DAC_PRECH] = (1<<DAC_PRECH_ONMSTR); + + msleep(1); + + /* Turn on DAC */ + dac_ctrl = (1<<DAC_CTRL_ONDACL)|(1<<DAC_CTRL_ONDACR)| + (1<<DAC_CTRL_ONLNOL)|(1<<DAC_CTRL_ONLNOR); + + write_reg(chip, DAC_CTRL, dac_ctrl); + chip->image[DAC_CTRL] = dac_ctrl; + + /* Mute sound */ + write_reg(chip, DAC_LMPG, 0x3F); + chip->image[DAC_LMPG] = 0x3F; + write_reg(chip, DAC_RMPG, 0x3F); + chip->image[DAC_RMPG] = 0x3F; + write_reg(chip, DAC_LLOG, 0x3F); + chip->image[DAC_LLOG] = 0x3F; + write_reg(chip, DAC_RLOG, 0x3F); + chip->image[DAC_RLOG] = 0x3F; + write_reg(chip, DAC_LLIG, 0x11); + chip->image[DAC_LLIG] = 0x11; + write_reg(chip, DAC_RLIG, 0x11); + chip->image[DAC_RLIG] = 0x11; + write_reg(chip, DAC_AUXG, 0x11); + chip->image[DAC_AUXG] = 0x11; + + /* Turn on SSC transmitter */ + __raw_writel(SSC_CR_TXEN, chip->regs + SSC_CR); + +out: + return retval; +} + +static int snd_at73c213_dev_free(snd_device_t *device) +{ + struct snd_at73c213 *chip = device->device_data; + + if (chip->regs) { + __raw_writel(SSC_CR_TXDIS, chip->regs + SSC_CR); + iounmap(chip->regs); + } + + if (chip->irq >= 0) + free_irq(chip->irq, chip); + + if (chip->ssc_clk) { + clk_disable(chip->ssc_clk); + clk_put(chip->ssc_clk); + } + + return 0; +} + +static int __devinit snd_at73c213_create(snd_card_t *card, + struct platform_device *pdev) +{ + static snd_device_ops_t ops = { + .dev_free = snd_at73c213_dev_free, + }; + struct snd_at73c213 *chip = get_chip(card); + struct resource *regs; + struct clk *ssc_clk; + int irq, retval; + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) + return -ENXIO; + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ssc_clk = clk_get(&pdev->dev, "mck"); + if (IS_ERR(ssc_clk)) + return PTR_ERR(ssc_clk); + clk_enable(ssc_clk); + chip->ssc_clk = ssc_clk; + + spin_lock_init(&chip->lock); + chip->card = card; + chip->pdev = pdev; + chip->irq = -1; + + retval = -ENOMEM; + + retval = spi_setup(chip->spi); + if (retval) + goto out; + + chip->regs = ioremap(regs->start, regs->end - regs->start + 1); + if (!chip->regs) + goto out; + + retval = request_irq(irq, snd_at73c213_interrupt, 0, "at73c213", chip); + if (retval) { + snd_printk("unable to request IRQ%d\n", irq); + goto out; + } + chip->irq = irq; + + memcpy(&chip->image, &snd_at73c213_original_image, + sizeof(snd_at73c213_original_image)); + + retval = snd_at73c213_chip_init(chip); + if (retval) + goto out; + + retval = snd_at73c213_new_pcm(chip, 0); + if (retval) + goto out; + + retval = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); + if (retval) + goto out; + + retval = snd_at73c213_mixer(chip); + if (retval) + goto out; + + snd_card_set_dev(card, &pdev->dev); + +out: + return retval; +} + +static int __devinit snd_at73c213_probe(struct platform_device *pdev) +{ + static int dev; + struct spi_board_info *binfo; + struct spi_master *smaster; + struct snd_at73c213 *chip; + snd_card_t *card; + int retval; + + if (dev >= SNDRV_CARDS) + return -ENODEV; + if (!enable[dev]) { + dev++; + return -ENOENT; + } + + if (spi < 0 || ssc < 0) + return -ENODEV; + + retval = -ENOMEM; + card = snd_card_new(index[dev], id[dev], THIS_MODULE, + sizeof(struct snd_at73c213)); + if (!card) + goto out; + + chip = card->private_data; + + retval = -ENODEV; + + /* Get the SPI bus */ + binfo = pdev->dev.platform_data; + if (!binfo) { + printk(KERN_WARNING "at73c213: could not get platform data\n"); + goto out; + } + + smaster = spi_busnum_to_master(spi); + if (!smaster) { + request_module("spi1"); + smaster = spi_busnum_to_master(spi); + if (!smaster) { + printk(KERN_WARNING + "at73c213: could not get " + "SPI bus %d, remembered to load " + "the spi_atmel module?\n", spi); + goto out; + } + } + + chip->spi = spi_new_device(smaster, binfo); + if (!chip->spi) { + printk(KERN_WARNING "at73c213: could not get SPI device %d\n", spi); + goto out; + } + + chip->spi->mode = SPI_MODE_1; + chip->spi->bits_per_word = 8; + + retval = snd_at73c213_create(card, pdev); + if (retval) + goto out_free_card; + + strcpy(card->driver, "at73c213"); + strcpy(card->shortname, "at73c213 (AVR32 STK1000)"); + sprintf(card->longname, "%s at %p (irq %i)", card->shortname, chip->regs, chip->irq); + + retval = snd_card_register(card); + if (retval) + goto out_free_card; + + platform_set_drvdata(pdev, card); + dev++; + return 0; + +out_free_card: + snd_card_free(card); +out: + return retval; +} + +static int __devexit snd_at73c213_remove(struct platform_device *pdev) +{ + struct snd_card *card = platform_get_drvdata(pdev); + struct snd_at73c213 *chip = card->private_data; + int retval; + + /* Stop playback */ + __raw_writel(SSC_CR_TXDIS, chip->regs + SSC_CR); + + /* Stop GLCK0 */ + __raw_writel(0, (void __iomem *)PM_BASE + PM_GCCTRL); + + /* Mute sound */ + write_reg(chip, DAC_LMPG, 0x3F); + chip->image[DAC_LMPG] = 0x3F; + write_reg(chip, DAC_RMPG, 0x3F); + chip->image[DAC_RMPG] = 0x3F; + write_reg(chip, DAC_LLOG, 0x3F); + chip->image[DAC_LLOG] = 0x3F; + write_reg(chip, DAC_RLOG, 0x3F); + chip->image[DAC_RLOG] = 0x3F; + write_reg(chip, DAC_LLIG, 0x11); + chip->image[DAC_LLIG] = 0x11; + write_reg(chip, DAC_RLIG, 0x11); + chip->image[DAC_RLIG] = 0x11; + write_reg(chip, DAC_AUXG, 0x11); + chip->image[DAC_AUXG] = 0x11; + + /* Turn off PA */ + write_reg(chip, PA_CTRL, (chip->image[PA_CTRL]|0x0F)); + chip->image[PA_CTRL] |= 0x0F; + msleep(10); + write_reg(chip, PA_CTRL, (1<<PA_CTRL_APALP)|0x0F); + chip->image[PA_CTRL] = (1<<PA_CTRL_APALP)|0x0F; + + /* Turn off external DAC */ + write_reg(chip, DAC_CTRL, 0x0C); + chip->image[DAC_CTRL] = 0x0C; + msleep(2); + write_reg(chip, DAC_CTRL, 0x00); + chip->image[DAC_CTRL] = 0x00; + + /* Turn off master power */ + write_reg(chip, DAC_PRECH, 0x00); + chip->image[DAC_PRECH] = 0x00; + + msleep(10); + +out: + if (chip->spi) + spi_unregister_device(chip->spi); + + if (card) { + snd_card_free(card); + platform_set_drvdata(pdev, NULL); + } + + return 0; +} + +#ifdef CONFIG_PM +static int snd_at73c213_suspend(struct platform_device *pdev, pm_message_t state, u32 level) +{ + struct snd_card *card = at32_get_drvdata(pdev); + struct snd_at73c213 *chip = card->private_data; + + printk(KERN_DEBUG "at73c213: suspending\n"); + + /* Stop SSC and GCLK0 */ + + spi_suspend(chip->spi, state); + + return 0; +} + +static int snd_at73c213_resume(struct platform_device *pdev, u32 level) +{ + struct snd_card *card = at32_get_drvdata(pdev); + struct snd_at73c213 *chip = card->private_data; + + printk(KERN_DEBUG "at73c213: resuming\n"); + + /* Start GLCK0 and SSC */ + + spi_resume(chip->spi); + + return 0; +} +#endif /* CONFIG_PM */ + +/* Driver core initialization */ +static struct platform_driver at73c213_driver = { + .probe = snd_at73c213_probe, + .remove = __devexit_p(snd_at73c213_remove), + .driver = { + .name = "at73c213", + } +#ifdef CONFIG_PM + .resume = snd_at73c213_resume, + .suspend = snd_at73c213_suspend, +#endif +}; + +static int __init at73c213_init(void) +{ + return platform_driver_register(&at73c213_driver); +} + +static void __exit at73c213_exit(void) +{ + platform_driver_unregister(&at73c213_driver); +} + +MODULE_AUTHOR("Hans-Christian Egtvedt <hcegtvedt@atmel.com>"); +MODULE_DESCRIPTION("Sound driver for at73c213 on STK1000"); +MODULE_LICENSE("GPL"); + +module_init(at73c213_init); +module_exit(at73c213_exit); + diff -urN linux-2.6.20.4-0rig/sound/avr32/at73c213.h linux-2.6.20.4-atmel/sound/avr32/at73c213.h --- linux-2.6.20.4-0rig/sound/avr32/at73c213.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/sound/avr32/at73c213.h 2007-03-24 16:42:29.000000000 +0100 @@ -0,0 +1,120 @@ +/* + * Driver for the AT73C213 16-bit stereo DAC on Atmel ATSTK1000 + * + * Copyright (C) 2006 Atmel Norway + * + * 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. + * + * The full GNU General Public License is included in this + * distribution in the file called COPYING. + */ + +#ifndef _SND_AT73C213_MIXER_H_ +#define _SND_AT73C213_MIXER_H_ + +/* DAC control register */ +#define DAC_CTRL 0x00 +#define DAC_CTRL_ONPADRV 7 +#define DAC_CTRL_ONAUXIN 6 +#define DAC_CTRL_ONDACR 5 +#define DAC_CTRL_ONDACL 4 +#define DAC_CTRL_ONLNOR 3 +#define DAC_CTRL_ONLNOL 2 +#define DAC_CTRL_ONLNIR 1 +#define DAC_CTRL_ONLNIL 0 + +/* DAC left line in gain register */ +#define DAC_LLIG 0x01 +#define DAC_LLIG_LLIG 0 + +/* DAC right line in gain register */ +#define DAC_RLIG 0x02 +#define DAC_RLIG_RLIG 0 + +/* DAC Left Master Playback Gain Register */ +#define DAC_LMPG 0x03 +#define DAC_LMPG_LMPG 0 + +/* DAC Right Master Playback Gain Register */ +#define DAC_RMPG 0x04 +#define DAC_RMPG_RMPG 0 + +/* DAC Left Line Out Gain Register */ +#define DAC_LLOG 0x05 +#define DAC_LLOG_LLOG 0 + +/* DAC Right Line Out Gain Register */ +#define DAC_RLOG 0x06 +#define DAC_RLOG_RLOG 0 + +/* DAC Output Level Control Register */ +#define DAC_OLC 0x07 +#define DAC_OLC_RSHORT 7 +#define DAC_OLC_ROLC 4 +#define DAC_OLC_LSHORT 3 +#define DAC_OLC_LOLC 0 + +/* DAC Mixer Control Register */ +#define DAC_MC 0x08 +#define DAC_MC_INVR 5 +#define DAC_MC_INVL 4 +#define DAC_MC_RMSMIN2 3 +#define DAC_MC_RMSMIN1 2 +#define DAC_MC_LMSMIN2 1 +#define DAC_MC_LMSMIN1 0 + +/* DAC Clock and Sampling Frequency Control Register */ +#define DAC_CSFC 0x09 +#define DAC_CSFC_OVRSEL 4 + +/* DAC Miscellaneous Register */ +#define DAC_MISC 0x0A +#define DAC_MISC_VCMCAPSEL 7 +#define DAC_MISC_DINTSEL 4 +#define DAC_MISC_DITHEN 3 +#define DAC_MISC_DEEMPEN 2 +#define DAC_MISC_NBITS 0 + +/* DAC Precharge Control Register */ +#define DAC_PRECH 0x0C +#define DAC_PRECH_PRCHGPDRV 7 +#define DAC_PRECH_PRCHGAUX1 6 +#define DAC_PRECH_PRCHGLNOR 5 +#define DAC_PRECH_PRCHGLNOL 4 +#define DAC_PRECH_PRCHGLNIR 3 +#define DAC_PRECH_PRCHGLNIL 2 +#define DAC_PRECH_PRCHG 1 +#define DAC_PRECH_ONMSTR 0 + +/* DAC Auxiliary Input Gain Control Register */ +#define DAC_AUXG 0x0D +#define DAC_AUXG_AUXG 0 + +/* DAC Reset Register */ +#define DAC_RST 0x10 +#define DAC_RST_RESMASK 2 +#define DAC_RST_RESFILZ 1 +#define DAC_RST_RSTZ 0 + +/* Power Amplifier Control Register */ +#define PA_CTRL 0x11 +#define PA_CTRL_APAON 6 +#define PA_CTRL_APAPRECH 5 +#define PA_CTRL_APALP 4 +#define PA_CTRL_APAGAIN 0 + +#endif + diff -urN linux-2.6.20.4-0rig/sound/avr32/Kconfig linux-2.6.20.4-atmel/sound/avr32/Kconfig --- linux-2.6.20.4-0rig/sound/avr32/Kconfig 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/sound/avr32/Kconfig 2007-03-24 16:42:29.000000000 +0100 @@ -0,0 +1,51 @@ +# ALSA AVR32 drivers + +menu "ALSA AVR32 devices" + depends on SND != n && AVR32 + +config SND_ATMEL_AC97 + tristate "Atmel AC97 Controller Driver" + depends on SND + select SND_PCM + select SND_AC97_CODEC + help + ALSA sound driver for the Atmel AC97 controller. + +config SND_ATMEL_AC97_USE_ALSA_MALLOC_CALLS + bool "Use the built-in malloc calls in the alsa driver" + default n + depends on SND_ATMEL_AC97 + help + Say Y if the built-in malloc calls in the alsa driver should be + used instead of the native dma_alloc_coherent and dma_free_coherent + function calls. Enabling this feature may break the rmmod feature. + +config SND_ATMEL_AC97C_USE_PDC + bool "Use PDC for DMA transfers to/from the Atmel AC97 Controller" + default n + depends on SND_ATMEL_AC97 + help + Say Y if PDC (Peripheral DMA Controller) is used for DMA transfers + to/from the Atmel AC97C instead of using the generic DMA framework. + +config SND_AT73C213 + tristate "Atmel AT73C213 DAC driver" + depends on SND && SPI_ATMEL + select SND_PCM + help + Say Y here if you want to use the Atmel AT73C213 external + DAC on the ATSTK1000 development board. + + To compile this driver as a module, choose M here: the + module will be called snd-at73c213. + +config SND_AT73C213_USE_ALSA_MALLOC_CALLS + bool "Use the built-in malloc calls in the alsa driver" + default n + depends on SND_AT73C213 + help + Say Y if the built-in malloc calls in the alsa driver should be + used instead of the native dma_alloc_coherent and dma_free_coherent + function calls. Enabling this feature may brake the rmmod feature. + +endmenu diff -urN linux-2.6.20.4-0rig/sound/avr32/Makefile linux-2.6.20.4-atmel/sound/avr32/Makefile --- linux-2.6.20.4-0rig/sound/avr32/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/sound/avr32/Makefile 2007-03-24 16:42:29.000000000 +0100 @@ -0,0 +1,9 @@ +# +# Makefile for ALSA +# + +snd-atmel-ac97-objs := ac97c.o +obj-$(CONFIG_SND_ATMEL_AC97) += snd-atmel-ac97.o + +snd-at73c213-objs := at73c213.o +obj-$(CONFIG_SND_AT73C213) += snd-at73c213.o diff -urN linux-2.6.20.4-0rig/sound/Kconfig linux-2.6.20.4-atmel/sound/Kconfig --- linux-2.6.20.4-0rig/sound/Kconfig 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/sound/Kconfig 2007-03-24 16:42:29.000000000 +0100 @@ -62,6 +62,8 @@ source "sound/arm/Kconfig" +source "sound/avr32/Kconfig" + source "sound/mips/Kconfig" # the following will depend on the order of config. diff -urN linux-2.6.20.4-0rig/sound/Makefile linux-2.6.20.4-atmel/sound/Makefile --- linux-2.6.20.4-0rig/sound/Makefile 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/sound/Makefile 2007-03-24 16:42:29.000000000 +0100 @@ -6,6 +6,7 @@ obj-$(CONFIG_SOUND_PRIME) += oss/ obj-$(CONFIG_DMASOUND) += oss/ obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ synth/ usb/ sparc/ parisc/ pcmcia/ mips/ +obj-$(CONFIG_SND) += avr32/ obj-$(CONFIG_SND_AOA) += aoa/ # This one must be compilable even if sound is configured out diff -urN linux-2.6.20.4-0rig/sound/oss/at32dac.c linux-2.6.20.4-atmel/sound/oss/at32dac.c --- linux-2.6.20.4-0rig/sound/oss/at32dac.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/sound/oss/at32dac.c 2007-03-24 16:42:29.000000000 +0100 @@ -0,0 +1,720 @@ +/* + * OSS Sound Driver for the Atmel AT32 on-chip DAC. + * + * Copyright (C) 2006 Atmel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ +#include <linux/clk.h> +#include <linux/dma-mapping.h> +#include <linux/fs.h> +#include <linux/init.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/sound.h> +#include <linux/soundcard.h> + +#include <asm/byteorder.h> +#include <asm/dma-controller.h> +#include <asm/io.h> +#include <asm/uaccess.h> + +/* We want to use the "bizarre" swap-bytes-in-each-halfword macro */ +#include <linux/byteorder/swabb.h> + +#include "at32dac.h" + +#define DMA_BUFFER_SIZE 32768 +#define DMA_PERIOD_SHIFT 10 +#define DMA_PERIOD_SIZE (1 << DMA_PERIOD_SHIFT) +#define DMA_WRITE_THRESHOLD DMA_PERIOD_SIZE + +struct sound_settings { + unsigned int format; + unsigned int channels; + unsigned int sample_rate; + /* log2(bytes per sample) */ + unsigned int input_order; +}; + +struct at32_dac { + spinlock_t lock; + void __iomem *regs; + + /* head and tail refer to number of words */ + struct { + u32 *buf; + int head; + int tail; + } dma; + + struct semaphore sem; + wait_queue_head_t write_wait; + + /* + * Read at most ucount bytes from ubuf, translate to 2-channel + * signed 16-bit big endian format and write to the DMA buffer + * as long as there is room left. Return the number of bytes + * successfully copied from ubuf, or -EFAULT if the first + * sample from ubuf couldn't be read. This function is not + * called unless there is room for at least one sample (4 + * bytes) in the DMA buffer. + */ + ssize_t (*trans)(struct at32_dac *dac, const char __user *ubuf, + size_t ucount); + + struct sound_settings dsp_settings; + struct dma_request_cyclic req; + + struct clk *mck; + struct clk *sample_clk; + struct platform_device *pdev; + int busy; + int playing; + int dev_dsp; +}; +static struct at32_dac *the_dac; + +static inline unsigned int at32dac_get_head(struct at32_dac *dac) +{ + return dac->dma.head & ((DMA_BUFFER_SIZE / 4) - 1); +} + +static inline unsigned int at32dac_get_tail(struct at32_dac *dac) +{ + return dac->dma.tail & ((DMA_BUFFER_SIZE / 4) - 1); +} + +static inline unsigned int at32dac_dma_space(struct at32_dac *dac) +{ + unsigned int space; + + space = ((dac->dma.tail - dac->dma.head - 1) + & ((DMA_BUFFER_SIZE / 4) - 1)); + return space; +} + +static void at32dac_update_dma_tail(struct at32_dac *dac) +{ + dma_addr_t dma_addr; + unsigned int new_tail; + + if (dac->playing) { + dma_addr = dma_get_current_pos(dac->req.req.dmac, + dac->req.req.channel); + new_tail = (dma_addr - dac->req.buffer_start) / 4; + if (new_tail >= dac->dma.head + && (dac->dma.tail < dac->dma.head + || dac->dma.tail > new_tail)) + printk(KERN_NOTICE "at32dac: underrun\n"); + dac->dma.tail = new_tail; + pr_debug("update tail: 0x%x - 0x%x = %u\n", + dma_addr, dac->req.buffer_start, dac->dma.tail); + } +} + +static int at32dac_start(struct at32_dac *dac) +{ + int ret; + + if (dac->playing) + return 0; + + memset(dac->dma.buf, 0, DMA_BUFFER_SIZE); + + clk_enable(dac->sample_clk); + + ret = dma_prepare_request_cyclic(dac->req.req.dmac, &dac->req); + if (ret) + goto out_stop_clock; + + pr_debug("Starting DMA...\n"); + ret = dma_start_request(dac->req.req.dmac, dac->req.req.channel); + if (ret) + goto out_stop_request; + + dac_writel(dac, CTRL, DAC_BIT(EN)); + dac->playing = 1; + + return 0; + +out_stop_request: + dma_stop_request(dac->req.req.dmac, + dac->req.req.channel); +out_stop_clock: + clk_disable(dac->sample_clk); + return ret; +} + +static int at32dac_stop(struct at32_dac *dac) +{ + if (dac->playing) { + dma_stop_request(dac->req.req.dmac, dac->req.req.channel); + dac_writel(dac, DATA, 0); + dac_writel(dac, CTRL, 0); + dac->playing = 0; + clk_disable(dac->sample_clk); + } + + return 0; +} + +static int at32dac_dma_prepare(struct at32_dac *dac) +{ + dac->dma.buf = dma_alloc_coherent(&dac->pdev->dev, DMA_BUFFER_SIZE, + &dac->req.buffer_start, GFP_KERNEL); + if (!dac->dma.buf) + return -ENOMEM; + + dac->dma.head = dac->dma.tail = 0; + dac->req.periods = DMA_BUFFER_SIZE / DMA_PERIOD_SIZE; + dac->req.buffer_size = DMA_BUFFER_SIZE; + + return 0; +} + +static void at32dac_dma_cleanup(struct at32_dac *dac) +{ + if (dac->dma.buf) + dma_free_coherent(&dac->pdev->dev, DMA_BUFFER_SIZE, + dac->dma.buf, dac->req.buffer_start); + dac->dma.buf = NULL; +} + +static void at32dac_dma_block_complete(struct dma_request *req) +{ + struct dma_request_cyclic *creq = to_dma_request_cyclic(req); + struct at32_dac *dac = container_of(creq, struct at32_dac, req); + + wake_up(&dac->write_wait); +} + +static void at32dac_dma_error(struct dma_request *req) +{ + printk(KERN_ERR "at32dac: DMA error\n"); +} + +static irqreturn_t at32dac_interrupt(int irq, void *dev_id) +{ + struct at32_dac *dac = dev_id; + u32 status; + + status = dac_readl(dac, INT_STATUS); + if (status & DAC_BIT(UNDERRUN)) { + printk(KERN_ERR "at32dac: Underrun detected\n"); + dac_writel(dac, INT_CLR, DAC_BIT(UNDERRUN)); + } else { + printk(KERN_ERR "at32dac: Spurious interrupt: status=0x%x\n", + status); + dac_writel(dac, INT_CLR, status); + } + + return IRQ_HANDLED; +} + +static ssize_t trans_s16be(struct at32_dac *dac, const char __user *ubuf, + size_t ucount) +{ + ssize_t ret; + + if (dac->dsp_settings.channels == 2) { + const u32 __user *up = (const u32 __user *)ubuf; + u32 sample; + + for (ret = 0; ret < (ssize_t)(ucount - 3); ret += 4) { + if (!at32dac_dma_space(dac)) + break; + + if (unlikely(__get_user(sample, up++))) { + if (ret == 0) + ret = -EFAULT; + break; + } + dac->dma.buf[at32dac_get_head(dac)] = sample; + dac->dma.head++; + } + } else { + const u16 __user *up = (const u16 __user *)ubuf; + u16 sample; + + for (ret = 0; ret < (ssize_t)(ucount - 1); ret += 2) { + if (!at32dac_dma_space(dac)) + break; + + if (unlikely(__get_user(sample, up++))) { + if (ret == 0) + ret = -EFAULT; + break; + } + dac->dma.buf[at32dac_get_head(dac)] + = (sample << 16) | sample; + dac->dma.head++; + } + } + + return ret; +} + +static ssize_t trans_s16le(struct at32_dac *dac, const char __user *ubuf, + size_t ucount) +{ + ssize_t ret; + + if (dac->dsp_settings.channels == 2) { + const u32 __user *up = (const u32 __user *)ubuf; + u32 sample; + + for (ret = 0; ret < (ssize_t)(ucount - 3); ret += 4) { + if (!at32dac_dma_space(dac)) + break; + + if (unlikely(__get_user(sample, up++))) { + if (ret == 0) + ret = -EFAULT; + break; + } + /* Swap bytes in each halfword */ + dac->dma.buf[at32dac_get_head(dac)] = swahb32(sample); + dac->dma.head++; + } + } else { + const u16 __user *up = (const u16 __user *)ubuf; + u16 sample; + + for (ret = 0; ret < (ssize_t)(ucount - 1); ret += 2) { + if (!at32dac_dma_space(dac)) + break; + + if (unlikely(__get_user(sample, up++))) { + if (ret == 0) + ret = -EFAULT; + break; + } + sample = swab16(sample); + dac->dma.buf[at32dac_get_head(dac)] + = (sample << 16) | sample; + dac->dma.head++; + } + } + + return ret; +} + +static ssize_t at32dac_dma_translate_from_user(struct at32_dac *dac, + const char __user *buffer, + size_t count) +{ + /* At least one buffer must be available at this point */ + pr_debug("at32dac: Copying %zu bytes from user...\n", count); + + return dac->trans(dac, buffer, count); +} + +static int at32dac_set_format(struct at32_dac *dac, int format) +{ + unsigned int order; + + switch (format) { + case AFMT_S16_BE: + order = 1; + dac->trans = trans_s16be; + break; + case AFMT_S16_LE: + order = 1; + dac->trans = trans_s16le; + break; + default: + printk("at32dac: Unsupported format: %d\n", format); + return -EINVAL; + } + + if (dac->dsp_settings.channels == 2) + order++; + + dac->dsp_settings.input_order = order; + dac->dsp_settings.format = format; + return 0; +} + +static int at32dac_set_sample_rate(struct at32_dac *dac, unsigned long rate) +{ + unsigned long new_rate; + int ret; + + ret = clk_set_rate(dac->sample_clk, 256 * rate); + if (ret < 0) + return ret; + + /* TODO: mplayer seems to have a problem with this */ +#if 0 + new_rate = clk_get_rate(dac->sample_clk); + dac->dsp_settings.sample_rate = new_rate / 256; +#else + dac->dsp_settings.sample_rate = rate; +#endif + + return 0; +} + +static ssize_t at32dac_dsp_write(struct file *file, + const char __user *buffer, + size_t count, loff_t *ppos) +{ + struct at32_dac *dac = file->private_data; + DECLARE_WAITQUEUE(wait, current); + unsigned int avail; + ssize_t copied; + ssize_t ret; + + /* Avoid address space checking in the translation functions */ + if (!access_ok(buffer, count, VERIFY_READ)) + return -EFAULT; + + down(&dac->sem); + + if (!dac->dma.buf) { + ret = at32dac_dma_prepare(dac); + if (ret) + goto out; + } + + add_wait_queue(&dac->write_wait, &wait); + ret = 0; + while (count > 0) { + do { + at32dac_update_dma_tail(dac); + avail = at32dac_dma_space(dac); + set_current_state(TASK_INTERRUPTIBLE); + if (avail >= DMA_WRITE_THRESHOLD) + break; + + if (file->f_flags & O_NONBLOCK) { + if (!ret) + ret = -EAGAIN; + goto out; + } + + pr_debug("Going to wait (avail = %u, count = %zu)\n", + avail, count); + + up(&dac->sem); + schedule(); + if (signal_pending(current)) { + if (!ret) + ret = -ERESTARTSYS; + goto out_nosem; + } + down(&dac->sem); + } while (1); + + copied = at32dac_dma_translate_from_user(dac, buffer, count); + if (copied < 0) { + if (!ret) + ret = -EFAULT; + goto out; + } + + at32dac_start(dac); + + count -= copied; + ret += copied; + } + +out: + up(&dac->sem); +out_nosem: + remove_wait_queue(&dac->write_wait, &wait); + set_current_state(TASK_RUNNING); + return ret; +} + +static int at32dac_dsp_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct at32_dac *dac = file->private_data; + int __user *up = (int __user *)arg; + struct audio_buf_info abinfo; + int val, ret; + + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, up); + + case SNDCTL_DSP_SPEED: + if (get_user(val, up)) + return -EFAULT; + if (val >= 0) { + at32dac_stop(dac); + ret = at32dac_set_sample_rate(dac, val); + if (ret) + return ret; + } + return put_user(dac->dsp_settings.sample_rate, up); + + case SNDCTL_DSP_STEREO: + if (get_user(val, up)) + return -EFAULT; + at32dac_stop(dac); + if (val && dac->dsp_settings.channels == 1) + dac->dsp_settings.input_order++; + else if (!val && dac->dsp_settings.channels != 1) + dac->dsp_settings.input_order--; + dac->dsp_settings.channels = val ? 2 : 1; + return 0; + + case SNDCTL_DSP_CHANNELS: + if (get_user(val, up)) + return -EFAULT; + + if (val) { + if (val < 0 || val > 2) + return -EINVAL; + + at32dac_stop(dac); + dac->dsp_settings.input_order + += val - dac->dsp_settings.channels; + dac->dsp_settings.channels = val; + } + return put_user(val, (int *)arg); + + case SNDCTL_DSP_GETFMTS: + return put_user(AFMT_S16_BE | AFMT_S16_BE, up); + + case SNDCTL_DSP_SETFMT: + if (get_user(val, up)) + return -EFAULT; + + if (val == AFMT_QUERY) { + val = dac->dsp_settings.format; + } else { + ret = at32dac_set_format(dac, val); + if (ret) + return ret; + } + return put_user(val, up); + + case SNDCTL_DSP_GETOSPACE: + at32dac_update_dma_tail(dac); + abinfo.fragsize = ((1 << dac->dsp_settings.input_order) + * (DMA_PERIOD_SIZE / 4)); + abinfo.bytes = (at32dac_dma_space(dac) + << dac->dsp_settings.input_order); + abinfo.fragstotal = ((DMA_BUFFER_SIZE * 4) + >> (DMA_PERIOD_SHIFT + + dac->dsp_settings.input_order)); + abinfo.fragments = ((abinfo.bytes + >> dac->dsp_settings.input_order) + / (DMA_PERIOD_SIZE / 4)); + pr_debug("fragments=%d fragstotal=%d fragsize=%d bytes=%d\n", + abinfo.fragments, abinfo.fragstotal, abinfo.fragsize, + abinfo.bytes); + return copy_to_user(up, &abinfo, sizeof(abinfo)) ? -EFAULT : 0; + + default: + printk("at32dac: Unimplemented ioctl cmd: 0x%x\n", cmd); + return -EINVAL; + } +} + +static int at32dac_dsp_open(struct inode *inode, struct file *file) +{ + struct at32_dac *dac = the_dac; + int ret; + + if (file->f_mode & FMODE_READ) + return -ENXIO; + + down(&dac->sem); + ret = -EBUSY; + if (dac->busy) + goto out; + + dac->dma.head = dac->dma.tail = 0; + + /* FIXME: What are the correct defaults? */ + dac->dsp_settings.channels = 2; + at32dac_set_format(dac, AFMT_S16_BE); + ret = at32dac_set_sample_rate(dac, 8000); + if (ret) + goto out; + + file->private_data = dac; + dac->busy = 1; + + ret = 0; + +out: + up(&dac->sem); + return ret; +} + +static int at32dac_dsp_release(struct inode *inode, struct file *file) +{ + struct at32_dac *dac = file->private_data; + + down(&dac->sem); + + at32dac_stop(dac); + at32dac_dma_cleanup(dac); + dac->busy = 0; + + up(&dac->sem); + + return 0; +} + +static struct file_operations at32dac_dsp_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .write = at32dac_dsp_write, + .ioctl = at32dac_dsp_ioctl, + .open = at32dac_dsp_open, + .release = at32dac_dsp_release, +}; + +static int __devinit at32dac_probe(struct platform_device *pdev) +{ + struct at32_dac *dac; + struct resource *regs; + struct clk *mck; + struct clk *sample_clk; + int irq; + int ret; + + if (the_dac) + return -EBUSY; + + regs = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!regs) + return -ENXIO; + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + mck = clk_get(&pdev->dev, "mck"); + if (IS_ERR(mck)) + return PTR_ERR(mck); + sample_clk = clk_get(&pdev->dev, "sample_clk"); + if (IS_ERR(sample_clk)) { + ret = PTR_ERR(sample_clk); + goto out_put_mck; + } + clk_enable(mck); + + ret = -ENOMEM; + dac = kzalloc(sizeof(struct at32_dac), GFP_KERNEL); + if (!dac) + goto out_disable_clk; + + spin_lock_init(&dac->lock); + init_MUTEX(&dac->sem); + init_waitqueue_head(&dac->write_wait); + dac->pdev = pdev; + dac->mck = mck; + dac->sample_clk = sample_clk; + + dac->regs = ioremap(regs->start, regs->end - regs->start + 1); + if (!dac->regs) + goto out_free_dac; + + ret = request_irq(irq, at32dac_interrupt, 0, "dac", dac); + if (ret) + goto out_unmap_regs; + + /* FIXME */ + dac->req.req.dmac = find_dma_controller(0); + if (!dac->req.req.dmac) + goto out_free_irq; + + ret = dma_alloc_channel(dac->req.req.dmac); + if (ret < 0) + goto out_free_irq; + + dac->req.req.channel = ret; + dac->req.req.block_complete = at32dac_dma_block_complete; + dac->req.req.error = at32dac_dma_error; + dac->req.data_reg = regs->start + DAC_DATA; + dac->req.periph_id = 2; /* FIXME */ + dac->req.direction = DMA_DIR_MEM_TO_PERIPH; + dac->req.width = DMA_WIDTH_32BIT; + + /* Make sure the DAC is silent and disabled */ + dac_writel(dac, DATA, 0); + dac_writel(dac, CTRL, 0); + + ret = register_sound_dsp(&at32dac_dsp_fops, -1); + if (ret < 0) + goto out_free_dma; + dac->dev_dsp = ret; + + /* TODO: Register mixer */ + + the_dac = dac; + platform_set_drvdata(pdev, dac); + + return 0; + +out_free_dma: + dma_release_channel(dac->req.req.dmac, dac->req.req.channel); +out_free_irq: + free_irq(irq, dac); +out_unmap_regs: + iounmap(dac->regs); +out_free_dac: + kfree(dac); +out_disable_clk: + clk_disable(mck); + clk_put(sample_clk); +out_put_mck: + clk_put(mck); + return ret; +} + +static int __devexit at32dac_remove(struct platform_device *pdev) +{ + struct at32_dac *dac; + + dac = platform_get_drvdata(pdev); + if (dac) { + unregister_sound_dsp(dac->dev_dsp); + dma_release_channel(dac->req.req.dmac, dac->req.req.channel); + free_irq(platform_get_irq(pdev, 0), dac); + iounmap(dac->regs); + clk_disable(dac->mck); + clk_put(dac->sample_clk); + clk_put(dac->mck); + kfree(dac); + platform_set_drvdata(pdev, NULL); + the_dac = NULL; + } + + return 0; +} + +static struct platform_driver at32dac_driver = { + .probe = at32dac_probe, + .remove = __devexit_p(at32dac_remove), + .driver = { + .name = "dac", + }, +}; + +static int __init at32dac_init(void) +{ + return platform_driver_register(&at32dac_driver); +} +module_init(at32dac_init); + +static void __exit at32dac_exit(void) +{ + platform_driver_unregister(&at32dac_driver); +} +module_exit(at32dac_exit); + +MODULE_AUTHOR("Haavard Skinnemoen <hskinnemoen@atmel.com>"); +MODULE_DESCRIPTION("DMA Sound Driver for the Atmel AT32 on-chip DAC"); +MODULE_LICENSE("GPL"); diff -urN linux-2.6.20.4-0rig/sound/oss/at32dac.h linux-2.6.20.4-atmel/sound/oss/at32dac.h --- linux-2.6.20.4-0rig/sound/oss/at32dac.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.20.4-atmel/sound/oss/at32dac.h 2007-03-24 16:42:29.000000000 +0100 @@ -0,0 +1,65 @@ +/* + * Register definitions for the Atmel AT32 on-chip DAC. + * + * Copyright (C) 2006 Atmel Corporation + * + * 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. + */ +#ifndef __ASM_AVR32_DAC_H__ +#define __ASM_AVR32_DAC_H__ + +/* DAC register offsets */ +#define DAC_DATA 0x0000 +#define DAC_CTRL 0x0008 +#define DAC_INT_MASK 0x000c +#define DAC_INT_EN 0x0010 +#define DAC_INT_DIS 0x0014 +#define DAC_INT_CLR 0x0018 +#define DAC_INT_STATUS 0x001c +#define DAC_PDC_DATA 0x0020 + +/* Bitfields in DATA */ +#define DAC_DATA_OFFSET 0 +#define DAC_DATA_SIZE 32 + +/* Bitfields in CTRL */ +#define DAC_SWAP_OFFSET 30 +#define DAC_SWAP_SIZE 1 +#define DAC_EN_OFFSET 31 +#define DAC_EN_SIZE 1 + +/* Bitfields in INT_MASK */ + +/* Bitfields in INT_EN */ + +/* Bitfields in INT_DIS */ +#define DAC_TX_READY_OFFSET 29 +#define DAC_TX_READY_SIZE 1 +#define DAC_TX_BUFFER_EMPTY_OFFSET 30 +#define DAC_TX_BUFFER_EMPTY_SIZE 1 +#define DAC_CHANNEL_TX_END_OFFSET 31 +#define DAC_CHANNEL_TX_END_SIZE 1 + +/* Bitfields in INT_CLR */ +#define DAC_UNDERRUN_OFFSET 28 +#define DAC_UNDERRUN_SIZE 1 + +/* Bitfields in INT_STATUS */ + +/* Bitfields in PDC_DATA */ + +/* Bit manipulation macros */ +#define DAC_BIT(name) (1 << DAC_##name##_OFFSET) +#define DAC_BF(name,value) (((value) & ((1 << DAC_##name##_SIZE) - 1)) << DAC_##name##_OFFSET) +#define DAC_BFEXT(name,value) (((value) >> DAC_##name##_OFFSET) & ((1 << DAC_##name##_SIZE) - 1)) +#define DAC_BFINS(name,value,old) (((old) & ~(((1 << DAC_##name##_SIZE) - 1) << DAC_##name##_OFFSET)) | DAC_BF(name,value)) + +/* Register access macros */ +#define dac_readl(port,reg) \ + __raw_readl((port)->regs + DAC_##reg) +#define dac_writel(port,reg,value) \ + __raw_writel((value), (port)->regs + DAC_##reg) + +#endif /* __ASM_AVR32_DAC_H__ */ diff -urN linux-2.6.20.4-0rig/sound/oss/Kconfig linux-2.6.20.4-atmel/sound/oss/Kconfig --- linux-2.6.20.4-0rig/sound/oss/Kconfig 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/sound/oss/Kconfig 2007-03-24 16:42:29.000000000 +0100 @@ -750,3 +750,7 @@ int "DAC channel" default "1" depends on SOUND_SH_DAC_AUDIO + +config SOUND_AT32_DAC + tristate "Atmel AT32 On-chip DAC support" + depends on SOUND_PRIME && AVR32 diff -urN linux-2.6.20.4-0rig/sound/oss/Makefile linux-2.6.20.4-atmel/sound/oss/Makefile --- linux-2.6.20.4-0rig/sound/oss/Makefile 2007-03-23 20:52:51.000000000 +0100 +++ linux-2.6.20.4-atmel/sound/oss/Makefile 2007-03-24 16:42:29.000000000 +0100 @@ -10,6 +10,7 @@ # Please leave it as is, cause the link order is significant ! +obj-$(CONFIG_SOUND_AT32_DAC) += at32dac.o obj-$(CONFIG_SOUND_SH_DAC_AUDIO) += sh_dac_audio.o obj-$(CONFIG_SOUND_HAL2) += hal2.o obj-$(CONFIG_SOUND_AEDSP16) += aedsp16.o