diff -NaurbB linux-2.6.9-pre1-bk18/arch/arm/configs/netbookpro_defconfig linux-2.6.9_nbp/arch/arm/configs/netbookpro_defconfig --- linux-2.6.9-pre1-bk18/arch/arm/configs/netbookpro_defconfig 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/arch/arm/configs/netbookpro_defconfig 2004-09-14 12:25:05.000000000 +0200 @@ -0,0 +1,1102 @@ +# +# Automatically generated make config: don't edit +# +CONFIG_ARM=y +CONFIG_MMU=y +CONFIG_UID16=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y + +# +# Code maturity level options +# +CONFIG_EXPERIMENTAL=y +# CONFIG_CLEAN_COMPILE is not set +# CONFIG_STANDALONE is not set +CONFIG_BROKEN=y +CONFIG_BROKEN_ON_SMP=y + +# +# General setup +# +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_POSIX_MQUEUE=y +# CONFIG_BSD_PROCESS_ACCT is not set +CONFIG_SYSCTL=y +# CONFIG_AUDIT is not set +CONFIG_LOG_BUF_SHIFT=20 +CONFIG_HOTPLUG=y +# CONFIG_IKCONFIG is not set +# CONFIG_EMBEDDED is not set +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_ALL is not set +CONFIG_FUTEX=y +CONFIG_EPOLL=y +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=y +CONFIG_IOSCHED_CFQ=y +CONFIG_CC_OPTIMIZE_FOR_SIZE=y + +# +# Loadable module support +# +CONFIG_MODULES=y +# CONFIG_MODULE_UNLOAD is not set +CONFIG_OBSOLETE_MODPARM=y +# CONFIG_MODVERSIONS is not set +# CONFIG_KMOD is not set + +# +# System Type +# +# CONFIG_ARCH_ADIFCC is not set +# CONFIG_ARCH_CLPS7500 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CO285 is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_CAMELOT is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_IOP3XX is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_L7200 is not set +CONFIG_ARCH_PXA=y +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_OMAP is not set +# CONFIG_ARCH_VERSATILE_PB is not set + +# +# CLPS711X/EP721X Implementations +# + +# +# Epxa10db +# + +# +# Footbridge Implementations +# + +# +# IOP3xx Implementation Options +# +# CONFIG_ARCH_IOP310 is not set +# CONFIG_ARCH_IOP321 is not set + +# +# IOP3xx Chipset Features +# + +# +# Intel IXP4xx Implementation Options +# + +# +# IXP4xx Platforms +# + +# +# IXP4xx Options +# + +# +# Intel PXA2xx Implementations +# +# CONFIG_ARCH_LUBBOCK is not set +# CONFIG_MACH_MAINSTONE is not set +# CONFIG_ARCH_PXA_IDP is not set +CONFIG_MACH_NETBOOKPRO=y +CONFIG_PXA25x=y + +# +# SA11x0 Implementations +# + +# +# TI OMAP Implementations +# + +# +# OMAP Core Type +# + +# +# OMAP Board Type +# + +# +# OMAP Feature Selections +# + +# +# S3C2410 Implementations +# + +# +# LH7A40X Implementations +# + +# +# Processor Type +# +CONFIG_CPU_32=y +CONFIG_CPU_XSCALE=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5T=y +CONFIG_CPU_TLB_V4WBI=y +CONFIG_CPU_MINICACHE=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +CONFIG_XSCALE_PMU=y + +# +# General setup +# +# CONFIG_ZBOOT_ROM is not set +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +# CONFIG_CPU_FREQ is not set + +# +# PCMCIA/CardBus support +# +CONFIG_PCMCIA=y +# CONFIG_PCMCIA_DEBUG is not set +# CONFIG_TCIC is not set +CONFIG_PCMCIA_PXA2XX=y + +# +# At least one math emulation must be selected +# +CONFIG_FPE_NWFPE=y +CONFIG_FPE_NWFPE_XP=y +# CONFIG_FPE_FASTFPE is not set +CONFIG_BINFMT_ELF=y +CONFIG_BINFMT_AOUT=y +# CONFIG_BINFMT_MISC is not set + +# +# Generic Driver Options +# +CONFIG_FW_LOADER=y +# CONFIG_DEBUG_DRIVER is not set +CONFIG_PM=y +# CONFIG_PREEMPT is not set +CONFIG_APM=y +# CONFIG_ARTHUR is not set +CONFIG_CMDLINE="root=1f04 mem=32M" +CONFIG_ALIGNMENT_TRAP=y + +# +# Parallel port support +# +# CONFIG_PARPORT is not set + +# +# Memory Technology Devices (MTD) +# +CONFIG_MTD=y +CONFIG_MTD_DEBUG=y +CONFIG_MTD_DEBUG_VERBOSE=1 +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_CONCAT is not set +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_CMDLINE_PARTS is not set +# CONFIG_MTD_AFS_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +CONFIG_NFTL=m +CONFIG_NFTL_RW=y +# CONFIG_INFTL 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=y +CONFIG_MTD_PCMCIA=m + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLKMTD 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_IDS=y +CONFIG_MTD_NAND_NETBOOKPRO=y +# CONFIG_MTD_NAND_DISKONCHIP is not set + +# +# Plug and Play support +# + +# +# Block devices +# +# CONFIG_BLK_DEV_FD is not set +CONFIG_BLK_DEV_LOOP=m +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +CONFIG_BLK_DEV_NBD=m +CONFIG_BLK_DEV_RAM=y +CONFIG_BLK_DEV_RAM_SIZE=6000 +CONFIG_BLK_DEV_INITRD=y + +# +# Multi-device support (RAID and LVM) +# +# CONFIG_MD is not set + +# +# Networking support +# +CONFIG_NET=y + +# +# Networking options +# +CONFIG_PACKET=y +CONFIG_PACKET_MMAP=y +# CONFIG_NETLINK_DEV 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_PNP=y +CONFIG_IP_PNP_DHCP=y +CONFIG_IP_PNP_BOOTP=y +# CONFIG_IP_PNP_RARP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set +# CONFIG_SYN_COOKIES is not set +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_IPV6 is not set +# CONFIG_NETFILTER is not set + +# +# SCTP Configuration (EXPERIMENTAL) +# +# CONFIG_IP_SCTP 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_NET_DIVERT is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_FASTROUTE is not set +# CONFIG_NET_HW_FLOWCONTROL is not set + +# +# QoS and/or fair queueing +# +# CONFIG_NET_SCHED is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +CONFIG_NETDEVICES=y +CONFIG_DUMMY=m +# CONFIG_BONDING is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set + +# +# Ethernet (10 or 100Mbit) +# +CONFIG_NET_ETHERNET=y +CONFIG_MII=y + +# +# Gigabit Ethernet (1000/10000 Mbit) +# + +# +# Token Ring devices +# + +# +# Wireless LAN (non-hamradio) +# +CONFIG_NET_RADIO=y + +# +# Obsolete Wireless cards support (pre-802.11) +# +CONFIG_STRIP=m +CONFIG_PCMCIA_WAVELAN=m +CONFIG_PCMCIA_NETWAVE=m + +# +# Wireless 802.11 Frequency Hopping cards support +# +CONFIG_PCMCIA_RAYCS=m + +# +# Wireless 802.11b ISA/PCI cards support +# +CONFIG_HERMES=m +CONFIG_ATMEL=m + +# +# Wireless 802.11b Pcmcia/Cardbus cards support +# +CONFIG_PCMCIA_HERMES=m +CONFIG_AIRO_CS=m +CONFIG_PCMCIA_ATMEL=m +CONFIG_PCMCIA_WL3501=m +CONFIG_NET_WIRELESS=y + +# +# PCMCIA network device support +# +CONFIG_NET_PCMCIA=y +CONFIG_PCMCIA_3C589=m +CONFIG_PCMCIA_3C574=m +CONFIG_PCMCIA_FMVJ18X=m +CONFIG_PCMCIA_PCNET=m +CONFIG_PCMCIA_NMCLAN=m +CONFIG_PCMCIA_SMC91C92=m +CONFIG_PCMCIA_XIRC2PS=m +CONFIG_PCMCIA_AXNET=m + +# +# Wan interfaces +# +# CONFIG_WAN is not set +CONFIG_PPP=m +CONFIG_PPP_MULTILINK=y +CONFIG_PPP_FILTER=y +CONFIG_PPP_ASYNC=m +CONFIG_PPP_SYNC_TTY=m +CONFIG_PPP_DEFLATE=m +CONFIG_PPP_BSDCOMP=m +CONFIG_PPPOE=m +CONFIG_SLIP=m +CONFIG_SLIP_COMPRESSED=y +CONFIG_SLIP_SMART=y +CONFIG_SLIP_MODE_SLIP6=y +# CONFIG_SHAPER is not set +# CONFIG_NETCONSOLE is not set + +# +# ATA/ATAPI/MFM/RLL support +# +CONFIG_IDE=y +CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +CONFIG_BLK_DEV_IDEDISK=y +# CONFIG_IDEDISK_MULTI_MODE is not set +# CONFIG_IDEDISK_STROKE is not set +CONFIG_BLK_DEV_IDECS=m +CONFIG_BLK_DEV_IDECD=m +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_BLK_DEV_IDESCSI is not set +# CONFIG_IDE_TASK_IOCTL is not set +# CONFIG_IDE_TASKFILE_IO is not set + +# +# IDE chipset support/bugfixes +# +CONFIG_IDE_GENERIC=y +# CONFIG_IDE_ARM is not set +# CONFIG_BLK_DEV_IDEDMA is not set +# CONFIG_IDEDMA_AUTO is not set +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI device support +# +CONFIG_SCSI=m +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=m +CONFIG_CHR_DEV_ST=m +# CONFIG_CHR_DEV_OSST is not set +CONFIG_BLK_DEV_SR=m +# CONFIG_BLK_DEV_SR_VENDOR is not set +CONFIG_CHR_DEV_SG=m + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +# CONFIG_SCSI_MULTI_LUN is not set +CONFIG_SCSI_CONSTANTS=y +# CONFIG_SCSI_LOGGING is not set + +# +# SCSI Transport Attributes +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set + +# +# SCSI low-level drivers +# +# CONFIG_SCSI_AIC7XXX_OLD is not set +# CONFIG_SCSI_DPT_I2O is not set +# CONFIG_SCSI_SATA is not set +# CONFIG_SCSI_EATA_PIO is not set +# CONFIG_SCSI_DEBUG is not set + +# +# PCMCIA SCSI adapter support +# +CONFIG_PCMCIA_AHA152X=m +CONFIG_PCMCIA_FDOMAIN=m +CONFIG_PCMCIA_NINJA_SCSI=m +CONFIG_PCMCIA_QLOGIC=m +CONFIG_PCMCIA_SYM53C500=m + +# +# Fusion MPT device support +# + +# +# IEEE 1394 (FireWire) support +# +# CONFIG_IEEE1394 is not set + +# +# I2O device support +# + +# +# ISDN subsystem +# +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +CONFIG_INPUT_MOUSEDEV_PSAUX=y +CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +# CONFIG_INPUT_JOYDEV is not set +# CONFIG_INPUT_TSDEV is not set +# CONFIG_INPUT_EVDEV is not set +# CONFIG_INPUT_EVBUG is not set + +# +# Input I/O drivers +# +# CONFIG_GAMEPORT is not set +CONFIG_SOUND_GAMEPORT=y +CONFIG_SERIO=y +# CONFIG_SERIO_I8042 is not set +CONFIG_SERIO_SERPORT=y +# CONFIG_SERIO_CT82C710 is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_NEWTON is not set +CONFIG_KEYBOARD_NETBOOKPRO=y +CONFIG_INPUT_MOUSE=y +# CONFIG_MOUSE_PS2 is not set +# CONFIG_MOUSE_SERIAL is not set +# CONFIG_MOUSE_VSXXXAA is not set +# CONFIG_INPUT_JOYSTICK is not set +CONFIG_INPUT_TOUCHSCREEN=y +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_INPUT_MISC is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +# CONFIG_SERIAL_8250 is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_PXA=y +CONFIG_SERIAL_PXA_CONSOLE=y +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_UNIX98_PTYS=y +CONFIG_LEGACY_PTYS=y +CONFIG_LEGACY_PTY_COUNT=256 +# CONFIG_QIC02_TAPE is not set + +# +# IPMI +# +# CONFIG_IPMI_HANDLER is not set + +# +# Watchdog Cards +# +# CONFIG_WATCHDOG is not set +# CONFIG_NVRAM is not set +# CONFIG_RTC is not set +# CONFIG_GEN_RTC is not set +# CONFIG_DTLK is not set +# CONFIG_R3964 is not set +# CONFIG_APPLICOM is not set + +# +# Ftape, the floppy tape device driver +# +# CONFIG_FTAPE is not set +# CONFIG_AGP is not set +# CONFIG_DRM is not set + +# +# PCMCIA character devices +# +# CONFIG_SYNCLINK_CS is not set +# CONFIG_RAW_DRIVER 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_ALGO_PXA2XX=y + +# +# I2C Hardware Bus support +# +# CONFIG_I2C_AMD756 is not set +# CONFIG_I2C_AMD8111 is not set +# CONFIG_I2C_ISA is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +CONFIG_I2C_PXA=y +CONFIG_I2C_PXA_SLAVE=y +# CONFIG_SCx200_ACB is not set + +# +# Hardware Sensors Chip support +# +# CONFIG_I2C_SENSOR is not set +# CONFIG_SENSORS_ADM1021 is not set +# CONFIG_SENSORS_ASB100 is not set +# CONFIG_SENSORS_DS1621 is not set +# CONFIG_SENSORS_FSCHER is not set +# CONFIG_SENSORS_GL518SM is not set +# CONFIG_SENSORS_IT87 is not set +# CONFIG_SENSORS_LM75 is not set +# CONFIG_SENSORS_LM78 is not set +# CONFIG_SENSORS_LM80 is not set +# CONFIG_SENSORS_LM83 is not set +# CONFIG_SENSORS_LM85 is not set +# CONFIG_SENSORS_LM90 is not set +# CONFIG_SENSORS_MAX1619 is not set +# CONFIG_SENSORS_VIA686A is not set +# CONFIG_SENSORS_W83781D is not set +# CONFIG_SENSORS_W83L785TS is not set +# CONFIG_SENSORS_W83627HF is not set + +# +# Other I2C Chip support +# +# CONFIG_SENSORS_EEPROM is not set +# CONFIG_SENSORS_PCF8574 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_RTC8564 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 + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set + +# +# Digital Video Broadcasting Devices +# +# CONFIG_DVB is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +CONFIG_EXT2_FS_XATTR=y +# CONFIG_EXT2_FS_POSIX_ACL is not set +# CONFIG_EXT2_FS_SECURITY is not set +CONFIG_EXT3_FS=m +# CONFIG_EXT3_FS_XATTR is not set +CONFIG_JBD=m +# CONFIG_JBD_DEBUG is not set +CONFIG_FS_MBCACHE=y +CONFIG_REISERFS_FS=m +# CONFIG_REISERFS_CHECK is not set +CONFIG_REISERFS_PROC_INFO=y +CONFIG_REISERFS_FS_XATTR=y +CONFIG_REISERFS_FS_POSIX_ACL=y +CONFIG_REISERFS_FS_SECURITY=y +# CONFIG_JFS_FS is not set +CONFIG_FS_POSIX_ACL=y +# CONFIG_XFS_FS is not set +# CONFIG_MINIX_FS is not set +CONFIG_ROMFS_FS=y +# CONFIG_QUOTA is not set +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +CONFIG_MSDOS_FS=y +CONFIG_VFAT_FS=y +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_SYSFS=y +# CONFIG_DEVFS_FS is not set +# CONFIG_DEVPTS_FS_XATTR is not set +# CONFIG_TMPFS is not set +# CONFIG_HUGETLBFS is not set +# CONFIG_HUGETLB_PAGE is not set +CONFIG_RAMFS=y + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +# CONFIG_JFFS_FS is not set +# CONFIG_JFFS2_FS is not set +# CONFIG_JFFS2_COMPRESSION_OPTIONS is not set +# CONFIG_CRAMFS is not set +# CONFIG_VXFS_FS is not set +# CONFIG_HPFS_FS is not set +# CONFIG_QNX4FS_FS is not set +# CONFIG_SYSV_FS is not set +# CONFIG_UFS_FS is not set + +# +# Network File Systems +# +CONFIG_NFS_FS=y +# CONFIG_NFS_V3 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_EXPORTFS is not set +CONFIG_SUNRPC=y +# CONFIG_RPCSEC_GSS_KRB5 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 + +# +# Partition Types +# +CONFIG_PARTITION_ADVANCED=y +# CONFIG_ACORN_PARTITION is not set +# CONFIG_OSF_PARTITION is not set +# CONFIG_AMIGA_PARTITION is not set +# CONFIG_ATARI_PARTITION is not set +# CONFIG_MAC_PARTITION is not set +CONFIG_MSDOS_PARTITION=y +# CONFIG_BSD_DISKLABEL is not set +# CONFIG_MINIX_SUBPARTITION is not set +# CONFIG_SOLARIS_X86_PARTITION is not set +# CONFIG_UNIXWARE_DISKLABEL is not set +# CONFIG_LDM_PARTITION is not set +# CONFIG_NEC98_PARTITION is not set +# CONFIG_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_EFI_PARTITION is not set + +# +# Native Language Support +# +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="iso8859-1" +# CONFIG_NLS_CODEPAGE_437 is not set +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +# CONFIG_NLS_ISO8859_1 is not set +# 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 + +# +# Profiling support +# +# CONFIG_PROFILING is not set + +# +# Graphics support +# +CONFIG_FB=y +CONFIG_FB_S1D138XX=y +# CONFIG_FB_PXA is not set +# CONFIG_FB_VIRTUAL is not set + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +# CONFIG_MDA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +CONFIG_PCI_CONSOLE=y +CONFIG_FONTS=y +# CONFIG_FONT_8x8 is not set +CONFIG_FONT_8x16=y +# CONFIG_FONT_6x11 is not set +# CONFIG_FONT_PEARL_8x8 is not set +# CONFIG_FONT_ACORN_8x8 is not set +# CONFIG_FONT_MINI_4x6 is not set +# CONFIG_FONT_SUN8x16 is not set +# CONFIG_FONT_SUN12x22 is not set + +# +# Logo configuration +# +CONFIG_LOGO=y +# CONFIG_LOGO_LINUX_MONO is not set +# CONFIG_LOGO_LINUX_VGA16 is not set +CONFIG_LOGO_LINUX_CLUT224=y + +# +# Sound +# +CONFIG_SOUND=y + +# +# Advanced Linux Sound Architecture +# +# CONFIG_SND is not set + +# +# Open Sound System +# +CONFIG_SOUND_PRIME=y +# CONFIG_SOUND_BT878 is not set +# CONFIG_SOUND_FUSION is not set +# CONFIG_SOUND_CS4281 is not set +# CONFIG_SOUND_ESSSOLO1 is not set +# CONFIG_SOUND_MAESTRO is not set +CONFIG_SOUND_PXA_AC97=y +CONFIG_SOUND_PXA_AUDIO=y +# CONFIG_SOUND_SONICVIBES is not set +# CONFIG_SOUND_TRIDENT is not set +# CONFIG_SOUND_MSNDCLAS is not set +# CONFIG_SOUND_MSNDPIN is not set +# CONFIG_SOUND_OSS is not set +# CONFIG_SOUND_TVMIXER is not set +# CONFIG_SOUND_AD1980 is not set +CONFIG_SOUND_WM97XX=y + +# +# Misc devices +# + +# +# USB support +# +CONFIG_USB=y +CONFIG_USB_PXA=y +# CONFIG_USB_DEBUG is not set + +# +# Miscellaneous USB options +# +# CONFIG_USB_DEVICEFS is not set +# CONFIG_USB_BANDWIDTH is not set +# CONFIG_USB_DYNAMIC_MINORS is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_EHCI_HCD is not set +CONFIG_USB_OHCI_HCD=y +# CONFIG_USB_UHCI_HCD is not set +# CONFIG_USB_SL811HS is not set + +# +# USB Device Class drivers +# +# CONFIG_USB_AUDIO is not set +# CONFIG_USB_BLUETOOTH_TTY is not set +# CONFIG_USB_MIDI is not set +# CONFIG_USB_ACM is not set +CONFIG_USB_PRINTER=m +CONFIG_USB_STORAGE=m +CONFIG_USB_STORAGE_DEBUG=y +CONFIG_USB_STORAGE_DATAFAB=y +CONFIG_USB_STORAGE_FREECOM=y +CONFIG_USB_STORAGE_ISD200=y +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_HP8200e is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set + +# +# USB Human Interface Devices (HID) +# +CONFIG_USB_HID=y +CONFIG_USB_HIDINPUT=y +# CONFIG_HID_FF is not set +# CONFIG_USB_HIDDEV is not set +# CONFIG_USB_AIPTEK is not set +# CONFIG_USB_WACOM is not set +# CONFIG_USB_KBTAB is not set +# CONFIG_USB_POWERMATE is not set +# CONFIG_USB_MTOUCH is not set +# CONFIG_USB_EGALAX is not set +# CONFIG_USB_XPAD is not set +# CONFIG_USB_ATI_REMOTE is not set + +# +# USB Imaging devices +# +# CONFIG_USB_MDC800 is not set +# CONFIG_USB_MICROTEK is not set +# CONFIG_USB_HPUSBSCSI is not set + +# +# USB Multimedia devices +# +# CONFIG_USB_DABUSB is not set + +# +# Video4Linux support is needed for USB Multimedia device support +# + +# +# USB Network adaptors +# +# 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 is not set + +# +# USB port drivers +# + +# +# USB Serial Converter support +# +# CONFIG_USB_SERIAL is not set + +# +# USB Miscellaneous drivers +# +# CONFIG_USB_EMI62 is not set +# CONFIG_USB_EMI26 is not set +# CONFIG_USB_TIGL 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_CYTHERM is not set +# CONFIG_USB_PHIDGETSERVO is not set + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set + +# +# MMC/SD Card support +# +CONFIG_MMC=m +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_BLOCK=m +CONFIG_MMC_PXA=m + +# +# Kernel hacking +# +CONFIG_FRAME_POINTER=y +CONFIG_DEBUG_USER=y +# CONFIG_DEBUG_INFO is not set +CONFIG_DEBUG_KERNEL=y +CONFIG_DEBUG_SLAB=y +# CONFIG_MAGIC_SYSRQ is not set +CONFIG_DEBUG_SPINLOCK=y +# CONFIG_DEBUG_WAITQ is not set +CONFIG_DEBUG_BUGVERBOSE=y +CONFIG_DEBUG_ERRORS=y +CONFIG_DEBUG_LL=y +# CONFIG_DEBUG_ICEDCC is not set + +# +# Security options +# +# CONFIG_SECURITY is not set + +# +# Cryptographic options +# +CONFIG_CRYPTO=y +# CONFIG_CRYPTO_HMAC is not set +CONFIG_CRYPTO_NULL=m +# CONFIG_CRYPTO_MD4 is not set +# CONFIG_CRYPTO_MD5 is not set +CONFIG_CRYPTO_SHA1=m +CONFIG_CRYPTO_SHA256=m +# CONFIG_CRYPTO_SHA512 is not set +CONFIG_CRYPTO_DES=m +CONFIG_CRYPTO_BLOWFISH=m +CONFIG_CRYPTO_TWOFISH=m +# CONFIG_CRYPTO_SERPENT is not set +CONFIG_CRYPTO_AES=m +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_ARC4 is not set +# CONFIG_CRYPTO_DEFLATE is not set +CONFIG_CRYPTO_MICHAEL_MIC=m +CONFIG_CRYPTO_CRC32C=m +# CONFIG_CRYPTO_TEST is not set + +# +# Library routines +# +CONFIG_CRC32=m +CONFIG_LIBCRC32C=y +CONFIG_ZLIB_INFLATE=m +CONFIG_ZLIB_DEFLATE=m diff -NaurbB linux-2.6.9-pre1-bk18/arch/arm/Kconfig linux-2.6.9_nbp/arch/arm/Kconfig --- linux-2.6.9-pre1-bk18/arch/arm/Kconfig 2005-08-15 01:14:40.000000000 +0200 +++ linux-2.6.9_nbp/arch/arm/Kconfig 2004-09-13 16:06:47.000000000 +0200 @@ -346,7 +346,7 @@ config CPU_FREQ bool "Support CPU clock change (EXPERIMENTAL)" - depends on (ARCH_SA1100 || ARCH_INTEGRATOR) && EXPERIMENTAL + depends on (ARCH_SA1100 || ARCH_INTEGRATOR || ARCH_PXA) && EXPERIMENTAL help CPU clock scaling allows you to change the clock speed of the running CPU on the fly. This is a nice method to save battery power, @@ -356,6 +356,10 @@ written) to implement the policy. If you don't understand what this is all about, it's safe to say 'N'. +config CPU_FREQ_TABLE + bool + depends on CPU_FREQ + default y # CPUfreq on SA11x0 is special -- it _needs_ the userspace governor @@ -382,7 +386,14 @@ If in doubt, say Y. -if (CPU_FREQ_INTEGRATOR) || (CPU_FREQ_SA1110) || (CPU_FREQ_SA1100) +config CPU_FREQ_PXA + bool + depends on CPU_FREQ && ARCH_PXA + default y + select CPU_FREQ_DEFAULT_GOV_USERSPACE + select CPU_FREQ_24_API if SYSCTL + +if (CPU_FREQ_INTEGRATOR) || (CPU_FREQ_SA1110) || (CPU_FREQ_SA1100) || (CPU_FREQ_PXA) source "drivers/cpufreq/Kconfig" diff -NaurbB linux-2.6.9-pre1-bk18/arch/arm/kernel/apm.c linux-2.6.9_nbp/arch/arm/kernel/apm.c --- linux-2.6.9-pre1-bk18/arch/arm/kernel/apm.c 2004-08-14 07:38:04.000000000 +0200 +++ linux-2.6.9_nbp/arch/arm/kernel/apm.c 2004-10-15 22:11:23.000000000 +0200 @@ -13,7 +13,6 @@ #include #include #include -#include #include #include #include @@ -25,6 +24,7 @@ #include #include #include +#include #include /* apm_power_info */ #include @@ -46,7 +46,13 @@ /* * Maximum number of events stored */ -#define APM_MAX_EVENTS 20 +#define APM_MAX_EVENTS 16 + +struct apm_queue { + unsigned int event_head; + unsigned int event_tail; + apm_event_t events[APM_MAX_EVENTS]; +}; /* * The per-file APM data @@ -54,27 +60,28 @@ struct apm_user { struct list_head list; - int suser: 1; - int writer: 1; - int reader: 1; - int suspend_wait: 1; + unsigned int suser: 1; + unsigned int writer: 1; + unsigned int reader: 1; + int suspend_result; + unsigned int suspend_state; +#define SUSPEND_NONE 0 /* no suspend pending */ +#define SUSPEND_PENDING 1 /* suspend pending read */ +#define SUSPEND_READ 2 /* suspend read, pending ack */ +#define SUSPEND_ACKED 3 /* suspend acked */ +#define SUSPEND_WAIT 4 /* waiting for suspend */ +#define SUSPEND_DONE 5 /* suspend completed */ - int suspends_pending; - int standbys_pending; - unsigned int suspends_read; - unsigned int standbys_read; + struct apm_queue queue; - int event_head; - int event_tail; - apm_event_t events[APM_MAX_EVENTS]; + struct task_struct *task; }; /* * Local variables */ static int suspends_pending; -static int standbys_pending; static int apm_disabled; static DECLARE_WAIT_QUEUE_HEAD(apm_waitqueue); @@ -83,14 +90,20 @@ /* * This is a list of everyone who has opened /dev/apm_bios */ -static spinlock_t user_list_lock = SPIN_LOCK_UNLOCKED; +static DECLARE_RWSEM(user_list_lock); static LIST_HEAD(apm_user_list); /* - * The kapmd info. + * kapmd info. kapmd provides us a process context to handle + * "APM" events within - specifically necessary if we're going + * to be suspending the system. */ -static struct task_struct *kapmd; +static DECLARE_WAIT_QUEUE_HEAD(kapmd_wait); static DECLARE_COMPLETION(kapmd_exit); +static spinlock_t kapmd_queue_lock = SPIN_LOCK_UNLOCKED; +static struct apm_queue kapmd_queue; + +static DECLARE_MUTEX(state_lock); static const char driver_version[] = "1.13"; /* no spaces */ @@ -102,19 +115,6 @@ */ static void __apm_get_power_status(struct apm_power_info *info) { -#if 0 && defined(CONFIG_SA1100_H3600) && defined(CONFIG_TOUCHSCREEN_H3600) - extern int h3600_apm_get_power_status(u_char *, u_char *, u_char *, - u_char *, u_short *); - - if (machine_is_h3600()) { - int dx; - h3600_apm_get_power_status(&info->ac_line_status, - &info->battery_status, &info->battery_flag, - &info->battery_life, &dx); - info->time = dx & 0x7fff; - info->units = dx & 0x8000 ? 0 : 1; - } -#endif } /* @@ -123,122 +123,145 @@ void (*apm_get_power_status)(struct apm_power_info *) = __apm_get_power_status; EXPORT_SYMBOL(apm_get_power_status); -static int queue_empty(struct apm_user *as) + +/* + * APM event queue management. + */ +static inline int queue_empty(struct apm_queue *q) { - return as->event_head == as->event_tail; + return q->event_head == q->event_tail; } -static apm_event_t get_queued_event(struct apm_user *as) +static inline apm_event_t queue_get_event(struct apm_queue *q) { - as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS; - return as->events[as->event_tail]; + q->event_tail = (q->event_tail + 1) % APM_MAX_EVENTS; + return q->events[q->event_tail]; } -static void queue_event_one_user(struct apm_user *as, apm_event_t event) +static void queue_add_event(struct apm_queue *q, apm_event_t event) { - as->event_head = (as->event_head + 1) % APM_MAX_EVENTS; - if (as->event_head == as->event_tail) { + q->event_head = (q->event_head + 1) % APM_MAX_EVENTS; + if (q->event_head == q->event_tail) { static int notified; if (notified++ == 0) printk(KERN_ERR "apm: an event queue overflowed\n"); - as->event_tail = (as->event_tail + 1) % APM_MAX_EVENTS; + q->event_tail = (q->event_tail + 1) % APM_MAX_EVENTS; } - as->events[as->event_head] = event; - - if (!as->suser || !as->writer) - return; + q->events[q->event_head] = event; +} - switch (event) { - case APM_SYS_SUSPEND: - case APM_USER_SUSPEND: - as->suspends_pending++; - suspends_pending++; - break; +static void queue_event(apm_event_t event) +{ + struct apm_user *as; - case APM_SYS_STANDBY: - case APM_USER_STANDBY: - as->standbys_pending++; - standbys_pending++; - break; + down_read(&user_list_lock); + list_for_each_entry(as, &apm_user_list, list) { + if (as->reader) + queue_add_event(&as->queue, event); } + up_read(&user_list_lock); + wake_up_interruptible(&apm_waitqueue); } -static void queue_event(apm_event_t event, struct apm_user *sender) +/* + * queue_suspend_event - queue an APM suspend event. + * + * Check that we're in a state where we can suspend. If not, + * return -EBUSY. Otherwise, queue an event to all "writer" + * users. If there are no "writer" users, return '1' to + * indicate that we can immediately suspend. + */ +static int queue_suspend_event(apm_event_t event, struct apm_user *sender) { - struct list_head *l; + struct apm_user *as; + int ret = 1; + + down(&state_lock); + down_read(&user_list_lock); - spin_lock(&user_list_lock); - list_for_each(l, &apm_user_list) { - struct apm_user *as = list_entry(l, struct apm_user, list); + /* + * If a thread is still processing, we can't suspend, so reject + * the request. + */ + list_for_each_entry(as, &apm_user_list, list) { + if (as != sender && as->reader && as->writer && as->suser && + as->suspend_state != SUSPEND_NONE) { + ret = -EBUSY; + goto out; + } + } - if (as != sender && as->reader) - queue_event_one_user(as, event); + list_for_each_entry(as, &apm_user_list, list) { + if (as != sender && as->reader && as->writer && as->suser) { + as->suspend_state = SUSPEND_PENDING; + suspends_pending++; + queue_add_event(&as->queue, event); + ret = 0; + } } - spin_unlock(&user_list_lock); + out: + up_read(&user_list_lock); + up(&state_lock); wake_up_interruptible(&apm_waitqueue); + return ret; } -static int apm_suspend(void) +static void apm_suspend(void) { - struct list_head *l; + struct apm_user *as; int err = pm_suspend(PM_SUSPEND_MEM); /* * Anyone on the APM queues will think we're still suspended. * Send a message so everyone knows we're now awake again. */ - queue_event(APM_NORMAL_RESUME, NULL); + queue_event(APM_NORMAL_RESUME); /* * Finally, wake up anyone who is sleeping on the suspend. */ - spin_lock(&user_list_lock); - list_for_each(l, &apm_user_list) { - struct apm_user *as = list_entry(l, struct apm_user, list); - + down(&state_lock); + down_read(&user_list_lock); + list_for_each_entry(as, &apm_user_list, list) { + if (as->suspend_state == SUSPEND_WAIT || + as->suspend_state == SUSPEND_ACKED) { as->suspend_result = err; - as->suspend_wait = 0; + as->suspend_state = SUSPEND_DONE; + } } - spin_unlock(&user_list_lock); + up_read(&user_list_lock); + up(&state_lock); - wake_up_interruptible(&apm_suspend_waitqueue); - return err; + wake_up(&apm_suspend_waitqueue); } static ssize_t apm_read(struct file *fp, char __user *buf, size_t count, loff_t *ppos) { struct apm_user *as = fp->private_data; apm_event_t event; - int i = count, ret = 0, nonblock = fp->f_flags & O_NONBLOCK; + int i = count, ret = 0; if (count < sizeof(apm_event_t)) return -EINVAL; - if (queue_empty(as) && nonblock) + if (queue_empty(&as->queue) && fp->f_flags & O_NONBLOCK) return -EAGAIN; - wait_event_interruptible(apm_waitqueue, !queue_empty(as)); + wait_event_interruptible(apm_waitqueue, !queue_empty(&as->queue)); - while ((i >= sizeof(event)) && !queue_empty(as)) { - event = get_queued_event(as); - printk(" apm_read: event=%d\n", event); + while ((i >= sizeof(event)) && !queue_empty(&as->queue)) { + event = queue_get_event(&as->queue); ret = -EFAULT; if (copy_to_user(buf, &event, sizeof(event))) break; - switch (event) { - case APM_SYS_SUSPEND: - case APM_USER_SUSPEND: - as->suspends_read++; - break; - - case APM_SYS_STANDBY: - case APM_USER_STANDBY: - as->standbys_read++; - break; - } + down(&state_lock); + if (as->suspend_state == SUSPEND_PENDING && + (event == APM_SYS_SUSPEND || event == APM_USER_SUSPEND)) + as->suspend_state = SUSPEND_READ; + up(&state_lock); buf += sizeof(event); i -= sizeof(event); @@ -252,10 +275,10 @@ static unsigned int apm_poll(struct file *fp, poll_table * wait) { - struct apm_user * as = fp->private_data; + struct apm_user *as = fp->private_data; poll_wait(fp, &apm_waitqueue, wait); - return queue_empty(as) ? 0 : POLLIN | POLLRDNORM; + return queue_empty(&as->queue) ? 0 : POLLIN | POLLRDNORM; } /* @@ -272,43 +295,95 @@ apm_ioctl(struct inode * inode, struct file *filp, u_int cmd, u_long arg) { struct apm_user *as = filp->private_data; + unsigned long flags; int err = -EINVAL; if (!as->suser || !as->writer) return -EPERM; switch (cmd) { - case APM_IOC_STANDBY: - break; - case APM_IOC_SUSPEND: + down(&state_lock); + + as->suspend_result = -EINTR; + + if (as->suspend_state == SUSPEND_READ) { + int pending; + /* * If we read a suspend command from /dev/apm_bios, * then the corresponding APM_IOC_SUSPEND ioctl is * interpreted as an acknowledge. */ - if (as->suspends_read > 0) { - as->suspends_read--; - as->suspends_pending--; + as->suspend_state = SUSPEND_ACKED; suspends_pending--; + pending = suspends_pending == 0; + up(&state_lock); + + /* + * If there are no further acknowledges required, + * suspend the system. + */ + if (pending) + apm_suspend(); + + /* + * Wait for the suspend/resume to complete. If there + * are pending acknowledges, we wait here for them. + * + * Note: we need to ensure that the PM subsystem does + * not kick us out of the wait when it suspends the + * threads. + */ + flags = current->flags; + current->flags |= PF_NOFREEZE; + + wait_event(apm_suspend_waitqueue, + as->suspend_state == SUSPEND_DONE); } else { - queue_event(APM_USER_SUSPEND, as); + as->suspend_state = SUSPEND_WAIT; + up(&state_lock); + + /* + * Otherwise it is a request to suspend the system. + * Queue an event for all readers, and expect an + * acknowledge from all writers who haven't already + * acknowledged. + */ + err = queue_suspend_event(APM_USER_SUSPEND, as); + if (err < 0) { + /* + * Avoid taking the lock here - this + * should be fine. + */ + as->suspend_state = SUSPEND_NONE; + break; } + if (err > 0) + apm_suspend(); + /* - * If there are outstanding suspend requests for other - * people on /dev/apm_bios, we must sleep for them. - * Last one to bed turns the lights out. + * Wait for the suspend/resume to complete. If there + * are pending acknowledges, we wait here for them. + * + * Note: we need to ensure that the PM subsystem does + * not kick us out of the wait when it suspends the + * threads. */ - if (suspends_pending > 0) { - as->suspend_wait = 1; - err = wait_event_interruptible(apm_suspend_waitqueue, - as->suspend_wait == 0); - if (err == 0) - err = as->suspend_result; - } else { - err = apm_suspend(); + flags = current->flags; + current->flags |= PF_NOFREEZE; + + wait_event_interruptible(apm_suspend_waitqueue, + as->suspend_state == SUSPEND_DONE); } + + current->flags = flags; + + down(&state_lock); + err = as->suspend_result; + as->suspend_state = SUSPEND_NONE; + up(&state_lock); break; } @@ -318,28 +393,28 @@ static int apm_release(struct inode * inode, struct file * filp) { struct apm_user *as = filp->private_data; + int pending = 0; + filp->private_data = NULL; - spin_lock(&user_list_lock); + down_write(&user_list_lock); list_del(&as->list); - spin_unlock(&user_list_lock); + up_write(&user_list_lock); /* * We are now unhooked from the chain. As far as new * events are concerned, we no longer exist. However, we - * need to balance standbys_pending and suspends_pending, - * which means the possibility of sleeping. + * need to balance suspends_pending, which means the + * possibility of sleeping. */ - if (as->standbys_pending > 0) { - standbys_pending -= as->standbys_pending; -// if (standbys_pending <= 0) -// standby(); - } - if (as->suspends_pending > 0) { - suspends_pending -= as->suspends_pending; - if (suspends_pending <= 0) - apm_suspend(); + down(&state_lock); + if (as->suspend_state != SUSPEND_NONE) { + suspends_pending -= 1; + pending = suspends_pending == 0; } + up(&state_lock); + if (pending) + apm_suspend(); kfree(as); return 0; @@ -364,9 +439,11 @@ as->writer = (filp->f_mode & FMODE_WRITE) == FMODE_WRITE; as->reader = (filp->f_mode & FMODE_READ) == FMODE_READ; - spin_lock(&user_list_lock); + as->task = current; + + down_write(&user_list_lock); list_add(&as->list, &apm_user_list); - spin_unlock(&user_list_lock); + up_write(&user_list_lock); filp->private_data = as; } @@ -438,7 +515,7 @@ info.ac_line_status = 0xff; info.battery_status = 0xff; info.battery_flag = 0xff; - info.battery_life = 255; + info.battery_life = -1; info.time = -1; info.units = -1; @@ -459,36 +536,82 @@ return ret; } -#endif -#if 0 -static int kapmd(void *startup) +static int apm_get_debug_info(char *buf, char **start, off_t fpos, int length) { - struct task_struct *tsk = current; + struct apm_user *as; + char *p = buf; - daemonize(); - strcpy(tsk->comm, "kapmd"); - kapmd = tsk; + p += sprintf(p, "sp%d\n", suspends_pending); - spin_lock_irq(&tsk->sigmask_lock); - siginitsetinv(&tsk->blocked, sigmask(SIGQUIT)); - recalc_sigpending(tsk); - spin_unlock_irq(&tsk->sigmask_lock); + down_read(&user_list_lock); + list_for_each_entry(as, &apm_user_list, list) + p += sprintf(p, "%16s:%5d: s%u w%u r%u res%d st%u qh%u qt%u\n", + as->task->comm, as->task->pid, + as->suser, as->writer, as->reader, + as->suspend_result, as->suspend_state, + as->queue.event_head, as->queue.event_tail); + up_read(&user_list_lock); + + return p - buf; +} +#endif - complete((struct completion *)startup); +static int kapmd(void *arg) +{ + daemonize("kapmd"); + current->flags |= PF_NOFREEZE; do { - set_task_state(tsk, TASK_INTERRUPTIBLE); - schedule(); - } while (!signal_pending(tsk)); + apm_event_t event; + int ret; + + wait_event_interruptible(kapmd_wait, + !queue_empty(&kapmd_queue) || !pm_active); + + if (!pm_active) + break; + + spin_lock_irq(&kapmd_queue_lock); + event = 0; + if (!queue_empty(&kapmd_queue)) + event = queue_get_event(&kapmd_queue); + spin_unlock_irq(&kapmd_queue_lock); + + switch (event) { + case 0: + break; + + case APM_LOW_BATTERY: + case APM_POWER_STATUS_CHANGE: + queue_event(event); + break; + + case APM_USER_SUSPEND: + case APM_SYS_SUSPEND: + ret = queue_suspend_event(event, NULL); + if (ret < 0) { + /* + * We were busy. Try again in 50ms. + */ + queue_add_event(&kapmd_queue, event); + msleep(50); + } + if (ret > 0) + apm_suspend(); + break; + + case APM_CRITICAL_SUSPEND: + apm_suspend(); + break; + } + } while (1); complete_and_exit(&kapmd_exit, 0); } -#endif static int __init apm_init(void) { -// struct completion startup = COMPLETION_INITIALIZER(startup); int ret; if (apm_disabled) { @@ -501,22 +624,25 @@ return -EINVAL; } -// ret = kernel_thread(kapmd, &startup, CLONE_FS | CLONE_FILES); -// if (ret) -// return ret; -// wait_for_completion(&startup); - pm_active = 1; + ret = kernel_thread(kapmd, NULL, CLONE_KERNEL); + if (ret < 0) { + pm_active = 0; + return ret; + } + #ifdef CONFIG_PROC_FS create_proc_info_entry("apm", 0, NULL, apm_get_info); + create_proc_info_entry("apm_debug", 0, NULL, apm_get_debug_info); #endif ret = misc_register(&apm_device); if (ret != 0) { - pm_active = 0; remove_proc_entry("apm", NULL); - send_sig(SIGQUIT, kapmd, 1); + + pm_active = 0; + wake_up(&kapmd_wait); wait_for_completion(&kapmd_exit); } @@ -527,9 +653,10 @@ { misc_deregister(&apm_device); remove_proc_entry("apm", NULL); + pm_active = 0; -// send_sig(SIGQUIT, kapmd, 1); -// wait_for_completion(&kapmd_exit); + wake_up(&kapmd_wait); + wait_for_completion(&kapmd_exit); } module_init(apm_init); @@ -556,3 +683,27 @@ __setup("apm=", apm_setup); #endif + +/** + * apm_queue_event - queue an APM event for kapmd + * @event: APM event + * + * Queue an APM event for kapmd to process and ultimately take the + * appropriate action. Only a subset of events are handled: + * %APM_LOW_BATTERY + * %APM_POWER_STATUS_CHANGE + * %APM_USER_SUSPEND + * %APM_SYS_SUSPEND + * %APM_CRITICAL_SUSPEND + */ +void apm_queue_event(apm_event_t event) +{ + unsigned long flags; + + spin_lock_irqsave(&kapmd_queue_lock, flags); + queue_add_event(&kapmd_queue, event); + spin_unlock_irqrestore(&kapmd_queue_lock, flags); + + wake_up_interruptible(&kapmd_wait); +} +EXPORT_SYMBOL(apm_queue_event); diff -NaurbB linux-2.6.9-pre1-bk18/arch/arm/kernel/irq.c linux-2.6.9_nbp/arch/arm/kernel/irq.c --- linux-2.6.9-pre1-bk18/arch/arm/kernel/irq.c 2005-08-15 01:14:40.000000000 +0200 +++ linux-2.6.9_nbp/arch/arm/kernel/irq.c 2004-09-22 00:14:40.000000000 +0200 @@ -47,6 +47,7 @@ #define MAX_IRQ_CNT 100000 static volatile unsigned long irq_err_count; +static int no_irq_errs = 1; static spinlock_t irq_controller_lock = SPIN_LOCK_UNLOCKED; static LIST_HEAD(irq_pending); @@ -235,7 +236,7 @@ static int count = 100; struct irqaction *action; - if (!count) + if (!count || no_irq_errs) return; count--; @@ -255,6 +256,8 @@ action = action->next; } while (action); printk("\n"); + + irq_err_count++; } static int diff -NaurbB linux-2.6.9-pre1-bk18/arch/arm/kernel/process.c linux-2.6.9_nbp/arch/arm/kernel/process.c --- linux-2.6.9-pre1-bk18/arch/arm/kernel/process.c 2005-08-15 01:14:33.000000000 +0200 +++ linux-2.6.9_nbp/arch/arm/kernel/process.c 2004-10-15 22:11:14.000000000 +0200 @@ -78,6 +78,9 @@ void (*pm_power_off)(void); EXPORT_SYMBOL(pm_power_off); +void (*pm_restart)(void); +EXPORT_SYMBOL(pm_restart); + /* * This is our default idle handler. We need to disable * interrupts here to ensure we don't miss a wakeup call. @@ -153,6 +156,9 @@ /* * Now call the architecture specific reboot code. */ + if (pm_restart) + pm_restart(); + else arch_reset(reboot_mode); /* diff -NaurbB linux-2.6.9-pre1-bk18/arch/arm/kernel/time.c linux-2.6.9_nbp/arch/arm/kernel/time.c --- linux-2.6.9-pre1-bk18/arch/arm/kernel/time.c 2005-08-15 01:14:40.000000000 +0200 +++ linux-2.6.9_nbp/arch/arm/kernel/time.c 2004-09-25 21:51:09.000000000 +0200 @@ -304,6 +304,23 @@ EXPORT_SYMBOL(do_settimeofday); +long save_time_delta(unsigned long rtc) +{ + return xtime.tv_sec - rtc; +} +EXPORT_SYMBOL(save_time_delta); + +void restore_time_delta(unsigned long rtc, long delta) +{ + struct timespec ts; + + ts.tv_sec = rtc + delta; + ts.tv_nsec = xtime.tv_nsec; + + do_settimeofday(&ts); +} +EXPORT_SYMBOL(restore_time_delta); + void timer_tick(struct pt_regs *regs) { profile_tick(CPU_PROFILING, regs); diff -NaurbB linux-2.6.9-pre1-bk18/arch/arm/mach-pxa/cpu-pxa.c linux-2.6.9_nbp/arch/arm/mach-pxa/cpu-pxa.c --- linux-2.6.9-pre1-bk18/arch/arm/mach-pxa/cpu-pxa.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/arch/arm/mach-pxa/cpu-pxa.c 2004-09-13 16:06:47.000000000 +0200 @@ -0,0 +1,320 @@ +/* + * linux/arch/arm/mach-pxa/cpu-pxa.c + * + * Copyright (C) 2002,2003 Intrinsyc Software + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * History: + * 31-Jul-2002 : Initial version [FB] + * 29-Jan-2003 : added PXA255 support [FB] + * 20-Apr-2003 : ported to v2.5 (Dustin McIntire, Sensoria Corp.) + * + * Note: + * This driver may change the memory bus clock rate, but will not do any + * platform specific access timing changes... for example if you have flash + * memory connected to CS0, you will need to register a platform specific + * notifier which will adjust the memory access strobes to maintain a + * minimum strobe width. + * + */ + +#include +#include +#include +#include +#include + +#include + +#define DEBUG 0 + +#ifdef DEBUG + static unsigned int freq_debug = DEBUG; + MODULE_PARM(freq_debug, "i"); + MODULE_PARM_DESC(freq_debug, "Set the debug messages to on=1/off=0"); +#else + #define freq_debug 0 +#endif + +typedef struct +{ + unsigned int khz; + unsigned int membus; + unsigned int cccr; + unsigned int div2; +} pxa_freqs_t; + +/* Define the refresh period in mSec for the SDRAM and the number of rows */ +#define SDRAM_TREF 64 /* standard 64ms SDRAM */ +#define SDRAM_ROWS 4096 /* 64MB=8192 32MB=4096 */ +#define MDREFR_DRI(x) ((x*SDRAM_TREF)/(SDRAM_ROWS*32)) + +#define CCLKCFG_TURBO 0x1 +#define CCLKCFG_FCS 0x2 +#define PXA25x_MIN_FREQ 99500 +#define PXA25x_MAX_FREQ 398100 +#define MDREFR_DB2_MASK (MDREFR_K2DB2 | MDREFR_K1DB2) +#define MDREFR_DRI_MASK 0xFFF + + +/* Use the run mode frequencies for the CPUFREQ_POLICY_PERFORMANCE policy */ +static pxa_freqs_t pxa255_run_freqs[] = +{ + /* CPU MEMBUS CCCR DIV2*/ + { 99500, 99500, 0x121, 1}, /* run= 99, turbo= 99, PXbus=50, SDRAM=50 */ + {132700, 132700, 0x123, 1}, /* run=133, turbo=133, PXbus=66, SDRAM=66 */ + {199100, 99500, 0x141, 0}, /* run=199, turbo=199, PXbus=99, SDRAM=99 */ + {265400, 132700, 0x143, 1}, /* run=265, turbo=265, PXbus=133, SDRAM=66 */ + {331800, 165900, 0x145, 1}, /* run=331, turbo=331, PXbus=166, SDRAM=83 */ + {398100, 99500, 0x161, 0}, /* run=398, turbo=398, PXbus=196, SDRAM=99 */ + {0,} +}; +#define NUM_RUN_FREQS (sizeof(pxa255_run_freqs)/sizeof(pxa_freqs_t)) + +static struct cpufreq_frequency_table pxa255_run_freq_table[NUM_RUN_FREQS+1]; + +/* Use the turbo mode frequencies for the CPUFREQ_POLICY_POWERSAVE policy */ +static pxa_freqs_t pxa255_turbo_freqs[] = +{ + /* CPU MEMBUS CCCR DIV2*/ + { 99500, 99500, 0x121, 1}, /* run=99, turbo= 99, PXbus=50, SDRAM=50 */ + {199100, 99500, 0x221, 0}, /* run=99, turbo=199, PXbus=50, SDRAM=99 */ + {298500, 99500, 0x321, 0}, /* run=99, turbo=287, PXbus=50, SDRAM=99 */ + {298600, 99500, 0x1c1, 0}, /* run=199, turbo=287, PXbus=99, SDRAM=99 */ + {398100, 99500, 0x241, 0}, /* run=199, turbo=398, PXbus=99, SDRAM=99 */ + {0,} +}; +#define NUM_TURBO_FREQS (sizeof(pxa255_turbo_freqs)/sizeof(pxa_freqs_t)) + +static struct cpufreq_frequency_table pxa255_turbo_freq_table[NUM_TURBO_FREQS+1]; + +extern unsigned get_clk_frequency_khz(int info); + +/* find a valid frequency point */ +static int pxa_verify_policy(struct cpufreq_policy *policy) +{ + int ret; + struct cpufreq_frequency_table *pxa_freqs_table; + + if(policy->policy == CPUFREQ_POLICY_PERFORMANCE) { + pxa_freqs_table = pxa255_run_freq_table; + } else if (policy->policy == CPUFREQ_POLICY_POWERSAVE) { + pxa_freqs_table = pxa255_turbo_freq_table; + } else { + printk("CPU PXA: Unknown policy found. " + "Using CPUFREQ_POLICY_PERFORMANCE\n"); + pxa_freqs_table = pxa255_run_freq_table; + } + ret=cpufreq_frequency_table_verify(policy, pxa_freqs_table); + + if(freq_debug) { + printk("Verified CPU policy: %dKhz min to %dKhz max\n", + policy->min, policy->max); + } + + return ret; +} + +static int pxa_set_target(struct cpufreq_policy *policy, + unsigned int target_freq, + unsigned int relation) +{ + int idx; + unsigned long cpus_allowed; + int cpu = policy->cpu; + struct cpufreq_freqs freqs; + pxa_freqs_t *pxa_freq_settings; + struct cpufreq_frequency_table *pxa_freqs_table; + unsigned long flags; + unsigned int unused; + unsigned int preset_mdrefr, postset_mdrefr; + + /* + * Save this threads cpus_allowed mask. + */ + cpus_allowed = current->cpus_allowed; + + /* + * Bind to the specified CPU. When this call returns, + * we should be running on the right CPU. + */ + set_cpus_allowed(current, 1 << cpu); + BUG_ON(cpu != smp_processor_id()); + + /* Get the current policy */ + if(policy->policy == CPUFREQ_POLICY_PERFORMANCE) { + pxa_freq_settings = pxa255_run_freqs; + pxa_freqs_table = pxa255_run_freq_table; + }else if (policy->policy == CPUFREQ_POLICY_POWERSAVE) { + pxa_freq_settings = pxa255_turbo_freqs; + pxa_freqs_table = pxa255_turbo_freq_table; + }else { + printk("CPU PXA: Unknown policy found. " + "Using CPUFREQ_POLICY_PERFORMANCE\n"); + pxa_freq_settings = pxa255_run_freqs; + pxa_freqs_table = pxa255_run_freq_table; + } + + /* Lookup the next frequency */ + if (cpufreq_frequency_table_target(policy, pxa_freqs_table, + target_freq, relation, &idx)) { + return -EINVAL; + } + + freqs.old = policy->cur; + freqs.new = pxa_freq_settings[idx].khz; + freqs.cpu = policy->cpu; + if(freq_debug) { + printk(KERN_INFO "Changing CPU frequency to %d Mhz, (SDRAM %d Mhz)\n", + freqs.new/1000, (pxa_freq_settings[idx].div2) ? + (pxa_freq_settings[idx].membus/2000) : + (pxa_freq_settings[idx].membus/1000)); + } + + void *ramstart = phys_to_virt(0xa0000000); + + /* + * Tell everyone what we're about to do... + * you should add a notify client with any platform specific + * Vcc changing capability + */ + cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE); + + /* Calculate the next MDREFR. If we're slowing down the SDRAM clock + * we need to preset the smaller DRI before the change. If we're speeding + * up we need to set the larger DRI value after the change. + */ + preset_mdrefr = postset_mdrefr = MDREFR; + if((MDREFR & MDREFR_DRI_MASK) > MDREFR_DRI(pxa_freq_settings[idx].membus)) { + preset_mdrefr = (preset_mdrefr & ~MDREFR_DRI_MASK) | + MDREFR_DRI(pxa_freq_settings[idx].membus); + } + postset_mdrefr = (postset_mdrefr & ~MDREFR_DRI_MASK) | + MDREFR_DRI(pxa_freq_settings[idx].membus); + + /* If we're dividing the memory clock by two for the SDRAM clock, this + * must be set prior to the change. Clearing the divide must be done + * after the change. + */ + if(pxa_freq_settings[idx].div2) { + preset_mdrefr |= MDREFR_DB2_MASK; + postset_mdrefr |= MDREFR_DB2_MASK; + } else { + postset_mdrefr &= ~MDREFR_DB2_MASK; + } + + local_irq_save(flags); + + /* Set new the CCCR */ + CCCR = pxa_freq_settings[idx].cccr; + + __asm__ __volatile__(" \ + ldr r4, [%1] ; /* load MDREFR */ \ + b 2f ; \ + .align 5 ; \ +1: \ + str %4, [%1] ; /* preset the MDREFR */ \ + mcr p14, 0, %2, c6, c0, 0 ; /* set CCLKCFG[FCS] */ \ + str %5, [%1] ; /* postset the MDREFR */ \ + \ + b 3f ; \ +2: b 1b ; \ +3: nop ; \ + " + : "=&r" (unused) + : "r" (&MDREFR), "r" (CCLKCFG_TURBO|CCLKCFG_FCS), "r" (ramstart), \ + "r" (preset_mdrefr), "r" (postset_mdrefr) + : "r4", "r5"); + local_irq_restore(flags); + + /* + * Restore the CPUs allowed mask. + */ + set_cpus_allowed(current, cpus_allowed); + + /* + * Tell everyone what we've just done... + * you should add a notify client with any platform specific + * SDRAM refresh timer adjustments + */ + cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE); + + return 0; +} + +static int pxa_cpufreq_init(struct cpufreq_policy *policy) +{ + unsigned long cpus_allowed; + unsigned int cpu = policy->cpu; + int i; + + cpus_allowed = current->cpus_allowed; + + set_cpus_allowed(current, 1 << cpu); + BUG_ON(cpu != smp_processor_id()); + + /* set default policy and cpuinfo */ + policy->governor = CPUFREQ_DEFAULT_GOVERNOR; + policy->policy = CPUFREQ_POLICY_PERFORMANCE; + policy->cpuinfo.max_freq = PXA25x_MAX_FREQ; + policy->cpuinfo.min_freq = PXA25x_MIN_FREQ; + policy->cpuinfo.transition_latency = 1000; /* FIXME: 1 ms, assumed */ + policy->cur = get_clk_frequency_khz(0); /* current freq */ + policy->min = policy->max = policy->cur; + + /* Generate the run cpufreq_frequency_table struct */ + for(i=0;i #include +#include #include #include +#include #include "generic.h" @@ -128,6 +130,13 @@ .resource = pxamci_resources, }; +void __init pxa_set_mci_info(struct pxamci_platform_data *info) +{ + pxamci_device.dev.platform_data = info; +} +EXPORT_SYMBOL(pxa_set_mci_info); + +#if defined(CONFIG_USB_GADGET_PXA2XX) && defined(CONFIG_PXA25x) static struct pxa2xx_udc_mach_info pxa_udc_info; @@ -163,6 +172,8 @@ } }; +#endif /* PXA2XX_UDC and PXA25x */ + static struct pxafb_mach_info pxa_fb_info; void __init set_pxa_fb_info(struct pxafb_mach_info *hard_pxa_fb_info) @@ -211,13 +222,36 @@ .id = 2, }; + +static u64 ac97_dma_mask = ~(u64)0; + + +static struct platform_device pxa_ac97_device = { + .name = "pxa-audio", + .id = 0, + .dev = { + .platform_data = NULL, + .dma_mask = &ac97_dma_mask, + .coherent_dma_mask = 0xffffffff + } +}; + +static struct platform_device pxa_i2c_device = { + .name = "pxa2xx-i2c", + .id = -1, +}; + static struct platform_device *devices[] __initdata = { + &pxa_i2c_device, &pxamci_device, +#if defined(CONFIG_USB_GADGET_PXA2XX) && defined(CONFIG_PXA25x) &udc_device, +#endif &pxafb_device, &ffuart_device, &btuart_device, &stuart_device, + &pxa_ac97_device, }; static int __init pxa_init(void) diff -NaurbB linux-2.6.9-pre1-bk18/arch/arm/mach-pxa/irq.c linux-2.6.9_nbp/arch/arm/mach-pxa/irq.c --- linux-2.6.9-pre1-bk18/arch/arm/mach-pxa/irq.c 2005-08-15 01:14:40.000000000 +0200 +++ linux-2.6.9_nbp/arch/arm/mach-pxa/irq.c 2004-09-26 15:52:17.000000000 +0200 @@ -96,23 +96,23 @@ type = __IRQT_RISEDGE | __IRQT_FALEDGE; } - printk(KERN_DEBUG "IRQ%d (GPIO%d): ", irq, gpio); +// printk(KERN_DEBUG "IRQ%d (GPIO%d): ", irq, gpio); pxa_gpio_mode(gpio | GPIO_IN); if (type & __IRQT_RISEDGE) { - printk("rising "); +// printk("rising "); __set_bit (gpio, GPIO_IRQ_rising_edge); } else __clear_bit (gpio, GPIO_IRQ_rising_edge); if (type & __IRQT_FALEDGE) { - printk("falling "); +// printk("falling "); __set_bit (gpio, GPIO_IRQ_falling_edge); } else __clear_bit (gpio, GPIO_IRQ_falling_edge); - printk("edges\n"); +// printk("edges\n"); GRER(gpio) = GPIO_IRQ_rising_edge[idx] & GPIO_IRQ_mask[idx]; GFER(gpio) = GPIO_IRQ_falling_edge[idx] & GPIO_IRQ_mask[idx]; diff -NaurbB linux-2.6.9-pre1-bk18/arch/arm/mach-pxa/Kconfig linux-2.6.9_nbp/arch/arm/mach-pxa/Kconfig --- linux-2.6.9-pre1-bk18/arch/arm/mach-pxa/Kconfig 2005-08-15 01:14:33.000000000 +0200 +++ linux-2.6.9_nbp/arch/arm/mach-pxa/Kconfig 2004-09-14 12:25:05.000000000 +0200 @@ -18,6 +18,14 @@ bool "Accelent Xscale IDP" select PXA25x +config MACH_NETBOOKPRO + bool "Psion Teklogix NetBook Pro" + select PXA25x + help + Select the Psion TekLogix NetBookPro machine build + + http://www.psionteklogix.com/ + endchoice endmenu diff -NaurbB linux-2.6.9-pre1-bk18/arch/arm/mach-pxa/Makefile linux-2.6.9_nbp/arch/arm/mach-pxa/Makefile --- linux-2.6.9-pre1-bk18/arch/arm/mach-pxa/Makefile 2004-08-14 07:36:58.000000000 +0200 +++ linux-2.6.9_nbp/arch/arm/mach-pxa/Makefile 2004-09-14 12:25:05.000000000 +0200 @@ -11,6 +11,7 @@ obj-$(CONFIG_ARCH_LUBBOCK) += lubbock.o obj-$(CONFIG_MACH_MAINSTONE) += mainstone.o obj-$(CONFIG_ARCH_PXA_IDP) += idp.o +obj-$(CONFIG_MACH_NETBOOKPRO) += netbookpro.o # Support for blinky lights led-y := leds.o @@ -22,3 +23,4 @@ # Misc features obj-$(CONFIG_PM) += pm.o sleep.o +obj-$(CONFIG_CPU_FREQ) += cpu-pxa.o diff -NaurbB linux-2.6.9-pre1-bk18/arch/arm/mach-pxa/netbookpro.c linux-2.6.9_nbp/arch/arm/mach-pxa/netbookpro.c --- linux-2.6.9-pre1-bk18/arch/arm/mach-pxa/netbookpro.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/arch/arm/mach-pxa/netbookpro.c 2004-11-21 23:46:50.000000000 +0100 @@ -0,0 +1,417 @@ +/* + * linux/arch/arm/mach-pxa/netbookpro.c + * + * Author: Ben Dooks + * Copyright (c) 2004 Simtec Electronics + * + * Based on: + * linux/arch/arm/mach-pxa/lubbock.c + * + * Author: Nicolas Pitre + * Created: Jun 15, 2001 + * Copyright: MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "generic.h" + +/*extern void printascii(const char *);*/ + +static void *sleep_save; + +int netbookpro_pm_pre_sleep(unsigned long resume_addr) +{ + /* printk(KERN_INFO "suspending to %08lx\n", resume_addr); */ + + if (sleep_save == NULL) + sleep_save = ioremap(0xA0000000, 0x1000); + + if (sleep_save == NULL) { + printk(KERN_ERR "system: cannot remap suspend save area\n"); + return -ENOMEM; + } + + writel(resume_addr, sleep_save); + writel(3, sleep_save+0xfc); + + /* printascii("netbookpro_pm_pre_sleep: issuing suspend to pcon\n");*/ + + nbp_pcon_write2(NETBOOKPRO_PCON_POWER, PCON_PWRSTATE_Suspend); + + /* printascii("(done)\n");*/ + + nbp_pwr_waitchange(); + + /* printascii("netbookpro_pm_pre_sleep: done pcon suspend\n");*/ + + PWER = 0; + PFER = 0; + PRER = 0; + PCFR = PCFR_OPDE | PCFR_FS; + + return 0; +} + +void netbookpro_pm_post_sleep(void) +{ + /*printascii("netbokkpro_pm_post_sleep:\n");*/ + + CKEN |= CKEN14_I2C; + ISAR = 0xE0 >> 1; + + ICR = I2C_ICR_INIT; + ICR |= ICR_SADIE | ICR_ALDIE | ICR_SSDIE; + ICR &= ~ICR_SCLE; + ICR |= ICR_IUE; + + /*printascii("netbookpro_pm_post_sleep: going back to run\n");*/ + +#if 0 + nbp_pcon_command(PCON_POWER_STATE, PCON_PWRSTATE_Run, + PCON_FLG_ASYNC, NULL); +#else + nbp_pcon_write2(NETBOOKPRO_PCON_POWER, PCON_PWRSTATE_Run); +#endif + + /*printascii("netbookpro_pm_post_sleep: waiting for ack\n");*/ + nbp_pwr_waitchange(); + + /*printascii("netbookpro_pm_post_sleep: resuming serial power\n");*/ + + nbp_pcon_command(I2C_SETSERIALPOWER, 1, PCON_FLG_ASYNC, NULL); + + /*printascii("netbookpro_pm_post_sleep: done\n");*/ +} + +static void __init netbookpro_init_irq(void) +{ + /*printascii("netbookpro_init_irq:\n");*/ + pxa_init_irq(); +} + +static u64 spi_dma_mask = ~(u64)0; + +static struct platform_device spi_device = { + .name = "netbookpro-spi", + .id = 0, + .dev = { + .platform_data = NULL, + .dma_mask = &spi_dma_mask, + .coherent_dma_mask = 0xffffffff + } +}; + +static struct resource fb_resources[] = { + [0] = { + .start = 0x14000000 + (1<<21), + .end = 0x14000000 + (1<<21) + (1280*1024)-1, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = 0x14000000, + .end = 0x14000fff, + .flags = IORESOURCE_MEM, + } +}; + +static struct platform_device fb_device = { + .name = "epson-s1d138xxx", + .id = 0, + .dev = { + .platform_data = NULL, + }, + .resource = fb_resources, + .num_resources = ARRAY_SIZE(fb_resources) +}; + +static struct platform_device keyb_device = { + .name = "netbookpro-keyboard", + .id = 0, + .dev = { + .platform_data = NULL, + }, +}; + +static struct resource sl811_resources[] = { + [0] = { + .start = PXA_CS4_PHYS + 0x000, + .end = PXA_CS4_PHYS + 0x100, + .flags = IORESOURCE_MEM, + }, + [1] = { + .start = PXA_CS4_PHYS + (1<<16) + 0x000, + .end = PXA_CS4_PHYS + (1<<16) + 0x100, + .flags = IORESOURCE_MEM, + }, + [2] = { + .start = IRQ_GPIO(2), + .end = IRQ_GPIO(2), + .flags = IORESOURCE_IRQ, + } +}; + +static void netbook2_port_power(struct device *dev, int is_on) +{ + nbp_pcon_command(I2C_SETUSBHOSTPOWER, is_on, PCON_FLG_ASYNC, NULL); +} + +static void netbook2_hc_reset(struct device *dev) +{ + GPCR(14) = GPIO_bit(14); + ndelay(400); /* nRST for 16+ clocks @ 48MHz */ + GPSR(14) = GPIO_bit(14); +} + +static struct sl811_platform_data netbook2_sl811 = { + // .can_wakeup = 1, + .potpg = 100, + .port_power = netbook2_port_power, + .reset = netbook2_hc_reset, +}; + +static struct platform_device sl811_device = { + .name = "sl811-hcd", + .id = -1, + .dev = { + .platform_data = &netbook2_sl811, + }, + .resource = sl811_resources, + .num_resources = ARRAY_SIZE(sl811_resources) +}; + +static struct platform_device nand_device = { + .name = "netbookpro-nand", + .id = -1, + .num_resources = 0, + .resource = NULL +}; + + +static struct platform_device *devices[] __initdata = { + &spi_device, + &fb_device, + &keyb_device, + &sl811_device, + &nand_device, +}; + +static LIST_HEAD(pcon_devs); +static LIST_HEAD(pcon_drvs); +static DECLARE_MUTEX(pcon_sem); + +struct pcon_device *pcon_dev_add(const char *name, struct device *dev, void *data) +{ + struct pcon_device *pcon; + + pcon = kmalloc(sizeof(struct pcon_device), GFP_KERNEL); + if (pcon) { + struct pcon_interface *drv; + + memset(pcon, 0, sizeof(struct pcon_device)); + pcon->dev = dev; + pcon->data = data; + + down(&pcon_sem); + list_add(&pcon->node, &pcon_devs); + list_for_each_entry(drv, &pcon_drvs, node) { + drv->add(pcon); + } + up(&pcon_sem); + } else { + pcon = ERR_PTR(-ENOMEM); + } + return pcon; +} +EXPORT_SYMBOL(pcon_dev_add); + +void pcon_dev_remove(struct pcon_device *pcon) +{ + struct pcon_interface *drv; + + down(&pcon_sem); + list_del(&pcon->node); + list_for_each_entry(drv, &pcon_drvs, node) { + drv->remove(pcon); + } + up(&pcon_sem); + kfree(pcon); +} +EXPORT_SYMBOL(pcon_dev_remove); + +int pcon_dev_suspend(struct pcon_device *pcon, int state) +{ + struct pcon_interface *drv; + int ret = 0; + + down(&pcon_sem); + list_for_each_entry(drv, &pcon_drvs, node) { + if (drv->suspend) { + ret = drv->suspend(pcon, state); + if (ret) + break; + } + } + up(&pcon_sem); + return ret; +} +EXPORT_SYMBOL(pcon_dev_suspend); + +int pcon_dev_resume(struct pcon_device *pcon) +{ + struct pcon_interface *drv; + int ret = 0; + + down(&pcon_sem); + list_for_each_entry(drv, &pcon_drvs, node) { + if (drv->resume) { + ret = drv->resume(pcon); + if (ret) + break; + } + } + up(&pcon_sem); + return ret; +} +EXPORT_SYMBOL(pcon_dev_resume); + +int pcon_drv_add(struct pcon_interface *drv) +{ + struct pcon_device *pcon; + + down(&pcon_sem); + list_add(&drv->node, &pcon_drvs); + list_for_each_entry(pcon, &pcon_devs, node) { + drv->add(pcon); + } + up(&pcon_sem); + return 0; +} +EXPORT_SYMBOL(pcon_drv_add); + +void pcon_drv_remove(struct pcon_interface *drv) +{ + struct pcon_device *pcon; + + down(&pcon_sem); + list_del(&drv->node); + list_for_each_entry(pcon, &pcon_devs, node) { + drv->remove(pcon); + } + up(&pcon_sem); +} +EXPORT_SYMBOL(pcon_drv_remove); + + +static irqreturn_t (*nbp_irq)(int, void *, struct pt_regs *); + +static void nbp_mci_timeout(unsigned long data) +{ + nbp_irq(IRQ_GPIO(9), (void *)data, NULL); +} + +static struct timer_list nbp_irq_timer = TIMER_INITIALIZER(nbp_mci_timeout, 0, 0); + +static irqreturn_t nbp_mci_irq(int irq, void *data, struct pt_regs *regs) +{ + mod_timer(&nbp_irq_timer, jiffies + 5 * HZ/100); + return IRQ_HANDLED; +} + +static int nbp_mci_init(struct device *dev, irqreturn_t (*fn)(int, void *, struct pt_regs *), void *data) +{ + int ret; + + nbp_irq = fn; + nbp_irq_timer.data = (unsigned long)data; + + ret = request_irq(IRQ_GPIO(9), nbp_mci_irq, 0, "pxamci", data); + if (ret == 0) + set_irq_type(IRQ_GPIO(9), IRQT_BOTHEDGE); + return ret; +} + +static void nbp_mci_setpower(struct device *dev, unsigned int vdd_bits) +{ + nbp_pcon_command(I2C_SETMMCPOWER, vdd_bits ? 1 : 0, 0, NULL); +} + +static void nbp_mci_exit(struct device *dev, void *data) +{ + free_irq(IRQ_GPIO(9), data); +} + +static struct pxamci_platform_data nbp_mci_info = { + .ocr_mask = MMC_VDD_32_33 | MMC_VDD_33_34, + .init = nbp_mci_init, + .setpower = nbp_mci_setpower, + .exit = nbp_mci_exit, +}; + +static void __init netbookpro_init(void) +{ + /*printascii("netbookpro_init:\n");*/ + + nbp_pcon_command(I2C_SETSERIALPOWER, 1, PCON_FLG_ASYNC, NULL); + platform_add_devices(devices, ARRAY_SIZE(devices)); + + pxa_set_mci_info(&nbp_mci_info); +} + +static struct map_desc netbookpro_io_desc[] __initdata = { +}; + +#define show_reg(x) printk(KERN_INFO "%s = %08x\n", #x, x) + +static void __init netbookpro_map_io(void) +{ + pxa_map_io(); + iotable_init(netbookpro_io_desc, ARRAY_SIZE(netbookpro_io_desc)); + + show_reg(MDCNFG); + show_reg(MDREFR); + show_reg(SXCNFG); + + /*printascii("sending command to setserialpower on\n");*/ +} + +MACHINE_START(NETBOOKPRO, "Psion Teklogix NetBookPro") + MAINTAINER("Ben Dooks, Vincent Sanders. Simtec Electronics ") + BOOT_MEM(0xa0000000, 0x40000000, io_p2v(0x40000000)) + BOOT_PARAMS(0xA0400100) + MAPIO(netbookpro_map_io) + INITIRQ(netbookpro_init_irq) + INITTIME(pxa_init_time) + INIT_MACHINE(netbookpro_init) +MACHINE_END diff -NaurbB linux-2.6.9-pre1-bk18/arch/arm/mach-pxa/pm.c linux-2.6.9_nbp/arch/arm/mach-pxa/pm.c --- linux-2.6.9-pre1-bk18/arch/arm/mach-pxa/pm.c 2004-08-14 07:37:15.000000000 +0200 +++ linux-2.6.9_nbp/arch/arm/mach-pxa/pm.c 2004-09-25 21:51:09.000000000 +0200 @@ -15,11 +15,13 @@ #include #include #include +#include #include #include #include #include +#include /* @@ -45,7 +47,7 @@ */ enum { SLEEP_SAVE_START = 0, - SLEEP_SAVE_OSCR, SLEEP_SAVE_OIER, + SLEEP_SAVE_OIER, SLEEP_SAVE_OSMR0, SLEEP_SAVE_OSMR1, SLEEP_SAVE_OSMR2, SLEEP_SAVE_OSMR3, SLEEP_SAVE_GPLR0, SLEEP_SAVE_GPLR1, SLEEP_SAVE_GPLR2, @@ -63,22 +65,24 @@ SLEEP_SAVE_SIZE }; +#ifdef CONFIG_MACH_NETBOOKPRO +#include +#endif static int pxa_pm_enter(u32 state) { unsigned long sleep_save[SLEEP_SAVE_SIZE]; unsigned long checksum = 0; - unsigned long delta; + long delta; int i; if (state != PM_SUSPEND_MEM) return -EINVAL; /* preserve current time */ - delta = xtime.tv_sec - RCNR; + delta = save_time_delta(RCNR); /* save vital registers */ - SAVE(OSCR); SAVE(OSMR0); SAVE(OSMR1); SAVE(OSMR2); @@ -93,11 +97,23 @@ SAVE(GAFR1_L); SAVE(GAFR1_U); SAVE(GAFR2_L); SAVE(GAFR2_U); +#ifdef CONFIG_MACH_NETBOOKPRO + if (netbookpro_pm_pre_sleep(virt_to_phys(pxa_cpu_resume)) != 0) { + printk(KERN_ERR "sleep code cannot allow sleep now\n"); + return -EINVAL; + } +#endif + SAVE(ICMR); ICMR = 0; SAVE(CKEN); + +#ifdef CONFIG_DEBUG_LL + CKEN = CKEN6_FFUART; +#else CKEN = 0; +#endif /* Note: wake up source are set up in each machine specific files */ @@ -108,7 +124,9 @@ RCSR = RCSR_SMR; /* set resume return address */ +#ifndef CONFIG_MACH_NETBOOKPRO PSPR = virt_to_phys(pxa_cpu_resume); +#endif /* before sleeping, calculate and save a checksum */ for (i = 0; i < SLEEP_SAVE_SIZE - 1; i++) @@ -118,6 +136,13 @@ /* *** go zzz *** */ pxa_cpu_suspend(); +#ifdef CONFIG_DEBUG_LL + CKEN |= CKEN6_FFUART; + udelay(10); + printascii("Wakeup and smell the coffee\n"); +#endif + + /* after sleeping, validate the checksum */ checksum = 0; for (i = 0; i < SLEEP_SAVE_SIZE - 1; i++) @@ -149,22 +174,31 @@ RESTORE(OSMR1); RESTORE(OSMR2); RESTORE(OSMR3); - RESTORE(OSCR); RESTORE(OIER); + /* OSMR0 is the system timer: make sure OSCR is sufficiently behind */ + OSCR = OSMR0 - LATCH; + RESTORE(CKEN); ICLR = 0; ICCR = 1; RESTORE(ICMR); +#ifdef CONFIG_MACH_NETBOOKPRO + netbookpro_pm_post_sleep(); +#endif + /* restore current time */ - xtime.tv_sec = RCNR + delta; + restore_time_delta(RCNR, delta); #ifdef DEBUG printk(KERN_DEBUG "*** made it back from resume\n"); #endif + /* re-read the clock info */ + get_clk_frequency_khz(1); + return 0; } diff -NaurbB linux-2.6.9-pre1-bk18/arch/arm/mach-pxa/sleep.S linux-2.6.9_nbp/arch/arm/mach-pxa/sleep.S --- linux-2.6.9-pre1-bk18/arch/arm/mach-pxa/sleep.S 2004-08-14 07:37:40.000000000 +0200 +++ linux-2.6.9_nbp/arch/arm/mach-pxa/sleep.S 2004-10-15 12:36:52.000000000 +0200 @@ -64,6 +64,37 @@ @ prepare pointer to physical address 0 (virtual mapping in generic.c) mov r2, #UNCACHED_PHYS_0 + @ Intel PXA255 Specification Update notes problems + @ about suspending with PXBus operating above 133MHz + @ (see Errata 31, GPIO output signals, ... unpredictable in sleep + @ + @ We keep the change-down close to the actual suspend on SDRAM + @ as possible to eliminate messing about with the refresh clock + @ as the system will restore with the original speed settings + @ + @ Ben Dooks, 13-Sep-2004 + + ldr r6, =CCCR + ldr r8, [r6] @ keep original value for resume + + @ ensure x1 for run and turbo mode with memory clock + bic r7, r8, #CCCR_M_MASK | CCCR_N_MASK + orr r7, r7, #(1<<5) | (2<<7) + + @ check that the memory frequency is within limits + and r14, r7, #CCCR_L_MASK + teq r14, #1 + bicne r7, r7, #CCCR_L_MASK + orrne r7, r7, #1 @@ 99.53MHz + + @ get ready for the change + @ note, since we are making turbo=run, do not remove the turbo + @ as this may cause non-turbo mode on resume + mrc p14, 0, r0, c6, c0, 0 + bic r0, r0, #2 @ clear change bit + mcr p14, 0, r0, c6, c0, 0 + orr r0, r0, #2 @ initiate change bit + @ align execution to a cache line b 1f @@ -74,6 +105,13 @@ @ All needed values are now in registers. @ These last instructions should be in cache + @ initate the frequency change... + str r7, [r6] + mcr p14, 0, r0, c6, c0, 0 + + @ restore the original cpu speed value for resume + str r8, [r6] + @ put SDRAM into self-refresh str r5, [r4] @@ -83,8 +121,7 @@ @ enter sleep mode mcr p14, 0, r1, c7, c0, 0 -20: nop - b 20b @ loop waiting for sleep +20: b 20b @ loop waiting for sleep /* * cpu_pxa_resume() diff -NaurbB linux-2.6.9-pre1-bk18/arch/arm/mach-pxa/time.c linux-2.6.9_nbp/arch/arm/mach-pxa/time.c --- linux-2.6.9-pre1-bk18/arch/arm/mach-pxa/time.c 2004-08-14 07:36:57.000000000 +0200 +++ linux-2.6.9_nbp/arch/arm/mach-pxa/time.c 2004-10-22 17:54:06.000000000 +0200 @@ -74,6 +74,8 @@ { int next_match; + write_seqlock(&xtime_lock); + /* Loop until we get ahead of the free running timer. * This ensures an exact clock tick count and time accuracy. * IRQs are disabled inside the loop to ensure coherence between @@ -95,6 +97,8 @@ next_match = (OSMR0 += LATCH); } while( (signed long)(next_match - OSCR) <= 8 ); + write_sequnlock(&xtime_lock); + return IRQ_HANDLED; } diff -NaurbB linux-2.6.9-pre1-bk18/arch/arm/mach-sa1100/pm.c linux-2.6.9_nbp/arch/arm/mach-sa1100/pm.c --- linux-2.6.9-pre1-bk18/arch/arm/mach-sa1100/pm.c 2004-08-14 07:36:32.000000000 +0200 +++ linux-2.6.9_nbp/arch/arm/mach-sa1100/pm.c 2004-09-25 21:51:09.000000000 +0200 @@ -30,6 +30,7 @@ #include #include #include +#include extern void sa1100_cpu_suspend(void); extern void sa1100_cpu_resume(void); @@ -44,7 +45,7 @@ */ enum { SLEEP_SAVE_SP = 0, - SLEEP_SAVE_OSCR, SLEEP_SAVE_OIER, + SLEEP_SAVE_OIER, SLEEP_SAVE_OSMR0, SLEEP_SAVE_OSMR1, SLEEP_SAVE_OSMR2, SLEEP_SAVE_OSMR3, SLEEP_SAVE_GPDR, SLEEP_SAVE_GAFR, @@ -58,18 +59,17 @@ static int sa11x0_pm_enter(u32 state) { - unsigned long sleep_save[SLEEP_SAVE_SIZE]; - unsigned long delta, gpio; + unsigned long gpio, sleep_save[SLEEP_SAVE_SIZE]; + long delta; if (state != PM_SUSPEND_MEM) return -EINVAL; /* preserve current time */ - delta = xtime.tv_sec - RCNR; + delta = save_time_delta(RCNR); gpio = GPLR; /* save vital registers */ - SAVE(OSCR); SAVE(OSMR0); SAVE(OSMR1); SAVE(OSMR2); @@ -131,11 +131,13 @@ RESTORE(OSMR1); RESTORE(OSMR2); RESTORE(OSMR3); - RESTORE(OSCR); RESTORE(OIER); + /* OSMR0 is the system timer: make sure OSCR is sufficiently behind */ + OSCR = OSMR0 - LATCH; + /* restore current time */ - xtime.tv_sec = RCNR + delta; + restore_time_delta(RCNR, delta); return 0; } diff -NaurbB linux-2.6.9-pre1-bk18/arch/arm/Makefile linux-2.6.9_nbp/arch/arm/Makefile --- linux-2.6.9-pre1-bk18/arch/arm/Makefile 2005-08-15 01:14:40.000000000 +0200 +++ linux-2.6.9_nbp/arch/arm/Makefile 2004-09-14 12:25:05.000000000 +0200 @@ -95,6 +95,7 @@ machine-$(CONFIG_ARCH_LH7A40X) := lh7a40x machine-$(CONFIG_ARCH_VERSATILE_PB) := versatile machine-$(CONFIG_ARCH_IMX) := imx +textaddr-$(CONFIG_MACH_NETBOOKPRO) := 0xc0408000 ifeq ($(CONFIG_ARCH_EBSA110),y) # This is what happens if you forget the IOCS16 line. diff -NaurbB linux-2.6.9-pre1-bk18/arch/arm/mm/init.c linux-2.6.9_nbp/arch/arm/mm/init.c --- linux-2.6.9-pre1-bk18/arch/arm/mm/init.c 2005-08-15 01:14:40.000000000 +0200 +++ linux-2.6.9_nbp/arch/arm/mm/init.c 2004-09-14 12:25:05.000000000 +0200 @@ -315,6 +315,13 @@ if (machine_is_p720t()) res_size = 0x00014000; + /* pre-reserve the area before the kernel for the bootloader + * on the netbook pro TODO: is 4Mb the correct amount of data + * to reserve */ + if (machine_is_netbookpro()) + res_size=0x00040000; + + #ifdef CONFIG_SA1111 /* * Because of the SA1111 DMA bug, we want to preserve our diff -NaurbB linux-2.6.9-pre1-bk18/Documentation/cpu-freq/user-guide.txt linux-2.6.9_nbp/Documentation/cpu-freq/user-guide.txt --- linux-2.6.9-pre1-bk18/Documentation/cpu-freq/user-guide.txt 2004-08-14 07:36:16.000000000 +0200 +++ linux-2.6.9_nbp/Documentation/cpu-freq/user-guide.txt 2004-09-13 16:06:47.000000000 +0200 @@ -18,7 +18,7 @@ Contents: --------- 1. Supported Architectures and Processors -1.1 ARM +1.1 ARM, PXA 1.2 x86 1.3 sparc64 1.4 ppc @@ -37,14 +37,15 @@ 1. Supported Architectures and Processors ========================================= -1.1 ARM -------- +1.1 ARM, PXA +------------ The following ARM processors are supported by cpufreq: ARM Integrator ARM-SA1100 ARM-SA1110 +Intel PXA 1.2 x86 diff -NaurbB linux-2.6.9-pre1-bk18/drivers/i2c/busses/i2c-pxa.c linux-2.6.9_nbp/drivers/i2c/busses/i2c-pxa.c --- linux-2.6.9-pre1-bk18/drivers/i2c/busses/i2c-pxa.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/drivers/i2c/busses/i2c-pxa.c 2004-10-25 23:16:30.000000000 +0200 @@ -0,0 +1,973 @@ +/* + * i2c_adap_pxa.c + * + * I2C adapter for the PXA I2C bus access. + * + * Copyright (C) 2002 Intrinsyc Software Inc. + * Copyright (C) 2004 Deep Blue Solutions Ltd. + * + * 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. + * + * History: + * Apr 2002: Initial version [CS] + * Jun 2002: Properly seperated algo/adap [FB] + * Jan 2003: Fixed several bugs concerning interrupt handling [Kai-Uwe Bloem] + * Jan 2003: added limited signal handling [Kai-Uwe Bloem] + * Sep 2004: Major rework to ensure efficient bus handling [RMK] + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#if 0 +#define DBGLVL KERN_INFO +#else +#define DBGLVL KERN_DEBUG +#endif + +/* + * Set this to zero to remove all debug statements via dead code elimination. + */ +#define DEBUG 0 + +//#define DEBUG 15 + +#if DEBUG +static unsigned int i2c_debug = DEBUG; +#else +#define i2c_debug 0 +#endif + +#undef VERBOSE + +#if defined(DEBUG) || defined(VERBOSE) +static void i2c_pxa_show_state(int lno, const char *fname) +{ +// printk(DBGLVL "state:%s:%d: ISR=%08x, ICR=%08x, IBMR=%02x\n", fname, lno, ISR, ICR, IBMR); +} +#endif + +#ifdef DEBUG +#define show_state() i2c_pxa_show_state(__LINE__, __FUNCTION__) +#else +#define show_state() do { } while(0) +#endif + +#if defined(VERBOSE) +#define show_verbose() i2c_pxa_show_state(__LINE__, __FUNCTION__) +#else +#define show_verbose() do { } while(0) +#endif + +#define eedbg(lvl, x...) do { if ((lvl) < 1) { printk(DBGLVL "" x); } } while(0) + +#define do_bit(val,bit,set,unset) do { printk("%s", ((val) & (bit)) ? set : unset); } while(0) + +static void decode_ISR(unsigned int val) +{ + printk(DBGLVL "ISR %08x: ", val); + do_bit(val, ISR_RWM, "RX ", "TX "); + do_bit(val, ISR_ACKNAK, "NAK ", "ACK "); + do_bit(val, ISR_UB, "Bsy ", "Rdy "); + do_bit(val, ISR_IBB, "BusBsy ", "BusRdy "); + do_bit(val, ISR_SSD, "SlaveStop ", ""); + do_bit(val, ISR_ALD, "ALD ", ""); + do_bit(val, ISR_ITE, "TxEmpty ", ""); + do_bit(val, ISR_IRF, "RxFull ", ""); + do_bit(val, ISR_GCAD, "GenCall ", ""); + do_bit(val, ISR_SAD, "SlaveAddr ", ""); + do_bit(val, ISR_BED, "BusErr ", ""); + printk(DBGLVL "\n"); +} + +static void decode_ICR(unsigned int val) +{ + printk(DBGLVL "ICR %08x: ", val); + + do_bit(val, ICR_START, "START ", ""); + do_bit(val, ICR_STOP, "STOP ", ""); + do_bit(val, ICR_ACKNAK, "ACKNAK ", ""); + do_bit(val, ICR_TB, "TB ", ""); + do_bit(val, ICR_MA, "MA ", ""); + do_bit(val, ICR_SCLE, "SCLE ", "scle "); + do_bit(val, ICR_IUE, "IUE ", "iue "); + do_bit(val, ICR_GCD, "GCD ", ""); + do_bit(val, ICR_ITEIE, "ITEIE ", ""); + do_bit(val, ICR_IRFIE, "IRFIE ", ""); + do_bit(val, ICR_BEIE, "BEIE ", ""); + do_bit(val, ICR_SSDIE, "SSDIE ", ""); + do_bit(val, ICR_ALDIE, "ALDIE ", ""); + do_bit(val, ICR_SADIE, "SADIE ", ""); + do_bit(val, ICR_UR, "UR", "ur"); + printk("\n"); +} + +/* + * New stuff... + */ +struct pxa_i2c { + spinlock_t lock; + wait_queue_head_t wait; + struct i2c_msg *msg; + unsigned int msg_num; + unsigned int msg_idx; + unsigned int msg_ptr; + + struct i2c_adapter adap; + + int irqlogidx; + u32 isrlog[32]; + u32 icrlog[32]; +}; + +static inline int i2c_pxa_is_slavemode(void) +{ + return !(ICR & ICR_SCLE); +} + + +static void i2c_pxa_stop(void) +{ + unsigned long icr = ICR; + + show_state(); + + icr |= ICR_STOP; + icr &= ~(ICR_START); + ICR = icr; + + show_state(); +} + +static void i2c_pxa_midbyte(void) +{ + unsigned long icr = ICR; + icr &= ~(ICR_START | ICR_STOP); + ICR = icr; +} + +static void i2c_pxa_transfer(int lastbyte, int receive, int midbyte) +{ + if (i2c_pxa_is_slavemode()) { + printk(DBGLVL "i2c_pxa_transfer: called in slave mode\n"); + return; + } + + if (lastbyte) + { + if (receive==I2C_RECEIVE) + ICR |= ICR_ACKNAK; + i2c_pxa_stop(); + } + else if (midbyte) + { + i2c_pxa_midbyte(); + } + + ICR |= ICR_TB; + show_state(); +} + +static void i2c_pxa_abort(void) +{ + unsigned long timeout = jiffies + HZ/4; + +#ifdef PXA_ABORT_MA + while ((long)(timeout - jiffies) > 0 && (ICR & ICR_TB)) { + msleep(1); + } + + ICR |= ICR_MA; + udelay(100); +#else + while ((long)(timeout - jiffies) > 0 && (IBMR & 0x1) == 0) { + i2c_pxa_transfer(1, I2C_RECEIVE, 1); + msleep(1); + } +#endif + ICR &= ~(ICR_MA | ICR_START | ICR_STOP); +} + +static int i2c_pxa_wait_bus_not_busy(void) +{ + int timeout = DEF_TIMEOUT; + + while (timeout-- && (ISR & (ISR_IBB | ISR_UB))) { + if ((ISR & ISR_SAD) != 0) { + timeout += 4; + } + msleep(2); + show_state(); + } + + if (timeout <= 0) { + show_verbose(); + show_state(); + } + + return (timeout<=0); +} + +static int i2c_pxa_wait_master(void) +{ + unsigned long timeout = jiffies + HZ*4; + + while ((long)(timeout - jiffies) > 0) { + if (i2c_debug > 1) + printk(DBGLVL "i2c_pxa_wait_master: %ld: ISR=%08x, ICR=%08x, IBMR=%02x\n", + (long)jiffies, ISR, ICR, IBMR); + + if (ISR & ISR_SAD) { + if (i2c_debug > 0) + printk(DBGLVL "i2c_pxa_wait_master: Slave detected\n"); + return 0; + } + + /* wait for unit and bus being not busy, and we also do a + * quick check of the i2c lines themselves to ensure they've + * gone high... + */ + if ((ISR & (ISR_UB | ISR_IBB)) == 0 && + (IBMR == 3)) { + if (i2c_debug > 0) + printk(DBGLVL "i2c_pxa_wait_master: done\n"); + return 1; + } + + msleep(1); + } + + if (i2c_debug > 0) + printk(DBGLVL "i2c_pxa_wait_master: did not free\n"); + return 0; +} + +static int i2c_pxa_set_master(void) +{ + if (i2c_debug) + printk(DBGLVL "I2C: setting to bus master\n"); + + if ((ISR & (ISR_UB | ISR_IBB)) != 0) { + printk(DBGLVL "set_master: unit is busy\n"); + if (!i2c_pxa_wait_master()) { + show_verbose(); + printk(DBGLVL "set_master: error: unit busy\n"); + return -EBUSY; + } + } + + ICR |= ICR_SCLE; + return 0; +} + +static int i2c_pxa_wait_slave(void) +{ + unsigned long timeout = jiffies + HZ*1; + + /* wait for stop */ + + show_state(); + + while ((long)(timeout - jiffies) > 0) { + if (i2c_debug > 1) + printk(DBGLVL "i2c_pxa_wait_slave: %ld: ISR=%08x, ICR=%08x, IBMR=%02x\n", + (long)jiffies, ISR, ICR, IBMR); + + if ((ISR & (ISR_UB|ISR_IBB)) == 0 || + (ISR & ISR_SAD) != 0 || + (ICR & ICR_SCLE) == 0) { + if (i2c_debug > 1) + printk(DBGLVL "i2c_pxa_wait_slave: done\n"); + return 1; + } + + msleep(1); + } + + if (i2c_debug > 0) + printk(DBGLVL "i2c_pxa_wait_slave: did not free\n"); + return 0; +} + +static int i2c_pxa_set_slave(int errcode) +{ + /* clear the hold on the bus, and take of anything else + * that has been configured */ + + show_state(); + + if (errcode == SLAVE_STARTED) { + // just fall through + + } else if (errcode < 0) { + udelay(100); /* simple delay */ + } else { + /* we need to wait for the stop condition to end */ + + /* if we where in stop, then clear... */ + if (ICR & ICR_STOP) { + udelay(100); + ICR &= ~ICR_STOP; + } + + if (!i2c_pxa_wait_slave()) { + show_verbose(); + printk(KERN_ERR "i2c_pxa_set_slave: wait timedout\n"); + return -EBUSY; + } + } + + ICR &= ~(ICR_STOP|ICR_ACKNAK|ICR_MA); + ICR &= ~ICR_SCLE; + + if (i2c_debug) { + printk(DBGLVL "ICR now %08x, ISR %08x\n", ICR, ISR); + decode_ICR(ICR); + } + + return 0; +} + +static void i2c_pxa_reset(void) +{ + unsigned int slave; + +#ifdef DEBUG + printk("Resetting I2C Controller Unit\n"); +#endif + + /* abort any transfer currently under way */ + i2c_pxa_abort(); + + /* reset according to 9.8 */ + ICR = ICR_UR; + ISR = I2C_ISR_INIT; + ICR &= ~ICR_UR; + + /* set the global I2C clock on */ + pxa_set_cken(CKEN14_I2C, 1); + + /* set our slave address */ + if (machine_is_netbookpro()) { + slave = 0xE0 >> 1; + } else { + slave = I2C_PXA_SLAVE_ADDR; + } + + printk(KERN_INFO "I2C: Slave address %d\n", slave); + ISAR = slave; + + /* set control register values */ + ICR = I2C_ICR_INIT; + +#ifdef CONFIG_I2C_PXA_SLAVE + printk(KERN_INFO "I2C: Enabling slave mode\n"); + ICR |= ICR_SADIE | ICR_ALDIE | ICR_SSDIE; + + i2c_pxa_set_slave(0); +#endif + + /* enable unit */ + ICR |= ICR_IUE; + udelay(100); +} + + +/* + * I2C EEPROM emulation. + */ +static struct i2c_eeprom_emu eeprom = { + .size = I2C_EEPROM_EMU_SIZE, +}; + +struct i2c_eeprom_emu *i2c_pxa_get_eeprom(void) +{ + return &eeprom; +} + +int +i2c_eeprom_emu_addwatcher(struct i2c_eeprom_emu *emu, + int addr, int size, + struct i2c_eeprom_emu_watcher *watcher) +{ + struct i2c_eeprom_emu_byte *ptr; + unsigned long flags; + + local_irq_save(flags); + + ptr = &emu->bytes[addr]; + + for (; size > 0; addr++, ptr++, size--) { + if (ptr->watcher != NULL) { + printk(KERN_INFO "i2c_eeprom: replacing eeprom watch on %02x\n", + addr); + } + + printk(DBGLVL "i2c_eeprom: adding watcher %p to 0x%02x\n", watcher, addr); + + ptr->watcher = watcher; + } + + local_irq_restore(flags); + + return 0; +} + +static int +i2c_eeprom_emu_event(void *pw, i2c_slave_event_t event, int val) +{ + eedbg(3, "i2c_eeprom_emu_event: %d, %d\n", event, val); + + switch (event) { + case I2C_SLAVE_EVENT_START: + if (val == I2C_SLAVE_START_WRITE) { + eeprom.seen_start = 1; + eedbg(2, "i2c_eeprom: write initiated\n"); + } else { + eeprom.seen_start = 0; + eedbg(2, "i2c_eeprom: read initiated\n"); + } + + break; + + case I2C_SLAVE_EVENT_STOP: + eeprom.seen_start = 0; + eedbg(2, "i2c_eeprom: received stop\n"); + break; + + case I2C_SLAVE_EVENT_NONE: + break; + + default: + eedbg(0, "i2c_eeprom: unhandled event\n"); + return -EINVAL; + } + + return 0; +} + +static int i2c_eeprom_emu_read(void *pw) +{ + int ret; + + ret = eeprom.bytes[eeprom.ptr].val; + eeprom.ptr = (eeprom.ptr + 1 ) % eeprom.size; + + return ret; +} + +static int i2c_eeprom_emu_write(void *pw, int val) +{ + struct i2c_eeprom_emu_byte *byte; + + eedbg(1, "i2c_eeprom_emu_write: ptr=0x%02x, val=0x%02x\n", + eeprom.ptr, val); + + if (eeprom.seen_start != 0) { + eedbg(2, "i2c_eeprom_emu_write: setting ptr %02x\n", val); + eeprom.ptr = val; + eeprom.seen_start = 0; + return 0; + } + + byte = &eeprom.bytes[eeprom.ptr]; + + byte->val = val; + byte->last_modified = jiffies; + + eedbg(1, "i2c_eeprom_emu_write: ptr=0x%02x, val=0x%02x\n", + eeprom.ptr, val); + + if (byte->watcher != NULL) { + if (byte->watcher->write != NULL) + (byte->watcher->write)(&eeprom, eeprom.ptr, val); + } + + eeprom.ptr = (eeprom.ptr + 1 ) % eeprom.size; + return 0; +} + +/* temporary client for testing purposes */ +struct i2c_slave_client tmp_client = { + .event = i2c_eeprom_emu_event, + .read = i2c_eeprom_emu_read, + .write = i2c_eeprom_emu_write +}; + +struct i2c_slave_client *pxa_slave = &tmp_client; + +/* + * PXA I2C adapter handler + */ +static void i2c_pxa_scream_blue_murder(struct pxa_i2c *i2c, const char *why) +{ + int i; + printk("i2c: error: %s\n", why); + printk("i2c: msg_num: %d msg_idx: %d msg_ptr: %d\n", + i2c->msg_num, i2c->msg_idx, i2c->msg_ptr); + printk("i2c: ICR: %08x ISR: %08x\ni2c: log: ", ICR, ISR); + for (i = 0; i < i2c->irqlogidx; i++) + printk("[%08x:%08x] ", i2c->isrlog[i], i2c->icrlog[i]); + printk("\n"); +} + +static inline unsigned int i2c_pxa_addr_byte(struct i2c_msg *msg) +{ + unsigned int addr = (msg->addr & 0x7f) << 1; + + if (msg->flags & I2C_M_RD) + addr |= 1; + + return addr; +} + +static inline void i2c_pxa_start_message(struct pxa_i2c *i2c) +{ + u32 icr; + + /* + * Step 1: target slave address into IDBR + */ + IDBR = i2c_pxa_addr_byte(i2c->msg); + + /* + * Step 2: initiate the write. + */ + icr = ICR & ~(ICR_STOP | ICR_ALDIE); + ICR = icr | ICR_START | ICR_TB; +} + +/* + * We are protected by the adapter bus semaphore. + */ +static int i2c_pxa_do_xfer(struct pxa_i2c *i2c, struct i2c_msg *msg, int num) +{ + long timeout; + int ret; + + /* + * Wait for the bus to become free. + */ + ret = i2c_pxa_wait_bus_not_busy(); + if (ret) { + printk(KERN_INFO "i2c_pxa: timeout waiting for bus free\n"); + ret = I2C_RETRY; + goto out; + } + + /* + * Set master mode. + */ + ret = i2c_pxa_set_master(); + if (ret) { + printk(KERN_INFO "i2c_pxa_set_master: error %d\n", ret); + ret = I2C_RETRY; + goto out; + } + + spin_lock_irq(&i2c->lock); + + i2c->msg = msg; + i2c->msg_num = num; + i2c->msg_idx = 0; + i2c->msg_ptr = 0; + i2c->irqlogidx = 0; + + i2c_pxa_start_message(i2c); + + spin_unlock_irq(&i2c->lock); + + /* + * The rest of the processing occurs in the interrupt handler. + */ + timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5); + + /* + * We place the return code in i2c->msg_idx. + */ + ret = i2c->msg_idx; + + if (timeout == 0) + i2c_pxa_scream_blue_murder(i2c, "timeout"); + + out: + return ret; +} + +/* + * i2c_pxa_master_complete - complete the message and wake up. + */ +static inline void i2c_pxa_master_complete(struct pxa_i2c *i2c, int ret) +{ + i2c->msg_ptr = 0; + i2c->msg = NULL; + i2c->msg_idx ++; + i2c->msg_num = 0; + if (ret) + i2c->msg_idx = ret; + wake_up(&i2c->wait); +} + +static void i2c_pxa_irq_txempty(struct pxa_i2c *i2c, u32 isr) +{ + u32 icr = ICR & ~(ICR_START|ICR_STOP|ICR_ACKNAK|ICR_TB); + + again: + /* + * If ISR_ALD is set, we lost arbitration. + */ + if (isr & ISR_ALD) { + /* + * Do we need to do anything here? The PXA docs + * are vague about what happens. + */ + i2c_pxa_scream_blue_murder(i2c, "ALD set"); + + /* + * We ignore this error. We seem to see spurious ALDs + * for seemingly no reason. If handle them as I think + * they should, we end up causing an I2C error, which + * is painful for some systems. + */ + return; /* ignore */ + } + + if (isr & ISR_BED) { + int ret = BUS_ERROR; + + /* + * I2C bus error - either the device NAK'd us, or + * something more serious happened. If we were NAK'd + * on the initial address phase, we can retry. + */ + if (isr & ISR_ACKNAK) { + if (i2c->msg_ptr == 0 && i2c->msg_idx == 0) + ret = I2C_RETRY; + else + ret = XFER_NAKED; + } + i2c_pxa_master_complete(i2c, ret); + } else if (isr & ISR_RWM) { + /* + * Read mode. We have just sent the address byte, and + * now we must initiate the transfer. + */ + if (i2c->msg_ptr == i2c->msg->len - 1 && + i2c->msg_idx == i2c->msg_num - 1) + icr |= ICR_STOP | ICR_ACKNAK; + + icr |= ICR_ALDIE | ICR_TB; + } else if (i2c->msg_ptr < i2c->msg->len) { + /* + * Write mode. Write the next data byte. + */ + IDBR = i2c->msg->buf[i2c->msg_ptr++]; + + icr |= ICR_ALDIE | ICR_TB; + + /* + * If this is the last byte of the last message, send + * a STOP. + */ + if (i2c->msg_ptr == i2c->msg->len && + i2c->msg_idx == i2c->msg_num - 1) + icr |= ICR_STOP; + } else if (i2c->msg_idx < i2c->msg_num - 1) { + /* + * Next segment of the message. + */ + i2c->msg_ptr = 0; + i2c->msg_idx ++; + i2c->msg++; + + /* + * If we aren't doing a repeated start and address, + * go back and try to send the next byte. Note that + * we do not support switching the R/W direction here. + */ + if (i2c->msg->flags & I2C_M_NOSTART) + goto again; + + /* + * Write the next address. + */ + IDBR = i2c_pxa_addr_byte(i2c->msg); + + /* + * And trigger a repeated start, and send the byte. + */ + icr &= ~ICR_ALDIE; + icr |= ICR_START | ICR_TB; + } else { + i2c_pxa_master_complete(i2c, 0); + } + + i2c->icrlog[i2c->irqlogidx-1] = icr; + + ICR = icr; +} + +static void i2c_pxa_irq_rxfull(struct pxa_i2c *i2c, u32 isr) +{ + u32 icr = ICR & ~(ICR_START|ICR_STOP|ICR_ACKNAK|ICR_TB); + + /* + * Read the byte. + */ + i2c->msg->buf[i2c->msg_ptr++] = IDBR; + + if (i2c->msg_ptr < i2c->msg->len) { + /* + * If this is the last byte of the last + * message, send a STOP. + */ + if (i2c->msg_ptr == i2c->msg->len - 1) + icr |= ICR_STOP | ICR_ACKNAK; + + icr |= ICR_ALDIE | ICR_TB; + } else { + i2c_pxa_master_complete(i2c, 0); + } + + i2c->icrlog[i2c->irqlogidx-1] = icr; + + ICR = icr; +} + +static irqreturn_t i2c_pxa_handler(int this_irq, void *dev_id, struct pt_regs *regs) +{ + struct pxa_i2c *i2c = dev_id; + int status/*, wakeup = 0*/; + status = (ISR); + + if (i2c_debug > 2 && 0) { + printk(DBGLVL "i2c_pxa_handler: status=%08x, ICR=%08x, IBMR=%02x\n", + status, ICR, IBMR); + decode_ISR(status); + } + + if (i2c->irqlogidx < sizeof(i2c->isrlog)/sizeof(u32)) + i2c->isrlog[i2c->irqlogidx++] = status; + + show_state(); + + /* + * Always clear all pending IRQs. + */ + ISR = status & (ISR_SSD|ISR_ALD|ISR_ITE|ISR_IRF|ISR_SAD|ISR_BED); + + if (status & ISR_SAD) { + if (i2c_debug > 0) { + printk(DBGLVL "ISR: SAD (Slave Addr Detect) mode is %s\n", + (status & ISR_RWM) ? "slave-rx" : "slave-tx"); + } + + if (pxa_slave != NULL) { + (pxa_slave->event)(NULL, + I2C_SLAVE_EVENT_START, + (status & ISR_RWM) ? I2C_SLAVE_START_READ : I2C_SLAVE_START_WRITE); + } + + show_verbose(); + + /* slave could interrupt in the middle of us + * generating a start condition... if this happens, we'd + * better back off and stop holding the poor thing up + */ + + ICR &= ~(ICR_START|ICR_STOP); + ICR |= ICR_TB; + + { + int timeout = 0x10000; + + while (1) { + if ((IBMR & 2) == 2) + break; + + timeout--; + + if (timeout <= 0) { + printk(KERN_ERR "timeout waiting for SCL high\n"); + break; + } + } + } + + ICR &= ~ICR_SCLE; + show_verbose(); + } + + if (status & ISR_SSD) { + if (i2c_debug > 2) + printk(DBGLVL "ISR: SSD (Slave Stop)\n"); + + if (pxa_slave != NULL) { + (pxa_slave->event)(NULL, I2C_SLAVE_EVENT_STOP, 0); + } + + if (i2c_debug > 2) + printk(DBGLVL "ISR: SSD (Slave Stop) acked\n"); + + if (0) + show_verbose(); + + /* + * If we have a master-mode message waiting, + * kick it off now that the slave has completed. + */ + if (i2c->msg) + i2c_pxa_master_complete(i2c, I2C_RETRY); + } + + if (i2c_pxa_is_slavemode()) { + if (status & ISR_BED){ + show_verbose(); + } + + if (status & ISR_ITE){ + int ret = (pxa_slave->read)(NULL); + + IDBR = ret; + ICR |= ICR_TB; /* allow next byte */ + } + + if (status & ISR_IRF){ + if (i2c_debug > 0) + printk(DBGLVL "ISR_IRF (slave)\n"); + + if (pxa_slave != NULL) { + (pxa_slave->write)(NULL, IDBR); + } + + ICR |= ICR_TB; + } + } else if (i2c->msg) { + if (status & ISR_ITE) + i2c_pxa_irq_txempty(i2c, status); + if (status & ISR_IRF) + i2c_pxa_irq_rxfull(i2c, status); + } else { + i2c_pxa_scream_blue_murder(i2c, "spurious irq"); + } + + return IRQ_HANDLED; +} + + +static int i2c_pxa_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num) +{ + struct pxa_i2c *i2c = adap->algo_data; + int ret, i; + + for (i = adap->retries; i >= 0; i--) { + ret = i2c_pxa_do_xfer(i2c, msgs, num); + if (ret != I2C_RETRY) + goto out; + + if (i2c_debug) + printk(KERN_INFO "Retrying transmission\n"); + udelay(100); + } + i2c_pxa_scream_blue_murder(i2c, "exhausted retries"); + ret = -EREMOTEIO; + out: + i2c_pxa_set_slave(ret); + return ret; +} + +static struct i2c_algorithm i2c_pxa_algorithm = { + .name = "PXA-I2C-Algorithm", + .id = I2C_ALGO_PXA, + .master_xfer = i2c_pxa_xfer, +}; + +static struct pxa_i2c i2c_pxa = { + .lock = SPIN_LOCK_UNLOCKED, + .wait = __WAIT_QUEUE_HEAD_INITIALIZER(i2c_pxa.wait), + .adap = { + .name = "pxa2xx-i2c", + .id = I2C_ALGO_PXA, + .algo = &i2c_pxa_algorithm, + .retries = 5, + }, +}; + +static int i2c_pxa_probe(struct device *dev) +{ + struct pxa_i2c *i2c = &i2c_pxa; + int ret; + + ret = request_irq(IRQ_I2C, i2c_pxa_handler, SA_INTERRUPT, + "pxa2xx-i2c", i2c); + if (ret) + goto out; + + i2c_pxa_reset(); + + i2c->adap.algo_data = i2c; + i2c->adap.dev.parent = dev; + + ret = i2c_add_adapter(&i2c->adap); + if (ret < 0) { + printk(KERN_INFO "I2C: Failed to add bus\n"); + free_irq(IRQ_I2C, i2c); + goto out; + } + + dev_set_drvdata(dev, i2c); + + printk(KERN_INFO "I2C: %s: PXA I2C adapter\n", i2c->adap.dev.bus_id); + + out: + return ret; +} + +static int i2c_pxa_remove(struct device *dev) +{ + struct pxa_i2c *i2c = dev_get_drvdata(dev); + + dev_set_drvdata(dev, NULL); + + i2c_del_adapter(&i2c->adap); + free_irq(IRQ_I2C, i2c); + + return 0; +} + +static struct device_driver i2c_pxa_driver = { + .name = "pxa2xx-i2c", + .bus = &platform_bus_type, + .probe = i2c_pxa_probe, + .remove = i2c_pxa_remove, +}; + +static int __init i2c_adap_pxa_init(void) +{ + return driver_register(&i2c_pxa_driver); +} + +static void i2c_adap_pxa_exit(void) +{ + return driver_unregister(&i2c_pxa_driver); +} + +module_init(i2c_adap_pxa_init); +module_exit(i2c_adap_pxa_exit); diff -NaurbB linux-2.6.9-pre1-bk18/drivers/i2c/busses/Kconfig linux-2.6.9_nbp/drivers/i2c/busses/Kconfig --- linux-2.6.9-pre1-bk18/drivers/i2c/busses/Kconfig 2005-08-15 01:14:33.000000000 +0200 +++ linux-2.6.9_nbp/drivers/i2c/busses/Kconfig 2004-09-25 21:51:04.000000000 +0200 @@ -252,6 +252,18 @@ This support is also available as a module. If so, the module will be called i2c-parport-light. +config I2C_PXA + tristate "Intel PXA2XX I2C adapter" + depends on I2C && EXPERIMENTAL && ARCH_PXA + help + Intel PXA2XX i2c adaptor + +config I2C_PXA_SLAVE + bool "Intel PXA2XX I2C Slave comms support" + depends on I2C_PXA + help + Enable PXA I2C device to act as slave as well as master + config I2C_PIIX4 tristate "Intel PIIX4" depends on I2C && PCI && EXPERIMENTAL && !64BIT diff -NaurbB linux-2.6.9-pre1-bk18/drivers/i2c/busses/Makefile linux-2.6.9_nbp/drivers/i2c/busses/Makefile --- linux-2.6.9-pre1-bk18/drivers/i2c/busses/Makefile 2005-08-15 01:14:33.000000000 +0200 +++ linux-2.6.9_nbp/drivers/i2c/busses/Makefile 2004-09-14 12:25:06.000000000 +0200 @@ -24,6 +24,7 @@ obj-$(CONFIG_I2C_PARPORT_LIGHT) += i2c-parport-light.o obj-$(CONFIG_I2C_PCA_ISA) += i2c-pca-isa.o obj-$(CONFIG_I2C_PIIX4) += i2c-piix4.o +obj-$(CONFIG_I2C_PXA) += i2c-pxa.o obj-$(CONFIG_I2C_PROSAVAGE) += i2c-prosavage.o obj-$(CONFIG_I2C_RPXLITE) += i2c-rpx.o obj-$(CONFIG_I2C_SAVAGE4) += i2c-savage4.o diff -NaurbB linux-2.6.9-pre1-bk18/drivers/i2c/chips/Kconfig linux-2.6.9_nbp/drivers/i2c/chips/Kconfig --- linux-2.6.9-pre1-bk18/drivers/i2c/chips/Kconfig 2005-08-15 01:14:41.000000000 +0200 +++ linux-2.6.9_nbp/drivers/i2c/chips/Kconfig 2004-09-25 21:51:28.000000000 +0200 @@ -251,6 +251,13 @@ This driver can also be built as a module. If so, the module will be called w83627hf. +config RTC_RICOH + tristate "Ricoh RTC Chips" + depends on I2C + help + Support for various Ricoh I2C RTC Chips + + endmenu menu "Other I2C Chip support" diff -NaurbB linux-2.6.9-pre1-bk18/drivers/i2c/chips/Makefile linux-2.6.9_nbp/drivers/i2c/chips/Makefile --- linux-2.6.9-pre1-bk18/drivers/i2c/chips/Makefile 2005-08-15 01:14:41.000000000 +0200 +++ linux-2.6.9_nbp/drivers/i2c/chips/Makefile 2004-09-14 12:25:06.000000000 +0200 @@ -31,6 +31,10 @@ obj-$(CONFIG_SENSORS_W83L785TS) += w83l785ts.o obj-$(CONFIG_ISP1301_OMAP) += isp1301_omap.o +obj-$(CONFIG_RTC_RICOH) += ricoh-rtc.o + +obj-$(CONFIG_MACH_NETBOOKPRO) += nbp-pcon.o + ifeq ($(CONFIG_I2C_DEBUG_CHIP),y) EXTRA_CFLAGS += -DDEBUG endif diff -NaurbB linux-2.6.9-pre1-bk18/drivers/i2c/chips/nbp-pcon.c linux-2.6.9_nbp/drivers/i2c/chips/nbp-pcon.c --- linux-2.6.9-pre1-bk18/drivers/i2c/chips/nbp-pcon.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/drivers/i2c/chips/nbp-pcon.c 2004-11-21 23:46:50.000000000 +0100 @@ -0,0 +1,615 @@ +/* + * linux/drivers/i2c/chips/nbp-pcon.c + * + * Driver for Psion Teklogix NetbookPro PCON chip + * Copyright (C) 2004 Simtec Electronics + * + * based on linux/drivers/acorn/char/pcf8583.c + * Copyright (C) 2000 Russell King + * + * 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. + * + */ +/* This driver provides the low-level i2c sending for the NetBookPro's + * power-management and general support processor. This allows other parts + * of the system to send messages to the chip for processing (including + * the userland) + * + * Note, this code does not provide the i2c client facilities needed by + * the PCon to send data to the systems that need information feeding in. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +struct pcon_client { + struct i2c_client client; + int thr_die; + wait_queue_head_t thr_wait; + wait_queue_head_t thr_sleep; + struct completion thr_complete; + void *pcon_dev; +}; + +/* pcon_cmd_ent + * + * command entry for the command queue +*/ + +struct pcon_cmd_ent { + struct pcon_cmd_ent *next; + struct completion done; + + unsigned int flags; + +#define PCON_FLG__ASYNC (1 << 0) /* defined elsewhere */ +#define PCON_FLG_CHKRDY (1 << 1) +#define PCON_FLG_RECVMSG (1 << 2) + + int sendlen; + int error; + unsigned char sendmsg[16]; + unsigned char recvmsg[1]; +}; + +static struct pcon_cmd_ent *queue; +static struct pcon_client *cur_client; + +#define PFX "pcon: " + +#if 0 +#define dbg(x...) do { if (debug) { printk(KERN_INFO "" x); } } while(0) +#else +#define dbg(x...) do { if (debug) { printk(KERN_DEBUG "" x); } } while(0) +#endif + +#if 0 +#define dbgcmd(x...) do { if (debug) { printk(KERN_INFO "" x); } } while(0) +#else +#define dbgcmd(x...) do { if (debug) { printk(KERN_DEBUG "" x); } } while(0) +#endif + +#if 0 +#define dbgrdy(x...) do { if (debug) { printk(KERN_INFO "" x); } } while(0) +#else +#define dbgrdy(x...) do { if (debug) { printk(KERN_DEBUG "" x); } } while(0) +#endif + +#if 0 +#undef dbgrdy +#undef dbgcmd +#undef dbg + +extern void printascii(const char *); +#define dbg(x...) do { char buff[200]; printascii(PFX); sprintf(buff, x); printascii(buff); } while(0) +#define dbgrdy(x...) dbg(x) +#define dbgcmd(x...) dbg(x) +#endif + +static int debug = 0; +//static int debug = 1; +MODULE_PARM(debug, "i"); + +static struct i2c_driver pcon_driver; + +#define PCON_I2C_ADDR (0x26 >> 1) + +/* we cannot allow the pcon to be `probed` as it seems to manage to + * make it reset the system shortly afterwards... +*/ + +static unsigned short ignore[] = { I2C_CLIENT_END }; +static unsigned short normal_addr[] = { ANY_I2C_BUS, PCON_I2C_ADDR, 0, + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END }; + +static struct i2c_client_address_data addr_data = { + .normal_i2c = ignore, + .normal_i2c_range = ignore, + .probe = ignore, + .probe_range = ignore, + .ignore = ignore, + .ignore_range = ignore, + .force = normal_addr, +}; + +static int +pcon_do_read(struct pcon_client *client, int loc, unsigned char *result) +{ + unsigned char loc_data[1]; + struct i2c_msg msgs[2] = { + { client->client.addr, 0, 1, loc_data }, + { client->client.addr, I2C_M_RD, 1, result } + }; + + dbgrdy("pcon_do_read: client=%p, loc=%d, result=%p\n", + client, loc, result); + + loc_data[0] = loc; + + return i2c_transfer(client->client.adapter, msgs, 2); +} + +static int +pcon_send(struct pcon_client *client, char *data, int length) +{ + struct i2c_msg msgs[1] = { + { client->client.addr, 0, length, data }, + }; + int ret; + + ret = i2c_transfer(client->client.adapter, msgs, 1); + + return (ret == 1) ? 1 : 0; +} + +static int +pcon_check_ready(struct pcon_client *client) +{ + unsigned char inbuff; + int ret; + + dbgrdy("%s: called (%ld)\n", __FUNCTION__, jiffies); + + while (1) { + /* the sfi code places this in the way to make sure that the + * friendly pcon isn't going to ack us... dunno if this is + * going to help us or not with our problems with reboot + * after suspend... + * + * yep, this delay fixes the problem. + */ + msleep(100); + + ret = pcon_do_read(client, NETBOOKPRO_PCON_COMMAND_ADDR, + &inbuff); + + dbgrdy("pcon_check_ready: ret=%d, inbuff=0x%02x\n", ret, inbuff); + + if (ret == 2 && inbuff == PCON_CMD_Ready) { + dbgrdy("%s: pcon ready (%ld)\n", __FUNCTION__, jiffies); + return 0; + } + + /* ok, we didn't get an ok response, let us + * wait for a little while and try again */ + + dbgrdy("%s: pcon not ready (%ld)\n", __FUNCTION__, jiffies); + + msleep(100); + } + + return 0; +} + +static int pcon_thread(void *param) +{ + struct pcon_client *pc = (struct pcon_client *)param; + unsigned long flags; + int ret; + + dbg("pcon_thread: param=%p, pc=%p\n", param,pc); + + daemonize("nbp-pcon"); + current->flags |= PF_NOFREEZE; + + while (1) { + struct pcon_cmd_ent *cmd; + + wait_event_interruptible(pc->thr_wait, pc->thr_die || queue); + + if (pc->thr_die) + break; + + /* + * remove one item from the queue, and execute it + */ + local_irq_save(flags); + cmd = queue; + if (cmd != NULL) + queue = cmd->next; + local_irq_restore(flags); + + if (!cmd) + continue; + + dbg("%s: sendmsg %p: %x %02x %02x %02x...\n", + __FUNCTION__, cmd, cmd->flags, + cmd->sendmsg[0], cmd->sendmsg[1], cmd->sendmsg[2]); + + if (cmd->flags & PCON_FLG_CHKRDY) + pcon_check_ready(pc); + + if (cmd->flags & PCON_FLG_RECVMSG) + ret = pcon_do_read(pc, cmd->sendmsg[0], cmd->recvmsg); + else + ret = pcon_send(pc, cmd->sendmsg, cmd->sendlen); + + cmd->error = ret; + + if ((cmd->flags & PCON_FLG_ASYNC) == 0) { + dbg("%s: completing command\n", __FUNCTION__); + complete(&cmd->done); + } else { + dbg("%s: freeing command\n", __FUNCTION__); + kfree(cmd); + } + } + + printk(KERN_DEBUG PFX "thread exiting\n"); + + complete_and_exit (&pc->thr_complete, 0); +} + +static int pcon_attach(struct i2c_adapter *adap, int addr, int kind) +{ + struct pcon_client *pc; + int ret; + + dbg("pcon_attach: adap=%p, addr=%02x, kind=%d\n", adap, addr, kind); + + if (addr != PCON_I2C_ADDR) { + printk("pcon_attach: unknown i2c address 0x%02x\n", addr); + return -ENOENT; + } + + pc = kmalloc(sizeof(*pc), GFP_KERNEL); + + dbg("pcon_attach: new client at %p\n", pc); + + if (pc == NULL) { + printk(KERN_ERR ": no memory for pcon client data\n"); + ret = -ENOMEM; + goto done; + } + + memset(pc, 0, sizeof(*pc)); + + strlcpy(pc->client.name, "PCON", I2C_NAME_SIZE); + i2c_set_clientdata(&pc->client, pc); + + pc->client.id = pcon_driver.id; + pc->client.flags = I2C_CLIENT_ALLOW_USE | I2C_DF_NOTIFY; + pc->client.addr = addr; + pc->client.adapter = adap; + pc->client.driver = &pcon_driver; + + init_waitqueue_head(&pc->thr_wait); + init_waitqueue_head(&pc->thr_sleep); + init_completion(&pc->thr_complete); + + dbg("pcon_attach: attaching new client\n"); + ret = i2c_attach_client(&pc->client); + dbg("pcon_attach: ret=%d\n", ret); + if (ret) { + printk(KERN_ERR "cannot attach client: %d\n", ret); + goto done; + } + + cur_client = pc; + + ret = kernel_thread(pcon_thread, pc, CLONE_KERNEL); + if (ret < 0) { + printk(KERN_ERR "cannot start thread: %d\n", ret); + i2c_detach_client(&pc->client); + goto done; + } + + dbg("pcon_attach: thread pid is %d\n", ret); + + if (1) { + printk(KERN_INFO "Moving power state to Run\n"); + nbp_pcon_write2(NETBOOKPRO_PCON_POWER, + PCON_PWRSTATE_Run); + } + pc->pcon_dev = pcon_dev_add("pcon", &pc->client.dev, pc); + +done: + if (ret < 0) + kfree(pc); + + return ret; +} + +static int pcon_probe(struct i2c_adapter *adap) +{ + return i2c_probe(adap, &addr_data, pcon_attach); +} + +static int pcon_detach(struct i2c_client *client) +{ + struct pcon_client *pc = i2c_get_clientdata(client); + int ret = i2c_detach_client(client); + + if (ret) { + dev_err(&client->dev, "Client deregistration failed, " + "client not detached.\n"); + } else { + pcon_dev_remove(pc->pcon_dev); + pc->thr_die = 1; + wake_up(&pc->thr_wait); + wait_for_completion(&pc->thr_complete); + kfree(pc); + } + + return ret; +} + +static int pcon_suspend(struct i2c_client *client, u32 state) +{ + struct pcon_client *pc = i2c_get_clientdata(client); + int ret = 0; + if (pc) + ret = pcon_dev_suspend(pc->pcon_dev, state); + return ret; +} + +static int pcon_resume(struct i2c_client *client) +{ + struct pcon_client *pc = i2c_get_clientdata(client); + int ret = 0; + if (pc) + ret = pcon_dev_resume(pc->pcon_dev); + return ret; +} + +static int +pcon_command(struct pcon_client *pc, unsigned int flags, unsigned char *msg, + int len, int *result) +{ + struct pcon_cmd_ent *ent; + unsigned long irq_flags; + int ret = 0; + + ent = kmalloc(sizeof(*ent), (flags & PCON_FLG_ASYNC) + ? GFP_ATOMIC : GFP_KERNEL); + if (ent == NULL) { + dbg("no memory to allocate command entity\n"); + printk(KERN_ERR "%s: no memory for command\n", __FUNCTION__); + return -ENOMEM; + } + + dbg("%s: got new block %p\n", __FUNCTION__, ent); + + init_completion(&ent->done); + ent->next = NULL; + ent->flags = flags; + ent->sendlen = len; + memcpy(ent->sendmsg, msg, len); + + dbg("%s: adding %p to queue of commands\n", __FUNCTION__, ent); + + local_irq_save(irq_flags); + + if (queue == NULL) { + queue = ent; + } else { + struct pcon_cmd_ent *ptr = queue; + + for (; ptr->next != NULL; ptr = ptr->next); + + ptr->next = ent; + } + + local_irq_restore(irq_flags); + + dbg("%s: checking if we need to wakeup for %p\n", __FUNCTION__, ent); + + if (pc != NULL) { + dbg("%s: waking client...\n", __FUNCTION__); + wake_up(&pc->thr_wait); + } + + if (flags & PCON_FLG_ASYNC) { + // nothing to do, leave it to it. + } else { + wait_for_completion(&ent->done); + + if (flags & PCON_FLG_RECVMSG) { + if (ent->error == 2) { + if (result) + *result = ent->recvmsg ? 1 : 0; + ret = 0; + } else + ret = ent->error; + } + + dbg("%s: freeing block %p\n", __FUNCTION__, ent); + /* dump this, we've been left with it*/ + kfree(ent); + } + + dbg("%s: done\n", __FUNCTION__); + + return ret; +} + +int +nbp_pcon_command(unsigned long command, unsigned int value, + unsigned int flags, int *result) +{ + unsigned char sendmsg[4]; + int sendlen; + + flags &= PCON_FLG_ASYNC; + if (flags != PCON_FLG_ASYNC) + might_sleep(); + + /* + * Decode the command into the message we need to send. + */ + switch (command) { + case I2C_SETREDLED: + flags |= PCON_FLG_CHKRDY; + sendlen = 3; + sendmsg[0] = NETBOOKPRO_PCON_COMMAND_ADDR; + sendmsg[1] = PCON_CMD_SetRedLED; + sendmsg[2] = value ? 1 : 0; + break; + + case I2C_SETSERIALPOWER: + flags |= PCON_FLG_CHKRDY; + sendlen = 3; + sendmsg[0] = NETBOOKPRO_PCON_COMMAND_ADDR; + sendmsg[1] = PCON_CMD_SetSerialPower; + sendmsg[2] = value ? 1 : 0; + break; + + case I2C_SETMMCPOWER: + flags |= PCON_FLG_CHKRDY; + sendlen = 3; + sendmsg[0] = NETBOOKPRO_PCON_COMMAND_ADDR; + sendmsg[1] = PCON_CMD_SetMMCPower; + sendmsg[2] = value ? 1 : 0; + break; + + case I2C_SETVMODEMPOWER: + flags |= PCON_FLG_CHKRDY; + sendlen = 3; + sendmsg[0] = NETBOOKPRO_PCON_COMMAND_ADDR; + sendmsg[1] = PCON_CMD_SetModemPower; + sendmsg[2] = value ? 1 : 0; + break; + + case I2C_SETUSBHOSTPOWER: + flags |= PCON_FLG_CHKRDY; + sendlen = 3; + sendmsg[0] = NETBOOKPRO_PCON_COMMAND_ADDR; + sendmsg[1] = PCON_CMD_SetUSBPower; + sendmsg[2] = value ? 1 : 0; + break; + + case PCON_MONITORBATTERY: + flags |= PCON_FLG_CHKRDY; + sendlen = 3; + sendmsg[0] = NETBOOKPRO_PCON_COMMAND_ADDR; + sendmsg[1] = PCON_CMD_BatteryMonitor; + sendmsg[2] = value ? 1 : 0; + break; + + case PCON_KEYBOARD_CMD: + sendlen = 2; + sendmsg[0] = NETBOOKPRO_PCON_KEYBOARD_CMD_ADDR; + sendmsg[1] = value; + break; + + case PCON_POWER_STATE: + sendlen = 2; + sendmsg[0] = NETBOOKPRO_PCON_POWER; + sendmsg[1] = value; + break; + + case I2C_SETPCMCIAPOWER: + sendlen = 2; + sendmsg[0] = NETBOOKPRO_PCON_POWER_PCMCIA; + sendmsg[1] = value ? 1 : 0; + break; + + case I2C_SETCFPOWER: + sendlen = 2; + sendmsg[0] = NETBOOKPRO_PCON_POWER_CF; + sendmsg[1] = value ? 1 : 0; + break; + + /* + * These two aren't currently used. + */ + case I2C_GETPCMCIAPOWER: + flags |= PCON_FLG_RECVMSG; + sendlen = 1; + sendmsg[0] = NETBOOKPRO_PCON_POWER_PCMCIA; + break; + + case I2C_GETCFPOWER: + flags |= PCON_FLG_RECVMSG; + sendlen = 1; + sendmsg[0] = NETBOOKPRO_PCON_POWER_CF; + break; + + default: + printk(KERN_ERR "%s: uknown set command %lu\n", + __FUNCTION__, command); + return -EINVAL; + } + + return pcon_command(cur_client, flags, sendmsg, sendlen, result); +} +EXPORT_SYMBOL(nbp_pcon_command); + +int nbp_pcon_rtc_wakeup(struct rtc_time *tm) +{ + unsigned char sendmsg[10]; + + sendmsg[0] = NETBOOKPRO_PCON_COMMAND_ADDR; + sendmsg[1] = PCON_CMD_SetWakeupAlarm; + sendmsg[2] = tm->tm_sec; + sendmsg[3] = tm->tm_min; + sendmsg[4] = tm->tm_hour; + sendmsg[5] = tm->tm_wday; /* 6 = Saturday */ + sendmsg[6] = tm->tm_mday; /* 1 = 1st */ + sendmsg[7] = tm->tm_mon; + sendmsg[8] = tm->tm_year >> 8; + sendmsg[9] = tm->tm_year & 255; + + return pcon_command(cur_client, PCON_FLG_CHKRDY, sendmsg, 10, NULL); +} +EXPORT_SYMBOL(nbp_pcon_rtc_wakeup); + +int +nbp_pcon_write2(unsigned int to, unsigned int val) +{ + if (cur_client != NULL) { + char data[2]; + + data[0] = to; + data[1] = val; + + pcon_send(cur_client, data, 2); + } else { + printk(KERN_ERR "%s: no client for write(%d,%d)\n", + __FUNCTION__, to, val); + } + + return 0; +} + + +static struct i2c_driver pcon_driver = { + .owner = THIS_MODULE, + .name = "PCON", + .id = I2C_DRIVERID_EXP3, + .flags = I2C_DF_NOTIFY, + .attach_adapter = pcon_probe, + .detach_client = pcon_detach, + .suspend = pcon_suspend, + .resume = pcon_resume, +}; + +static __init int pcon_init(void) +{ + return i2c_add_driver(&pcon_driver); +} + +static __exit void pcon_exit(void) +{ + i2c_del_driver(&pcon_driver); +} + +MODULE_AUTHOR("Ben Dooks "); +MODULE_DESCRIPTION("Psion NetbookPro PCon chip driver"); +MODULE_LICENSE("GPL"); + +module_init(pcon_init); +module_exit(pcon_exit); diff -NaurbB linux-2.6.9-pre1-bk18/drivers/i2c/chips/ricoh-rtc.c linux-2.6.9_nbp/drivers/i2c/chips/ricoh-rtc.c --- linux-2.6.9-pre1-bk18/drivers/i2c/chips/ricoh-rtc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/drivers/i2c/chips/ricoh-rtc.c 2004-11-07 23:47:20.000000000 +0100 @@ -0,0 +1,559 @@ +/* linux/drivers/i2c/chips/ricoh-rtc.c + * + * (c) 2004 Simtec Electronics, Ben Dooks + * + * + * Based on drivers/i2c/chips/rtc8564.c + * + * Driver for Ricoh RTC Chips + * + * This should support the R2051 series, and the RV5C386 which + * are very similar chips. +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include "ricoh-rtc.h" + +struct ricoh_rtc { + struct i2c_client client; + struct rtc_time alarm; + + ricohrtc_type_t type; + const char *name; + unsigned char hrmode; + unsigned char ctrl1; + unsigned char ctrl2; +}; + +static unsigned short ignore[] = { I2C_CLIENT_END }; +static unsigned short force_addr[] = { + ANY_I2C_BUS, 0x32, 0, + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END +}; + +static struct i2c_client_address_data addr_data = { + .normal_i2c = ignore, + .normal_i2c_range = ignore, + .probe = ignore, + .probe_range = ignore, + .ignore = ignore, + .ignore_range = ignore, + .force = force_addr, +}; + +#define I2C_DRIVERID_RICOHRTC (0x100) + +static struct i2c_driver ricohrtc_driver; + +/* misc device registration */ + +static struct ricoh_rtc *cur_rtc; + +static int ricohrtc_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg); + +static struct file_operations ricohrtc_fops = { + .ioctl = ricohrtc_ioctl, +}; + +static struct miscdevice ricohrtc_dev = { + .minor = RTC_MINOR, + .name = "rtc", + .devfs_name = "rtc", + .fops = &ricohrtc_fops, + .dev = NULL +}; + +/* register access code */ + +static int ricohrtc_read(struct ricoh_rtc *rtc, + ricohrtc_reg_t reg, + unsigned char *buf, + unsigned int length) +{ + unsigned int ret; + unsigned char addr; + struct i2c_msg msgs[2] = { + { rtc->client.addr, 0, 1, &addr }, + { rtc->client.addr, I2C_M_RD, length, buf } + }; + + addr = ((unsigned char)reg) << 4; + + if (rtc == NULL || buf == NULL) { + pr_debug("%s: null argmuent\n", __FUNCTION__); + return -EINVAL; + } + + ret = i2c_transfer(rtc->client.adapter, msgs, 2); + if (ret != 2) { + pr_debug("%s: i2c_transfer() returned %d\n", + __FUNCTION__, ret); + if (ret == 0) + ret = 1; + return ret; + } + + return 0; +} + +static int ricohrtc_write(struct ricoh_rtc *rtc, + ricohrtc_reg_t reg, + unsigned char *buf, + unsigned int length) +{ + unsigned int ret; + unsigned char addr; + struct i2c_msg msgs[2] = { + { rtc->client.addr, 0, 1, &addr }, + { rtc->client.addr, I2C_M_NOSTART, length, buf } + }; + + addr = ((unsigned char)reg) << 4; + + if (rtc == NULL || buf == NULL) { + pr_debug("%s: null argmuent\n", __FUNCTION__); + return -EINVAL; + } + + ret = i2c_transfer(rtc->client.adapter, msgs, 2); + if (ret != 2) { + pr_debug("%s: i2c_transfer() returned %d\n", + __FUNCTION__, ret); + + if (ret == 0) + ret = 1; + return ret; + } + + return 0; +} + +static char * +ricohrtc_type_to_name(ricohrtc_type_t type) +{ + switch (type) { + default: + case RICOH_UNKNOWN: + return "unknown"; + case RICOH_R2051: + return "R2051"; + case RICOH_R5C386: + return "R5C386"; + } + + return NULL; +} + +/* i2c code */ + +static int ricohrtc_detach_client(struct i2c_client *client) +{ + i2c_detach_client(client); + + kfree(i2c_get_clientdata(client)); + return 0; +} + +static int ricohrtc_detect(struct i2c_adapter *adapter, int address, int kind) +{ + struct ricoh_rtc *rtc; + unsigned char tmp; + unsigned char tmp2; + int err = 0, stopped = 0; + + pr_debug("%s: adapter=%p, address=%d, kind=%d\n", + __FUNCTION__, adapter, address, kind); + + rtc = kmalloc(sizeof(*rtc), GFP_KERNEL); + if (rtc == NULL) { + printk(KERN_ERR "no memory for new ricoh rtc\n"); + err = -ENOMEM; + goto exit; + } + + memset(rtc, 0, sizeof(*rtc)); + + rtc->client.addr = address; + rtc->client.adapter = adapter; + rtc->client.driver = &ricohrtc_driver; + rtc->client.flags = 0; + + strlcpy(rtc->client.name, "ricoh-rtc", I2C_NAME_SIZE); + + i2c_set_clientdata(&rtc->client, rtc); + + /* ok, we'll check to see if we can read both the registers */ + + if (ricohrtc_read(rtc, RICOHRTC_REG_Control1, &rtc->ctrl1, 1) != 0) { + printk(KERN_ERR "%s: cannot ready RICOHRTC_REG_Control1\n", + __FUNCTION__); + err = -ENOENT; + goto exit_free; + } + + if (ricohrtc_read(rtc, RICOHRTC_REG_Control2, &rtc->ctrl2, 1) != 0) { + printk(KERN_ERR "%s: cannot ready RICOHRTC_REG_Control2\n", + __FUNCTION__); + err = -ENOENT; + goto exit_free; + } + + pr_debug("%s: ctrl1=%02x, ctrl2=%02x\n", + __FUNCTION__, rtc->ctrl1, rtc->ctrl2); + + /* + * Only the RV5C386 allows bit 7 to be set to one. + */ + if (ricohrtc_read(rtc, RICOHRTC_REG_OscillatorAdj, &tmp, 1) != 0) { + printk(KERN_ERR "%s: cannot read OscillatorAdj\n", + __FUNCTION__); + err = -ENOENT; + goto exit_free; + } + + if ((tmp & 0x80) == 0) { + tmp2 = tmp | 0x80; + + if (ricohrtc_write(rtc, RICOHRTC_REG_OscillatorAdj, &tmp2, 1) != 0) { + printk(KERN_ERR "%s: cannot read OscillatorAdj\n", + __FUNCTION__); + err = -ENOENT; + goto exit_free; + } + + if (ricohrtc_read(rtc, RICOHRTC_REG_OscillatorAdj, &tmp, 1) != 0) { + printk(KERN_ERR "%s: cannot read OscillatorAdj\n", + __FUNCTION__); + err = -ENOENT; + goto exit_free; + } + + /* put the oscillator adjust register back where it was */ + tmp2 ^= 0x80; + + if (ricohrtc_write(rtc, RICOHRTC_REG_OscillatorAdj, &tmp2, 1) != 0) { + printk(KERN_ERR "%s: cannot ready OscillatorAdj\n", + __FUNCTION__); + err = -ENOENT; + goto exit_free; + } + } + + if (tmp & 0x80) { + rtc->type = RICOH_R5C386; + } else { + rtc->type = RICOH_R2051; + } + + /* work out if this is a 12 or 24hr mode */ + + if (rtc->ctrl1 & RICOHRTC_CTRL1_24HR) + rtc->hrmode = 24; + else + rtc->hrmode = 12; + + /* check to see if the crystal or oscillator stopped? */ + + switch (rtc->type) { + case RICOH_R2051: + stopped = !(rtc->ctrl2 & R2051RTC_CTRL2_XSTP); + if (stopped) { + rtc->ctrl2 |= R2051RTC_CTRL2_XSTP; + ricohrtc_write(rtc, RICOHRTC_REG_Control2, + &rtc->ctrl2, 1); + } + break; + + case RICOH_R5C386: + stopped = rtc->ctrl2 & RV386RTC_CTRL2_XSTP; + if (stopped) { + rtc->ctrl2 &= ~RV386RTC_CTRL2_XSTP; + ricohrtc_write(rtc, RICOHRTC_REG_Control2, + &rtc->ctrl2, 1); + } + break; + + default:; + } + + + /* register our device */ + + if ((err = i2c_attach_client(&rtc->client))) { + printk(KERN_ERR "%s: failed to attach client (%d)\n", + __FUNCTION__, err); + goto exit_free; + } + + if (cur_rtc == NULL) { + cur_rtc = rtc; + + misc_register(&ricohrtc_dev); + } + + rtc->name = ricohrtc_type_to_name(rtc->type); + printk(KERN_INFO "I2C: Ricoh %s RTC, %dHr mode%s\n", + rtc->name, rtc->hrmode, + stopped ? ", oscillator restarted" : ""); + + return 0; + + exit_free: + kfree(rtc); + exit: + printk(KERN_ERR "%s: exiting with error %d\n", __FUNCTION__, err); + return err; +} + +/* get/set time routines */ + +static int +ricohrtc_get_datetime(struct ricoh_rtc *rtc, + struct rtc_time *dt) +{ + unsigned char buf[15]; + int ret = -EIO; + + if (rtc == NULL || dt == NULL) { + pr_debug("%s: invalid parameters\n", __FUNCTION__); + return -EINVAL; + } + + memset(buf, 0, sizeof(buf)); + memset(dt, 0, sizeof(*dt)); + + ret = ricohrtc_read(rtc, 0, buf, RICOHRTC_REG_Years+1); + if (ret) + return ret; + + /* century stored in minute alarm reg */ + dt->tm_year = BCD2BIN(buf[RICOHRTC_REG_Years]); + dt->tm_mday = BCD2BIN(buf[RICOHRTC_REG_DayOfMonth] & 0x3f); + dt->tm_wday = BCD2BIN(buf[RICOHRTC_REG_DayOfWeek] & 7); + dt->tm_mon = BCD2BIN(buf[RICOHRTC_REG_Months] & 0x1f) - 1; + dt->tm_sec = BCD2BIN(buf[RICOHRTC_REG_Seconds] & 0x7f); + dt->tm_min = BCD2BIN(buf[RICOHRTC_REG_Minutes] & 0x7f); + dt->tm_hour = BCD2BIN(buf[RICOHRTC_REG_Hours] & 0x3f); + + /* + * Convert the "binary" 12 hour time into 24 hour time. + */ + if (rtc->hrmode == 12) { + if (dt->tm_hour == 12 || dt->tm_hour == 32) + dt->tm_hour -= 12; + if (dt->tm_hour >= 20) + dt->tm_hour -= 20 - 12; + } + + if (buf[RICOHRTC_REG_Months] & RICOHRTC_REG_Months_Y2K) + dt->tm_year += 100; + + pr_debug("%s: %d,%d,%d %d(%d)/%d/%d\n", + __FUNCTION__, + dt->tm_hour, dt->tm_min, dt->tm_sec, + dt->tm_mday, dt->tm_wday, dt->tm_mon, dt->tm_year); + + return 0; +} + +static int +ricohrtc_set_datetime(struct ricoh_rtc *rtc, + struct rtc_time *dt, + int datetoo) +{ + unsigned char buf[RICOHRTC_REG_OscillatorAdj]; + unsigned int hr; + int ret; + int len = RICOHRTC_REG_Hours; + + pr_debug("%s: %d,%d,%d %d(%d)/%d/%d\n", + __FUNCTION__, + dt->tm_hour, dt->tm_min, dt->tm_sec, + dt->tm_mday, dt->tm_wday, dt->tm_mon, dt->tm_year); + + hr = dt->tm_hour; + + /* + * If we're running in 12 hour mode, convert to 12 hour time. + * Note: this is designed to come out correctly once the + * BCD conversion is done. + */ + if (rtc->hrmode == 12) { + if (hr >= 12) + hr += 20 - 12; + if (hr == 0 || hr == 20) + hr += 12; + } + + buf[RICOHRTC_REG_Seconds] = BIN2BCD(dt->tm_sec); + buf[RICOHRTC_REG_Minutes] = BIN2BCD(dt->tm_min); + buf[RICOHRTC_REG_Hours] = BIN2BCD(hr); + + if (datetoo) { + len = RICOHRTC_REG_Years; + buf[RICOHRTC_REG_DayOfMonth] = BIN2BCD(dt->tm_mday); + buf[RICOHRTC_REG_DayOfWeek] = BIN2BCD(dt->tm_wday); + buf[RICOHRTC_REG_Months] = BIN2BCD(dt->tm_mon + 1) & 0x1f; + + buf[RICOHRTC_REG_Years] = BIN2BCD(dt->tm_year % 100); + + if (dt->tm_year >= 2000 - 1900) + buf[RICOHRTC_REG_Months] |= RICOHRTC_REG_Months_Y2K; + } + + ret = ricohrtc_write(rtc, RICOHRTC_REG_Seconds, buf, len+1); + if (ret != 0) + ret = -EIO; + + return ret; +} + +static int +ricohrtc_set_alarm(struct ricoh_rtc *rtc, struct rtc_time *tm) +{ + memcpy(&rtc->alarm, tm, sizeof(struct rtc_time)); + return 0; +} + +/* ioctl interface for rtc */ + +static int ricohrtc_ioctl(struct inode *inode, + struct file *file, + unsigned int cmd, + unsigned long arg) +{ + struct ricoh_rtc *rtc = cur_rtc; + struct rtc_time rtctm; + int ret; + + pr_debug("ioctl(%p,%p,0x%08x,%ld)\n", inode, file, cmd, arg); + + switch (cmd) { + case RTC_AIE_OFF: + memset(&rtctm, 0, sizeof(struct rtc_time)); + nbp_pcon_rtc_wakeup(&rtctm); + return 0; + + case RTC_AIE_ON: + nbp_pcon_rtc_wakeup(&rtc->alarm); + return 0; + + case RTC_ALM_READ: + ret = copy_to_user((void *)arg, &rtc->alarm, sizeof(rtctm)); + return ret ? -EFAULT : 0; + + case RTC_ALM_SET: + if (copy_from_user(&rtctm, (void *)arg, sizeof(rtctm))) + return -EFAULT; + return ricohrtc_set_alarm(rtc, &rtctm); + + case RTC_RD_TIME: + ricohrtc_get_datetime(rtc, &rtctm); + ret = copy_to_user((void *)arg, &rtctm, sizeof(rtctm)); + return ret ? -EFAULT : 0; + + case RTC_SET_TIME: + if (!capable(CAP_SYS_TIME)) + return -EACCES; + + if (copy_from_user(&rtctm, (void *)arg, sizeof(rtctm))) + return -EFAULT; + + return ricohrtc_set_datetime(rtc, &rtctm, 1); + + case RTC_EPOCH_READ: + return put_user(1900, (unsigned long *)arg); + } + + return -EINVAL; +} + +/* + * Return current RTC seconds since 1970-01-01 00:00:00, the standard + * Unix Epoch. + */ +static unsigned long ricohrtc_time(struct i2c_client *client) +{ + struct ricoh_rtc *rtc = i2c_get_clientdata(client); + struct rtc_time rtctm; + + ricohrtc_get_datetime(rtc, &rtctm); + + return mktime(rtctm.tm_year + 1900, rtctm.tm_mon + 1, rtctm.tm_mday, + rtctm.tm_hour, rtctm.tm_min, rtctm.tm_sec); +} + + +static int ricohrtc_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_probe(adapter, &addr_data, ricohrtc_detect); +} + +static unsigned long ricohrtc_delta; + +extern unsigned long save_time_delta(unsigned long); +extern void restore_time_delta(unsigned long, unsigned long); + +static int ricohrtc_suspend(struct i2c_client *client, u32 state) +{ + ricohrtc_delta = save_time_delta(ricohrtc_time(client)); + return 0; +} + +static int ricohrtc_resume(struct i2c_client *client) +{ + restore_time_delta(ricohrtc_time(client), ricohrtc_delta); + return 0; +} + +static struct i2c_driver ricohrtc_driver = { + .owner = THIS_MODULE, + .name = "ricoh-rtc", + .id = I2C_DRIVERID_RICOHRTC, + .flags = I2C_DF_NOTIFY, + .attach_adapter = ricohrtc_attach_adapter, + .detach_client = ricohrtc_detach_client, + .suspend = ricohrtc_suspend, + .resume = ricohrtc_resume, +}; + +/* module initialisation and finalisation, and definitions */ + +static int __init ricohrtc_init(void) +{ + printk("Ricoh RTC Driver, (c) 2004 Simtec Electronics\n"); + + pr_debug("%s: $Id: ricoh-rtc.c,v 1.4 2004/08/27 12:09:02 ben Exp $\n", __FUNCTION__); + return i2c_add_driver(&ricohrtc_driver); +} + +static void __exit ricohrtc_exit(void) +{ + i2c_del_driver(&ricohrtc_driver); +} + +MODULE_AUTHOR("Ben Dooks "); +MODULE_DESCRIPTION("Ricoh RTC Drivers"); +MODULE_LICENSE("GPL"); + +module_init(ricohrtc_init); +module_exit(ricohrtc_exit); diff -NaurbB linux-2.6.9-pre1-bk18/drivers/i2c/chips/ricoh-rtc.h linux-2.6.9_nbp/drivers/i2c/chips/ricoh-rtc.h --- linux-2.6.9-pre1-bk18/drivers/i2c/chips/ricoh-rtc.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/drivers/i2c/chips/ricoh-rtc.h 2004-09-25 21:51:14.000000000 +0200 @@ -0,0 +1,49 @@ + +typedef enum { + RICOHRTC_REG_Seconds = 0, + RICOHRTC_REG_Minutes = 1, + RICOHRTC_REG_Hours = 2, + RICOHRTC_REG_DayOfWeek = 3, + RICOHRTC_REG_DayOfMonth = 4, + RICOHRTC_REG_Months = 5, + RICOHRTC_REG_Years = 6, + RICOHRTC_REG_OscillatorAdj = 7, + RICOHRTC_REG_AlarmWMinute = 8, + RICOHRTC_REG_AlarmWHour = 9, + RICOHRTC_REG_AlarmWDayOfWeek = 10, + RICOHRTC_REG_AlarmDMinute = 11, + RICOHRTC_REG_AlarmDHour = 12, + RICOHRTC_REG_Control1 = 14, + RICOHRTC_REG_Control2 = 15 +} ricohrtc_reg_t; + +typedef enum { + RICOH_UNKNOWN, + RICOH_R2051, + RICOH_R5C386 +} ricohrtc_type_t; + +#define RICOHRTC_CTRL1_WALE (1<<7) +#define RICOHRTC_CTRL1_DALE (1<<6) +#define RICOHRTC_CTRL1_24HR (1<<5) +#define RICOHRTC_CTRL1_SCRATCH (1<<4) +#define RICOHRTC_CTRL1_TEST (1<<3) +#define RICOHRTC_CTRL1_CTMask (0x7) + +#define RICOHRTC_CTRL2_VDSL (1<<7) +#define RICOHRTC_CTRL2_VDET (1<<6) + +#define RV386RTC_CTRL2_SCRATCH2 (1<<5) +#define R2051RTC_CTRL2_XSTP (1<<5) + +#define R2051RTC_CTRL2_VDSL_16V (1<<4) +#define RV386RTC_CTRL2_XSTP (1<<4) + +#define RICOHRTC_CTRL2_SCRATCH1 (1<<3) +#define RICOHRTC_CTRL2_CTFG (1<<2) +#define RICOHRTC_CTRL2_WAFG (1<<1) +#define RICOHRTC_CTRL2_DAFG (1<<0) + +#define RICOHRTC_REG_Months_Y2K (1<<7) + + diff -NaurbB linux-2.6.9-pre1-bk18/drivers/i2c/i2c-core.c linux-2.6.9_nbp/drivers/i2c/i2c-core.c --- linux-2.6.9-pre1-bk18/drivers/i2c/i2c-core.c 2004-08-14 07:36:17.000000000 +0200 +++ linux-2.6.9_nbp/drivers/i2c/i2c-core.c 2004-09-25 21:50:53.000000000 +0200 @@ -38,6 +38,11 @@ static DECLARE_MUTEX(core_lists); static DEFINE_IDR(i2c_adapter_idr); +#if 0 +#undef dev_dbg +#define dev_dbg(dev, msg, args...) do { printk(KERN_INFO msg, ##args); }while(0) +#endif + int i2c_device_probe(struct device *dev) { return -ENODEV; @@ -517,9 +522,31 @@ return 1; } +static int i2c_device_suspend(struct device *dev, u32 state) +{ + struct i2c_client *client = to_i2c_client(dev); + int ret = 0; + + if (dev->driver && client->driver->suspend) + ret = client->driver->suspend(client, state); + return ret; +} + +static int i2c_device_resume(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + int ret = 0; + + if (dev->driver && client->driver->resume) + ret = client->driver->resume(client); + return ret; +} + struct bus_type i2c_bus_type = { .name = "i2c", .match = i2c_device_match, + .suspend = i2c_device_suspend, + .resume = i2c_device_resume, }; static int __init i2c_init(void) diff -NaurbB linux-2.6.9-pre1-bk18/drivers/i2c/i2c-pxa.h linux-2.6.9_nbp/drivers/i2c/i2c-pxa.h --- linux-2.6.9-pre1-bk18/drivers/i2c/i2c-pxa.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/drivers/i2c/i2c-pxa.h 2004-09-13 19:07:18.000000000 +0200 @@ -0,0 +1,76 @@ +/* + * i2c_pxa.h + * + * Copyright (C) 2002 Intrinsyc Software Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#ifndef _I2C_PXA_H_ +#define _I2C_PXA_H_ + +struct i2c_algo_pxa_data +{ + void (*write_byte) (u8 value); + u8 (*read_byte) (void); + void (*start) (void); + void (*repeat_start) (void); + void (*stop) (void); + void (*abort) (void); + int (*wait_bus_not_busy) (void); + int (*wait_for_interrupt) (int wait_type); + void (*transfer) (int lastbyte, int receive, int midbyte); + void (*reset) (void); + + int udelay; + int timeout; +}; + +#define DEF_TIMEOUT 3 +#define BUS_ERROR (-EREMOTEIO) +#define ACK_DELAY 0 /* time to delay before checking bus error */ +#define MAX_MESSAGES 65536 /* maximum number of messages to send */ + +#define I2C_SLEEP_TIMEOUT 2 /* time to sleep for on i2c transactions */ +#define I2C_RETRY (-2000) /* an error has occurred retry transmit */ +#define I2C_TRANSMIT 1 +#define I2C_RECEIVE 0 +#define I2C_PXA_SLAVE_ADDR 0x1 /* slave pxa unit address */ +#define I2C_ICR_INIT (ICR_BEIE | ICR_IRFIE | ICR_ITEIE | ICR_GCD | ICR_SCLE) /* ICR initialization value */ +/* ICR initialize bit values +* +* 15. FM 0 (100 Khz operation) +* 14. UR 0 (No unit reset) +* 13. SADIE 0 (Disables the unit from interrupting on slave addresses +* matching its slave address) +* 12. ALDIE 0 (Disables the unit from interrupt when it loses arbitration +* in master mode) +* 11. SSDIE 0 (Disables interrupts from a slave stop detected, in slave mode) +* 10. BEIE 1 (Enable interrupts from detected bus errors, no ACK sent) +* 9. IRFIE 1 (Enable interrupts from full buffer received) +* 8. ITEIE 1 (Enables the I2C unit to interrupt when transmit buffer empty) +* 7. GCD 1 (Disables i2c unit response to general call messages as a slave) +* 6. IUE 0 (Disable unit until we change settings) +* 5. SCLE 1 (Enables the i2c clock output for master mode (drives SCL) +* 4. MA 0 (Only send stop with the ICR stop bit) +* 3. TB 0 (We are not transmitting a byte initially) +* 2. ACKNAK 0 (Send an ACK after the unit receives a byte) +* 1. STOP 0 (Do not send a STOP) +* 0. START 0 (Do not send a START) +* +*/ + +#define I2C_ISR_INIT 0x7FF /* status register init */ +/* I2C status register init values + * + * 10. BED 1 (Clear bus error detected) + * 9. SAD 1 (Clear slave address detected) + * 7. IRF 1 (Clear IDBR Receive Full) + * 6. ITE 1 (Clear IDBR Transmit Empty) + * 5. ALD 1 (Clear Arbitration Loss Detected) + * 4. SSD 1 (Clear Slave Stop Detected) + */ + +#endif diff -NaurbB linux-2.6.9-pre1-bk18/drivers/ide/legacy/ide-cs.c linux-2.6.9_nbp/drivers/ide/legacy/ide-cs.c --- linux-2.6.9-pre1-bk18/drivers/ide/legacy/ide-cs.c 2004-08-14 07:37:26.000000000 +0200 +++ linux-2.6.9_nbp/drivers/ide/legacy/ide-cs.c 2004-10-07 18:19:54.000000000 +0200 @@ -412,12 +412,6 @@ /* FIXME: if this fails we need to queue the cleanup somehow -- need to investigate the required PCMCIA magic */ ide_unregister(info->hd); - /* deal with brain dead IDE resource management */ - request_region(link->io.BasePort1, link->io.NumPorts1, - info->node.dev_name); - if (link->io.NumPorts2) - request_region(link->io.BasePort2, link->io.NumPorts2, - info->node.dev_name); } info->ndev = 0; link->dev = NULL; diff -NaurbB linux-2.6.9-pre1-bk18/drivers/input/input.c linux-2.6.9_nbp/drivers/input/input.c --- linux-2.6.9-pre1-bk18/drivers/input/input.c 2004-08-14 07:36:59.000000000 +0200 +++ linux-2.6.9_nbp/drivers/input/input.c 2004-11-09 01:01:39.000000000 +0100 @@ -439,8 +439,8 @@ if (!dev->rep[REP_DELAY] && !dev->rep[REP_PERIOD]) { dev->timer.data = (long) dev; dev->timer.function = input_repeat_key; - dev->rep[REP_DELAY] = 250; - dev->rep[REP_PERIOD] = 33; + dev->rep[REP_DELAY] = 700; + dev->rep[REP_PERIOD] = 90; } INIT_LIST_HEAD(&dev->h_list); diff -NaurbB linux-2.6.9-pre1-bk18/drivers/input/keyboard/Kconfig linux-2.6.9_nbp/drivers/input/keyboard/Kconfig --- linux-2.6.9-pre1-bk18/drivers/input/keyboard/Kconfig 2004-08-14 07:38:04.000000000 +0200 +++ linux-2.6.9_nbp/drivers/input/keyboard/Kconfig 2004-09-14 12:25:06.000000000 +0200 @@ -96,3 +96,10 @@ To compile this driver as a module, choose M here: the module will be called amikbd. + +config KEYBOARD_NETBOOKPRO + tristate "NetBookPro Keyboard support" + depends on MACH_NETBOOKPRO && INPUT && INPUT_KEYBOARD + help + Say Y here if you want to use the NetBookPro keyboard on your + system. diff -NaurbB linux-2.6.9-pre1-bk18/drivers/input/keyboard/Makefile linux-2.6.9_nbp/drivers/input/keyboard/Makefile --- linux-2.6.9-pre1-bk18/drivers/input/keyboard/Makefile 2004-08-14 07:38:11.000000000 +0200 +++ linux-2.6.9_nbp/drivers/input/keyboard/Makefile 2004-09-14 12:25:06.000000000 +0200 @@ -12,3 +12,4 @@ obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o obj-$(CONFIG_KEYBOARD_98KBD) += 98kbd.o +obj-$(CONFIG_KEYBOARD_NETBOOKPRO) += netbookpro.o diff -NaurbB linux-2.6.9-pre1-bk18/drivers/input/keyboard/netbookpro.c linux-2.6.9_nbp/drivers/input/keyboard/netbookpro.c --- linux-2.6.9-pre1-bk18/drivers/input/keyboard/netbookpro.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/drivers/input/keyboard/netbookpro.c 2004-11-19 22:05:38.000000000 +0100 @@ -0,0 +1,384 @@ +/* drivers/input/keyboard/netbookpro.c + * + * (c) 2004 Simtec Electronics + * Written by Ben Dooks + * + * $Id: netbookpro.c,v 1.10 2004/09/14 20:25:30 rmk Exp $ +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* things that come from the framebuffer driver */ + +extern void netbookpro_backlight_mod(int by); + +#define PFX "nbp-keyb: " + +static int debug; + +#define DBG(x...) \ + do { \ + if (debug) \ + pr_debug(PFX x); \ + } while (0) + +/* keyboard input device registration */ + +static struct input_dev kbd_dev; + +/* keyboard mapping code */ + +#define NBP_NUMKEYCODES (0x80) + +static unsigned char normal_map[NBP_NUMKEYCODES] = { + [0x5b] = KEY_ESC, + [0x5c] = KEY_1, + [0x5d] = KEY_2, + [0x47] = KEY_3, + [0x36] = KEY_4, + [0x37] = KEY_5, + [0x5e] = KEY_6, + [0x2d] = KEY_7, + [0x1d] = KEY_8, + [0x1e] = KEY_9, + [0x11] = KEY_0, + [0x0f] = KEY_MINUS, + [0x0d] = KEY_BACKSPACE, /* delete or backspace? */ + + [0x02] = KEY_TAB, + [0x4f] = KEY_Q, + [0x53] = KEY_W, + [0x46] = KEY_E, + [0x35] = KEY_R, + [0x38] = KEY_T, + [0x03] = KEY_Y, + [0x2c] = KEY_U, + [0x1c] = KEY_I, + [0x1f] = KEY_O, + [0x12] = KEY_P, + [0x10] = KEY_EQUAL, + [0x00] = KEY_ENTER, + + [0x3f] = KEY_BACKSLASH, /* ? correct */ + [0x50] = KEY_A, + [0x52] = KEY_S, + [0x45] = KEY_D, + [0x42] = KEY_F, + [0x39] = KEY_G, + [0x61] = KEY_H, + [0x2b] = KEY_J, + [0x1b] = KEY_K, + [0x20] = KEY_L, + [0x13] = KEY_SEMICOLON, + [0x29] = KEY_APOSTROPHE, + + [0x07] = KEY_LEFTSHIFT, + [0x51] = KEY_Z, + [0x54] = KEY_X, + [0x44] = KEY_C, + [0x43] = KEY_V, + [0x3a] = KEY_B, + [0x06] = KEY_N, + [0x2a] = KEY_M, + [0x28] = KEY_COMMA, + [0x5f] = KEY_DOT, + [0x60] = KEY_UP, + [0x15] = KEY_RIGHTSHIFT, + + [0x23] = KEY_LEFTCTRL, + [0x41] = KEY_LEFTALT, + [0x31] = KEY_LEFTMETA, + [0x34] = KEY_SPACE, + [0x4d] = KEY_SLASH, + [0x04] = KEY_LEFT, + [0x05] = KEY_DOWN, + [0x01] = KEY_RIGHT, +}; + +/* + * Numlock mappings. This table defines only the special keys + * which are affected by the numlock mode. Other keys are mapped + * by the above table. + */ +static unsigned char numlck_map[NBP_NUMKEYCODES] = { + [0x2d] = KEY_KP7, + [0x1d] = KEY_KP8, + [0x1e] = KEY_KP9, + [0x11] = KEY_KPSLASH, + + [0x2c] = KEY_KP4, + [0x1c] = KEY_KP5, + [0x1f] = KEY_KP6, + [0x12] = KEY_KPASTERISK, + + [0x2b] = KEY_KP1, + [0x1b] = KEY_KP2, + [0x20] = KEY_KP3, + [0x13] = KEY_KPMINUS, + + [0x2a] = KEY_KP0, + [0x28] = KEY_KPDOT, + [0x5f] = KEY_KPPLUS, +}; + +/* + * mappings for function key - note *ONLY* keys that produce a unique + * keycode should be here - others should be handled in userspace as + * keymap modifiers , fn is the left meta modifier + */ +static unsigned char fn_map[NBP_NUMKEYCODES] = { + [0x02] = KEY_CAPSLOCK, + [0x41] = KEY_UNKNOWN, + [0x4d] = KEY_F1, + [0x04] = KEY_HOME, + [0x60] = KEY_PAGEUP, + [0x05] = KEY_PAGEDOWN, + [0x01] = KEY_END, + [0x28] = KEY_BRIGHTNESSDOWN, + [0x5f] = KEY_BRIGHTNESSUP, + [0x5b] = KEY_SLEEP +}; + +/* + * Record the state of key presses, so that we can do the right thing + * after the FN key is released. + */ +#define KP_NONE (0) +#define KP_DOWN (1 << 0) +#define KP_FN (1 << 1) +#define KP_NUMLCK (1 << 2) + +static unsigned char keystate[NBP_NUMKEYCODES]; + +static int num_lck; + +#define key_fn (keystate[0x31] & KP_DOWN) +#define key_shift (keystate[0x15] & KP_DOWN) +#define key_alt (keystate[0x41] & KP_DOWN) + +static void nbp_keyb_process_key(unsigned int key) +{ + unsigned char *map = normal_map; + unsigned int up; + + up = key & 0x80; + key &= 0x7f; + + /* + * Use the Fn map if the function key is pressed and this + * is a key press, or the key is currently in "pressed Fn" + * state. We do not want to map a press without Fn and a + * subsequent release of the same key with Fn to different + * keycodes. + * + * Ditto for the numlock state. + */ + if (fn_map[key] && ((key_fn && !up) || keystate[key] & KP_FN)) { + keystate[key] |= KP_FN; + map = fn_map; + } else if (numlck_map[key] && ((num_lck && !up) || keystate[key] & KP_NUMLCK)) { + keystate[key] |= KP_NUMLCK; + map = numlck_map; + } + + /* + * The KP_DOWN bit always indicates whether a key is + * pressed or released. Releases always clear all + * state about the key. + */ + if (up) + keystate[key] = KP_NONE; + else + keystate[key] |= KP_DOWN; + + /* + * Now handle the special right-shift + alt combination + * for numlock. + */ + if (key == 0x41 && key_shift && key_alt) /* alt key */ + num_lck ^= 1; + + /* + * Map the keycode from the LX keyboard code to the input + * subsystem keycode, using the appropriate mapping table. + */ + key = map[key]; + + /* + * Some keys have additional operations after we have + * decoded them. We don't report these "special" keys + * to the input layer. + */ + switch (key) { + case KEY_BRIGHTNESSUP: + if(!up) + netbookpro_backlight_mod(4); + break; + + case KEY_BRIGHTNESSDOWN: + if(!up) + netbookpro_backlight_mod(-4); + break; + + case KEY_SLEEP: + if(!up) { +#ifdef TEST_ALARM_WAKEUP + RTAR = RCNR + 60; +#endif + DBG("System suspend by keyboard\n"); +#ifdef CONFIG_APM + apm_queue_event(APM_SYS_SUSPEND); +#endif + } + break; + + default: + input_report_key(&kbd_dev, key, !up); + break; + } +} + +static void nbp_keyb_reset(void) +{ + unsigned char *map; + int key; + + for (key = 0; key < NBP_NUMKEYCODES; key++) + if (keystate[key] & KP_DOWN) { + map = normal_map; + if (keystate[key] & KP_FN) + map = fn_map; + else if (keystate[key] & KP_NUMLCK) + map = numlck_map; + keystate[key] = KP_NONE; + input_report_key(&kbd_dev, map[key], 0); + } + + num_lck = 0; +} + +static int nbp_keyb_sendready(int flags) +{ + int ret; + + DBG("nbp_keyb_sendready: flags=%d\n", flags); + + ret = nbp_pcon_command(PCON_KEYBOARD_CMD, + NETBOOKPRO_PCON_KEYBOARD_CMD_VAL_READY, + flags, NULL); + + DBG("nbp_keyb_sendready: returned %d\n", ret); + + return ret < 0 ? 1 : 0; +} + + +static int nbp_keyb_eeprom_write(struct i2c_eeprom_emu *eep, + int addr, int val) +{ + DBG("nbp_keyb_eeprom_write(%p,%d,0x%02x)\n", eep, addr, val); + + if (addr != 0) + return 0; + + nbp_keyb_sendready(PCON_FLG_ASYNC); + nbp_keyb_process_key((unsigned char)val); + + return 0; +} + +static struct i2c_eeprom_emu_watcher nbp_keyb_watcher = { + .read = NULL, + .write = nbp_keyb_eeprom_write +}; + +static int nbp_keyb_add(struct pcon_device *cdev) +{ + int i; + + init_input_dev(&kbd_dev); + + kbd_dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP); + kbd_dev.keycode = normal_map; + kbd_dev.keycodesize = sizeof(unsigned char); + kbd_dev.keycodemax = ARRAY_SIZE(normal_map); + + for (i = 0; i < ARRAY_SIZE(normal_map); i++) { + if (normal_map[i]) + __set_bit(normal_map[i], kbd_dev.keybit); + } + for (i = 0; i < ARRAY_SIZE(numlck_map); i++) { + if (numlck_map[i]) + __set_bit(numlck_map[i], kbd_dev.keybit); + } + for (i = 0; i < ARRAY_SIZE(fn_map); i++) { + if (fn_map[i]) + __set_bit(fn_map[i], kbd_dev.keybit); + } + + kbd_dev.name = "NetBookPro Keyboard"; + kbd_dev.phys = "pcon/input0"; + kbd_dev.dev = cdev->dev; + + input_register_device(&kbd_dev); + + printk(KERN_INFO "input: %s\n", kbd_dev.name); + + i2c_eeprom_emu_addwatcher(i2c_pxa_get_eeprom(), + NETBOOKPRO_I2C_EEPEMU_KEYBOARD_START, + NETBOOKPRO_I2C_EEPEMU_KEYBOARD_SIZE, + &nbp_keyb_watcher); + + return 0; +} + +static void nbp_keyb_remove(struct pcon_device *cdev) +{ + /* what about our eeprom emulation? */ + + input_unregister_device(&kbd_dev); +} + +static int nbp_keyb_resume(struct pcon_device *cdev) +{ + nbp_keyb_reset(); + return 0; +} + +static struct pcon_interface nbp_keyb_intf = { + .add = nbp_keyb_add, + .remove = nbp_keyb_remove, + .resume = nbp_keyb_resume, +}; + +static int __init nbp_keyb_init(void) +{ + printk(KERN_INFO "NetBookPro Keyboard Driver, (c) 2004 Simtec Electronics\n"); + + return pcon_drv_add(&nbp_keyb_intf); +} + +static void __exit nbp_keyb_exit(void) +{ + pcon_drv_remove(&nbp_keyb_intf); +} + +module_init(nbp_keyb_init); +module_exit(nbp_keyb_exit); + +MODULE_AUTHOR("Ben Dooks, Simtec Electronics "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Psion Teklogix NetBookPro Keyboard Driver"); + +module_param(debug, int, 0600); diff -NaurbB linux-2.6.9-pre1-bk18/drivers/misc/Makefile linux-2.6.9_nbp/drivers/misc/Makefile --- linux-2.6.9-pre1-bk18/drivers/misc/Makefile 2004-08-14 07:38:04.000000000 +0200 +++ linux-2.6.9_nbp/drivers/misc/Makefile 2004-09-14 12:25:06.000000000 +0200 @@ -4,3 +4,4 @@ obj- := misc.o # Dummy rule to force built-in.o to be made obj-$(CONFIG_IBM_ASM) += ibmasm/ +obj-$(CONFIG_MACH_NETBOOKPRO) += netbookpro/ \ Kein Zeilenumbruch am Dateiende. diff -NaurbB linux-2.6.9-pre1-bk18/drivers/misc/netbookpro/Makefile linux-2.6.9_nbp/drivers/misc/netbookpro/Makefile --- linux-2.6.9-pre1-bk18/drivers/misc/netbookpro/Makefile 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/drivers/misc/netbookpro/Makefile 2004-10-15 21:24:17.000000000 +0200 @@ -0,0 +1,5 @@ +obj-$(CONFIG_MACH_NETBOOKPRO) := nbp-spi.o +obj-$(CONFIG_MACH_NETBOOKPRO) += nbp-battmon.o +obj-$(CONFIG_MACH_NETBOOKPRO) += nbp-power.o +obj-$(CONFIG_MACH_NETBOOKPRO) += nvram.o +obj-$(CONFIG_APM) += nbp-apm.o diff -NaurbB linux-2.6.9-pre1-bk18/drivers/misc/netbookpro/nbp-apm.c linux-2.6.9_nbp/drivers/misc/netbookpro/nbp-apm.c --- linux-2.6.9-pre1-bk18/drivers/misc/netbookpro/nbp-apm.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/drivers/misc/netbookpro/nbp-apm.c 2004-09-21 01:20:34.000000000 +0200 @@ -0,0 +1,133 @@ +/* drivers/misc/netbookpro/nbp-apm.c + * + * (c) 2004 Simtec Electronics + * Written by Ben Dooks +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "nbp-power.h" + +static void nbp_apm_get_powerstatus(struct apm_power_info *pwr) +{ + struct pcon_battinfo batt; + + pr_debug("nbp_apm_get_powerstatus(%p)\n", pwr); + + if (nbp_battmon_getstate(&batt)) { + pr_debug("apm: failed to read power info\n"); + return; + } + + if (batt.flags & NBP_PCON_PWRF_ACPresent) + pwr->ac_line_status = APM_AC_ONLINE; + else + pwr->ac_line_status = APM_AC_OFFLINE; + + if (batt.flags & NBP_PCON_PWRF_BatteryPreset) { + if (batt.charge_state == NBP_PCON_CS_Charging) { + pwr->battery_status = APM_BATTERY_STATUS_CHARGING; + pwr->battery_flag = APM_BATTERY_FLAG_CHARGING; + } else { + pwr->battery_flag = 0; + } + + if (batt.main_batt_capacity_percent >= 20) { + pwr->battery_status = APM_BATTERY_STATUS_HIGH; + pwr->battery_flag |= APM_BATTERY_FLAG_HIGH; + } else if (batt.main_batt_capacity_percent >= 5) { + pwr->battery_status = APM_BATTERY_STATUS_LOW; + pwr->battery_flag |= APM_BATTERY_FLAG_LOW; + } else { + pwr->battery_status = APM_BATTERY_STATUS_CRITICAL; + pwr->battery_flag |= APM_BATTERY_FLAG_CRITICAL; + } + + if (batt.discharge_current) { + /* + * max_capacity is in mAh, discharge_current is mA, + * so mAh/mA = hours not minutes. --rmk + */ + pwr->units = APM_UNITS_SECS; + pwr->time = batt.max_capacity * + batt.main_batt_capacity_percent * 36 / + batt.discharge_current; + + if (pwr->time >= 32767) { + pwr->time /= 60; + pwr->units = APM_UNITS_MINS; + } + } + + pwr->battery_life = batt.main_batt_capacity_percent; + } else { + pwr->battery_status = APM_BATTERY_STATUS_NOT_PRESENT; + pwr->battery_flag = APM_BATTERY_FLAG_NOT_PRESENT; + } +} + +/* + * irq 1 is "power failing" + * irq 2 is "please suspend" + */ +static irqreturn_t power_irq(int irqno, void *arg, struct pt_regs *regs) +{ + apm_queue_event((apm_event_t)(int)arg); + return IRQ_HANDLED; +} + +static int __init nbp_apm_init(void) +{ + int ret; + + ret = request_irq(IRQ_GPIO(0), power_irq, SA_INTERRUPT, + "Battery Warning", (void *)APM_CRITICAL_SUSPEND); + if (ret) { + printk(KERN_ERR "Failed to get irq for battery warning\n"); + return ret; + } + + ret = request_irq(IRQ_GPIO(1), power_irq, SA_INTERRUPT, + "Suspend Request", (void *)APM_SYS_SUSPEND); + if (ret) { + printk(KERN_ERR "Failed to get irq for suspend request\n"); + free_irq(IRQ_GPIO(0), (void *)APM_CRITICAL_SUSPEND); + return ret; + } + + set_irq_type(IRQ_GPIO(0), IRQT_RISING); + set_irq_type(IRQ_GPIO(1), IRQT_BOTHEDGE); + + apm_get_power_status = nbp_apm_get_powerstatus; + + return 0; +} + +static void __exit nbp_apm_exit(void) +{ + free_irq(IRQ_GPIO(1), (void *)APM_SYS_SUSPEND); + free_irq(IRQ_GPIO(0), (void *)APM_CRITICAL_SUSPEND); + + apm_get_power_status = NULL; +} + +module_init(nbp_apm_init); +module_exit(nbp_apm_exit); + +MODULE_AUTHOR("Ben Dooks, Simtec Electronics "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Psion Teklogix NetBookPro APM interface"); diff -NaurbB linux-2.6.9-pre1-bk18/drivers/misc/netbookpro/nbp-battmon.c linux-2.6.9_nbp/drivers/misc/netbookpro/nbp-battmon.c --- linux-2.6.9-pre1-bk18/drivers/misc/netbookpro/nbp-battmon.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/drivers/misc/netbookpro/nbp-battmon.c 2004-10-07 18:19:05.000000000 +0200 @@ -0,0 +1,427 @@ +/* drivers/misc/netbookpro/nbp-battmon.c + * + * (c) 2004 Simtec Electronics + * Written by Ben Dooks +*/ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "nbp-power.h" + +#define PFX "nbp-battmon: " + +static int pid; +static int time_to_die; +static int recv_data; +static int thr_wakeup; + +static unsigned long last_at; +static struct pcon_battinfo last_data; + +static wait_queue_head_t thr_wait; +static wait_queue_head_t thr_sleep; +static struct completion thr_complete; + +static struct proc_dir_entry *proc_drv_ent; +static struct proc_dir_entry *proc_stat_ent; + +static char proc_fname_drv[] = "driver/battery"; +static char proc_fname_stat[] = "driver/battery-stats"; + +struct power_stats { + unsigned long ok; + unsigned long bad; +}; + +static struct power_stats stats; + + +/* code */ + +static int nbp_battmon_setstate(int enable) +{ + int ret; + + printk(KERN_DEBUG PFX "nbp_battmon_setstate: %d\n", enable); + + ret = nbp_pcon_command(PCON_MONITORBATTERY, enable, 0, NULL); + + printk(KERN_DEBUG PFX "nbp_battmon_setstate: returned %d\n", ret); + + return ret < 0 ? 1 : 0; +} + +void nbp_battmon_start(void) +{ + printk(KERN_DEBUG PFX "issuing start...\n"); + + thr_wakeup = 1; + wmb(); + wake_up(&thr_sleep); +} + +static int nbp_battmon_thread(void *param) +{ + unsigned long timeout = 60*HZ; + int frozen = 0; + + printk(KERN_DEBUG PFX "thread starting\n"); + + daemonize("nbp-battmon"); + allow_signal(SIGTERM); + + while (1) { + timeout = HZ*60; + + do { + timeout = interruptible_sleep_on_timeout(&thr_sleep, + timeout); + + /* make swsusp happy with our thread */ + if (current->flags & PF_FREEZE) { + frozen = 1; + refrigerator(PF_FREEZE); + } + + } while (!signal_pending(current) && + timeout > 0 && !thr_wakeup); + + if (thr_wakeup || timeout <= 0) + break; + } + + printk(KERN_DEBUG PFX "thread woken\n"); + + if (nbp_battmon_setstate(1) == 0) { + printk(KERN_INFO PFX "Battery monitor ready\n"); + } + + while (1) { + retry: + timeout = 30*HZ; + + do { + timeout = interruptible_sleep_on_timeout(&thr_wait, + timeout); + + /* make swsusp happy with our thread */ + if (current->flags & PF_FREEZE) { + frozen = 1; + refrigerator(PF_FREEZE); + } + } while (!signal_pending(current) && + timeout > 0 && + !recv_data && !thr_wakeup); + + if (signal_pending(current)) { + printk(KERN_DEBUG PFX "signal pending\n"); + break; + } + + if (time_to_die) { + printk(KERN_DEBUG PFX "time to die\n"); + break; + } + + if (frozen) { + printk(KERN_DEBUG PFX "waiting for re-start\n"); + + if (!thr_wakeup) { + goto retry; + } + + if (nbp_battmon_setstate(1) == 0) { + printk(KERN_INFO PFX "Battery monitor ready\n"); + } + + printk(KERN_DEBUG PFX "ok, restarting\n"); + frozen = 0; + } + + while (recv_data) { + recv_data--; + } + + if (thr_wakeup) { + thr_wakeup = 0; + + if (0) { + nbp_battmon_setstate(1); + nbp_spi_reset(); + } + + printk(KERN_INFO PFX "system restarted\n"); + } + + if (timeout <= 0) { + printk(KERN_INFO PFX "timeout waiting for state, " + "retrying enable%s\n", + (jiffies - last_at) > (HZ*60) ? " and reset" : ""); + + /* policy on timeout: + * first, we'll try re-issuing the battery monitor cmd + * second, issue the command and reset the spi + */ + + if (1) { + nbp_battmon_setstate(1); + + if ((jiffies - last_at) > (HZ * 60)) { + nbp_spi_reset(); + } + } + } + } + + printk(KERN_DEBUG PFX "thread exiting\n"); + + complete_and_exit (&thr_complete, 0); +} + +void nbp_battmon_rx(void *data) +{ + unsigned int *src = data; + unsigned int *dst; + struct pcon_battinfo tmp_data; + unsigned int tmp, res; + int count; + + dst = (unsigned int *)&tmp_data; + + // need to 16bit byteswap the data + + pr_debug(PFX "%ld seconds since last good measurement\n", + (jiffies - last_at) / HZ); + + for (count = 0; count < sizeof(last_data)/sizeof(*dst); count++) { + tmp = src[count]; + res = tmp >> 8; + res &= ~0xff00; + res |= (tmp << 8) & ~0xff0000; + dst[count] = res; + } + + pr_debug(PFX "=> magic=%04x, flags=%04x, batt_cap=%d\n", + tmp_data.magic, tmp_data.flags, + tmp_data.main_batt_capacity_percent); + + if (tmp_data.magic == NETBOOKPRO_PCON_BATTINFO_MAGIC) { + memcpy(&last_data, &tmp_data, sizeof(last_data)); + + last_at = jiffies; + recv_data++; + stats.ok++; + + wake_up(&thr_wait); + } else { + stats.bad++; + } +} + +int nbp_battmon_getstate(pcon_battinfo_t *info) +{ + unsigned long flags; + int ret = -ENOENT; + + local_irq_save(flags); + if (last_data.magic == NETBOOKPRO_PCON_BATTINFO_MAGIC) { + ret = 0; + memcpy(info, &last_data, sizeof(*info)); + } + local_irq_restore(flags); + return ret; +} + + +int nbp_battmon_proc_rd_stats(char *page, + char **start, + off_t off, + int count, + int *eof, + void *data) +{ + unsigned long timediff; + int i; + + /* work out how long since last management frame received */ + timediff = jiffies - last_at; + timediff /= (HZ/10); + + i = sprintf(page+0, "Battery Monitoring Stats:\n"); + i+= sprintf(page+i, "\n"); + + i+= sprintf(page+i, "Good frames: %ld\n", stats.ok); + i+= sprintf(page+i, "Bad frames: %ld\n", stats.bad); + i+= sprintf(page+i, "Time since last frame %ld.%ld seconds\n", + timediff / 10, timediff % 10); + + return i; +} + + +int nbp_battmon_proc_rd_drv(char *page, + char **start, + off_t off, + int count, + int *eof, + void *data) +{ + struct pcon_battinfo bi; + unsigned long last_wrote; + unsigned long flags; + int i; + + local_irq_save(flags); + last_wrote = last_at; + memcpy(&bi, &last_data, sizeof(bi)); + local_irq_restore(flags); + + i = sprintf(page+0, "\nBattery State:"); + + i += sprintf(page+i, " Main Battery %sPresent\n", + ( bi.flags & NBP_PCON_PWRF_BatteryPreset ) ? "" : "Not "); + + i += sprintf(page+i, " AC %sPlugged In\n", + ( bi.flags & NBP_PCON_PWRF_ACPresent) ? "" : "Not " ); + + i += sprintf(page+i, " Charging %s\n", + ( bi.flags & NBP_PCON_PWRF_ChargeEnabled ) ? "Enabled" : "Disabled"); + + if( !(bi.flags & NBP_PCON_PWRF_BackupBatteryPreset) ) + i += sprintf(page+i, " Backup Battery Not Present\n"); + if( !(bi.flags & NBP_PCON_PWRF_Calibrated) ) + i += sprintf(page+i, " Main Battery Not Calibrated\n"); + if( bi.flags & NBP_PCON_PWRF_Calibrating ) + i += sprintf(page+i, " Main Battery Calibrating\n"); + i += sprintf(page+i, "\nBattery Levels:"); + i += sprintf(page+i, "Maximum Capacity: %d\n", + bi.max_capacity ); + i += sprintf(page+i, " Temperature: %d\n", + bi.temperature ); + i += sprintf(page+i, " Main Battery mV: %d\n", + bi.main_batt_voltage ); + i += sprintf(page+i, " Backup Battery mV: %d\n", + bi.backup_batt_voltage ); + i += sprintf(page+i, " Main Battery Capacity %%: %d\n", + bi.main_batt_capacity_percent ); + i += sprintf(page+i, " Backup Battery Capacity %%: %d\n", + bi.backup_batt_capacity_percent ); + i += sprintf(page+i, " Charge Current: %d\n", + bi.charge_current ); + i += sprintf(page+i, " Discharge Current: %d\n", + bi.discharge_current ); + i += sprintf(page+i, "\nCharge State:"); + + switch( (nbp_pcon_chargestate_t)bi.charge_state ) + { + case NBP_PCON_CS_NotCharging: + i += sprintf(page+i, " Not Charging\n"); + break; + case NBP_PCON_CS_Charging: + i += sprintf(page+i, " Charging\n"); + break; + case NBP_PCON_CS_QuickChargeComplete: + i += sprintf(page+i, " Quick Charging\n"); + break; + case NBP_PCON_CS_ChargeComplete: + i += sprintf(page+i, " Charging Complete\n"); + break; + default: + i += sprintf(page+i, " Unknown state (%d)\n", bi.charge_state); + break; + } + + return i; +} + + + + + +int __init nbp_battmon_init(void) +{ + printk(KERN_INFO "NetBookPro Battery Monitor, (c) 2004 Simtec Electronics"); + + memset(&stats, 0, sizeof(stats)); + + init_waitqueue_head (&thr_wait); + init_waitqueue_head (&thr_sleep); + init_completion (&thr_complete); + + pid = -1; + time_to_die = 0; + thr_wakeup = 0; + + if (1) { + pid = kernel_thread(nbp_battmon_thread, NULL, 0); + if (pid < 0) { + printk(PFX "error %d starting driver thread\n", pid); + return pid; + } + + printk(KERN_DEBUG "started thread %d\n", pid); + } + + proc_drv_ent = create_proc_read_entry(proc_fname_drv, 0, NULL, + nbp_battmon_proc_rd_drv, NULL); + + if (proc_drv_ent == NULL) { + printk(KERN_ERR PFX "failed to create /proc/%s entry\n", + proc_fname_drv); + } else { + printk(KERN_INFO PFX "registers /proc/%s\n", proc_fname_drv); + } + + proc_stat_ent = create_proc_read_entry(proc_fname_stat, 0, NULL, + nbp_battmon_proc_rd_stats, NULL); + + if (proc_stat_ent == NULL) { + printk(KERN_ERR PFX "failed to create /proc/%s entry\n", + proc_fname_stat); + } else { + printk(KERN_INFO PFX "registers /proc/%s\n", proc_fname_stat); + } + + return 0; +} + +void __exit nbp_battmon_exit(void) +{ + int ret; + + printk(KERN_DEBUG PFX "killing driver thread\n"); + + if (pid >= 0) { + time_to_die = 1; + wmb(); + + ret = kill_proc(pid, SIGTERM, 1); + + if (ret < 0) { + printk(KERN_ERR PFX "failed to signal thread exit\n"); + return; + } + + wait_for_completion(&thr_complete); + } +} + +module_init(nbp_battmon_init); +module_exit(nbp_battmon_exit); + +MODULE_AUTHOR("Ben Dooks, Simtec Electronics "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Psion Teklogix NetBookPro Battery Monitor"); diff -NaurbB linux-2.6.9-pre1-bk18/drivers/misc/netbookpro/nbp-power.c linux-2.6.9_nbp/drivers/misc/netbookpro/nbp-power.c --- linux-2.6.9-pre1-bk18/drivers/misc/netbookpro/nbp-power.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/drivers/misc/netbookpro/nbp-power.c 2004-11-19 22:05:34.000000000 +0100 @@ -0,0 +1,349 @@ +/* drivers/misc/netbookpro/nbp-power.c + * + * (c) 2004 Simtec Electronics + * Written by Ben Dooks + * + * $Id: nbp-power.c,v 1.16 2004/09/14 20:24:50 rmk Exp $ +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +#include "nbp-power.h" + +#define PFX "nbp-power: " + +static int debug; + +#define DBG(x...) \ + do { \ + if (debug) \ + pr_debug("nbp-power: " x); \ + } while (0) + +static struct i2c_eeprom_emu *nbp_pwr_eeprom; + +static int pid; +static int time_to_die; +static int recv_data; + +static DECLARE_WAIT_QUEUE_HEAD(ext_wait); +static DECLARE_WAIT_QUEUE_HEAD(thr_wait); +static DECLARE_COMPLETION(thr_complete); + +static pcon_pwrstate_t pwr_state; +static unsigned long pwr_lastseen; + +/* code */ + + +static const char *nbp_pwr_decodestate(pcon_pwrstate_t state) +{ + switch (state) { + case PCON_PWRSTATE_HWReset: + return "Hardware-Reset"; + + case PCON_PWRSTATE_SWReset: + return "Software-Reset"; + + case PCON_PWRSTATE_Off: + return "Off"; + + case PCON_PWRSTATE_Suspend: + return "Suspend"; + + case PCON_PWRSTATE_Sleep: + return "Sleep"; + + case PCON_PWRSTATE_Booting: + return "Booting"; + + case PCON_PWRSTATE_LoadingOS: + return "Loading-OS"; + + case PCON_PWRSTATE_Idle: + return "Idle"; + + case PCON_PWRSTATE_Run: + return "Run"; + + case PCON_PWRSTATE_Comatose: + return "Comatose"; + + case PCON_PWRSTATE_PowerFail: + return "Power-Fail"; + } + + return "Unknown-Power-State"; +} + +static int nbp_pwr_eeprom_write(struct i2c_eeprom_emu *eep, + int addr, int val) +{ + DBG("nbp_pwr_eeprom_write(%p,%d,0x%02x)\n", eep, addr, val); + + recv_data = 1; + wmb(); + wake_up(&thr_wait); + wake_up(&ext_wait); + + DBG("received power level %d (%s)\n", val, nbp_pwr_decodestate(val)); + + return 0; +} + +static struct i2c_eeprom_emu_watcher nbp_pwr_watcher = { + .read = NULL, + .write = nbp_pwr_eeprom_write +}; + +/* + * This is inherently racy. + */ +int nbp_pwr_waitchange(void) +{ + unsigned long timeout = HZ*2; + + interruptible_sleep_on_timeout(&ext_wait, timeout); + + if (timeout == 0) { + printk(KERN_ERR ": failed to get power state change\n"); + return -ETIMEDOUT; + } + + return nbp_pwr_eeprom->bytes[NETBOOKPRO_I2C_EEPEMU_POWER_START].val; +} + + +#ifndef CONFIG_APM +/* + * This is the "old" suspend management code. We now go via + * the APM layer so that userspace is informed of changes. + * See nbp-apm.c for implementation. + */ +static int do_suspend; +static int suspmon_pid; +static DECLARE_WAIT_QUEUE_HEAD(suspmon_wait); +static DECLARE_COMPLETION(suspmon_complete); + +static irqreturn_t power_irq(int irqno, void *arg, struct pt_regs *regs) +{ + do_suspend = 1; + wmb(); + wake_up(&suspmon_wait); + + return IRQ_HANDLED; +} + +static int nbp_pwr_suspmon(void *arg) +{ + daemonize("nbp-suspendmon"); + current->flags |= PF_NOFREEZE; + + while (1) { + wait_event_interruptible(suspmon_wait, do_suspend || time_to_die); + + if (time_to_die) + break; + + if (do_suspend) { + do_suspend = 0; + + DBG("System suspending\n"); + pm_suspend(PM_SUSPEND_MEM); + DBG("pm_suspend() returned\n"); + } + + DBG("suspendmon thread run\n"); + } + + DBG("suspendmon thread exiting\n"); + + complete_and_exit (&suspmon_complete, 0); +} + +static int __init suspmon_init(void) +{ + int ret; + + ret = kernel_thread(nbp_pwr_suspmon, NULL, CLONE_KERNEL); + if (ret < 0) { + printk(PFX "error %d starting driver thread", suspmon_pid); + return ret; + } + if (request_irq(IRQ_GPIO(0), power_irq, + SA_INTERRUPT, "Battery Warning", NULL) < 0) { + printk(KERN_ERR "Failed to get irq for battery warning\n"); + } + + if (request_irq(IRQ_GPIO(1), power_irq, + SA_INTERRUPT, "Suspend Request", NULL) < 0) { + printk(KERN_ERR "Failed to get irq for suspend request\n"); + } + + set_irq_type(IRQ_GPIO(0), IRQT_RISING); + set_irq_type(IRQ_GPIO(1), IRQT_BOTHEDGE); + + return 0; +} + +static void suspmon_exit(void) +{ + wake_up(&suspmon_wait); + wait_for_completion(&suspmon_complete); +} +#else +#define suspmon_init() (0) +#define suspmon_exit() do { } while (0) +#endif + + +static int nbp_pwr_thread(void *arg) +{ + daemonize("nbp-powermgr"); + current->flags |= PF_NOFREEZE; + + nbp_pcon_command(PCON_POWER_STATE, PCON_PWRSTATE_Run, 0, NULL); + + while (1) { + wait_event_interruptible(thr_wait, recv_data || time_to_die); + + if (time_to_die) { + DBG("time to die\n"); + break; + } + + if (recv_data) { + pcon_pwrstate_t state; + recv_data = 0; + + state = nbp_pwr_eeprom->bytes[NETBOOKPRO_I2C_EEPEMU_POWER_START].val; + printk(KERN_INFO "Power state is now %s (%d)\n", + nbp_pwr_decodestate(state), state); + + switch (state) { + case PCON_PWRSTATE_Run: + printk(KERN_INFO "starting power monitor\n"); + nbp_battmon_start(); + break; + + default: + state = state; + } + + pwr_state = state; + pwr_lastseen = jiffies; + } + } + + DBG("thread exiting\n"); + + complete_and_exit (&thr_complete, 0); +} + +static void nbp_pwr_power_off(void) +{ + nbp_pcon_command(PCON_POWER_STATE, PCON_PWRSTATE_Off, 0, NULL); +} + +static void nbp_pwr_restart(void) +{ + nbp_pcon_command(PCON_POWER_STATE, PCON_PWRSTATE_HWReset, 0, NULL); +} + +static int nbp_pwr_add(struct pcon_device *cdev) +{ + nbp_pwr_eeprom = i2c_pxa_get_eeprom(); + + i2c_eeprom_emu_addwatcher(nbp_pwr_eeprom, + NETBOOKPRO_I2C_EEPEMU_POWER_START, + NETBOOKPRO_I2C_EEPEMU_POWER_SIZE, + &nbp_pwr_watcher); + + pid = kernel_thread(nbp_pwr_thread, NULL, CLONE_KERNEL); + if (pid < 0) { + printk(PFX "error %d starting driver thread\n", pid); + return pid; + } + + DBG("driver started thread %d\n", pid); + + pm_power_off = nbp_pwr_power_off; + pm_restart = nbp_pwr_restart; + + return 0; +} + +static void nbp_pwr_remove(struct pcon_device *cdev) +{ + int ret; + + pm_restart = NULL; + pm_power_off = NULL; + + DBG("Killing driver thread\n"); + + if (pid >= 0) { + time_to_die = 1; + wmb(); + ret = kill_proc(pid, SIGTERM, 1); + + if (ret < 0) { + printk(KERN_ERR PFX "failed to signal thread exit\n"); + } else { + wait_for_completion(&thr_complete); + } + } + + /* FIXME - what about our eeprom? */ +} + +static struct pcon_interface nbp_pwr_intf = { + .add = nbp_pwr_add, + .remove = nbp_pwr_remove, +}; + +static int __init nbp_pwr_init(void) +{ + int ret; + + printk(KERN_INFO "NetBookPro Power Manager, (c) 2004 Simtec Electronics\n"); + + ret = suspmon_init(); + if (ret == 0) { + ret = pcon_drv_add(&nbp_pwr_intf); + if (ret) + suspmon_exit(); + } + + return ret; +} + +static void __exit nbp_pwr_exit(void) +{ + pcon_drv_remove(&nbp_pwr_intf); + suspmon_exit(); +} + +module_init(nbp_pwr_init); +module_exit(nbp_pwr_exit); + +MODULE_AUTHOR("Ben Dooks, Simtec Electronics "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Psion Teklogix NetBookPro Power Management"); + +module_param(debug, int, 0600); diff -NaurbB linux-2.6.9-pre1-bk18/drivers/misc/netbookpro/nbp-power.h linux-2.6.9_nbp/drivers/misc/netbookpro/nbp-power.h --- linux-2.6.9-pre1-bk18/drivers/misc/netbookpro/nbp-power.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/drivers/misc/netbookpro/nbp-power.h 2004-10-05 16:40:35.000000000 +0200 @@ -0,0 +1,44 @@ + +extern void nbp_battmon_rx(void *data); +extern void nbp_battmon_start(void); +extern void nbp_spi_reset(void); + +typedef enum nbp_pcon_pwrflags { + NBP_PCON_PWRF_BatteryPreset = 0x0001, + NBP_PCON_PWRF_ACPresent = 0x0002, + NBP_PCON_PWRF_ChargeEnabled = 0x0004, + NBP_PCON_PWRF_BackupBatteryPreset = 0x0008, + NBP_PCON_PWRF_Calibrated = 0x0010, + NBP_PCON_PWRF_Calibrating = 0x0020 +} nbp_pcon_pwrflags_t;; + +typedef enum nbp_pcon_chargestate { + NBP_PCON_CS_NotCharging = 0, + NBP_PCON_CS_Charging = 1, + NBP_PCON_CS_QuickChargeComplete = 2, + NBP_PCON_CS_ChargeComplete = 3 +} nbp_pcon_chargestate_t; + + +/* pcon_battinfo_t should be 32bytes long */ + +typedef struct pcon_battinfo { + u16 flags; + u16 unused[4]; + u16 max_capacity; + u16 temperature; + u16 charge_state; + u16 main_batt_voltage; + u16 backup_batt_voltage; + u8 main_batt_capacity_percent; + u8 backup_batt_capacity_percent; + u16 charge_current; + u16 discharge_current; + u16 pad[2]; + u16 magic; +} __attribute__((__packed__)) pcon_battinfo_t; + +#define NETBOOKPRO_PCON_BATTINFO_MAGIC (0xADB0) + +extern int nbp_battmon_getstate(pcon_battinfo_t *info); + diff -NaurbB linux-2.6.9-pre1-bk18/drivers/misc/netbookpro/nbp-spi.c linux-2.6.9_nbp/drivers/misc/netbookpro/nbp-spi.c --- linux-2.6.9-pre1-bk18/drivers/misc/netbookpro/nbp-spi.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/drivers/misc/netbookpro/nbp-spi.c 2004-10-05 16:40:35.000000000 +0200 @@ -0,0 +1,412 @@ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +#include "nbp-power.h" + +#define PFX "nbp-spi: " + +/* this driver is simply to deal with receiving SPI transfers via + * dma. The transmit side is set to just send the same buffer each time + * to ensure nothing is transmitted. + * + * The receive buffers are treated like a ring buffer, with the last + * buffer in the ring marked as the stop point. When the spi request is + * deal with, the buffer is marked as free again. +*/ + +#define SPI_ENABLE (1) + +#define SPI_DMASIZE (32) +#define SPI_RXBUFFERS (12) + +#define IS_LASTRX(x) ((x) >= (SPI_RXBBUFFERS-1)) + +#define SPI_RXDMA_CMD \ + ( DCMD_INCTRGADDR | \ + DCMD_FLOWSRC | \ + DCMD_ENDIRQEN | \ + DCMD_BURST32 | \ + DCMD_WIDTH2 | \ + SPI_DMASIZE ) + +// DCMD_ENDIAN + + +//#define TX_IRQCFG (DCMD_ENDIRQEN) +#define TX_IRQCFG (0) + +#define SPI_TXDMA_CMD \ + ( DCMD_INCSRCADDR | \ + DCMD_FLOWSRC | \ + DCMD_BURST32 | \ + DCMD_WIDTH2 | \ + TX_IRQCFG | \ + SPI_DMASIZE ) + +/* size of this structure must be aligned to 4 words */ +struct spi_buffer { + pxa_dma_desc dma_desc; + unsigned char data[SPI_DMASIZE]; +}; + +/* + * This describes the DMA data buffer. + */ +struct spi_dma_data { + struct spi_buffer rx[SPI_RXBUFFERS] __attribute__((aligned(16))); + struct spi_buffer tx __attribute__((aligned(16))); +}; + +/* + * This macro returns the DMA address of element 'x' from the above + * structure. + */ +#define SPI_DMAPTR(x) (spi_dmaaddr + offsetof(struct spi_dma_data, x)) + +/* dma descriptors for the channels */ + +static dma_addr_t spi_dmaaddr; +static struct spi_dma_data *spi_cpuaddr; +static int rx_chan; +static int tx_chan; + +/* keep a mapping of the dma buffer's physical addresses for reference */ +static dma_addr_t spi_rxphys[SPI_RXBUFFERS]; + +/* receive state information */ + +static unsigned int rx_last; /* last read */ + +static int tx_irqs; + +#if 0 +static void dbg(const char *msg,...) +{ + va_list va; + char buff[1024]; + + va_start(va, msg); + vsprintf(buff, msg, va); + va_end(va); + + printascii(buff); +} +#else +#define dbg(msg,x...) do { } while (0) /*printk(KERN_DEBUG msg ,##x )*/ +#endif + +#if 0 +static void dbg_buff(const char *name, struct spi_buffer *buff) +{ + unsigned int *ptr = (unsigned int *)buff->data; + + dbg(PFX "%s desc[%p]: DDADR=%08x DSA=%08x DTA=%08x CMD=%08x\n", + name, buff, + buff->dma_desc.ddadr, + buff->dma_desc.dsadr, + buff->dma_desc.dtadr, + buff->dma_desc.dcmd); + + dbg(PFX "%s buffend:%p: %08x %08x %08x %08x\n", name, + ptr, ptr[0], ptr[1], ptr[2], ptr[3]); + dbg(PFX "%s buffend:%p: %08x %08x %08x %08x\n", name + ptr, ptr[4], ptr[5], ptr[6], ptr[7]); + +} +#else +#define dbg_buff(n,b) do { } while(0) +#endif + +#if 0 +static void dbg_state(const char *where) +{ + dbg(PFX "%s:\n", where); + dbg(PFX "SSCR0=%08x, SSCR1=%08x, SSSR=%08x\n", + SSCR0, SSCR1, SSSR); + dbg(PFX "DCSR(rx_chan)=%08x, DCSR(tx_chan)=%08x\n", + DCSR(rx_chan), DCSR(tx_chan)); + dbg(PFX "DCMD(rx_chan)=%08x, DCMD(tx_chan)=%08x\n", + DCMD(rx_chan), DCMD(tx_chan)); + dbg(PFX "DDADR(rx_chan)=%08x DDADR(tx_chan)=%08x\n", + DDADR(rx_chan), DDADR(tx_chan)); + dbg(PFX "DSADR(rx_chan)=%08x DSADR(tx_chan)=%08x\n", + DDADR(rx_chan), DDADR(tx_chan)); + dbg(PFX "DTADR(rx_chan)=%08x DTADR(tx_chan)=%08x\n", + DDADR(rx_chan), DDADR(tx_chan)); +} +#else +#define dbg_state(where) do { } while (0) +#endif + +/* + * Zero the data areas, and initialise the DMA ring buffers. + */ +static int nbp_spi_init_dmabuf(struct device *dev) +{ + int i; + + spi_cpuaddr = dma_alloc_coherent(dev, sizeof(struct spi_dma_data), + &spi_dmaaddr, GFP_KERNEL); + + if (spi_cpuaddr == NULL) { + printk(KERN_ERR PFX "no memory for DMA buffers\n"); + return -ENOMEM; + } + + memset(spi_cpuaddr, 0, sizeof(struct spi_dma_data)); + + for (i = 0; i < SPI_RXBUFFERS; i++) { + u32 ddadr; + + ddadr = spi_dmaaddr; + if (i != SPI_RXBUFFERS - 1) + ddadr = SPI_DMAPTR(rx[i + 1].dma_desc); + + spi_cpuaddr->rx[i].dma_desc.ddadr = ddadr; + spi_cpuaddr->rx[i].dma_desc.dsadr = __PREG(SSDR); + spi_cpuaddr->rx[i].dma_desc.dtadr = SPI_DMAPTR(rx[i].data); + spi_cpuaddr->rx[i].dma_desc.dcmd = SPI_RXDMA_CMD; + + spi_rxphys[i] = SPI_DMAPTR(rx[i].dma_desc); + + dbg_buff("rx init", &spi_cpuaddr->rx[i]); + } + + spi_cpuaddr->tx.dma_desc.ddadr = SPI_DMAPTR(tx.dma_desc); + spi_cpuaddr->tx.dma_desc.dsadr = SPI_DMAPTR(tx.data); + spi_cpuaddr->tx.dma_desc.dtadr = __PREG(SSDR); + spi_cpuaddr->tx.dma_desc.dcmd = SPI_TXDMA_CMD; + + dbg_buff("tx init", &spi_cpuaddr->tx); + + return 0; +} + +static void nbp_spi_free_dmabuf(struct device *dev) +{ + dma_free_coherent(dev, sizeof(struct spi_dma_data), spi_cpuaddr, + spi_dmaaddr); +} + +/* + * Disable the SPI engine and its associated DMA channels. + */ +static void nbp_spi_disable(void) +{ + dbg(PFX "disabling SPI\n"); + + SSCR0 &= ~SSCR0_SSE; + + DCSR(rx_chan) = 0; + DCSR(tx_chan) = 0; + + pxa_set_cken(CKEN3_SSP, 0); +} + +/* + * Enable the SPI engine and its associated DMA channels. + */ +static void nbp_spi_enable(void) +{ + dbg(PFX "Enabling SPI\n"); + + pxa_set_cken(CKEN3_SSP, 1); + + DRCMRRXSSDR = rx_chan | DRCMR_MAPVLD; + DRCMR1 = tx_chan | DRCMR_MAPVLD; + + rx_last = 0; + DDADR(rx_chan) = SPI_DMAPTR(rx[0].dma_desc); + DDADR(tx_chan) = SPI_DMAPTR(tx.dma_desc); + + /* configure the SPI engine */ + + /* from 2.4: + * + * SSCR0 -> 0xF | (1<<7) (0x8F) + * SSCR1 -> (11<<6) | (15<<10) = (0x3EC0) + */ + + //SSCR0 = SSCR0_Motorola | SSCR0_DataSize(16) | SSCR0_SerClkDiv(2); + //SSCR1 = SSCR1_TxTresh(12) | SSCR1_RxTresh(16); + + SSCR0 = SSCR0_SSE | SSCR0_Motorola | SSCR0_DataSize(16) | SSCR0_SerClkDiv(4); + SSCR1 = SSCR1_TxTresh(12) | SSCR1_RxTresh(16); + + DCSR(rx_chan) |= DCSR_RUN; + DCSR(tx_chan) |= DCSR_RUN; + + barrier(); + + dbg_state(__func__); +} + +void nbp_spi_reset(void) +{ + dbg_state(__func__); + + if (0) { + /* set the ssp's pin configurations */ + pxa_gpio_mode(GPIO19_DREQ1_MD); + pxa_gpio_mode(GPIO23_SCLK_md); + pxa_gpio_mode(GPIO24_SFRM_MD); + pxa_gpio_mode(GPIO25_STXD_MD); + pxa_gpio_mode(GPIO26_SRXD_MD); + } + + nbp_spi_disable(); + nbp_spi_enable(); +} + + +#define DCSR_IRQS (0xf) + +static void spi_dma_rxirq(int irq, void *param, struct pt_regs *regs) +{ + unsigned long ddadr; + unsigned long dcsr; + unsigned long dtadr; + + dcsr = DCSR(rx_chan); + ddadr = DDADR(rx_chan); + dtadr = DTADR(rx_chan); + + dbg(PFX "rx irq received, dcsr=%08lx, ddadr=%08lx, dtadr=%08lx (tx %d)\n", + dcsr, ddadr, dtadr, tx_irqs); + + if (dcsr & DCSR_BUSERR) { + printk(KERN_ERR PFX "rx: dma error (%08lx)\n", dcsr); + + DCSR(rx_chan) = (DCSR(rx_chan) & ~DCSR_IRQS) | DCSR_BUSERR; + } + + if (dcsr & DCSR_ENDINTR) { + unsigned int buff = rx_last; + + DCSR(rx_chan) = (DCSR(rx_chan) & ~DCSR_IRQS) | DCSR_ENDINTR; + + dbg(PFX "rx: dma channel end\n"); + + while (1) { + struct spi_buffer *bufptr = &spi_cpuaddr->rx[buff]; + + buff = (buff + 1) % SPI_RXBUFFERS; + + // see if this is the last buffer... + if (spi_rxphys[buff] == (ddadr & ~15)) + break; + + rx_last = buff; + + dbg_buff("rx done", bufptr); + + nbp_battmon_rx(bufptr->data); + + /* zero the buffer we where given */ + memset(bufptr->data, 0, sizeof(bufptr->data)); + } + } +} + +static void spi_dma_txirq(int irq, void *param, struct pt_regs *regs) +{ + unsigned int dcsr; + + dcsr = DCSR(tx_chan); + DCSR(tx_chan) |= DCSR_ENDINTR | DCSR_STARTINTR | DCSR_BUSERR; + + printk(KERN_DEBUG PFX "tx: dcsr=%08x\n", dcsr); + + tx_irqs++; +} + +static int __init nbp_spi_probe(struct device *dev) +{ + int ret; + + ret = nbp_spi_init_dmabuf(dev); + if (ret) + return ret; + + rx_chan = pxa_request_dma("SPI RX", DMA_PRIO_LOW, spi_dma_rxirq, NULL); + if (rx_chan < 0) { + printk(KERN_ERR PFX "failed to allocate RX DMA channel (%d)\n", + rx_chan); + nbp_spi_free_dmabuf(dev); + return rx_chan; + } + + tx_chan = pxa_request_dma("SPI TX", DMA_PRIO_LOW, spi_dma_txirq, NULL); + if (tx_chan < 0) { + printk(KERN_ERR PFX "failed to allocate TX DMA channel (%d)\n", + tx_chan); + nbp_spi_free_dmabuf(dev); + return tx_chan; + } + + printk(KERN_INFO PFX "SPI: RX DMA %d, TX DMA %d\n", rx_chan, tx_chan); + + if (SPI_ENABLE) { + nbp_spi_reset(); + } + + return 0; +} + +static int nbp_spi_suspend(struct device *dev, u32 state, u32 level) +{ + if (level == SUSPEND_DISABLE) + nbp_spi_disable(); + + return 0; +} + +static int nbp_spi_resume(struct device *dev, u32 level) +{ + if (level == RESUME_ENABLE) + nbp_spi_enable(); + + return 0; +} + + +static struct device_driver spi_driver = { + .name = "netbookpro-spi", + .bus = &platform_bus_type, + .probe = nbp_spi_probe, + .suspend = nbp_spi_suspend, + .resume = nbp_spi_resume, +}; + +static int __init nbp_spi_init(void) +{ + printk(KERN_INFO "NetBookPro SPI Driver, (c) 2004 Simtec Electronics\n"); + return driver_register(&spi_driver); +} + +static void __exit nbp_spi_exit(void) +{ + driver_unregister(&spi_driver); +} + +module_init(nbp_spi_init); +module_exit(nbp_spi_exit); + +MODULE_AUTHOR("Ben Dooks, Simtec Electronics "); +MODULE_DESCRIPTION("Simple SPI Driver for PXA2XX for Psion NetBookPro"); +MODULE_LICENSE("GPL"); diff -NaurbB linux-2.6.9-pre1-bk18/drivers/misc/netbookpro/nvram.c linux-2.6.9_nbp/drivers/misc/netbookpro/nvram.c --- linux-2.6.9-pre1-bk18/drivers/misc/netbookpro/nvram.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/drivers/misc/netbookpro/nvram.c 2004-10-15 21:23:39.000000000 +0200 @@ -0,0 +1,467 @@ +/* drivers/misc/netbookpro/nvram.c + * + * (c) 2004 Simtec Electronics + * + * Based (heavily) on CMOS/NV-RAM driver in drivers/char/nvram.c +*/ + +#define DEBUG +#define NVRAM_VERSION "1.3" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define I2C_DRIVERID_NVRAM 0x0 + +#define EEPROM_ADDRESS (0xA6 >> 1) + +static unsigned short ignore[] = { I2C_CLIENT_END }; +static unsigned short force_addr[] = { + ANY_I2C_BUS, EEPROM_ADDRESS, 0, + I2C_CLIENT_END, I2C_CLIENT_END, I2C_CLIENT_END +}; + +static struct i2c_client_address_data addr_data = { + .normal_i2c = ignore, + .normal_i2c_range = ignore, + .probe = ignore, + .probe_range = ignore, + .ignore = ignore, + .ignore_range = ignore, + .force = force_addr, +}; + +extern struct list_head misc_list; + +#define NVRAM_BYTES (128) + +// nvram flags +#define NVRAM_EXCL (2) +#define NVRAM_WRITE (1) + +static int nvram_open_cnt; +static int nvram_open_mode; +static struct i2c_client *nvram_client; + +static DECLARE_MUTEX(nvram_lock); +static DECLARE_MUTEX(nvram_state_lock); + +// we have a full 128 bytes of nvram to play with + +static int touch_unlocked = 0; + +#define NVRAM_ACCESS_READ I2C_M_RD +#define NVRAM_ACCESS_WRITE I2C_M_NOSTART + +/* + * nvram_access - low level EEPROM IO accessor + * @client: i2c client + * @buf: data buffer to read/write + * @addr: EEPROM address (0 <= addr <= NVRAM_BYTES + * @len: size of data length + * @mode: %NVRAM_ACCESS_READ or %NVRAM_ACCESS_WRITE + */ +static int nvram_access(struct i2c_client *client, unsigned char *buf, + unsigned char addr, size_t len, int mode) +{ + struct i2c_msg msgs[2]; + int ret; + + msgs[0].addr = client->addr; + msgs[0].flags = 0; + msgs[0].len = 1; + msgs[0].buf = &addr; + + msgs[1].addr = client->addr; + msgs[1].flags = mode; + msgs[1].len = len; + msgs[1].buf = buf; + + ret = i2c_transfer(client->adapter, msgs, 2); + + return ret == 2 ? 0 : ret < 0 ? ret : -EIO; +} + +static void nv_write_byte(unsigned char addr, unsigned char byte) +{ + if (addr >= EEPROM_NETBOOK2_BOOST_SECURITY_ADDRESS && + addr < EEPROM_NETBOOK2_BOOST_SECURITY_ADDRESS + + EEPROM_NETBOOK2_BOOST_SECURITY_SIZE) + return; + + if (addr >= EEPROM_NETBOOK2_TOUCH_CALIBRATION_DATA_ADDRESS && + addr < EEPROM_NETBOOK2_TOUCH_CALIBRATION_DATA_ADDRESS + + EEPROM_NETBOOK2_TOUCH_CALIBRATION_DATA_SIZE && + !touch_unlocked) + return; + + nvram_access(nvram_client, &byte, addr, 1, NVRAM_ACCESS_WRITE); +} + +/* + * The are the file operation function for user access to /dev/nvram + */ + +static long long +nvram_llseek(struct file *file, loff_t offset, int origin) +{ + pr_debug("nvram_llseek(%p, %ld, %d)\n", file, (long)offset, origin); + + switch (origin) { + case 0: + /* nothing to do */ + break; + case 1: + offset += file->f_pos; + break; + case 2: + offset += NVRAM_BYTES; + break; + } + return (offset >= 0) ? (file->f_pos = offset) : -EINVAL; +} + + + +static ssize_t +nvram_read(struct file *file, char *buf, size_t count, loff_t *ppos) +{ + unsigned char contents[NVRAM_BYTES]; + loff_t loc = *ppos; + ssize_t ret; + + /* read *ppos for maximum size of nvram, or count bytes */ + + down(&nvram_state_lock); + if (!nvram_client) { + ret = -ENXIO; + goto out; + } + + if (loc >= NVRAM_BYTES) { + ret = 0; + goto out; + } + + if ((loc + count) > NVRAM_BYTES) + count = NVRAM_BYTES - (unsigned int)loc; + + pr_debug("nvram_read(%p,%p,%ld,%ld)\n", + file, buf, (long)count, (long)loc); + + if (count == 0) { + ret = 0; + goto out; + } + + memset(contents, 0xff, sizeof(contents)); + + ret = nvram_access(nvram_client, contents, (unsigned char)loc, + count, NVRAM_ACCESS_READ); + if (ret) { + pr_debug("nvram_access: returned %d\n", ret); + goto out; + } + + if (copy_to_user(buf, contents, count)) + return -EFAULT; + + *ppos = loc + count; + ret = count; + + out: + up(&nvram_state_lock); + + return ret; +} + +static ssize_t +nvram_write(struct file *file, const char *buf, size_t count, loff_t *ppos) +{ + unsigned char contents[NVRAM_BYTES]; + unsigned char *tmp; + loff_t loc = *ppos; + ssize_t ret; + int i; + + down(&nvram_state_lock); + if (!nvram_client) { + ret = -ENXIO; + goto out; + } + + if (loc >= NVRAM_BYTES) { + ret = 0; + goto out; + } + + if ((loc + count) > NVRAM_BYTES) + count = NVRAM_BYTES - (unsigned int)loc; + + pr_debug("nvram_write(%p,%p,%d,%lld)\n", file, buf, count, loc); + + if (count == 0) { + ret = 0; + goto out; + } + + if (copy_from_user(contents, buf, count)) { + ret = -EFAULT; + goto out; + } + + for (i = loc, tmp = contents; count-- > 0; i++, tmp++) + nv_write_byte(i, *tmp); + + touch_unlocked = 0; + + *ppos = i; + ret = tmp - contents; + + out: + up(&nvram_state_lock); + + return ret; +} + + +static int +nvram_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int addr, ret; + + pr_debug("nvram_ioctl(%p,%p,0x%x,0x%lx)\n", + inode, file, cmd, arg); + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + down(&nvram_state_lock); + if (!nvram_client) { + ret = -ENXIO; + goto out; + } + + switch (cmd) { + case NVRAM_INIT: + for (addr = 0; addr < NVRAM_BYTES; addr++) + nv_write_byte(addr, 0); + + touch_unlocked = 0; + ret = 0; + break; + + case NVRAM_SETCKS: + touch_unlocked = 0; + ret = 0; + break; + + case NVRAM_UNLOCK_TOUCH_CALIBRATION: + touch_unlocked = 1; + ret = 0; + break; + + default: + ret = -ENOTTY; + break; + } + out: + up(&nvram_state_lock); + + return ret; +} + +static int nvram_open(struct inode *inode, struct file *file) +{ + int ret; + + pr_debug("nvram_open(%p,%p) (%d)\n", inode, file, file->f_flags); + + down(&nvram_state_lock); + if (!nvram_client) { + ret = -ENXIO; + goto out; + } + + if ((nvram_open_cnt && (file->f_flags & O_EXCL)) || + (nvram_open_mode & NVRAM_EXCL) || + ((file->f_mode & FMODE_WRITE) && (nvram_open_mode & NVRAM_WRITE))) { + ret = -EBUSY; + goto out; + } + + if (file->f_flags & O_EXCL) + nvram_open_mode |= NVRAM_EXCL; + if (file->f_mode & FMODE_WRITE) + nvram_open_mode |= NVRAM_WRITE; + nvram_open_cnt++; + ret = 0; + + out: + up(&nvram_state_lock); + + return ret; +} + + +static int nvram_release(struct inode *inode, struct file *file) +{ + pr_debug("nvram_release(%p,%p)\n", inode, file); + + down(&nvram_state_lock); + + nvram_open_cnt--; + + /* if only one instance is open, clear the EXCL bit */ + if (nvram_open_mode & NVRAM_EXCL) + nvram_open_mode &= ~NVRAM_EXCL; + if (file->f_mode & 2) + nvram_open_mode &= ~NVRAM_WRITE; + + up(&nvram_state_lock); + + return 0; +} + + +static struct file_operations nvram_fops = { + .owner = THIS_MODULE, + .llseek = nvram_llseek, + .read = nvram_read, + .write = nvram_write, + .ioctl = nvram_ioctl, + .open = nvram_open, + .release = nvram_release, +}; + +static struct miscdevice nvram_dev = { + .minor = NVRAM_MINOR, + .name = "nvram", + .fops = &nvram_fops +}; + +static struct i2c_driver nvram_driver; + +static int nvram_detect(struct i2c_adapter *adapter, int address, int kind) +{ + int ret; + + down(&nvram_state_lock); + if (nvram_client) { + ret = -EBUSY; + goto out; + } + + nvram_client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); + if (!nvram_client) { + ret = -ENOMEM; + goto out; + } + + memset(nvram_client, 0, sizeof(struct i2c_client)); + + nvram_client->addr = address; + nvram_client->adapter = adapter; + nvram_client->driver = &nvram_driver; + strlcpy(nvram_client->name, "nvram", I2C_NAME_SIZE); + + ret = i2c_attach_client(nvram_client); + if (ret) { + printk(KERN_ERR "%s: failed to attach client (%d)\n", + __FUNCTION__, ret); + goto free; + } + + printk(KERN_INFO "I2C: NVRAM at bus address 0x%x\n", address); + + ret = misc_register(&nvram_dev); + if (ret) { + printk(KERN_ERR "nvram: can't misc_register on minor=%d\n", + NVRAM_MINOR); + goto remove_i2c; + } + +#if 0 + if (!create_proc_read_entry("driver/nvram", 0, 0, nvram_read_proc, + NULL)) { + printk(KERN_ERR "nvram: can't create /proc/driver/nvram\n"); + ret = -ENOMEM; + goto outmisc; + } +#endif + + goto out; + +#if 0 + outmisc: + misc_deregister(&nvram_dev); +#endif + remove_i2c: + i2c_detach_client(nvram_client); + free: + kfree(nvram_client); + nvram_client = NULL; + out: + up(&nvram_state_lock); + return ret; +} + +static int nvram_attach_adapter(struct i2c_adapter *adapter) +{ + return i2c_probe(adapter, &addr_data, nvram_detect); +} + +static int nvram_detach_client(struct i2c_client *client) +{ + //remove_proc_entry("driver/nvram", 0); + misc_deregister(&nvram_dev); + + down(&nvram_state_lock); + i2c_detach_client(client); + kfree(client); + nvram_client = NULL; + up(&nvram_state_lock); + return 0; +} + +static struct i2c_driver nvram_driver = { + .owner = THIS_MODULE, + .name = "nvram", + .id = I2C_DRIVERID_NVRAM, + .flags = I2C_DF_NOTIFY, + .attach_adapter = nvram_attach_adapter, + .detach_client = nvram_detach_client, +}; + +static int __init nvram_init(void) +{ + printk(KERN_INFO "Non-volatile memory driver v" NVRAM_VERSION "\n"); + return i2c_add_driver(&nvram_driver); +} + +static void __exit nvram_exit(void) +{ + i2c_del_driver(&nvram_driver); +} + +module_init(nvram_init); +module_exit(nvram_exit); + +MODULE_LICENSE("GPL"); diff -NaurbB linux-2.6.9-pre1-bk18/drivers/mmc/mmc_block.c linux-2.6.9_nbp/drivers/mmc/mmc_block.c --- linux-2.6.9-pre1-bk18/drivers/mmc/mmc_block.c 2005-08-15 01:14:41.000000000 +0200 +++ linux-2.6.9_nbp/drivers/mmc/mmc_block.c 2004-09-21 11:19:22.000000000 +0200 @@ -428,7 +428,7 @@ struct mmc_blk_data *md = mmc_get_drvdata(card); if (md) { - blk_stop_queue(md->queue.queue); + mmc_queue_suspend(&md->queue); } return 0; } @@ -439,7 +439,7 @@ if (md) { mmc_blk_set_blksize(md, card); - blk_start_queue(md->queue.queue); + mmc_queue_resume(&md->queue); } return 0; } diff -NaurbB linux-2.6.9-pre1-bk18/drivers/mmc/mmc.c linux-2.6.9_nbp/drivers/mmc/mmc.c --- linux-2.6.9-pre1-bk18/drivers/mmc/mmc.c 2005-08-15 01:14:41.000000000 +0200 +++ linux-2.6.9_nbp/drivers/mmc/mmc.c 2004-09-21 11:19:22.000000000 +0200 @@ -300,58 +300,121 @@ return ocr; } -static void mmc_decode_cid(struct mmc_cid *cid, u32 *resp) +#define UNSTUFF_BITS(resp,start,size) \ + ({ \ + const u32 __mask = (1 << (size)) - 1; \ + const int __off = 3 - ((start) / 32); \ + const int __shft = (start) & 31; \ + u32 __res; \ + \ + __res = resp[__off] >> __shft; \ + if ((size) + __shft >= 32) \ + __res |= resp[__off-1] << (32 - __shft); \ + __res & __mask; \ + }) + +/* + * Given the decoded CSD structure, decode the raw CID to our CID structure. + */ +static void mmc_decode_cid(struct mmc_card *card) +{ + u32 *resp = card->raw_cid; + + memset(&card->cid, 0, sizeof(struct mmc_cid)); + + /* + * The selection of the format here is guesswork based upon + * information people have sent to date. + */ + switch (card->csd.mmca_vsn) { + case 0: /* MMC v1.? */ + case 1: /* MMC v1.4 */ + card->cid.manfid = UNSTUFF_BITS(resp, 104, 24); + card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8); + card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8); + card->cid.prod_name[2] = UNSTUFF_BITS(resp, 80, 8); + card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8); + card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8); + card->cid.prod_name[5] = UNSTUFF_BITS(resp, 56, 8); + card->cid.prod_name[6] = UNSTUFF_BITS(resp, 48, 8); + card->cid.hwrev = UNSTUFF_BITS(resp, 44, 4); + card->cid.fwrev = UNSTUFF_BITS(resp, 40, 4); + card->cid.serial = UNSTUFF_BITS(resp, 16, 24); + card->cid.month = UNSTUFF_BITS(resp, 12, 4); + card->cid.year = UNSTUFF_BITS(resp, 8, 4) + 1997; + break; + + case 2: /* MMC v2.x ? */ + case 3: /* MMC v3.x ? */ + card->cid.manfid = UNSTUFF_BITS(resp, 120, 8); + card->cid.oemid = UNSTUFF_BITS(resp, 104, 16); + card->cid.prod_name[0] = UNSTUFF_BITS(resp, 96, 8); + card->cid.prod_name[1] = UNSTUFF_BITS(resp, 88, 8); + card->cid.prod_name[2] = UNSTUFF_BITS(resp, 80, 8); + card->cid.prod_name[3] = UNSTUFF_BITS(resp, 72, 8); + card->cid.prod_name[4] = UNSTUFF_BITS(resp, 64, 8); + card->cid.prod_name[5] = UNSTUFF_BITS(resp, 56, 8); + card->cid.serial = UNSTUFF_BITS(resp, 16, 32); + card->cid.month = UNSTUFF_BITS(resp, 12, 4); + card->cid.year = UNSTUFF_BITS(resp, 8, 4) + 1997; + break; + + default: + printk("%s: card has unknown MMCA version %d\n", + card->host->host_name, card->csd.mmca_vsn); + mmc_card_set_bad(card); + break; + } +} + +/* + * Given a 128-bit response, decode to our card CSD structure. + */ +static void mmc_decode_csd(struct mmc_card *card) { - memset(cid, 0, sizeof(struct mmc_cid)); + struct mmc_csd *csd = &card->csd; + unsigned int e, m, csd_struct; + u32 *resp = card->raw_csd; - cid->manfid = resp[0] >> 8; - cid->prod_name[0] = resp[0]; - cid->prod_name[1] = resp[1] >> 24; - cid->prod_name[2] = resp[1] >> 16; - cid->prod_name[3] = resp[1] >> 8; - cid->prod_name[4] = resp[1]; - cid->prod_name[5] = resp[2] >> 24; - cid->prod_name[6] = resp[2] >> 16; - cid->prod_name[7] = '\0'; - cid->hwrev = (resp[2] >> 12) & 15; - cid->fwrev = (resp[2] >> 8) & 15; - cid->serial = (resp[2] & 255) << 16 | (resp[3] >> 16); - cid->month = (resp[3] >> 12) & 15; - cid->year = (resp[3] >> 8) & 15; -} - -static void mmc_decode_csd(struct mmc_csd *csd, u32 *resp) -{ - unsigned int e, m; - - csd->mmc_prot = (resp[0] >> 26) & 15; - m = (resp[0] >> 19) & 15; - e = (resp[0] >> 16) & 7; + /* + * We only understand CSD structure v1.1 and v2. + * v2 has extra information in bits 15, 11 and 10. + */ + csd_struct = UNSTUFF_BITS(resp, 126, 2); + if (csd_struct != 1 && csd_struct != 2) { + printk("%s: unrecognised CSD structure version %d\n", + card->host->host_name, csd_struct); + mmc_card_set_bad(card); + return; + } + + csd->mmca_vsn = UNSTUFF_BITS(resp, 122, 4); + m = UNSTUFF_BITS(resp, 115, 4); + e = UNSTUFF_BITS(resp, 112, 3); csd->tacc_ns = (tacc_exp[e] * tacc_mant[m] + 9) / 10; - csd->tacc_clks = ((resp[0] >> 8) & 255) * 100; + csd->tacc_clks = UNSTUFF_BITS(resp, 104, 8) * 100; - m = (resp[0] >> 3) & 15; - e = resp[0] & 7; + m = UNSTUFF_BITS(resp, 99, 4); + e = UNSTUFF_BITS(resp, 96, 3); csd->max_dtr = tran_exp[e] * tran_mant[m]; - csd->cmdclass = (resp[1] >> 20) & 0xfff; + csd->cmdclass = UNSTUFF_BITS(resp, 84, 12); - e = (resp[2] >> 15) & 7; - m = (resp[1] << 2 | resp[2] >> 30) & 0x3fff; + e = UNSTUFF_BITS(resp, 47, 3); + m = UNSTUFF_BITS(resp, 62, 12); csd->capacity = (1 + m) << (e + 2); - csd->read_blkbits = (resp[1] >> 16) & 15; + csd->read_blkbits = UNSTUFF_BITS(resp, 80, 4); } /* - * Locate a MMC card on this MMC host given a CID. + * Locate a MMC card on this MMC host given a raw CID. */ -static struct mmc_card * -mmc_find_card(struct mmc_host *host, struct mmc_cid *cid) +static struct mmc_card *mmc_find_card(struct mmc_host *host, u32 *raw_cid) { struct mmc_card *card; list_for_each_entry(card, &host->cards, node) { - if (memcmp(&card->cid, cid, sizeof(struct mmc_cid)) == 0) + if (memcmp(card->raw_cid, raw_cid, sizeof(card->raw_cid)) == 0) return card; } return NULL; @@ -361,7 +424,7 @@ * Allocate a new MMC card, and assign a unique RCA. */ static struct mmc_card * -mmc_alloc_card(struct mmc_host *host, struct mmc_cid *cid, unsigned int *frca) +mmc_alloc_card(struct mmc_host *host, u32 *raw_cid, unsigned int *frca) { struct mmc_card *card, *c; unsigned int rca = *frca; @@ -371,7 +434,7 @@ return ERR_PTR(-ENOMEM); mmc_init_card(card, host); - memcpy(&card->cid, cid, sizeof(struct mmc_cid)); + memcpy(card->raw_cid, raw_cid, sizeof(card->raw_cid)); again: list_for_each_entry(c, &host->cards, node) @@ -456,7 +519,7 @@ * to be discovered. Add new cards to the list. * * Create a mmc_card entry for each discovered card, assigning - * it an RCA, and save the CID. + * it an RCA, and save the raw CID for decoding later. */ static void mmc_discover_cards(struct mmc_host *host) { @@ -465,7 +528,6 @@ while (1) { struct mmc_command cmd; - struct mmc_cid cid; cmd.opcode = MMC_ALL_SEND_CID; cmd.arg = 0; @@ -482,11 +544,9 @@ break; } - mmc_decode_cid(&cid, cmd.resp); - - card = mmc_find_card(host, &cid); + card = mmc_find_card(host, cmd.resp); if (!card) { - card = mmc_alloc_card(host, &cid, &first_rca); + card = mmc_alloc_card(host, cmd.resp, &first_rca); if (IS_ERR(card)) { err = PTR_ERR(card); break; @@ -502,7 +562,7 @@ err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); if (err != MMC_ERR_NONE) - card->state |= MMC_STATE_DEAD; + mmc_card_set_dead(card); } } @@ -523,11 +583,14 @@ err = mmc_wait_for_cmd(host, &cmd, CMD_RETRIES); if (err != MMC_ERR_NONE) { - card->state |= MMC_STATE_DEAD; + mmc_card_set_dead(card); continue; } - mmc_decode_csd(&card->csd, cmd.resp); + memcpy(card->raw_csd, cmd.resp, sizeof(card->raw_csd)); + + mmc_decode_csd(card); + mmc_decode_cid(card); } } @@ -573,7 +636,7 @@ if (err == MMC_ERR_NONE) continue; - card->state |= MMC_STATE_DEAD; + mmc_card_set_dead(card); } } @@ -678,9 +741,9 @@ */ if (!mmc_card_present(card) && !mmc_card_dead(card)) { if (mmc_register_card(card)) - card->state |= MMC_STATE_DEAD; + mmc_card_set_dead(card); else - card->state |= MMC_STATE_PRESENT; + mmc_card_set_present(card); } /* diff -NaurbB linux-2.6.9-pre1-bk18/drivers/mmc/mmc_queue.c linux-2.6.9_nbp/drivers/mmc/mmc_queue.c --- linux-2.6.9-pre1-bk18/drivers/mmc/mmc_queue.c 2005-08-15 01:14:41.000000000 +0200 +++ linux-2.6.9_nbp/drivers/mmc/mmc_queue.c 2004-09-21 11:19:22.000000000 +0200 @@ -15,6 +15,9 @@ #include #include "mmc_queue.h" +#define MMC_QUEUE_EXIT (1 << 0) +#define MMC_QUEUE_SUSPENDED (1 << 1) + /* * Prepare a MMC request. Essentially, this means passing the * preparation off to the media driver. The media driver will @@ -66,14 +69,9 @@ daemonize("mmcqd"); - spin_lock_irq(¤t->sighand->siglock); - sigfillset(¤t->blocked); - recalc_sigpending(); - spin_unlock_irq(¤t->sighand->siglock); - - mq->thread = current; complete(&mq->thread_complete); + down(&mq->thread_sem); add_wait_queue(&mq->thread_wq, &wait); do { struct request *req = NULL; @@ -85,9 +83,11 @@ spin_unlock(q->queue_lock); if (!req) { - if (!mq->thread) + if (mq->flags & MMC_QUEUE_EXIT) break; + up(&mq->thread_sem); schedule(); + down(&mq->thread_sem); continue; } set_current_state(TASK_RUNNING); @@ -95,6 +95,7 @@ mq->issue_fn(mq, req); } while (1); remove_wait_queue(&mq->thread_wq, &wait); + up(&mq->thread_sem); complete_and_exit(&mq->thread_complete, 0); return 0; @@ -148,6 +149,7 @@ init_completion(&mq->thread_complete); init_waitqueue_head(&mq->thread_wq); + init_MUTEX(&mq->thread_sem); ret = kernel_thread(mmc_queue_thread, mq, CLONE_KERNEL); if (ret < 0) { @@ -165,7 +166,7 @@ void mmc_cleanup_queue(struct mmc_queue *mq) { - mq->thread = NULL; + mq->flags |= MMC_QUEUE_EXIT; wake_up(&mq->thread_wq); wait_for_completion(&mq->thread_complete); blk_cleanup_queue(mq->queue); @@ -172,5 +173,50 @@ mq->card = NULL; } - EXPORT_SYMBOL(mmc_cleanup_queue); + +/** + * mmc_queue_suspend - suspend a MMC request queue + * @mq: MMC queue to suspend + * + * Stop the block request queue, and wait for our thread to + * complete any outstanding requests. This ensures that we + * won't suspend while a request is being processed. + */ +void mmc_queue_suspend(struct mmc_queue *mq) +{ + request_queue_t *q = mq->queue; + unsigned long flags; + + if (!(mq->flags & MMC_QUEUE_SUSPENDED)) { + mq->flags |= MMC_QUEUE_SUSPENDED; + + spin_lock_irqsave(q->queue_lock, flags); + blk_stop_queue(q); + spin_unlock_irqrestore(q->queue_lock, flags); + + down(&mq->thread_sem); + } +} +EXPORT_SYMBOL(mmc_queue_suspend); + +/** + * mmc_queue_resume - resume a previously suspended MMC request queue + * @mq: MMC queue to resume + */ +void mmc_queue_resume(struct mmc_queue *mq) +{ + request_queue_t *q = mq->queue; + unsigned long flags; + + if (mq->flags & MMC_QUEUE_SUSPENDED) { + mq->flags &= ~MMC_QUEUE_SUSPENDED; + + up(&mq->thread_sem); + + spin_lock_irqsave(q->queue_lock, flags); + blk_start_queue(q); + spin_unlock_irqrestore(q->queue_lock, flags); + } +} +EXPORT_SYMBOL(mmc_queue_resume); diff -NaurbB linux-2.6.9-pre1-bk18/drivers/mmc/mmc_queue.h linux-2.6.9_nbp/drivers/mmc/mmc_queue.h --- linux-2.6.9-pre1-bk18/drivers/mmc/mmc_queue.h 2005-08-15 01:14:33.000000000 +0200 +++ linux-2.6.9_nbp/drivers/mmc/mmc_queue.h 2004-09-21 01:20:34.000000000 +0200 @@ -8,7 +8,8 @@ struct mmc_card *card; struct completion thread_complete; wait_queue_head_t thread_wq; - struct task_struct *thread; + struct semaphore thread_sem; + unsigned int flags; struct request *req; int (*prep_fn)(struct mmc_queue *, struct request *); int (*issue_fn)(struct mmc_queue *, struct request *); @@ -25,5 +26,7 @@ extern int mmc_init_queue(struct mmc_queue *, struct mmc_card *, spinlock_t *); extern void mmc_cleanup_queue(struct mmc_queue *); +extern void mmc_queue_suspend(struct mmc_queue *); +extern void mmc_queue_resume(struct mmc_queue *); #endif diff -NaurbB linux-2.6.9-pre1-bk18/drivers/mmc/mmc_sysfs.c linux-2.6.9_nbp/drivers/mmc/mmc_sysfs.c --- linux-2.6.9-pre1-bk18/drivers/mmc/mmc_sysfs.c 2005-08-15 01:14:33.000000000 +0200 +++ linux-2.6.9_nbp/drivers/mmc/mmc_sysfs.c 2004-09-21 01:20:34.000000000 +0200 @@ -31,11 +31,12 @@ /* * This currently matches any MMC driver to any MMC card - drivers * themselves make the decision whether to drive this card in their - * probe method. + * probe method. However, we force "bad" cards to fail. */ static int mmc_bus_match(struct device *dev, struct device_driver *drv) { - return 1; + struct mmc_card *card = dev_to_mmc_card(dev); + return !mmc_card_bad(card); } static int @@ -66,8 +67,9 @@ i = 0; add_env("MMC_CCC=%s", ccc); - add_env("MMC_MANFID=%03x", card->cid.manfid); - add_env("MMC_SLOT_NAME=%s", card->dev.bus_id); + add_env("MMC_MANFID=%06x", card->cid.manfid); + add_env("MMC_NAME=%s", mmc_card_name(card)); + add_env("MMC_OEMID=%04x", card->cid.oemid); return 0; } @@ -157,20 +159,28 @@ } \ static DEVICE_ATTR(name, S_IRUGO, mmc_dev_show_##name, NULL) -MMC_ATTR(date, "%02d/%04d\n", card->cid.month, 1997 + card->cid.year); +MMC_ATTR(cid, "%08x%08x%08x%08x\n", card->raw_cid[0], card->raw_cid[1], + card->raw_cid[2], card->raw_cid[3]); +MMC_ATTR(csd, "%08x%08x%08x%08x\n", card->raw_csd[0], card->raw_csd[1], + card->raw_csd[2], card->raw_csd[3]); +MMC_ATTR(date, "%02d/%04d\n", card->cid.month, card->cid.year); MMC_ATTR(fwrev, "0x%x\n", card->cid.fwrev); MMC_ATTR(hwrev, "0x%x\n", card->cid.hwrev); -MMC_ATTR(manfid, "0x%03x\n", card->cid.manfid); -MMC_ATTR(serial, "0x%06x\n", card->cid.serial); +MMC_ATTR(manfid, "0x%06x\n", card->cid.manfid); MMC_ATTR(name, "%s\n", card->cid.prod_name); +MMC_ATTR(oemid, "0x%04x\n", card->cid.oemid); +MMC_ATTR(serial, "0x%08x\n", card->cid.serial); static struct device_attribute *mmc_dev_attributes[] = { + &dev_attr_cid, + &dev_attr_csd, &dev_attr_date, &dev_attr_fwrev, &dev_attr_hwrev, &dev_attr_manfid, - &dev_attr_serial, &dev_attr_name, + &dev_attr_oemid, + &dev_attr_serial, }; /* diff -NaurbB linux-2.6.9-pre1-bk18/drivers/mmc/pxamci.c linux-2.6.9_nbp/drivers/mmc/pxamci.c --- linux-2.6.9-pre1-bk18/drivers/mmc/pxamci.c 2005-08-15 01:14:41.000000000 +0200 +++ linux-2.6.9_nbp/drivers/mmc/pxamci.c 2004-09-21 01:20:34.000000000 +0200 @@ -33,6 +33,8 @@ #include #include +#include + #include "pxamci.h" #ifdef CONFIG_MMC_DEBUG @@ -41,6 +43,8 @@ #define DBG(x...) do { } while (0) #endif +#define pxa_dma_desc_t pxa_dma_desc + struct pxamci_host { struct mmc_host *mmc; spinlock_t lock; @@ -52,13 +56,14 @@ unsigned int cmdat; unsigned int imask; unsigned int power_mode; + struct pxamci_platform_data *pdata; struct mmc_request *mrq; struct mmc_command *cmd; struct mmc_data *data; dma_addr_t sg_dma; - struct pxa_dma_desc *sg_cpu; + struct pxa_dma_desc_t *sg_cpu; dma_addr_t dma_buf; unsigned int dma_size; @@ -172,7 +177,7 @@ if (size) { host->sg_cpu[i].ddadr = host->sg_dma + (i + 1) * - sizeof(struct pxa_dma_desc); + sizeof(struct pxa_dma_desc_t); } else { host->sg_cpu[i].ddadr = DDADR_STOP; } @@ -384,9 +389,8 @@ if (host->power_mode != ios->power_mode) { host->power_mode = ios->power_mode; - /* - * power control? none on the lubbock. - */ + if (host->pdata && host->pdata->setpower) + host->pdata->setpower(mmc->dev, ios->vdd); if (ios->power_mode == MMC_POWER_ON) host->cmdat |= CMDAT_INIT; @@ -407,6 +411,12 @@ DCSR(dma) = DCSR_STARTINTR|DCSR_ENDINTR|DCSR_BUSERR; } +static irqreturn_t pxamci_detect_irq(int irq, void *devid, struct pt_regs *regs) +{ + mmc_detect_change(devid); + return IRQ_HANDLED; +} + static int pxamci_probe(struct device *dev) { struct platform_device *pdev = to_platform_device(dev); @@ -433,11 +443,14 @@ mmc->ops = &pxamci_ops; mmc->f_min = 312500; mmc->f_max = 20000000; - mmc->ocr_avail = MMC_VDD_32_33; host = mmc_priv(mmc); host->mmc = mmc; host->dma = -1; + host->pdata = pdev->dev.platform_data; + mmc->ocr_avail = host->pdata ? + host->pdata->ocr_mask : + MMC_VDD_32_33|MMC_VDD_33_34; host->sg_cpu = dma_alloc_coherent(dev, PAGE_SIZE, &host->sg_dma, GFP_KERNEL); if (!host->sg_cpu) { @@ -465,6 +478,9 @@ writel(0, host->base + MMC_SPI); writel(64, host->base + MMC_RESTO); + /* some bootloaders leave interrupts unmasked... correct */ + writel(host->imask, host->base + MMC_I_MASK); + pxa_gpio_mode(GPIO6_MMCCLK_MD); pxa_gpio_mode(GPIO8_MMCCS0_MD); pxa_set_cken(CKEN12_MMC, 1); @@ -481,6 +497,9 @@ dev_set_drvdata(dev, mmc); + if (host->pdata && host->pdata->init) + host->pdata->init(dev, pxamci_detect_irq, mmc); + mmc_add_host(mmc); return 0; @@ -509,6 +528,9 @@ if (mmc) { struct pxamci_host *host = mmc_priv(mmc); + if (host->pdata && host->pdata->exit) + host->pdata->exit(dev, mmc); + mmc_remove_host(mmc); pxamci_stop_clock(host); @@ -516,6 +538,8 @@ END_CMD_RES|PRG_DONE|DATA_TRAN_DONE, host->base + MMC_I_MASK); + pxa_set_cken(CKEN12_MMC, 0); + free_irq(host->irq, host); pxa_free_dma(host->dma); iounmap(host->base); diff -NaurbB linux-2.6.9-pre1-bk18/drivers/mtd/chips/cfi_cmdset_0001.c linux-2.6.9_nbp/drivers/mtd/chips/cfi_cmdset_0001.c --- linux-2.6.9-pre1-bk18/drivers/mtd/chips/cfi_cmdset_0001.c 2005-08-15 01:14:41.000000000 +0200 +++ linux-2.6.9_nbp/drivers/mtd/chips/cfi_cmdset_0001.c 2004-09-13 12:43:27.000000000 +0200 @@ -4,7 +4,7 @@ * * (C) 2000 Red Hat. GPL'd * - * $Id: cfi_cmdset_0001.c,v 1.154 2004/08/09 13:19:43 dwmw2 Exp $ + * $Id: cfi_cmdset_0001.c,v 1.155 2004/08/12 06:40:22 eric Exp $ * * * 10/10/2000 Nicolas Pitre @@ -39,6 +39,10 @@ // debugging, turns off buffer write mode if set to 1 #define FORCE_WORD_WRITE 0 +#define MANUFACTURER_INTEL 0x0089 +#define I82802AB 0x00ad +#define I82802AC 0x00ac + static int cfi_intelext_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); //static int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); //static int cfi_intelext_read_fact_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); @@ -55,7 +59,7 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *, int); -static struct mtd_info *cfi_intelext_setup (struct map_info *); +static struct mtd_info *cfi_intelext_setup (struct mtd_info *); static int cfi_intelext_partition_fixup(struct map_info *, struct cfi_private **); static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len, @@ -63,6 +67,11 @@ static void cfi_intelext_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, size_t len); +static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode); +static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr); +#include "fwh_lock.h" + + /* * *********** SETUP AND PROBE BITS *********** @@ -123,8 +132,9 @@ #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 map_info *map, void* param) +static void fixup_intel_strataflash(struct mtd_info *mtd, void* param) { + struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; struct cfi_pri_amdstd *extp = cfi->cmdset_priv; @@ -134,16 +144,18 @@ } #endif -static void fixup_st_m28w320ct(struct map_info *map, void* param) +static void fixup_st_m28w320ct(struct mtd_info *mtd, void* param) { + struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; cfi->cfiq->BufWriteTimeoutTyp = 0; /* Not supported */ cfi->cfiq->BufWriteTimeoutMax = 0; /* Not supported */ } -static void fixup_st_m28w320cb(struct map_info *map, void* param) +static void fixup_st_m28w320cb(struct mtd_info *mtd, void* param) { + struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; /* Note this is done after the region info is endian swapped */ @@ -151,24 +163,50 @@ (cfi->cfiq->EraseRegionInfo[1] & 0xffff0000) | 0x3e; }; -static struct cfi_fixup fixup_table[] = { +static void fixup_use_point(struct mtd_info *mtd, void *param) +{ + struct map_info *map = mtd->priv; + if (!mtd->point && map_is_linear(map)) { + mtd->point = cfi_intelext_point; + mtd->unpoint = cfi_intelext_unpoint; + } +} + +static void fixup_use_write_buffers(struct mtd_info *mtd, void *param) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + if (cfi->cfiq->BufWriteTimeoutTyp) { + printk(KERN_INFO "Using buffer write method\n" ); + mtd->write = cfi_intelext_write_buffers; + } +} + +static struct cfi_fixup cfi_fixup_table[] = { #ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE - { - CFI_MFR_ANY, CFI_ID_ANY, - fixup_intel_strataflash, NULL - }, + { CFI_MFR_ANY, CFI_ID_ANY, fixup_intel_strataflash, NULL }, #endif - { - 0x0020, /* STMicroelectronics */ - 0x00ba, /* M28W320CT */ - fixup_st_m28w320ct, NULL - }, { - 0x0020, /* STMicroelectronics */ - 0x00bb, /* M28W320CB */ - fixup_st_m28w320cb, NULL - }, { - 0, 0, NULL, NULL - } +#if !FORCE_WORD_WRITE + { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL }, +#endif + { CFI_MFR_ST, 0x00ba, /* M28W320CT */ fixup_st_m28w320ct, NULL }, + { CFI_MFR_ST, 0x00bb, /* M28W320CB */ fixup_st_m28w320cb, NULL }, + { 0, 0, NULL, NULL } +}; + +static struct cfi_fixup jedec_fixup_table[] = { + { MANUFACTURER_INTEL, I82802AB, fixup_use_fwh_lock, NULL, }, + { MANUFACTURER_INTEL, I82802AC, fixup_use_fwh_lock, NULL, }, + { 0, 0, NULL, NULL } +}; +static struct cfi_fixup fixup_table[] = { + /* The CFI vendor ids and the JEDEC vendor IDs appear + * to be common. It is like the devices id's are as + * well. This table is to pick all cases where + * we know that is the case. + */ + { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_point, NULL }, + { 0, 0, NULL, NULL } }; /* This routine is made available to other mtd code via @@ -181,8 +219,30 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary) { struct cfi_private *cfi = map->fldrv_priv; + struct mtd_info *mtd; int i; + mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); + if (!mtd) { + printk(KERN_ERR "Failed to allocate memory for MTD device\n"); + return NULL; + } + memset(mtd, 0, sizeof(*mtd)); + mtd->priv = map; + mtd->type = MTD_NORFLASH; + + /* Fill in the default mtd operations */ + mtd->erase = cfi_intelext_erase_varsize; + mtd->read = cfi_intelext_read; + mtd->write = cfi_intelext_write_words; + mtd->sync = cfi_intelext_sync; + mtd->lock = cfi_intelext_lock; + mtd->unlock = cfi_intelext_unlock; + mtd->suspend = cfi_intelext_suspend; + mtd->resume = cfi_intelext_resume; + mtd->flags = MTD_CAP_NORFLASH; + mtd->name = map->name; + if (cfi->cfi_mode == CFI_MODE_CFI) { /* * It's a real CFI chip, not one for which the probe @@ -193,8 +253,10 @@ struct cfi_pri_intelext *extp; extp = (struct cfi_pri_intelext*)cfi_read_pri(map, adr, sizeof(*extp), "Intel/Sharp"); - if (!extp) + if (!extp) { + kfree(mtd); return NULL; + } /* Do some byteswapping if necessary */ extp->FeatureSupport = le32_to_cpu(extp->FeatureSupport); @@ -204,7 +266,7 @@ /* Install our own private info structure */ cfi->cmdset_priv = extp; - cfi_fixup(map, fixup_table); + cfi_fixup(mtd, cfi_fixup_table); #ifdef DEBUG_CFI_FEATURES /* Tell the user about it in lots of lovely detail */ @@ -215,6 +277,12 @@ printk(KERN_NOTICE "cfi_cmdset_0001: Erase suspend on write enabled\n"); } } + else if (cfi->cfi_mode == CFI_MODE_JEDEC) { + /* Apply jedec specific fixups */ + cfi_fixup(mtd, jedec_fixup_table); + } + /* Apply generic fixups */ + cfi_fixup(mtd, fixup_table); for (i=0; i< cfi->numchips; i++) { cfi->chips[i].word_write_time = 1<cfiq->WordWriteTimeoutTyp; @@ -225,28 +293,19 @@ map->fldrv = &cfi_intelext_chipdrv; - return cfi_intelext_setup(map); + return cfi_intelext_setup(mtd); } -static struct mtd_info *cfi_intelext_setup(struct map_info *map) +static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd) { + struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; - struct mtd_info *mtd; unsigned long offset = 0; int i,j; unsigned long devsize = (1<cfiq->DevSize) * cfi->interleave; - mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); //printk(KERN_DEBUG "number of CFI chips: %d\n", cfi->numchips); - if (!mtd) { - printk(KERN_ERR "Failed to allocate memory for MTD device\n"); - goto setup_err; - } - - memset(mtd, 0, sizeof(*mtd)); - mtd->priv = map; - mtd->type = MTD_NORFLASH; mtd->size = devsize * cfi->numchips; mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips; @@ -286,34 +345,10 @@ mtd->eraseregions[i].numblocks); } - /* Also select the correct geometry setup too */ - mtd->erase = cfi_intelext_erase_varsize; - mtd->read = cfi_intelext_read; - - if (map_is_linear(map)) { - mtd->point = cfi_intelext_point; - mtd->unpoint = cfi_intelext_unpoint; - } - - if ( cfi->cfiq->BufWriteTimeoutTyp && !FORCE_WORD_WRITE) { - printk(KERN_INFO "Using buffer write method\n" ); - mtd->write = cfi_intelext_write_buffers; - } else { - printk(KERN_INFO "Using word write method\n" ); - mtd->write = cfi_intelext_write_words; - } #if 0 mtd->read_user_prot_reg = cfi_intelext_read_user_prot_reg; mtd->read_fact_prot_reg = cfi_intelext_read_fact_prot_reg; #endif - mtd->sync = cfi_intelext_sync; - mtd->lock = cfi_intelext_lock; - mtd->unlock = cfi_intelext_unlock; - mtd->suspend = cfi_intelext_suspend; - mtd->resume = cfi_intelext_resume; - mtd->flags = MTD_CAP_NORFLASH; - map->fldrv = &cfi_intelext_chipdrv; - mtd->name = map->name; /* This function has the potential to distort the reality a bit and therefore should be called last. */ @@ -517,6 +552,7 @@ case FL_ERASING: if (!(cfip->FeatureSupport & 2) || !(mode == FL_READY || mode == FL_POINT || + !cfip || (mode == FL_WRITING && (cfip->SuspendCmdSupport & 1)))) goto sleep; @@ -882,7 +918,7 @@ int base_offst,reg_sz; /* Check that we actually have some protection registers */ - if(!(extp->FeatureSupport&64)){ + if(!extp || !(extp->FeatureSupport&64)){ printk(KERN_WARNING "%s: This flash device has no protection data to read!\n",map->name); return 0; } @@ -901,7 +937,7 @@ int base_offst,reg_sz; /* Check that we actually have some protection registers */ - if(!(extp->FeatureSupport&64)){ + if(!extp || !(extp->FeatureSupport&64)){ printk(KERN_WARNING "%s: This flash device has no protection data to read!\n",map->name); return 0; } @@ -1306,102 +1342,6 @@ return 0; } -typedef int (*varsize_frob_t)(struct map_info *map, struct flchip *chip, - unsigned long adr, int len, void *thunk); - -static int cfi_intelext_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob, - loff_t ofs, size_t len, void *thunk) -{ - struct map_info *map = mtd->priv; - struct cfi_private *cfi = map->fldrv_priv; - unsigned long adr; - int chipnum, ret = 0; - int i, first; - struct mtd_erase_region_info *regions = mtd->eraseregions; - - if (ofs > mtd->size) - return -EINVAL; - - if ((len + ofs) > mtd->size) - return -EINVAL; - - /* Check that both start and end of the requested erase are - * aligned with the erasesize at the appropriate addresses. - */ - - i = 0; - - /* Skip all erase regions which are ended before the start of - the requested erase. Actually, to save on the calculations, - we skip to the first erase region which starts after the - start of the requested erase, and then go back one. - */ - - while (i < mtd->numeraseregions && ofs >= regions[i].offset) - i++; - i--; - - /* OK, now i is pointing at the erase region in which this - erase request starts. Check the start of the requested - erase range is aligned with the erase size which is in - effect here. - */ - - if (ofs & (regions[i].erasesize-1)) - return -EINVAL; - - /* Remember the erase region we start on */ - first = i; - - /* Next, check that the end of the requested erase is aligned - * with the erase region at that address. - */ - - while (inumeraseregions && (ofs + len) >= regions[i].offset) - i++; - - /* As before, drop back one to point at the region in which - the address actually falls - */ - i--; - - if ((ofs + len) & (regions[i].erasesize-1)) - return -EINVAL; - - chipnum = ofs >> cfi->chipshift; - adr = ofs - (chipnum << cfi->chipshift); - - i=first; - - while(len) { - unsigned long chipmask; - int size = regions[i].erasesize; - - ret = (*frob)(map, &cfi->chips[chipnum], adr, size, thunk); - - if (ret) - return ret; - - adr += size; - len -= size; - - chipmask = (1 << cfi->chipshift) - 1; - if ((adr & chipmask) == ((regions[i].offset + size * regions[i].numblocks) & chipmask)) - i++; - - if (adr >> cfi->chipshift) { - adr = 0; - chipnum++; - - if (chipnum >= cfi->numchips) - break; - } - } - - return 0; -} - - static int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, int len, void *thunk) { @@ -1437,7 +1377,8 @@ spin_unlock(chip->mutex); INVALIDATE_CACHED_RANGE(map, adr, len); - msleep(chip->erase_time / 2); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((chip->erase_time*HZ)/(2*1000)); spin_lock(chip->mutex); /* FIXME. Use a timer to check this, and return immediately. */ @@ -1548,7 +1489,7 @@ ofs = instr->addr; len = instr->len; - ret = cfi_intelext_varsize_frob(mtd, do_erase_oneblock, ofs, len, NULL); + ret = cfi_varsize_frob(mtd, do_erase_oneblock, ofs, len, NULL); if (ret) return ret; @@ -1693,17 +1634,17 @@ #ifdef DEBUG_LOCK_BITS printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n", __FUNCTION__, ofs, len); - cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock, + cfi_varsize_frob(mtd, do_printlockstatus_oneblock, ofs, len, 0); #endif - ret = cfi_intelext_varsize_frob(mtd, do_xxlock_oneblock, + ret = cfi_varsize_frob(mtd, do_xxlock_oneblock, ofs, len, DO_XXLOCK_ONEBLOCK_LOCK); #ifdef DEBUG_LOCK_BITS printk(KERN_DEBUG "%s: lock status after, ret=%d\n", __FUNCTION__, ret); - cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock, + cfi_varsize_frob(mtd, do_printlockstatus_oneblock, ofs, len, 0); #endif @@ -1717,17 +1658,17 @@ #ifdef DEBUG_LOCK_BITS printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n", __FUNCTION__, ofs, len); - cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock, + cfi_varsize_frob(mtd, do_printlockstatus_oneblock, ofs, len, 0); #endif - ret = cfi_intelext_varsize_frob(mtd, do_xxlock_oneblock, + ret = cfi_varsize_frob(mtd, do_xxlock_oneblock, ofs, len, DO_XXLOCK_ONEBLOCK_UNLOCK); #ifdef DEBUG_LOCK_BITS printk(KERN_DEBUG "%s: lock status after, ret=%d\n", __FUNCTION__, ret); - cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock, + cfi_varsize_frob(mtd, do_printlockstatus_oneblock, ofs, len, 0); #endif diff -NaurbB linux-2.6.9-pre1-bk18/drivers/mtd/chips/cfi_cmdset_0002.c linux-2.6.9_nbp/drivers/mtd/chips/cfi_cmdset_0002.c --- linux-2.6.9-pre1-bk18/drivers/mtd/chips/cfi_cmdset_0002.c 2004-08-14 07:36:56.000000000 +0200 +++ linux-2.6.9_nbp/drivers/mtd/chips/cfi_cmdset_0002.c 2004-09-13 12:43:27.000000000 +0200 @@ -13,7 +13,7 @@ * * This code is GPL * - * $Id: cfi_cmdset_0002.c,v 1.106 2004/08/09 14:02:32 dwmw2 Exp $ + * $Id: cfi_cmdset_0002.c,v 1.108 2004/09/02 01:59:22 eric Exp $ * */ @@ -40,13 +40,15 @@ #define MAX_WORD_RETRIES 3 +#define MANUFACTURER_AMD 0x0001 +#define MANUFACTURER_SST 0x00BF +#define SST49LF004B 0x0060 + static int cfi_amdstd_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); static int cfi_amdstd_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); static int cfi_amdstd_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); static int cfi_amdstd_erase_chip(struct mtd_info *, struct erase_info *); static int cfi_amdstd_erase_varsize(struct mtd_info *, struct erase_info *); -static int cfi_amdstd_lock_varsize(struct mtd_info *, loff_t, size_t); -static int cfi_amdstd_unlock_varsize(struct mtd_info *, loff_t, size_t); static void cfi_amdstd_sync (struct mtd_info *); static int cfi_amdstd_suspend (struct mtd_info *); static void cfi_amdstd_resume (struct mtd_info *); @@ -55,8 +57,11 @@ static void cfi_amdstd_destroy(struct mtd_info *); struct mtd_info *cfi_cmdset_0002(struct map_info *, int); -static struct mtd_info *cfi_amdstd_setup (struct map_info *); +static struct mtd_info *cfi_amdstd_setup (struct mtd_info *); +static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode); +static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr); +#include "fwh_lock.h" static struct mtd_chip_driver cfi_amdstd_chipdrv = { .probe = NULL, /* Not usable directly */ @@ -66,7 +71,6 @@ }; -/* #define DEBUG_LOCK_BITS */ /* #define DEBUG_CFI_FEATURES */ @@ -122,8 +126,9 @@ #ifdef AMD_BOOTLOC_BUG /* Wheee. Bring me the head of someone at AMD. */ -static void fixup_amd_bootblock(struct map_info *map, void* param) +static void fixup_amd_bootblock(struct mtd_info *mtd, void* param) { + struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; struct cfi_pri_amdstd *extp = cfi->cmdset_priv; __u8 major = extp->MajorVersion; @@ -141,25 +146,92 @@ } #endif -static struct cfi_fixup fixup_table[] = { +static void fixup_use_write_buffers(struct mtd_info *mtd, void *param) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + if (cfi->cfiq->BufWriteTimeoutTyp) { + DEBUG(MTD_DEBUG_LEVEL1, "Using buffer write method\n" ); + mtd->write = cfi_amdstd_write_buffers; + } +} + +static void fixup_use_secsi(struct mtd_info *mtd, void *param) +{ + /* Setup for chips with a secsi area */ + mtd->read_user_prot_reg = cfi_amdstd_secsi_read; + mtd->read_fact_prot_reg = cfi_amdstd_secsi_read; +} + +static void fixup_use_erase_chip(struct mtd_info *mtd, void *param) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + if ((cfi->cfiq->NumEraseRegions == 1) && + ((cfi->cfiq->EraseRegionInfo[0] & 0xffff) == 0)) { + mtd->erase = cfi_amdstd_erase_chip; + } + +} + +static struct cfi_fixup cfi_fixup_table[] = { #ifdef AMD_BOOTLOC_BUG - { - 0x0001, /* AMD */ - CFI_ID_ANY, - fixup_amd_bootblock, NULL - }, + { CFI_MFR_AMD, CFI_ID_ANY, fixup_amd_bootblock, NULL }, +#endif + { CFI_MFR_AMD, 0x0050, fixup_use_secsi, NULL, }, + { CFI_MFR_AMD, 0x0053, fixup_use_secsi, NULL, }, + { CFI_MFR_AMD, 0x0055, fixup_use_secsi, NULL, }, + { CFI_MFR_AMD, 0x0056, fixup_use_secsi, NULL, }, + { CFI_MFR_AMD, 0x005C, fixup_use_secsi, NULL, }, + { CFI_MFR_AMD, 0x005F, fixup_use_secsi, NULL, }, +#if !FORCE_WORD_WRITE + { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL, }, #endif { 0, 0, NULL, NULL } }; +static struct cfi_fixup jedec_fixup_table[] = { + { MANUFACTURER_SST, SST49LF004B, fixup_use_fwh_lock, NULL, }, + { 0, 0, NULL, NULL } +}; + +static struct cfi_fixup fixup_table[] = { + /* The CFI vendor ids and the JEDEC vendor IDs appear + * to be common. It is like the devices id's are as + * well. This table is to pick all cases where + * we know that is the case. + */ + { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_erase_chip, NULL }, + { 0, 0, NULL, NULL } +}; struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) { struct cfi_private *cfi = map->fldrv_priv; - unsigned char bootloc; + struct mtd_info *mtd; int i; + mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); + if (!mtd) { + printk(KERN_WARNING "Failed to allocate memory for MTD device\n"); + return NULL; + } + memset(mtd, 0, sizeof(*mtd)); + mtd->priv = map; + mtd->type = MTD_NORFLASH; + + /* Fill in the default mtd operations */ + mtd->erase = cfi_amdstd_erase_varsize; + mtd->write = cfi_amdstd_write_words; + mtd->read = cfi_amdstd_read; + mtd->sync = cfi_amdstd_sync; + mtd->suspend = cfi_amdstd_suspend; + mtd->resume = cfi_amdstd_resume; + mtd->flags = MTD_CAP_NORFLASH; + mtd->name = map->name; + if (cfi->cfi_mode==CFI_MODE_CFI){ + unsigned char bootloc; /* * It's a real CFI chip, not one for which the probe * routine faked a CFI structure. So we read the feature @@ -169,13 +241,16 @@ struct cfi_pri_amdstd *extp; extp = (struct cfi_pri_amdstd*)cfi_read_pri(map, adr, sizeof(*extp), "Amd/Fujitsu"); - if (!extp) + if (!extp) { + kfree(mtd); return NULL; + } /* Install our own private info structure */ cfi->cmdset_priv = extp; - cfi_fixup(map, fixup_table); + /* Apply cfi device specific fixups */ + cfi_fixup(mtd, cfi_fixup_table); #ifdef DEBUG_CFI_FEATURES /* Tell the user about it in lots of lovely detail */ @@ -233,11 +308,19 @@ printk(KERN_WARNING "MTD %s(): Unsupported device type %d\n", __func__, cfi->device_type); + kfree(mtd); + kfree(extp); return NULL; } } } /* CFI mode */ + else if (cfi->cfi_mode == CFI_MODE_JEDEC) { + /* Apply jedec specific fixups */ + cfi_fixup(mtd, jedec_fixup_table); + } + /* Apply generic fixups */ + cfi_fixup(mtd, fixup_table); for (i=0; i< cfi->numchips; i++) { cfi->chips[i].word_write_time = 1<cfiq->WordWriteTimeoutTyp; @@ -247,31 +330,21 @@ map->fldrv = &cfi_amdstd_chipdrv; - return cfi_amdstd_setup(map); + return cfi_amdstd_setup(mtd); } -static struct mtd_info *cfi_amdstd_setup(struct map_info *map) +static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd) { + struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; - struct mtd_info *mtd; unsigned long devsize = (1<cfiq->DevSize) * cfi->interleave; unsigned long offset = 0; int i,j; - mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); printk(KERN_NOTICE "number of %s chips: %d\n", (cfi->cfi_mode == CFI_MODE_CFI)?"CFI":"JEDEC",cfi->numchips); - - if (!mtd) { - printk(KERN_WARNING "Failed to allocate memory for MTD device\n"); - goto setup_err; - } - - memset(mtd, 0, sizeof(*mtd)); - mtd->priv = map; - mtd->type = MTD_NORFLASH; - /* Also select the correct geometry setup too */ + /* Select the correct geometry setup */ mtd->size = devsize * cfi->numchips; mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips; @@ -312,54 +385,10 @@ } #endif - if (mtd->numeraseregions == 1 - && ((cfi->cfiq->EraseRegionInfo[0] & 0xffff) + 1) == 1) { - mtd->erase = cfi_amdstd_erase_chip; - } else { - mtd->erase = cfi_amdstd_erase_varsize; - mtd->lock = cfi_amdstd_lock_varsize; - mtd->unlock = cfi_amdstd_unlock_varsize; - } - - if ( cfi->cfiq->BufWriteTimeoutTyp && !FORCE_WORD_WRITE) { - DEBUG(MTD_DEBUG_LEVEL1, "Using buffer write method\n" ); - mtd->write = cfi_amdstd_write_buffers; - } else { - DEBUG(MTD_DEBUG_LEVEL1, "Using word write method\n" ); - mtd->write = cfi_amdstd_write_words; - } - - mtd->read = cfi_amdstd_read; - /* FIXME: erase-suspend-program is broken. See http://lists.infradead.org/pipermail/linux-mtd/2003-December/009001.html */ printk(KERN_NOTICE "cfi_cmdset_0002: Disabling erase-suspend-program due to code brokenness.\n"); - /* does this chip have a secsi area? */ - if(cfi->mfr==1){ - - switch(cfi->id){ - case 0x50: - case 0x53: - case 0x55: - case 0x56: - case 0x5C: - case 0x5F: - /* Yes */ - mtd->read_user_prot_reg = cfi_amdstd_secsi_read; - mtd->read_fact_prot_reg = cfi_amdstd_secsi_read; - default: - ; - } - } - - - mtd->sync = cfi_amdstd_sync; - mtd->suspend = cfi_amdstd_suspend; - mtd->resume = cfi_amdstd_resume; - mtd->flags = MTD_CAP_NORFLASH; - map->fldrv = &cfi_amdstd_chipdrv; - mtd->name = map->name; __module_get(THIS_MODULE); return mtd; @@ -434,6 +463,7 @@ goto sleep; if (!(mode == FL_READY || mode == FL_POINT + || !cfip || (mode == FL_WRITING && (cfip->EraseSuspend & 0x2)) || (mode == FL_WRITING && (cfip->EraseSuspend & 0x1)))) goto sleep; @@ -958,7 +988,7 @@ struct cfi_private *cfi = map->fldrv_priv; unsigned long timeo = jiffies + HZ; /* see comments in do_write_oneword() regarding uWriteTimeo. */ - static unsigned long uWriteTimeout = ( HZ / 1000 ) + 1; + unsigned long uWriteTimeout = ( HZ / 1000 ) + 1; int ret = -EIO; unsigned long cmd_adr; int z, words; @@ -1229,100 +1259,7 @@ } -typedef int (*frob_t)(struct map_info *map, struct flchip *chip, - unsigned long adr, void *thunk); - - -static int cfi_amdstd_varsize_frob(struct mtd_info *mtd, frob_t frob, - loff_t ofs, size_t len, void *thunk) -{ - struct map_info *map = mtd->priv; - struct cfi_private *cfi = map->fldrv_priv; - unsigned long adr; - int chipnum, ret = 0; - int i, first; - struct mtd_erase_region_info *regions = mtd->eraseregions; - - if (ofs > mtd->size) - return -EINVAL; - - if ((len + ofs) > mtd->size) - return -EINVAL; - - /* Check that both start and end of the requested erase are - * aligned with the erasesize at the appropriate addresses. - */ - - i = 0; - - /* Skip all erase regions which are ended before the start of - the requested erase. Actually, to save on the calculations, - we skip to the first erase region which starts after the - start of the requested erase, and then go back one. - */ - - while (i < mtd->numeraseregions && ofs >= regions[i].offset) - i++; - i--; - - /* OK, now i is pointing at the erase region in which this - erase request starts. Check the start of the requested - erase range is aligned with the erase size which is in - effect here. - */ - - if (ofs & (regions[i].erasesize-1)) - return -EINVAL; - - /* Remember the erase region we start on */ - first = i; - - /* Next, check that the end of the requested erase is aligned - * with the erase region at that address. - */ - - while (inumeraseregions && (ofs + len) >= regions[i].offset) - i++; - - /* As before, drop back one to point at the region in which - the address actually falls - */ - i--; - - if ((ofs + len) & (regions[i].erasesize-1)) - return -EINVAL; - - chipnum = ofs >> cfi->chipshift; - adr = ofs - (chipnum << cfi->chipshift); - - i=first; - - while (len) { - ret = (*frob)(map, &cfi->chips[chipnum], adr, thunk); - - if (ret) - return ret; - - adr += regions[i].erasesize; - len -= regions[i].erasesize; - - if (adr % (1<< cfi->chipshift) == ((regions[i].offset + (regions[i].erasesize * regions[i].numblocks)) %( 1<< cfi->chipshift))) - i++; - - if (adr >> cfi->chipshift) { - adr = 0; - chipnum++; - - if (chipnum >= cfi->numchips) - break; - } - } - - return 0; -} - - -static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk) +static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, int len, void *thunk) { struct cfi_private *cfi = map->fldrv_priv; unsigned long timeo = jiffies + HZ; @@ -1415,7 +1352,7 @@ ofs = instr->addr; len = instr->len; - ret = cfi_amdstd_varsize_frob(mtd, do_erase_oneblock, ofs, len, NULL); + ret = cfi_varsize_frob(mtd, do_erase_oneblock, ofs, len, NULL); if (ret) return ret; @@ -1588,137 +1525,6 @@ } } - -#ifdef DEBUG_LOCK_BITS - -static int do_printlockstatus_oneblock(struct map_info *map, - struct flchip *chip, - unsigned long adr, - void *thunk) -{ - struct cfi_private *cfi = map->fldrv_priv; - int ofs_factor = cfi->interleave * cfi->device_type; - - cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); - printk(KERN_DEBUG "block status register for 0x%08lx is %x\n", - adr, cfi_read_query(map, adr+(2*ofs_factor))); - cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL); - - return 0; -} - - -#define debug_dump_locks(mtd, frob, ofs, len, thunk) \ - cfi_amdstd_varsize_frob((mtd), (frob), (ofs), (len), (thunk)) - -#else - -#define debug_dump_locks(...) - -#endif /* DEBUG_LOCK_BITS */ - - -struct xxlock_thunk { - uint8_t val; - flstate_t state; -}; - - -#define DO_XXLOCK_ONEBLOCK_LOCK ((struct xxlock_thunk){0x01, FL_LOCKING}) -#define DO_XXLOCK_ONEBLOCK_UNLOCK ((struct xxlock_thunk){0x00, FL_UNLOCKING}) - - -/* - * FIXME - this is *very* specific to a particular chip. It likely won't - * work for all chips that require unlock. It also hasn't been tested - * with interleaved chips. - */ -static int do_xxlock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk) -{ - struct cfi_private *cfi = map->fldrv_priv; - struct xxlock_thunk *xxlt = (struct xxlock_thunk *)thunk; - int ret; - - /* - * This is easy because these are writes to registers and not writes - * to flash memory - that means that we don't have to check status - * and timeout. - */ - - adr += chip->start; - /* - * lock block registers: - * - on 64k boundariesand - * - bit 1 set high - * - block lock registers are 4MiB lower - overflow subtract (danger) - */ - adr = ((adr & ~0xffff) | 0x2) + ~0x3fffff; - - cfi_spin_lock(chip->mutex); - ret = get_chip(map, chip, adr, FL_LOCKING); - if (ret) { - cfi_spin_unlock(chip->mutex); - return ret; - } - - chip->state = xxlt->state; - map_write(map, CMD(xxlt->val), adr); - - /* Done and happy. */ - chip->state = FL_READY; - put_chip(map, chip, adr); - cfi_spin_unlock(chip->mutex); - return 0; -} - - -static int cfi_amdstd_lock_varsize(struct mtd_info *mtd, - loff_t ofs, - size_t len) -{ - int ret; - - DEBUG(MTD_DEBUG_LEVEL3, - "%s: lock status before, ofs=0x%08llx, len=0x%08zX\n", - __func__, ofs, len); - debug_dump_locks(mtd, do_printlockstatus_oneblock, ofs, len, 0); - - ret = cfi_amdstd_varsize_frob(mtd, do_xxlock_oneblock, ofs, len, - (void *)&DO_XXLOCK_ONEBLOCK_LOCK); - - DEBUG(MTD_DEBUG_LEVEL3, - "%s: lock status after, ret=%d\n", - __func__, ret); - - debug_dump_locks(mtd, do_printlockstatus_oneblock, ofs, len, 0); - - return ret; -} - - -static int cfi_amdstd_unlock_varsize(struct mtd_info *mtd, - loff_t ofs, - size_t len) -{ - int ret; - - DEBUG(MTD_DEBUG_LEVEL3, - "%s: lock status before, ofs=0x%08llx, len=0x%08zX\n", - __func__, ofs, len); - debug_dump_locks(mtd, do_printlockstatus_oneblock, ofs, len, 0); - - ret = cfi_amdstd_varsize_frob(mtd, do_xxlock_oneblock, ofs, len, - (void *)&DO_XXLOCK_ONEBLOCK_UNLOCK); - - DEBUG(MTD_DEBUG_LEVEL3, - "%s: lock status after, ret=%d\n", - __func__, ret); - debug_dump_locks(mtd, do_printlockstatus_oneblock, ofs, len, 0); - - return ret; -} - - static void cfi_amdstd_destroy(struct mtd_info *mtd) { struct map_info *map = mtd->priv; diff -NaurbB linux-2.6.9-pre1-bk18/drivers/mtd/chips/cfi_util.c linux-2.6.9_nbp/drivers/mtd/chips/cfi_util.c --- linux-2.6.9-pre1-bk18/drivers/mtd/chips/cfi_util.c 2004-08-14 07:37:15.000000000 +0200 +++ linux-2.6.9_nbp/drivers/mtd/chips/cfi_util.c 2004-09-13 12:43:27.000000000 +0200 @@ -7,7 +7,7 @@ * * This code is covered by the GPL. * - * $Id: cfi_util.c,v 1.4 2004/07/14 08:38:44 dwmw2 Exp $ + * $Id: cfi_util.c,v 1.5 2004/08/12 06:40:23 eric Exp $ * */ @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -74,19 +75,114 @@ EXPORT_SYMBOL(cfi_read_pri); -void cfi_fixup(struct map_info *map, struct cfi_fixup* fixups) +void cfi_fixup(struct mtd_info *mtd, struct cfi_fixup *fixups) { + struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; struct cfi_fixup *f; for (f=fixups; f->fixup; f++) { if (((f->mfr == CFI_MFR_ANY) || (f->mfr == cfi->mfr)) && ((f->id == CFI_ID_ANY) || (f->id == cfi->id))) { - f->fixup(map, f->param); + f->fixup(mtd, f->param); } } } EXPORT_SYMBOL(cfi_fixup); +int cfi_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob, + loff_t ofs, size_t len, void *thunk) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + unsigned long adr; + int chipnum, ret = 0; + int i, first; + struct mtd_erase_region_info *regions = mtd->eraseregions; + + if (ofs > mtd->size) + return -EINVAL; + + if ((len + ofs) > mtd->size) + return -EINVAL; + + /* Check that both start and end of the requested erase are + * aligned with the erasesize at the appropriate addresses. + */ + + i = 0; + + /* Skip all erase regions which are ended before the start of + the requested erase. Actually, to save on the calculations, + we skip to the first erase region which starts after the + start of the requested erase, and then go back one. + */ + + while (i < mtd->numeraseregions && ofs >= regions[i].offset) + i++; + i--; + + /* OK, now i is pointing at the erase region in which this + erase request starts. Check the start of the requested + erase range is aligned with the erase size which is in + effect here. + */ + + if (ofs & (regions[i].erasesize-1)) + return -EINVAL; + + /* Remember the erase region we start on */ + first = i; + + /* Next, check that the end of the requested erase is aligned + * with the erase region at that address. + */ + + while (inumeraseregions && (ofs + len) >= regions[i].offset) + i++; + + /* As before, drop back one to point at the region in which + the address actually falls + */ + i--; + + if ((ofs + len) & (regions[i].erasesize-1)) + return -EINVAL; + + chipnum = ofs >> cfi->chipshift; + adr = ofs - (chipnum << cfi->chipshift); + + i=first; + + while(len) { + unsigned long chipmask; + int size = regions[i].erasesize; + + ret = (*frob)(map, &cfi->chips[chipnum], adr, size, thunk); + + if (ret) + return ret; + + adr += size; + len -= size; + + chipmask = (1 << cfi->chipshift) - 1; + if ((adr & chipmask) == ((regions[i].offset + size * regions[i].numblocks) & chipmask)) + i++; + + if (adr >> cfi->chipshift) { + adr = 0; + chipnum++; + + if (chipnum >= cfi->numchips) + break; + } + } + + return 0; +} + +EXPORT_SYMBOL(cfi_varsize_frob); + MODULE_LICENSE("GPL"); diff -NaurbB linux-2.6.9-pre1-bk18/drivers/mtd/chips/fwh_lock.h linux-2.6.9_nbp/drivers/mtd/chips/fwh_lock.h --- linux-2.6.9-pre1-bk18/drivers/mtd/chips/fwh_lock.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/drivers/mtd/chips/fwh_lock.h 2004-09-13 13:41:20.000000000 +0200 @@ -0,0 +1,104 @@ +#ifndef FWH_LOCK_H +#define FWH_LOCK_H + + +enum fwh_lock_state { + FWH_UNLOCKED = 0, + FWH_DENY_WRITE = 1, + FWH_IMMUTABLE = 2, + FWH_DENY_READ = 4, +}; + +struct fwh_xxlock_thunk { + enum fwh_lock_state val; + flstate_t state; +}; + + +#define FWH_XXLOCK_ONEBLOCK_LOCK ((struct fwh_xxlock_thunk){ FWH_DENY_WRITE, FL_LOCKING}) +#define FWH_XXLOCK_ONEBLOCK_UNLOCK ((struct fwh_xxlock_thunk){ FWH_UNLOCKED, FL_UNLOCKING}) + +/* + * This locking/unlock is specific to firmware hub parts. Only one + * is known that supports the Intel command set. Firmware + * hub parts cannot be interleaved as they are on the LPC bus + * so this code has not been tested with interleaved chips, + * and will likely fail in that context. + */ +static int fwh_xxlock_oneblock(struct map_info *map, struct flchip *chip, + unsigned long adr, int len, void *thunk) +{ + struct cfi_private *cfi = map->fldrv_priv; + struct fwh_xxlock_thunk *xxlt = (struct fwh_xxlock_thunk *)thunk; + int ret; + + /* Refuse the operation if the we cannot look behind the chip */ + if (chip->start < 0x400000) { + return -EIO; + } + /* + * lock block registers: + * - on 64k boundariesand + * - bit 1 set high + * - block lock registers are 4MiB lower - overflow subtract (danger) + * + * The address manipulation is first done on the logical address + * which is 0 at the start of the chip, and then the offset of + * the individual chip is addted to it. Any other order a weird + * map offset could cause problems. + */ + adr = (adr & ~0xffffUL) | 0x2; + adr += chip->start - 0x400000; + + /* + * This is easy because these are writes to registers and not writes + * to flash memory - that means that we don't have to check status + * and timeout. + */ + cfi_spin_lock(chip->mutex); + ret = get_chip(map, chip, adr, FL_LOCKING); + if (ret) { + cfi_spin_unlock(chip->mutex); + return ret; + } + + chip->state = xxlt->state; + map_write(map, CMD(xxlt->val), adr); + + /* Done and happy. */ + chip->state = FL_READY; + put_chip(map, chip, adr); + cfi_spin_unlock(chip->mutex); + return 0; +} + + +static int fwh_lock_varsize(struct mtd_info *mtd, loff_t ofs, size_t len) +{ + int ret; + + ret = cfi_varsize_frob(mtd, fwh_xxlock_oneblock, ofs, len, + (void *)&FWH_XXLOCK_ONEBLOCK_LOCK); + + return ret; +} + + +static int fwh_unlock_varsize(struct mtd_info *mtd, loff_t ofs, size_t len) +{ + int ret; + + ret = cfi_varsize_frob(mtd, fwh_xxlock_oneblock, ofs, len, + (void *)&FWH_XXLOCK_ONEBLOCK_UNLOCK); + + return ret; +} + +static void fixup_use_fwh_lock(struct mtd_info *mtd, void *param) +{ + printk(KERN_NOTICE "using fwh lock/unlock method\n"); + /* Setup for the chips with the fwh lock method */ + mtd->lock = fwh_lock_varsize; + mtd->unlock = fwh_unlock_varsize; +} +#endif /* FWH_LOCK_H */ diff -NaurbB linux-2.6.9-pre1-bk18/drivers/mtd/chips/gen_probe.c linux-2.6.9_nbp/drivers/mtd/chips/gen_probe.c --- linux-2.6.9-pre1-bk18/drivers/mtd/chips/gen_probe.c 2004-08-14 07:37:40.000000000 +0200 +++ linux-2.6.9_nbp/drivers/mtd/chips/gen_probe.c 2004-09-13 12:43:27.000000000 +0200 @@ -2,7 +2,7 @@ * Routines common to all CFI-type probes. * (C) 2001-2003 Red Hat, Inc. * GPL'd - * $Id: gen_probe.c,v 1.19 2004/07/13 22:33:32 dwmw2 Exp $ + * $Id: gen_probe.c,v 1.21 2004/08/14 15:14:05 dwmw2 Exp $ */ #include @@ -64,7 +64,7 @@ interleave and device type, etc. */ if (!genprobe_new_chip(map, cp, &cfi)) { /* The probe didn't like it */ - printk(KERN_WARNING "%s: Found no %s device at location zero\n", + printk(KERN_DEBUG "%s: Found no %s device at location zero\n", cp->name, map->name); return NULL; } @@ -169,8 +169,12 @@ cfi->interleave = nr_chips; - for (type = 0; type < 3; type++) { - cfi->device_type = 1<device_type = type; if (cp->probe_chip(map, 0, NULL, cfi)) return 1; diff -NaurbB linux-2.6.9-pre1-bk18/drivers/mtd/chips/jedec_probe.c linux-2.6.9_nbp/drivers/mtd/chips/jedec_probe.c --- linux-2.6.9-pre1-bk18/drivers/mtd/chips/jedec_probe.c 2005-08-15 01:14:41.000000000 +0200 +++ linux-2.6.9_nbp/drivers/mtd/chips/jedec_probe.c 2004-09-13 12:43:27.000000000 +0200 @@ -1,7 +1,7 @@ /* Common Flash Interface probe code. (C) 2000 Red Hat. GPL'd. - $Id: jedec_probe.c,v 1.51 2004/07/14 14:44:30 thayne Exp $ + $Id: jedec_probe.c,v 1.54 2004/09/10 19:15:41 thayne Exp $ See JEDEC (http://www.jedec.org/) standard JESD21C (section 3.5) for the standard this probe goes back to. @@ -1379,6 +1379,22 @@ ERASEINFO(0x01000,256), } }, { + .mfr_id = MANUFACTURER_SST, /* should be CFI */ + .dev_id = SST39LF160, + .name = "SST 39LF160", + .uaddr = { + [0] = MTD_UADDR_0x5555_0x2AAA, /* x8 */ + [1] = MTD_UADDR_0x5555_0x2AAA /* x16 */ + }, + .DevSize = SIZE_2MiB, + .CmdSet = P_ID_AMD_STD, + .NumEraseRegions= 2, + .regions = { + ERASEINFO(0x1000,256), + ERASEINFO(0x1000,256) + } + + }, { .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ .dev_id = M29W800DT, .name = "ST M29W800DT", @@ -1653,9 +1669,11 @@ * as they will ignore the writes and dont care what address * the F0 is written to */ if(cfi->addr_unlock1) { - /*printk("reset unlock called %x %x \n",cfi->addr_unlock1,cfi->addr_unlock2);*/ - cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL); - cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, CFI_DEVICETYPE_X8, NULL); + DEBUG( MTD_DEBUG_LEVEL3, + "reset unlock called %x %x \n", + cfi->addr_unlock1,cfi->addr_unlock2); + cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, cfi->device_type, NULL); } cfi_send_gen_cmd(0xF0, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL); @@ -1700,7 +1718,6 @@ static int cfi_jedec_setup(struct cfi_private *p_cfi, int index) { int i,num_erase_regions; - unsigned long mask; __u8 uaddr; printk("Found: %s\n",jedec_table[index].name); @@ -1736,9 +1753,8 @@ } /* Mask out address bits which are smaller than the device type */ - mask = ~(p_cfi->device_type-1); - p_cfi->addr_unlock1 = unlock_addrs[uaddr].addr1 & mask; - p_cfi->addr_unlock2 = unlock_addrs[uaddr].addr2 & mask; + p_cfi->addr_unlock1 = unlock_addrs[uaddr].addr1; + p_cfi->addr_unlock2 = unlock_addrs[uaddr].addr2; return 1; /* ok */ } @@ -1759,7 +1775,6 @@ int rc = 0; /* failure until all tests pass */ u32 mfr, id; __u8 uaddr; - unsigned long mask; /* * The IDs must match. For X16 and X32 devices operating in @@ -1810,18 +1825,16 @@ goto match_done; } - mask = ~(cfi->device_type-1); - DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): check unlock addrs 0x%.4x 0x%.4x\n", __func__, cfi->addr_unlock1, cfi->addr_unlock2 ); if ( MTD_UADDR_UNNECESSARY != uaddr && MTD_UADDR_DONT_CARE != uaddr - && ( (unlock_addrs[uaddr].addr1 & mask) != cfi->addr_unlock1 || - (unlock_addrs[uaddr].addr2 & mask) != cfi->addr_unlock2 ) ) { + && ( unlock_addrs[uaddr].addr1 != cfi->addr_unlock1 || + unlock_addrs[uaddr].addr2 != cfi->addr_unlock2 ) ) { DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): 0x%.4lx 0x%.4lx did not match\n", __func__, - unlock_addrs[uaddr].addr1 & mask, - unlock_addrs[uaddr].addr2 & mask); + unlock_addrs[uaddr].addr1 + unlock_addrs[uaddr].addr2); goto match_done; } @@ -1857,10 +1870,10 @@ */ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): return to ID mode\n", __func__ ); if(cfi->addr_unlock1) { - cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL); - cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, cfi->device_type, NULL); } - cfi_send_gen_cmd(0x90, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0x90, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL); /* FIXME - should have a delay before continuing */ match_done: @@ -1876,16 +1889,14 @@ retry: if (!cfi->numchips) { - unsigned long mask = ~(cfi->device_type-1); - uaddr_idx++; if (MTD_UADDR_UNNECESSARY == uaddr_idx) return 0; /* Mask out address bits which are smaller than the device type */ - cfi->addr_unlock1 = unlock_addrs[uaddr_idx].addr1 & mask; - cfi->addr_unlock2 = unlock_addrs[uaddr_idx].addr2 & mask; + cfi->addr_unlock1 = unlock_addrs[uaddr_idx].addr1; + cfi->addr_unlock2 = unlock_addrs[uaddr_idx].addr2; } /* Make certain we aren't probing past the end of map */ @@ -1916,10 +1927,10 @@ /* Autoselect Mode */ if(cfi->addr_unlock1) { - cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL); - cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, cfi->device_type, NULL); } - cfi_send_gen_cmd(0x90, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0x90, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL); /* FIXME - should have a delay before continuing */ if (!cfi->numchips) { diff -NaurbB linux-2.6.9-pre1-bk18/drivers/mtd/devices/blkmtd-24.c linux-2.6.9_nbp/drivers/mtd/devices/blkmtd-24.c --- linux-2.6.9-pre1-bk18/drivers/mtd/devices/blkmtd-24.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/drivers/mtd/devices/blkmtd-24.c 2004-09-13 13:41:20.000000000 +0200 @@ -0,0 +1,1056 @@ +/* + * $Id: blkmtd-24.c,v 1.23 2004/08/09 18:49:42 dmarlin Exp $ + * + * blkmtd.c - use a block device as a fake MTD + * + * Author: Simon Evans + * + * Copyright (C) 2001,2002 Simon Evans + * + * Licence: GPL + * + * How it works: + * The driver uses raw/io to read/write the device and the page + * cache to cache access. Writes update the page cache with the + * new data and mark it dirty and add the page into a kiobuf. + * When the kiobuf becomes full or the next extry is to an earlier + * block in the kiobuf then it is flushed to disk. This allows + * writes to remained ordered and gives a small and simple outgoing + * write cache. + * + * It can be loaded Read-Only to prevent erases and writes to the + * medium. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CONFIG_MTD_DEBUG +#ifdef CONFIG_PROC_FS +# include +# define BLKMTD_PROC_DEBUG + static struct proc_dir_entry *blkmtd_proc; +#endif +#endif + + +#define err(format, arg...) printk(KERN_ERR "blkmtd: " format "\n" , ## arg) +#define info(format, arg...) printk(KERN_INFO "blkmtd: " format "\n" , ## arg) +#define warn(format, arg...) printk(KERN_WARNING "blkmtd: " format "\n" , ## arg) +#define crit(format, arg...) printk(KERN_CRIT "blkmtd: " format "\n" , ## arg) + + +/* Default erase size in KiB, always make it a multiple of PAGE_SIZE */ +#define CONFIG_MTD_BLKDEV_ERASESIZE (128 << 10) /* 128KiB */ +#define VERSION "1.10" + +/* Info for the block device */ +struct blkmtd_dev { + struct list_head list; + struct block_device *binding; + struct mtd_info mtd_info; + struct kiobuf *rd_buf, *wr_buf; + long iobuf_locks; + struct semaphore wrbuf_mutex; +}; + + +/* Static info about the MTD, used in cleanup_module */ +static LIST_HEAD(blkmtd_device_list); + + +static void blkmtd_sync(struct mtd_info *mtd); + +#define MAX_DEVICES 4 + +/* Module parameters passed by insmod/modprobe */ +char *device[MAX_DEVICES]; /* the block device to use */ +int erasesz[MAX_DEVICES]; /* optional default erase size */ +int ro[MAX_DEVICES]; /* optional read only flag */ +int sync; + + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Simon Evans "); +MODULE_DESCRIPTION("Emulate an MTD using a block device"); +MODULE_PARM(device, "1-4s"); +MODULE_PARM_DESC(device, "block device to use"); +MODULE_PARM(erasesz, "1-4i"); +MODULE_PARM_DESC(erasesz, "optional erase size to use in KiB. eg 4=4KiB."); +MODULE_PARM(ro, "1-4i"); +MODULE_PARM_DESC(ro, "1=Read only, writes and erases cause errors"); +MODULE_PARM(sync, "i"); +MODULE_PARM_DESC(sync, "1=Synchronous writes"); + + +/** + * read_pages - read in pages via the page cache + * @dev: device to read from + * @pagenrs: list of page numbers wanted + * @pagelst: storage for struce page * pointers + * @pages: count of pages wanted + * + * Read pages, getting them from the page cache if available + * else reading them in from disk if not. pagelst must be preallocated + * to hold the page count. + */ +static int read_pages(struct blkmtd_dev *dev, int pagenrs[], struct page **pagelst, int pages) +{ + kdev_t kdev; + struct page *page; + int cnt = 0; + struct kiobuf *iobuf; + int err = 0; + + if(!dev) { + err("read_pages: PANIC dev == NULL"); + return -EIO; + } + kdev = to_kdev_t(dev->binding->bd_dev); + + DEBUG(2, "read_pages: reading %d pages\n", pages); + if(test_and_set_bit(0, &dev->iobuf_locks)) { + err = alloc_kiovec(1, &iobuf); + if (err) { + crit("cant allocate kiobuf"); + return -ENOMEM; + } +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) + iobuf->blocks = kmalloc(KIO_MAX_SECTORS * sizeof(unsigned long), GFP_KERNEL); + if(iobuf->blocks == NULL) { + crit("cant allocate iobuf blocks"); + free_kiovec(1, &iobuf); + return -ENOMEM; + } +#endif + } else { + iobuf = dev->rd_buf; + } + + iobuf->nr_pages = 0; + iobuf->length = 0; + iobuf->offset = 0; + iobuf->locked = 1; + + for(cnt = 0; cnt < pages; cnt++) { + page = grab_cache_page(dev->binding->bd_inode->i_mapping, pagenrs[cnt]); + pagelst[cnt] = page; + if(!Page_Uptodate(page)) { + iobuf->blocks[iobuf->nr_pages] = pagenrs[cnt]; + iobuf->maplist[iobuf->nr_pages++] = page; + } + } + + if(iobuf->nr_pages) { + iobuf->length = iobuf->nr_pages << PAGE_SHIFT; + err = brw_kiovec(READ, 1, &iobuf, kdev, iobuf->blocks, PAGE_SIZE); + DEBUG(3, "blkmtd: read_pages: finished, err = %d\n", err); + if(err < 0) { + while(pages--) { + ClearPageUptodate(pagelst[pages]); + unlock_page(pagelst[pages]); + page_cache_release(pagelst[pages]); + } + } else { + while(iobuf->nr_pages--) { + SetPageUptodate(iobuf->maplist[iobuf->nr_pages]); + } + err = 0; + } + } + + + if(iobuf != dev->rd_buf) { +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) + kfree(iobuf->blocks); +#endif + free_kiovec(1, &iobuf); + } else { + clear_bit(0, &dev->iobuf_locks); + } + DEBUG(2, "read_pages: done, err = %d\n", err); + return err; +} + + +/** + * commit_pages - commit pages in the writeout kiobuf to disk + * @dev: device to write to + * + * If the current dev has pages in the dev->wr_buf kiobuf, + * they are written to disk using brw_kiovec() + */ +static int commit_pages(struct blkmtd_dev *dev) +{ + struct kiobuf *iobuf = dev->wr_buf; + kdev_t kdev = to_kdev_t(dev->binding->bd_dev); + int err = 0; + + iobuf->length = iobuf->nr_pages << PAGE_SHIFT; + iobuf->locked = 1; + if(iobuf->length) { + int i; + DEBUG(2, "blkmtd: commit_pages: nrpages = %d\n", iobuf->nr_pages); + /* Check all the pages are dirty and lock them */ + for(i = 0; i < iobuf->nr_pages; i++) { + struct page *page = iobuf->maplist[i]; + BUG_ON(!PageDirty(page)); + lock_page(page); + } + err = brw_kiovec(WRITE, 1, &iobuf, kdev, iobuf->blocks, PAGE_SIZE); + DEBUG(3, "commit_write: committed %d pages err = %d\n", iobuf->nr_pages, err); + while(iobuf->nr_pages) { + struct page *page = iobuf->maplist[--iobuf->nr_pages]; + ClearPageDirty(page); + SetPageUptodate(page); + unlock_page(page); + page_cache_release(page); + } + } + + DEBUG(2, "blkmtd: sync: end, err = %d\n", err); + iobuf->offset = 0; + iobuf->nr_pages = 0; + iobuf->length = 0; + return err; +} + + +/** + * write_pages - write block of data to device via the page cache + * @dev: device to write to + * @buf: data source or NULL if erase (output is set to 0xff) + * @to: offset into output device + * @len: amount to data to write + * @retlen: amount of data written + * + * Grab pages from the page cache and fill them with the source data. + * Non page aligned start and end result in a readin of the page and + * part of the page being modified. Pages are added to the wr_buf kiobuf + * until this becomes full or the next page written to has a lower pagenr + * then the current max pagenr in the kiobuf. + */ +static int write_pages(struct blkmtd_dev *dev, const u_char *buf, loff_t to, + size_t len, int *retlen) +{ + int pagenr, offset; + size_t start_len = 0, end_len; + int pagecnt = 0; + struct kiobuf *iobuf = dev->wr_buf; + int err = 0; + struct page *pagelst[2]; + int pagenrs[2]; + int readpages = 0; + int ignorepage = -1; + + pagenr = to >> PAGE_SHIFT; + offset = to & ~PAGE_MASK; + + DEBUG(2, "blkmtd: write_pages: buf = %p to = %ld len = %zd pagenr = %d offset = %d\n", + buf, (long)to, len, pagenr, offset); + + *retlen = 0; + /* see if we have to do a partial write at the start */ + if(offset) { + start_len = ((offset + len) > PAGE_SIZE) ? PAGE_SIZE - offset : len; + len -= start_len; + } + + /* calculate the length of the other two regions */ + end_len = len & ~PAGE_MASK; + len -= end_len; + + if(start_len) { + pagenrs[0] = pagenr; + readpages++; + pagecnt++; + } + if(len) + pagecnt += len >> PAGE_SHIFT; + if(end_len) { + pagenrs[readpages] = pagenr + pagecnt; + readpages++; + pagecnt++; + } + + DEBUG(3, "blkmtd: write: start_len = %zd len = %zd end_len = %zd pagecnt = %d\n", + start_len, len, end_len, pagecnt); + + down(&dev->wrbuf_mutex); + + if(iobuf->nr_pages && ((pagenr <= iobuf->blocks[iobuf->nr_pages-1]) + || (iobuf->nr_pages + pagecnt) >= KIO_STATIC_PAGES)) { + + if((pagenr == iobuf->blocks[iobuf->nr_pages-1]) + && ((iobuf->nr_pages + pagecnt) < KIO_STATIC_PAGES)) { + iobuf->nr_pages--; + ignorepage = pagenr; + } else { + DEBUG(3, "blkmtd: doing writeout pagenr = %d max_pagenr = %ld pagecnt = %d idx = %d\n", + pagenr, iobuf->blocks[iobuf->nr_pages-1], + pagecnt, iobuf->nr_pages); + commit_pages(dev); + } + } + + if(readpages) { + err = read_pages(dev, pagenrs, pagelst, readpages); + if(err < 0) + goto readin_err; + } + + if(start_len) { + /* do partial start region */ + struct page *page; + + DEBUG(3, "blkmtd: write: doing partial start, page = %d len = %zd offset = %d\n", + pagenr, start_len, offset); + page = pagelst[0]; + BUG_ON(!buf); + if(PageDirty(page) && pagenr != ignorepage) { + err("to = %lld start_len = %zd len = %zd end_len = %zd pagenr = %d ignorepage = %d\n", + to, start_len, len, end_len, pagenr, ignorepage); + BUG(); + } + memcpy(page_address(page)+offset, buf, start_len); + SetPageDirty(page); + SetPageUptodate(page); + unlock_page(page); + buf += start_len; + *retlen = start_len; + err = 0; + iobuf->blocks[iobuf->nr_pages] = pagenr++; + iobuf->maplist[iobuf->nr_pages] = page; + iobuf->nr_pages++; + } + + /* Now do the main loop to a page aligned, n page sized output */ + if(len) { + int pagesc = len >> PAGE_SHIFT; + DEBUG(3, "blkmtd: write: whole pages start = %d, count = %d\n", + pagenr, pagesc); + while(pagesc) { + struct page *page; + + /* see if page is in the page cache */ + DEBUG(3, "blkmtd: write: grabbing page %d from page cache\n", pagenr); + page = grab_cache_page(dev->binding->bd_inode->i_mapping, pagenr); + if(PageDirty(page) && pagenr != ignorepage) { + BUG(); + } + if(!page) { + warn("write: cant grab cache page %d", pagenr); + err = -ENOMEM; + goto write_err; + } + if(!buf) { + memset(page_address(page), 0xff, PAGE_SIZE); + } else { + memcpy(page_address(page), buf, PAGE_SIZE); + buf += PAGE_SIZE; + } + iobuf->blocks[iobuf->nr_pages] = pagenr++; + iobuf->maplist[iobuf->nr_pages] = page; + iobuf->nr_pages++; + SetPageDirty(page); + SetPageUptodate(page); + unlock_page(page); + pagesc--; + *retlen += PAGE_SIZE; + } + } + + if(end_len) { + /* do the third region */ + struct page *page; + DEBUG(3, "blkmtd: write: doing partial end, page = %d len = %zd\n", + pagenr, end_len); + page = pagelst[readpages-1]; + BUG_ON(!buf); + if(PageDirty(page) && pagenr != ignorepage) { + err("to = %lld start_len = %zd len = %zd end_len = %zd pagenr = %d ignorepage = %d\n", + to, start_len, len, end_len, pagenr, ignorepage); + BUG(); + } + memcpy(page_address(page), buf, end_len); + SetPageDirty(page); + SetPageUptodate(page); + unlock_page(page); + DEBUG(3, "blkmtd: write: writing out partial end\n"); + *retlen += end_len; + err = 0; + iobuf->blocks[iobuf->nr_pages] = pagenr; + iobuf->maplist[iobuf->nr_pages] = page; + iobuf->nr_pages++; + } + + DEBUG(2, "blkmtd: write: end, retlen = %zd, err = %d\n", *retlen, err); + + if(sync) { +write_err: + commit_pages(dev); + } + +readin_err: + up(&dev->wrbuf_mutex); + return err; +} + + +/* erase a specified part of the device */ +static int blkmtd_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + struct blkmtd_dev *dev = mtd->priv; + struct mtd_erase_region_info *einfo = mtd->eraseregions; + int numregions = mtd->numeraseregions; + size_t from; + u_long len; + int err = -EIO; + size_t retlen; + + /* check readonly */ + if(!dev->wr_buf) { + err("error: mtd%d trying to erase readonly device %s", + mtd->index, mtd->name); + instr->state = MTD_ERASE_FAILED; + goto erase_callback; + } + + instr->state = MTD_ERASING; + from = instr->addr; + len = instr->len; + + /* check erase region has valid start and length */ + DEBUG(2, "blkmtd: erase: dev = `%s' from = 0x%zx len = 0x%lx\n", + bdevname(dev->binding->bd_dev), from, len); + while(numregions) { + DEBUG(3, "blkmtd: checking erase region = 0x%08X size = 0x%X num = 0x%x\n", + einfo->offset, einfo->erasesize, einfo->numblocks); + if(from >= einfo->offset + && from < einfo->offset + (einfo->erasesize * einfo->numblocks)) { + if(len == einfo->erasesize + && ( (from - einfo->offset) % einfo->erasesize == 0)) + break; + } + numregions--; + einfo++; + } + + if(!numregions) { + /* Not a valid erase block */ + err("erase: invalid erase request 0x%lX @ 0x%08zX", len, from); + instr->state = MTD_ERASE_FAILED; + err = -EIO; + } + + if(instr->state != MTD_ERASE_FAILED) { + /* do the erase */ + DEBUG(3, "Doing erase from = %zd len = %ld\n", from, len); + err = write_pages(dev, NULL, from, len, &retlen); + if(err < 0) { + err("erase failed err = %d", err); + instr->state = MTD_ERASE_FAILED; + } else { + instr->state = MTD_ERASE_DONE; + err = 0; + } + } + + DEBUG(3, "blkmtd: erase: checking callback\n"); + erase_callback: + mtd_erase_callback(instr); + DEBUG(2, "blkmtd: erase: finished (err = %d)\n", err); + return err; +} + + +/* read a range of the data via the page cache */ +static int blkmtd_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct blkmtd_dev *dev = mtd->priv; + int err = 0; + int offset; + int pagenr, pages; + struct page **pagelst; + int *pagenrs; + int i; + + *retlen = 0; + + DEBUG(2, "blkmtd: read: dev = `%s' from = %lld len = %zd buf = %p\n", + bdevname(dev->binding->bd_dev), from, len, buf); + + pagenr = from >> PAGE_SHIFT; + offset = from - (pagenr << PAGE_SHIFT); + + pages = (offset+len+PAGE_SIZE-1) >> PAGE_SHIFT; + DEBUG(3, "blkmtd: read: pagenr = %d offset = %d, pages = %d\n", + pagenr, offset, pages); + + pagelst = kmalloc(sizeof(struct page *) * pages, GFP_KERNEL); + if(!pagelst) + return -ENOMEM; + pagenrs = kmalloc(sizeof(int) * pages, GFP_KERNEL); + if(!pagenrs) { + kfree(pagelst); + return -ENOMEM; + } + for(i = 0; i < pages; i++) + pagenrs[i] = pagenr+i; + + err = read_pages(dev, pagenrs, pagelst, pages); + if(err) + goto readerr; + + pagenr = 0; + while(pages) { + struct page *page; + int cpylen; + + DEBUG(3, "blkmtd: read: looking for page: %d\n", pagenr); + page = pagelst[pagenr]; + + cpylen = (PAGE_SIZE > len) ? len : PAGE_SIZE; + if(offset+cpylen > PAGE_SIZE) + cpylen = PAGE_SIZE-offset; + + memcpy(buf + *retlen, page_address(page) + offset, cpylen); + offset = 0; + len -= cpylen; + *retlen += cpylen; + pagenr++; + pages--; + unlock_page(page); + if(!PageDirty(page)) + page_cache_release(page); + } + + readerr: + kfree(pagelst); + kfree(pagenrs); + DEBUG(2, "blkmtd: end read: retlen = %zd, err = %d\n", *retlen, err); + return err; +} + + +/* write data to the underlying device */ +static int blkmtd_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct blkmtd_dev *dev = mtd->priv; + int err; + + *retlen = 0; + if(!len) + return 0; + + DEBUG(2, "blkmtd: write: dev = `%s' to = %lld len = %zd buf = %p\n", + bdevname(dev->binding->bd_dev), to, len, buf); + + /* handle readonly and out of range numbers */ + + if(!dev->wr_buf) { + err("error: trying to write to a readonly device %s", mtd->name); + return -EROFS; + } + + if(to >= mtd->size) { + return -ENOSPC; + } + + if(to + len > mtd->size) { + len = (mtd->size - to); + } + + err = write_pages(dev, buf, to, len, retlen); + if(err < 0) + *retlen = 0; + else + err = 0; + DEBUG(2, "blkmtd: write: end, err = %d\n", err); + return err; +} + + +/* sync the device - wait until the write queue is empty */ +static void blkmtd_sync(struct mtd_info *mtd) +{ + struct blkmtd_dev *dev = mtd->priv; + struct kiobuf *iobuf = dev->wr_buf; + + DEBUG(2, "blkmtd: sync: called\n"); + if(iobuf == NULL) + return; + + DEBUG(3, "blkmtd: kiovec: length = %d nr_pages = %d\n", + iobuf->length, iobuf->nr_pages); + down(&dev->wrbuf_mutex); + if(iobuf->nr_pages) + commit_pages(dev); + up(&dev->wrbuf_mutex); +} + + +#ifdef BLKMTD_PROC_DEBUG +/* procfs stuff */ +static int blkmtd_proc_read(char *page, char **start, off_t off, + int count, int *eof, void *data) +{ + int len; + struct list_head *temp1, *temp2; + + MOD_INC_USE_COUNT; + + /* Count the size of the page lists */ + + len = sprintf(page, "dev\twr_idx\tmax_idx\tnrpages\tclean\tdirty\tlocked\tlru\n"); + list_for_each_safe(temp1, temp2, &blkmtd_device_list) { + struct blkmtd_dev *dev = list_entry(temp1, struct blkmtd_dev, + list); + struct list_head *temp; + struct page *pagei; + + int clean = 0, dirty = 0, locked = 0, lru = 0; + /* Count the size of the page lists */ + list_for_each(temp, &dev->binding->bd_inode->i_mapping->clean_pages) { + pagei = list_entry(temp, struct page, list); + clean++; + if(PageLocked(pagei)) + locked++; + if(PageDirty(pagei)) + dirty++; + if(PageLRU(pagei)) + lru++; + } + list_for_each(temp, &dev->binding->bd_inode->i_mapping->dirty_pages) { + pagei = list_entry(temp, struct page, list); + if(PageLocked(pagei)) + locked++; + if(PageDirty(pagei)) + dirty++; + if(PageLRU(pagei)) + lru++; + } + list_for_each(temp, &dev->binding->bd_inode->i_mapping->locked_pages) { + pagei = list_entry(temp, struct page, list); + if(PageLocked(pagei)) + locked++; + if(PageDirty(pagei)) + dirty++; + if(PageLRU(pagei)) + lru++; + } + + len += sprintf(page+len, "mtd%d:\t%ld\t%d\t%ld\t%d\t%d\t%d\t%d\n", + dev->mtd_info.index, + (dev->wr_buf && dev->wr_buf->nr_pages) ? + dev->wr_buf->blocks[dev->wr_buf->nr_pages-1] : 0, + (dev->wr_buf) ? dev->wr_buf->nr_pages : 0, + dev->binding->bd_inode->i_mapping->nrpages, + clean, dirty, locked, lru); + } + + if(len <= count) + *eof = 1; + + MOD_DEC_USE_COUNT; + return len; +} +#endif + + +static void free_device(struct blkmtd_dev *dev) +{ + DEBUG(2, "blkmtd: free_device() dev = %p\n", dev); + if(dev) { + del_mtd_device(&dev->mtd_info); + info("mtd%d: [%s] removed", dev->mtd_info.index, + dev->mtd_info.name + strlen("blkmtd: ")); + if(dev->mtd_info.eraseregions) + kfree(dev->mtd_info.eraseregions); + if(dev->mtd_info.name) + kfree(dev->mtd_info.name); + + if(dev->rd_buf) { + dev->rd_buf->locked = 0; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) + if(dev->rd_buf->blocks) + kfree(dev->rd_buf->blocks); +#endif + free_kiovec(1, &dev->rd_buf); + } + if(dev->wr_buf) { + dev->wr_buf->locked = 0; +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) + if(dev->wr_buf->blocks) + kfree(dev->rw_buf->blocks); +#endif + free_kiovec(1, &dev->wr_buf); + } + + if(dev->binding) { + kdev_t kdev = to_kdev_t(dev->binding->bd_dev); + invalidate_inode_pages(dev->binding->bd_inode); + set_blocksize(kdev, 1 << 10); + blkdev_put(dev->binding, BDEV_RAW); + } + kfree(dev); + } +} + + +/* For a given size and initial erase size, calculate the number + * and size of each erase region. Goes round the loop twice, + * once to find out how many regions, then allocates space, + * then round the loop again to fill it in. + */ +static struct mtd_erase_region_info *calc_erase_regions( + size_t erase_size, size_t total_size, int *regions) +{ + struct mtd_erase_region_info *info = NULL; + + DEBUG(2, "calc_erase_regions, es = %zd size = %zd regions = %d\n", + erase_size, total_size, *regions); + /* Make any user specified erasesize be a power of 2 + and at least PAGE_SIZE */ + if(erase_size) { + int es = erase_size; + erase_size = 1; + while(es != 1) { + es >>= 1; + erase_size <<= 1; + } + if(erase_size < PAGE_SIZE) + erase_size = PAGE_SIZE; + } else { + erase_size = CONFIG_MTD_BLKDEV_ERASESIZE; + } + + *regions = 0; + + do { + int tot_size = total_size; + int er_size = erase_size; + int count = 0, offset = 0, regcnt = 0; + + while(tot_size) { + count = tot_size / er_size; + if(count) { + tot_size = tot_size % er_size; + if(info) { + DEBUG(2, "adding to erase info off=%d er=%d cnt=%d\n", + offset, er_size, count); + (info+regcnt)->offset = offset; + (info+regcnt)->erasesize = er_size; + (info+regcnt)->numblocks = count; + (*regions)++; + } + regcnt++; + offset += (count * er_size); + } + while(er_size > tot_size) + er_size >>= 1; + } + if(info == NULL) { + info = kmalloc(regcnt * sizeof(struct mtd_erase_region_info), GFP_KERNEL); + if(!info) + break; + } + } while(!(*regions)); + DEBUG(2, "calc_erase_regions done, es = %zd size = %zd regions = %d\n", + erase_size, total_size, *regions); + return info; +} + + +extern kdev_t name_to_kdev_t(char *line) __init; + + +static struct blkmtd_dev *add_device(char *devname, int readonly, int erase_size) +{ + int maj, min; + kdev_t kdev; + int mode; + struct blkmtd_dev *dev; + +#ifdef MODULE + struct file *file = NULL; + struct inode *inode; +#endif + + if(!devname) + return NULL; + + /* Get a handle on the device */ + mode = (readonly) ? O_RDONLY : O_RDWR; + +#ifdef MODULE + + file = filp_open(devname, mode, 0); + if(IS_ERR(file)) { + err("error: cant open device %s", devname); + DEBUG(2, "blkmtd: filp_open returned %ld\n", PTR_ERR(file)); + return NULL; + } + + /* determine is this is a block device and + * if so get its major and minor numbers + */ + inode = file->f_dentry->d_inode; + if(!S_ISBLK(inode->i_mode)) { + err("%s not a block device", devname); + filp_close(file, NULL); + return NULL; + } + kdev = inode->i_rdev; + filp_close(file, NULL); +#else + kdev = name_to_kdev_t(devname); +#endif /* MODULE */ + + if(!kdev) { + err("bad block device: `%s'", devname); + return NULL; + } + + maj = MAJOR(kdev); + min = MINOR(kdev); + DEBUG(1, "blkmtd: found a block device major = %d, minor = %d\n", + maj, min); + + if(maj == MTD_BLOCK_MAJOR) { + err("attempting to use an MTD device as a block device"); + return NULL; + } + + DEBUG(1, "blkmtd: devname = %s\n", bdevname(kdev)); + + dev = kmalloc(sizeof(struct blkmtd_dev), GFP_KERNEL); + if(dev == NULL) + return NULL; + + memset(dev, 0, sizeof(struct blkmtd_dev)); + if(alloc_kiovec(1, &dev->rd_buf)) { + err("cant allocate read iobuf"); + goto devinit_err; + } +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) + dev->rd_buf->blocks = kmalloc(KIO_MAX_SECTORS * sizeof(unsigned long), GFP_KERNEL); + if(dev->rd_buf->blocks == NULL) { + crit("cant allocate rd_buf blocks"); + goto devinit_err; + } +#endif + + if(!readonly) { + if(alloc_kiovec(1, &dev->wr_buf)) { + err("cant allocate kiobuf - readonly enabled"); + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4) + } else { + dev->wr_buf->blocks = kmalloc(KIO_MAX_SECTORS * sizeof(unsigned long), GFP_KERNEL); + if(dev->wr_buf->blocks == NULL) { + crit("cant allocate wr_buf blocks - readonly enabled"); + free_kiovec(1, &iobuf); + } +#endif + } + if(dev->wr_buf) + init_MUTEX(&dev->wrbuf_mutex); + } + + /* get the block device */ + dev->binding = bdget(kdev_t_to_nr(MKDEV(maj, min))); + if(blkdev_get(dev->binding, mode, 0, BDEV_RAW)) + goto devinit_err; + + if(set_blocksize(kdev, PAGE_SIZE)) { + err("cant set block size to PAGE_SIZE on %s", bdevname(kdev)); + goto devinit_err; + } + + dev->mtd_info.size = dev->binding->bd_inode->i_size & PAGE_MASK; + + /* Setup the MTD structure */ + /* make the name contain the block device in */ + dev->mtd_info.name = kmalloc(sizeof("blkmtd: ") + strlen(devname), GFP_KERNEL); + if(dev->mtd_info.name == NULL) + goto devinit_err; + + sprintf(dev->mtd_info.name, "blkmtd: %s", devname); + dev->mtd_info.eraseregions = calc_erase_regions(erase_size, dev->mtd_info.size, + &dev->mtd_info.numeraseregions); + if(dev->mtd_info.eraseregions == NULL) + goto devinit_err; + + dev->mtd_info.erasesize = dev->mtd_info.eraseregions->erasesize; + DEBUG(1, "blkmtd: init: found %d erase regions\n", + dev->mtd_info.numeraseregions); + + if(readonly) { + dev->mtd_info.type = MTD_ROM; + dev->mtd_info.flags = MTD_CAP_ROM; + } else { + dev->mtd_info.type = MTD_RAM; + dev->mtd_info.flags = MTD_CAP_RAM; + } + dev->mtd_info.erase = blkmtd_erase; + dev->mtd_info.read = blkmtd_read; + dev->mtd_info.write = blkmtd_write; + dev->mtd_info.sync = blkmtd_sync; + dev->mtd_info.point = 0; + dev->mtd_info.unpoint = 0; + dev->mtd_info.priv = dev; + dev->mtd_info.owner = THIS_MODULE; + + list_add(&dev->list, &blkmtd_device_list); + if (add_mtd_device(&dev->mtd_info)) { + /* Device didnt get added, so free the entry */ + list_del(&dev->list); + free_device(dev); + return NULL; + } else { + info("mtd%d: [%s] erase_size = %dKiB %s", + dev->mtd_info.index, dev->mtd_info.name + strlen("blkmtd: "), + dev->mtd_info.erasesize >> 10, + (dev->wr_buf) ? "" : "(read-only)"); + } + + return dev; + + devinit_err: + free_device(dev); + return NULL; +} + + +/* Cleanup and exit - sync the device and kill of the kernel thread */ +static void __devexit cleanup_blkmtd(void) +{ + struct list_head *temp1, *temp2; +#ifdef BLKMTD_PROC_DEBUG + if(blkmtd_proc) { + remove_proc_entry("blkmtd_debug", NULL); + } +#endif + + /* Remove the MTD devices */ + list_for_each_safe(temp1, temp2, &blkmtd_device_list) { + struct blkmtd_dev *dev = list_entry(temp1, struct blkmtd_dev, + list); + blkmtd_sync(&dev->mtd_info); + free_device(dev); + } +} + +#ifndef MODULE + +/* Handle kernel boot params */ + + +static int __init param_blkmtd_device(char *str) +{ + int i; + + for(i = 0; i < MAX_DEVICES; i++) { + device[i] = str; + DEBUG(2, "blkmtd: device setup: %d = %s\n", i, device[i]); + strsep(&str, ","); + } + return 1; +} + + +static int __init param_blkmtd_erasesz(char *str) +{ + int i; + for(i = 0; i < MAX_DEVICES; i++) { + char *val = strsep(&str, ","); + if(val) + erasesz[i] = simple_strtoul(val, NULL, 0); + DEBUG(2, "blkmtd: erasesz setup: %d = %d\n", i, erasesz[i]); + } + + return 1; +} + + +static int __init param_blkmtd_ro(char *str) +{ + int i; + for(i = 0; i < MAX_DEVICES; i++) { + char *val = strsep(&str, ","); + if(val) + ro[i] = simple_strtoul(val, NULL, 0); + DEBUG(2, "blkmtd: ro setup: %d = %d\n", i, ro[i]); + } + + return 1; +} + + +static int __init param_blkmtd_sync(char *str) +{ + if(str[0] == '1') + sync = 1; + return 1; +} + +__setup("blkmtd_device=", param_blkmtd_device); +__setup("blkmtd_erasesz=", param_blkmtd_erasesz); +__setup("blkmtd_ro=", param_blkmtd_ro); +__setup("blkmtd_sync=", param_blkmtd_sync); + +#endif + + +/* Startup */ +static int __init init_blkmtd(void) +{ + int i; + + /* Check args - device[0] is the bare minimum*/ + if(!device[0]) { + err("error: missing `device' name\n"); + return -EINVAL; + } + + for(i = 0; i < MAX_DEVICES; i++) + add_device(device[i], ro[i], erasesz[i] << 10); + + if(list_empty(&blkmtd_device_list)) + goto init_err; + + info("version " VERSION); + +#ifdef BLKMTD_PROC_DEBUG + /* create proc entry */ + DEBUG(2, "Creating /proc/blkmtd_debug\n"); + blkmtd_proc = create_proc_read_entry("blkmtd_debug", 0444, + NULL, blkmtd_proc_read, NULL); + if(blkmtd_proc == NULL) { + err("Cant create /proc/blkmtd_debug"); + } else { + blkmtd_proc->owner = THIS_MODULE; + } +#endif + + if(!list_empty(&blkmtd_device_list)) + /* Everything is ok if we got here */ + return 0; + + init_err: + return -EINVAL; +} + +module_init(init_blkmtd); +module_exit(cleanup_blkmtd); diff -NaurbB linux-2.6.9-pre1-bk18/drivers/mtd/maps/amd76xrom.c linux-2.6.9_nbp/drivers/mtd/maps/amd76xrom.c --- linux-2.6.9-pre1-bk18/drivers/mtd/maps/amd76xrom.c 2004-08-14 07:36:32.000000000 +0200 +++ linux-2.6.9_nbp/drivers/mtd/maps/amd76xrom.c 2004-09-13 12:43:27.000000000 +0200 @@ -2,7 +2,7 @@ * amd76xrom.c * * Normal mappings of chips in physical memory - * $Id: amd76xrom.c,v 1.12 2004/07/14 14:44:31 thayne Exp $ + * $Id: amd76xrom.c,v 1.13 2004/08/12 06:40:23 eric Exp $ */ #include @@ -12,61 +12,73 @@ #include #include #include +#include +#include #include #include #include +#include #define xstr(s) str(s) #define str(s) #s #define MOD_NAME xstr(KBUILD_BASENAME) -#define MTD_DEV_NAME_LENGTH 16 +#define ADDRESS_NAME_LEN 18 + +#define ROM_PROBE_STEP_SIZE (64*1024) /* 64KiB */ + +struct amd76xrom_window { + void *virt; + unsigned long phys; + unsigned long size; + struct list_head maps; + struct resource rsrc; + struct pci_dev *pdev; +}; struct amd76xrom_map_info { + struct list_head list; struct map_info map; struct mtd_info *mtd; - unsigned long window_addr; - u32 window_start, window_size; - struct pci_dev *pdev; - struct resource window_rsrc; - struct resource rom_rsrc; - char mtd_name[MTD_DEV_NAME_LENGTH]; + struct resource rsrc; + char map_name[sizeof(MOD_NAME) + 2 + ADDRESS_NAME_LEN]; }; - -static struct amd76xrom_map_info amd76xrom_map = { - .map = { - .name = MOD_NAME, - .size = 0, - .bankwidth = 1, - } - /* remaining fields of structure are initialized to 0 */ +static struct amd76xrom_window amd76xrom_window = { + .maps = LIST_HEAD_INIT(amd76xrom_window.maps), }; - -static void amd76xrom_cleanup(struct amd76xrom_map_info *info) +static void amd76xrom_cleanup(struct amd76xrom_window *window) { + struct amd76xrom_map_info *map, *scratch; u8 byte; + if (window->pdev) { /* Disable writes through the rom window */ - pci_read_config_byte(info->pdev, 0x40, &byte); - pci_write_config_byte(info->pdev, 0x40, byte & ~1); + pci_read_config_byte(window->pdev, 0x40, &byte); + pci_write_config_byte(window->pdev, 0x40, byte & ~1); + } - if (info->mtd) { - del_mtd_device(info->mtd); - map_destroy(info->mtd); - info->mtd = NULL; - info->map.virt = 0; - } - if (info->rom_rsrc.parent) - release_resource(&info->rom_rsrc); - if (info->window_rsrc.parent) - release_resource(&info->window_rsrc); - - if (info->window_addr) { - iounmap((void *)(info->window_addr)); - info->window_addr = 0; + /* Free all of the mtd devices */ + list_for_each_entry_safe(map, scratch, &window->maps, list) { + if (map->rsrc.parent) { + release_resource(&map->rsrc); + } + del_mtd_device(map->mtd); + map_destroy(map->mtd); + list_del(&map->list); + kfree(map); + } + if (window->rsrc.parent) + release_resource(&window->rsrc); + + if (window->virt) { + iounmap(window->virt); + window->virt = NULL; + window->phys = 0; + window->size = 0; + window->pdev = NULL; } } @@ -74,35 +86,28 @@ static int __devinit amd76xrom_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) { - struct rom_window { - u32 start; - u32 size; - u8 segen_bits; - }; - static struct rom_window rom_window[] = { - /* - * Need the 5MiB window for chips that have block lock/unlock - * registers located below 4MiB window. - */ - { 0xffb00000, 5*1024*1024, (1<<7) | (1<<6), }, - { 0xffc00000, 4*1024*1024, (1<<7), }, - { 0xffff0000, 64*1024, 0 }, - { 0 , 0, 0 }, - }; - static const u32 rom_probe_sizes[] = { - 5*1024*1024, 4*1024*1024, 2*1024*1024, 1024*1024, 512*1024, - 256*1024, 128*1024, 64*1024, 0}; static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL }; u8 byte; - struct amd76xrom_map_info *info = &amd76xrom_map; - struct rom_window *window; - int i; - u32 rom_size; + struct amd76xrom_window *window = &amd76xrom_window; + struct amd76xrom_map_info *map; + unsigned long map_top; - info->pdev = pdev; - window = &rom_window[0]; + /* Remember the pci dev I find the window in */ + window->pdev = pdev; + + /* Assume the rom window is properly setup, and find it's size */ + pci_read_config_byte(pdev, 0x43, &byte); + if ((byte & ((1<<7)|(1<<6))) == ((1<<7)|(1<<6))) { + window->phys = 0xffb00000; /* 5MiB */ + } + else if ((byte & (1<<7)) == (1<<7)) { + window->phys = 0xffc00000; /* 4MiB */ + } + else { + window->phys = 0xffff0000; /* 64KiB */ + } + window->size = 0xffffffffUL - window->phys + 1UL; - while (window->size) { /* * Try to reserve the window mem region. If this fails then * it is likely due to a fragment of the window being @@ -110,22 +115,25 @@ * request_mem_region() fails then once the rom size is * discovered we will try to reserve the unreserved fragment. */ - info->window_rsrc.name = MOD_NAME; - info->window_rsrc.start = window->start; - info->window_rsrc.end = window->start + window->size - 1; - info->window_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; - if (request_resource(&iomem_resource, &info->window_rsrc)) { - info->window_rsrc.parent = NULL; + window->rsrc.name = MOD_NAME; + window->rsrc.start = window->phys; + window->rsrc.end = window->phys + window->size - 1; + window->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; + if (request_resource(&iomem_resource, &window->rsrc)) { + window->rsrc.parent = NULL; printk(KERN_ERR MOD_NAME " %s(): Unable to register resource" " 0x%.08lx-0x%.08lx - kernel bug?\n", __func__, - info->window_rsrc.start, info->window_rsrc.end); + window->rsrc.start, window->rsrc.end); } +#if 0 + /* Enable the selected rom window */ pci_read_config_byte(pdev, 0x43, &byte); - pci_write_config_byte(pdev, 0x43, byte | window->segen_bits); + pci_write_config_byte(pdev, 0x43, byte | rwindow->segen_bits); +#endif /* Enable writes through the rom window */ pci_read_config_byte(pdev, 0x40, &byte); @@ -133,109 +141,126 @@ /* FIXME handle registers 0x80 - 0x8C the bios region locks */ - printk(KERN_NOTICE MOD_NAME " window : %x at %x\n", - window->size, window->start); /* For write accesses caches are useless */ - info->window_addr = - (unsigned long)ioremap_nocache(window->start, - window->size); - - if (!info->window_addr) { - printk(KERN_ERR "Failed to ioremap\n"); - continue; + window->virt = ioremap_nocache(window->phys, window->size); + if (!window->virt) { + printk(KERN_ERR MOD_NAME ": Failed to ioremap\n"); + goto failed; } - info->mtd = NULL; + /* Get the first address to look for an rom chip at */ + map_top = window->phys; - for(i = 0; (rom_size = rom_probe_sizes[i]); i++) { - char **chip_type; - if (rom_size > window->size) { - continue; - } - info->map.phys = window->start + window->size - rom_size; - info->map.virt = - info->window_addr + window->size - rom_size; - info->map.size = rom_size; - simple_map_init(&info->map); - chip_type = rom_probe_types; - for(; !info->mtd && *chip_type; chip_type++) { - info->mtd = do_map_probe(*chip_type, &amd76xrom_map.map); + /* Loop trhough adn look for rom chips */ + map = 0; + while((map_top - 1) < 0xffffffffUL) { + struct cfi_private *cfi; + unsigned long offset; + int i; + + if (!map) { + map = kmalloc(sizeof(*map), GFP_KERNEL); } - if (info->mtd) goto found_mtd; + if (!map) { + printk(KERN_ERR MOD_NAME ": kmalloc failed"); + goto failed; } - iounmap((void *)(info->window_addr)); - info->window_addr = 0; + memset(map, 0, sizeof(*map)); + INIT_LIST_HEAD(&map->list); + map->map.name = map->map_name; + map->map.phys = map_top; + offset = map_top - window->phys; + map->map.virt = ((unsigned long)(window->virt)) + offset; + map->map.size = 0xffffffffUL - map_top + 1UL; + /* Set the name of the map to the address I am trying */ + sprintf(map->map_name, "%s @%08lx", + MOD_NAME, map->map.phys); + + /* There is not generic VPP support */ + for(map->map.bankwidth = 32; map->map.bankwidth; + map->map.bankwidth >>=1) + { + char **probe_type; + /* Skip bankwidths that are not supported */ + if (!map_bankwidth_supported(map->map.bankwidth)) + continue; - /* Disable writes through the rom window */ - pci_read_config_byte(pdev, 0x40, &byte); - pci_write_config_byte(pdev, 0x40, byte & ~1); + /* Setup the map methods */ + simple_map_init(&map->map); - window++; + /* Try all of the probe methods */ + probe_type = rom_probe_types; + for(; *probe_type; probe_type++) { + map->mtd = do_map_probe(*probe_type, &map->map); + if (map->mtd) + goto found; } - goto failed; - - found_mtd: - printk(KERN_NOTICE MOD_NAME " chip at offset: 0x%x\n", - window->size - rom_size); - - info->mtd->owner = THIS_MODULE; - - if (!info->window_rsrc.parent) { - /* failed to reserve entire window - try fragments */ - info->window_rsrc.name = MOD_NAME; - info->window_rsrc.start = window->start; - info->window_rsrc.end = window->start + window->size - rom_size - 1; - info->window_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; - if (request_resource(&iomem_resource, &info->window_rsrc)) { - printk(KERN_ERR MOD_NAME - ": cannot reserve window resource fragment\n"); -#if 0 - /* - * The BIOS e820 usually reserves this so it isn't - * usually an error. - */ - goto failed; -#endif } + map_top += ROM_PROBE_STEP_SIZE; + continue; + found: + /* Trim the size of we are larger than the map */ + if (map->mtd->size > map->map.size) { + printk(KERN_WARNING MOD_NAME + " rom(%u) larger than window(%lu). fixing...\n", + map->mtd->size, map->map.size); + map->mtd->size = map->map.size; } - - add_mtd_device(info->mtd); - info->window_start = window->start; - info->window_size = window->size; - - if (info->window_rsrc.parent) { + if (window->rsrc.parent) { /* * Registering the MTD device in iomem may not be possible * if there is a BIOS "reserved" and BUSY range. If this * fails then continue anyway. */ - snprintf(info->mtd_name, MTD_DEV_NAME_LENGTH, - "mtd%d", info->mtd->index); - - info->rom_rsrc.name = info->mtd_name; - info->rom_rsrc.start = window->start + window->size - rom_size; - info->rom_rsrc.end = window->start + window->size - 1; - info->rom_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; - if (request_resource(&info->window_rsrc, &info->rom_rsrc)) { + map->rsrc.name = map->map_name; + map->rsrc.start = map->map.phys; + map->rsrc.end = map->map.phys + map->mtd->size - 1; + map->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; + if (request_resource(&window->rsrc, &map->rsrc)) { printk(KERN_ERR MOD_NAME ": cannot reserve MTD resource\n"); - info->rom_rsrc.parent = NULL; + map->rsrc.parent = NULL; + } } + + /* Make the whole region visible in the map */ + map->map.virt = (unsigned long)(window->virt); + map->map.phys = window->phys; + cfi = map->map.fldrv_priv; + for(i = 0; i < cfi->numchips; i++) { + cfi->chips[i].start += offset; } + /* Now that the mtd devices is complete claim and export it */ + map->mtd->owner = THIS_MODULE; + add_mtd_device(map->mtd); + + + /* Calculate the new value of map_top */ + map_top += map->mtd->size; + + /* File away the map structure */ + list_add(&map->list, &window->maps); + map = 0; + } + + /* Free any left over map structures */ + if (map) { + kfree(map); + } return 0; failed: - amd76xrom_cleanup(info); + amd76xrom_cleanup(window); return -ENODEV; } static void __devexit amd76xrom_remove_one (struct pci_dev *pdev) { - struct amd76xrom_map_info *info = &amd76xrom_map; + struct amd76xrom_window *window = &amd76xrom_window; - amd76xrom_cleanup(info); + amd76xrom_cleanup(window); } static struct pci_device_id amd76xrom_pci_tbl[] = { @@ -270,7 +295,6 @@ } } if (pdev) { - amd76xrom_map.pdev = pdev; return amd76xrom_init_one(pdev, &amd76xrom_pci_tbl[0]); } return -ENXIO; @@ -281,7 +305,7 @@ static void __exit cleanup_amd76xrom(void) { - amd76xrom_remove_one(amd76xrom_map.pdev); + amd76xrom_remove_one(amd76xrom_window.pdev); } module_init(init_amd76xrom); diff -NaurbB linux-2.6.9-pre1-bk18/drivers/mtd/maps/ebony.c linux-2.6.9_nbp/drivers/mtd/maps/ebony.c --- linux-2.6.9-pre1-bk18/drivers/mtd/maps/ebony.c 2004-08-14 07:37:38.000000000 +0200 +++ linux-2.6.9_nbp/drivers/mtd/maps/ebony.c 2004-09-13 12:43:27.000000000 +0200 @@ -1,11 +1,11 @@ /* - * $Id: ebony.c,v 1.10 2004/07/12 21:59:44 dwmw2 Exp $ + * $Id: ebony.c,v 1.11 2004/08/23 20:38:26 mporter Exp $ * * Mapping for Ebony user flash * - * Matt Porter + * Matt Porter * - * Copyright 2002 MontaVista Software Inc. + * Copyright 2002-2004 MontaVista Software Inc. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -21,9 +21,10 @@ #include #include #include +#include #include #include -#include +#include static struct mtd_info *flash; @@ -63,7 +64,7 @@ int __init init_ebony(void) { u8 fpga0_reg; - unsigned long fpga0_adr; + u8 *fpga0_adr; unsigned long long small_flash_base, large_flash_base; fpga0_adr = ioremap64(EBONY_FPGA_ADDR, 16); @@ -160,5 +161,5 @@ module_exit(cleanup_ebony); MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Matt Porter "); +MODULE_AUTHOR("Matt Porter "); MODULE_DESCRIPTION("MTD map and partitions for IBM 440GP Ebony boards"); diff -NaurbB linux-2.6.9-pre1-bk18/drivers/mtd/maps/edb7312.c linux-2.6.9_nbp/drivers/mtd/maps/edb7312.c --- linux-2.6.9-pre1-bk18/drivers/mtd/maps/edb7312.c 2005-08-15 01:14:41.000000000 +0200 +++ linux-2.6.9_nbp/drivers/mtd/maps/edb7312.c 2004-09-13 12:43:27.000000000 +0200 @@ -71,14 +71,14 @@ #endif -static int mtd_parts_nb; -static struct mtd_partition *mtd_parts; +static int mtd_parts_nb = 0; +static struct mtd_partition *mtd_parts = 0; int __init init_edb7312nor(void) { static const char *rom_probe_types[] = PROBETYPES; const char **type; - const char *part_type = NULL; + const char *part_type = 0; printk(KERN_NOTICE MSG_PREFIX "0x%08x at 0x%08x\n", WINDOW_SIZE, WINDOW_ADDR); @@ -92,7 +92,7 @@ simple_map_init(&edb7312nor_map); - mymtd = NULL; + mymtd = 0; type = rom_probe_types; for(; !mymtd && *type; type++) { mymtd = do_map_probe(*type, &edb7312nor_map); diff -NaurbB linux-2.6.9-pre1-bk18/drivers/mtd/maps/ichxrom.c linux-2.6.9_nbp/drivers/mtd/maps/ichxrom.c --- linux-2.6.9-pre1-bk18/drivers/mtd/maps/ichxrom.c 2004-08-14 07:36:12.000000000 +0200 +++ linux-2.6.9_nbp/drivers/mtd/maps/ichxrom.c 2004-09-13 12:43:27.000000000 +0200 @@ -2,7 +2,7 @@ * ichxrom.c * * Normal mappings of chips in physical memory - * $Id: ichxrom.c,v 1.8 2004/07/16 17:43:11 dwmw2 Exp $ + * $Id: ichxrom.c,v 1.10 2004/08/12 07:49:04 eric Exp $ */ #include @@ -12,187 +12,87 @@ #include #include #include +#include +#include #include #include #include -#include +#include #define xstr(s) str(s) #define str(s) #s #define MOD_NAME xstr(KBUILD_BASENAME) -#define MTD_DEV_NAME_LENGTH 16 - -#define RESERVE_MEM_REGION 0 - +#define ADDRESS_NAME_LEN 18 -#define MANUFACTURER_INTEL 0x0089 -#define I82802AB 0x00ad -#define I82802AC 0x00ac +#define ROM_PROBE_STEP_SIZE (64*1024) /* 64KiB */ -#define ICHX_FWH_REGION_START 0xFF000000UL -#define ICHX_FWH_REGION_SIZE 0x01000000UL #define BIOS_CNTL 0x4e #define FWH_DEC_EN1 0xE3 #define FWH_DEC_EN2 0xF0 #define FWH_SEL1 0xE8 #define FWH_SEL2 0xEE -struct ichxrom_map_info { - struct map_info map; - struct mtd_info *mtd; - unsigned long window_addr; +struct ichxrom_window { + void * virt; + unsigned long phys; + unsigned long size; + struct list_head maps; + struct resource rsrc; struct pci_dev *pdev; - struct resource window_rsrc; - struct resource rom_rsrc; - char mtd_name[MTD_DEV_NAME_LENGTH]; }; -static inline unsigned long addr(struct map_info *map, unsigned long ofs) -{ - unsigned long offset; - offset = ((8*1024*1024) - map->size) + ofs; - if (offset >= (4*1024*1024)) { - offset += 0x400000; - } - return map->map_priv_1 + 0x400000 + offset; -} - -static inline unsigned long dbg_addr(struct map_info *map, unsigned long addr) -{ - return addr - map->map_priv_1 + ICHX_FWH_REGION_START; -} - -static map_word ichxrom_read(struct map_info *map, unsigned long ofs) -{ - map_word val; - int i; - switch(map->bankwidth) { - case 1: val.x[0] = __raw_readb(addr(map, ofs)); break; - case 2: val.x[0] = __raw_readw(addr(map, ofs)); break; - case 4: val.x[0] = __raw_readl(addr(map, ofs)); break; -#if BITS_PER_LONG >= 64 - case 8: val.x[0] = __raw_readq(addr(map, ofs)); break; -#endif - default: val.x[0] = 0; break; - } - for(i = 1; i < map_words(map); i++) { - val.x[i] = 0; - } - return val; -} - -static void ichxrom_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) -{ - memcpy_fromio(to, addr(map, from), len); -} - -static void ichxrom_write(struct map_info *map, map_word d, unsigned long ofs) -{ - switch(map->bankwidth) { - case 1: __raw_writeb(d.x[0], addr(map,ofs)); break; - case 2: __raw_writew(d.x[0], addr(map,ofs)); break; - case 4: __raw_writel(d.x[0], addr(map,ofs)); break; -#if BITS_PER_LONG >= 64 - case 8: __raw_writeq(d.x[0], addr(map,ofs)); break; -#endif - } - mb(); -} - -static void ichxrom_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) -{ - memcpy_toio(addr(map, to), from, len); -} - -static struct ichxrom_map_info ichxrom_map = { - .map = { - .name = MOD_NAME, - .phys = NO_XIP, - .size = 0, - .bankwidth = 1, - .read = ichxrom_read, - .copy_from = ichxrom_copy_from, - .write = ichxrom_write, - .copy_to = ichxrom_copy_to, - /* Firmware hubs only use vpp when being programmed - * in a factory setting. So in-place programming - * needs to use a different method. - */ - }, - /* remaining fields of structure are initialized to 0 */ +struct ichxrom_map_info { + struct list_head list; + struct map_info map; + struct mtd_info *mtd; + struct resource rsrc; + char map_name[sizeof(MOD_NAME) + 2 + ADDRESS_NAME_LEN]; }; -enum fwh_lock_state { - FWH_DENY_WRITE = 1, - FWH_IMMUTABLE = 2, - FWH_DENY_READ = 4, +static struct ichxrom_window ichxrom_window = { + .maps = LIST_HEAD_INIT(ichxrom_window.maps), }; -static void ichxrom_cleanup(struct ichxrom_map_info *info) +static void ichxrom_cleanup(struct ichxrom_window *window) { + struct ichxrom_map_info *map, *scratch; u16 word; /* Disable writes through the rom window */ - pci_read_config_word(info->pdev, BIOS_CNTL, &word); - pci_write_config_word(info->pdev, BIOS_CNTL, word & ~1); + pci_read_config_word(window->pdev, BIOS_CNTL, &word); + pci_write_config_word(window->pdev, BIOS_CNTL, word & ~1); - if (info->mtd) { - del_mtd_device(info->mtd); - map_destroy(info->mtd); - info->mtd = NULL; - info->map.virt = 0; - } - if (info->rom_rsrc.parent) - release_resource(&info->rom_rsrc); - if (info->window_rsrc.parent) - release_resource(&info->window_rsrc); - - if (info->window_addr) { - iounmap((void *)(info->window_addr)); - info->window_addr = 0; + /* Free all of the mtd devices */ + list_for_each_entry_safe(map, scratch, &window->maps, list) { + if (map->rsrc.parent) + release_resource(&map->rsrc); + del_mtd_device(map->mtd); + map_destroy(map->mtd); + list_del(&map->list); + kfree(map); + } + if (window->rsrc.parent) + release_resource(&window->rsrc); + if (window->virt) { + iounmap(window->virt); + window->virt = NULL; + window->phys = 0; + window->size = 0; + window->pdev = NULL; } } -static int ichxrom_set_lock_state(struct mtd_info *mtd, loff_t ofs, size_t len, - enum fwh_lock_state state) -{ - struct map_info *map = mtd->priv; - unsigned long start = ofs; - unsigned long end = start + len -1; - - /* FIXME do I need to guard against concurrency here? */ - /* round down to 64K boundaries */ - start = start & ~0xFFFF; - end = end & ~0xFFFF; - while (start <= end) { - unsigned long ctrl_addr; - ctrl_addr = addr(map, start) - 0x400000 + 2; - writeb(state, ctrl_addr); - start = start + 0x10000; - } - return 0; -} - -static int ichxrom_lock(struct mtd_info *mtd, loff_t ofs, size_t len) -{ - return ichxrom_set_lock_state(mtd, ofs, len, FWH_DENY_WRITE); -} - -static int ichxrom_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) -{ - return ichxrom_set_lock_state(mtd, ofs, len, 0); -} - static int __devinit ichxrom_init_one (struct pci_dev *pdev, const struct pci_device_id *ent) { + static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL }; + struct ichxrom_window *window = &ichxrom_window; + struct ichxrom_map_info *map; + unsigned long map_top; + u8 byte; u16 word; - struct ichxrom_map_info *info = &ichxrom_map; - unsigned long map_size; - static char *probes[] = { "cfi_probe", "jedec_probe" }; - struct cfi_private *cfi; /* For now I just handle the ichx and I assume there * are not a lot of resources up at the top of the address @@ -204,25 +104,55 @@ * Also you can page firmware hubs if an 8MB window isn't enough * but don't currently handle that case either. */ + window->pdev = pdev; - info->pdev = pdev; + /* Find a region continuous to the end of the ROM window */ + window->phys = 0; + pci_read_config_byte(pdev, FWH_DEC_EN1, &byte); + if (byte == 0xff) { + window->phys = 0xffc00000; + pci_read_config_byte(pdev, FWH_DEC_EN2, &byte); + if ((byte & 0x0f) == 0x0f) { + window->phys = 0xff400000; + } + else if ((byte & 0x0e) == 0x0e) { + window->phys = 0xff500000; + } + else if ((byte & 0x0c) == 0x0c) { + window->phys = 0xff600000; + } + else if ((byte & 0x08) == 0x08) { + window->phys = 0xff700000; + } + } + else if ((byte & 0xfe) == 0xfe) { + window->phys = 0xffc80000; + } + else if ((byte & 0xfc) == 0xfc) { + window->phys = 0xffd00000; + } + else if ((byte & 0xf8) == 0xf8) { + window->phys = 0xffd80000; + } + else if ((byte & 0xf0) == 0xf0) { + window->phys = 0xffe00000; + } + else if ((byte & 0xe0) == 0xe0) { + window->phys = 0xffe80000; + } + else if ((byte & 0xc0) == 0xc0) { + window->phys = 0xfff00000; + } + else if ((byte & 0x80) == 0x80) { + window->phys = 0xfff80000; + } - /* - * Try to reserve the window mem region. If this fails then - * it is likely due to the window being "reseved" by the BIOS. - */ - info->window_rsrc.name = MOD_NAME; - info->window_rsrc.start = ICHX_FWH_REGION_START; - info->window_rsrc.end = ICHX_FWH_REGION_START + ICHX_FWH_REGION_SIZE - 1; - info->window_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; - if (request_resource(&iomem_resource, &info->window_rsrc)) { - info->window_rsrc.parent = NULL; - printk(KERN_ERR MOD_NAME - " %s(): Unable to register resource" - " 0x%.08lx-0x%.08lx - kernel bug?\n", - __func__, - info->window_rsrc.start, info->window_rsrc.end); + if (window->phys == 0) { + printk(KERN_ERR MOD_NAME ": Rom window is closed\n"); + goto failed; } + window->phys -= 0x400000UL; + window->size = (0xffffffffUL - window->phys) + 1UL; /* Enable writes through the rom window */ pci_read_config_word(pdev, BIOS_CNTL, &word); @@ -235,115 +165,159 @@ } pci_write_config_word(pdev, BIOS_CNTL, word | 1); + /* + * Try to reserve the window mem region. If this fails then + * it is likely due to the window being "reseved" by the BIOS. + */ + window->rsrc.name = MOD_NAME; + window->rsrc.start = window->phys; + window->rsrc.end = window->phys + window->size - 1; + window->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; + if (request_resource(&iomem_resource, &window->rsrc)) { + window->rsrc.parent = NULL; + printk(KERN_DEBUG MOD_NAME + ": %s(): Unable to register resource" + " 0x%.08lx-0x%.08lx - kernel bug?\n", + __func__, + window->rsrc.start, window->rsrc.end); + } /* Map the firmware hub into my address space. */ - /* Does this use too much virtual address space? */ - info->window_addr = (unsigned long)ioremap( - ICHX_FWH_REGION_START, ICHX_FWH_REGION_SIZE); - if (!info->window_addr) { - printk(KERN_ERR "Failed to ioremap\n"); + window->virt = ioremap_nocache(window->phys, window->size); + if (!window->virt) { + printk(KERN_ERR MOD_NAME ": ioremap(%08lx, %08lx) failed\n", + window->phys, window->size); goto failed; } - /* For now assume the firmware has setup all relevant firmware - * windows. We don't have enough information to handle this case - * intelligently. + /* Get the first address to look for an rom chip at */ + map_top = window->phys; + if ((window->phys & 0x3fffff) != 0) { + map_top = window->phys + 0x400000; + } +#if 1 + /* The probe sequence run over the firmware hub lock + * registers sets them to 0x7 (no access). + * Probe at most the last 4M of the address space. */ + if (map_top < 0xffc00000) { + map_top = 0xffc00000; + } +#endif - /* FIXME select the firmware hub and enable a window to it. */ - - info->mtd = NULL; - info->map.map_priv_1 = info->window_addr; - - /* Loop through the possible bankwidths */ - for(ichxrom_map.map.bankwidth = 4; ichxrom_map.map.bankwidth; ichxrom_map.map.bankwidth >>= 1) { - map_size = ICHX_FWH_REGION_SIZE; - while(!info->mtd && (map_size > 0)) { + /* Loop through and look for a rom chips */ + map = 0; + while((map_top - 1) < 0xffffffffUL) { + struct cfi_private *cfi; + unsigned long offset; int i; - info->map.size = map_size; - for(i = 0; i < sizeof(probes)/sizeof(char *); i++) { - info->mtd = do_map_probe(probes[i], &ichxrom_map.map); - if (info->mtd) - break; - } - map_size -= 512*1024; - } - if (info->mtd) - break; + + if (!map) { + map = kmalloc(sizeof(*map), GFP_KERNEL); } - if (!info->mtd) { + if (!map) { + printk(KERN_ERR MOD_NAME ": kmalloc failed"); goto failed; } - cfi = ichxrom_map.map.fldrv_priv; - if ((cfi->mfr == MANUFACTURER_INTEL) && ( - (cfi->id == I82802AB) || - (cfi->id == I82802AC))) - { - /* If it is a firmware hub put in the special lock - * and unlock routines. + memset(map, 0, sizeof(*map)); + INIT_LIST_HEAD(&map->list); + map->map.name = map->map_name; + map->map.phys = map_top; + offset = map_top - window->phys; + map->map.virt = ((unsigned long)(window->virt)) + offset; + map->map.size = 0xffffffffUL - map_top + 1UL; + /* Set the name of the map to the address I am trying */ + sprintf(map->map_name, "%s @%08lx", + MOD_NAME, map->map.phys); + + /* Firmware hubs only use vpp when being programmed + * in a factory setting. So in-place programming + * needs to use a different method. */ - info->mtd->lock = ichxrom_lock; - info->mtd->unlock = ichxrom_unlock; - } - if (info->mtd->size > info->map.size) { - printk(KERN_WARNING MOD_NAME " rom(%u) larger than window(%lu). fixing...\n", - info->mtd->size, info->map.size); - info->mtd->size = info->map.size; + for(map->map.bankwidth = 32; map->map.bankwidth; + map->map.bankwidth >>= 1) + { + char **probe_type; + /* Skip bankwidths that are not supported */ + if (!map_bankwidth_supported(map->map.bankwidth)) + continue; + + /* Setup the map methods */ + simple_map_init(&map->map); + + /* Try all of the probe methods */ + probe_type = rom_probe_types; + for(; *probe_type; probe_type++) { + map->mtd = do_map_probe(*probe_type, &map->map); + if (map->mtd) + goto found; + } + } + map_top += ROM_PROBE_STEP_SIZE; + continue; + found: + /* Trim the size if we are larger than the map */ + if (map->mtd->size > map->map.size) { + printk(KERN_WARNING MOD_NAME + " rom(%u) larger than window(%lu). fixing...\n", + map->mtd->size, map->map.size); + map->mtd->size = map->map.size; } - - info->mtd->owner = THIS_MODULE; - add_mtd_device(info->mtd); - - if (info->window_rsrc.parent) { + if (window->rsrc.parent) { /* * Registering the MTD device in iomem may not be possible * if there is a BIOS "reserved" and BUSY range. If this * fails then continue anyway. */ - snprintf(info->mtd_name, MTD_DEV_NAME_LENGTH, - "mtd%d", info->mtd->index); - - info->rom_rsrc.name = info->mtd_name; - info->rom_rsrc.start = ICHX_FWH_REGION_START - + ICHX_FWH_REGION_SIZE - map_size; - info->rom_rsrc.end = ICHX_FWH_REGION_START - + ICHX_FWH_REGION_SIZE; - info->rom_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; - if (request_resource(&info->window_rsrc, &info->rom_rsrc)) { + map->rsrc.name = map->map_name; + map->rsrc.start = map->map.phys; + map->rsrc.end = map->map.phys + map->mtd->size - 1; + map->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; + if (request_resource(&window->rsrc, &map->rsrc)) { printk(KERN_ERR MOD_NAME ": cannot reserve MTD resource\n"); - info->rom_rsrc.parent = NULL; + map->rsrc.parent = NULL; } } + /* Make the whole region visible in the map */ + map->map.virt = (unsigned long)(window->virt); + map->map.phys = window->phys; + cfi = map->map.fldrv_priv; + for(i = 0; i < cfi->numchips; i++) { + cfi->chips[i].start += offset; + } + + /* Now that the mtd devices is complete claim and export it */ + map->mtd->owner = THIS_MODULE; + add_mtd_device(map->mtd); + + + /* Calculate the new value of map_top */ + map_top += map->mtd->size; + + /* File away the map structure */ + list_add(&map->list, &window->maps); + map = 0; + } + + /* Free any left over map structures */ + if (map) { + kfree(map); + } + return 0; failed: - ichxrom_cleanup(info); + ichxrom_cleanup(window); return -ENODEV; } static void __devexit ichxrom_remove_one (struct pci_dev *pdev) { - struct ichxrom_map_info *info = &ichxrom_map; - u16 word; - - del_mtd_device(info->mtd); - map_destroy(info->mtd); - info->mtd = NULL; - info->map.map_priv_1 = 0; - - iounmap((void *)(info->window_addr)); - info->window_addr = 0; - - /* Disable writes through the rom window */ - pci_read_config_word(pdev, BIOS_CNTL, &word); - pci_write_config_word(pdev, BIOS_CNTL, word & ~1); - -#if RESERVE_MEM_REGION - release_mem_region(ICHX_FWH_REGION_START, ICHX_FWH_REGION_SIZE); -#endif + struct ichxrom_window *window = &ichxrom_window; + ichxrom_cleanup(window); } static struct pci_device_id ichxrom_pci_tbl[] __devinitdata = { @@ -371,7 +345,6 @@ }; #endif -static struct pci_dev *mydev; int __init init_ichxrom(void) { struct pci_dev *pdev; @@ -385,7 +358,6 @@ } } if (pdev) { - mydev = pdev; return ichxrom_init_one(pdev, &ichxrom_pci_tbl[0]); } return -ENXIO; @@ -396,7 +368,7 @@ static void __exit cleanup_ichxrom(void) { - ichxrom_remove_one(mydev); + ichxrom_remove_one(ichxrom_window.pdev); } module_init(init_ichxrom); diff -NaurbB linux-2.6.9-pre1-bk18/drivers/mtd/maps/impa7.c linux-2.6.9_nbp/drivers/mtd/maps/impa7.c --- linux-2.6.9-pre1-bk18/drivers/mtd/maps/impa7.c 2005-08-15 01:14:41.000000000 +0200 +++ linux-2.6.9_nbp/drivers/mtd/maps/impa7.c 2004-09-13 12:43:27.000000000 +0200 @@ -77,7 +77,7 @@ { static const char *rom_probe_types[] = PROBETYPES; const char **type; - const char *part_type = NULL; + const char *part_type = 0; int i; static struct { u_long addr; u_long size; } pt[NUM_FLASHBANKS] = { { WINDOW_ADDR0, WINDOW_SIZE0 }, @@ -99,7 +99,7 @@ } simple_map_init(&impa7_map[i]); - impa7_mtd[i] = NULL; + impa7_mtd[i] = 0; type = rom_probe_types; for(; !impa7_mtd[i] && *type; type++) { impa7_mtd[i] = do_map_probe(*type, &impa7_map[i]); diff -NaurbB linux-2.6.9-pre1-bk18/drivers/mtd/maps/ipaq-flash.c linux-2.6.9_nbp/drivers/mtd/maps/ipaq-flash.c --- linux-2.6.9-pre1-bk18/drivers/mtd/maps/ipaq-flash.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/drivers/mtd/maps/ipaq-flash.c 2004-09-13 13:41:20.000000000 +0200 @@ -0,0 +1,464 @@ +/* + * Flash memory access on iPAQ Handhelds (either SA1100 or PXA250 based) + * + * (C) 2000 Nicolas Pitre + * (C) 2002 Hewlett-Packard Company + * (C) 2003 Christian Pellegrin , : concatenation of multiple flashes + * + * $Id: ipaq-flash.c,v 1.1 2004/08/24 19:06:09 joshua Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#ifdef CONFIG_MTD_CONCAT +#include +#endif + +#include +#include +#include + + +#ifndef CONFIG_IPAQ_HANDHELD +#error This is for iPAQ Handhelds only +#endif +#ifdef CONFIG_SA1100_JORNADA56X + +static void jornada56x_set_vpp(struct map_info *map, int vpp) +{ + if (vpp) + GPSR = GPIO_GPIO26; + else + GPCR = GPIO_GPIO26; + GPDR |= GPIO_GPIO26; +} + +#endif + +#ifdef CONFIG_SA1100_JORNADA720 + +static void jornada720_set_vpp(struct map_info *map, int vpp) +{ + if (vpp) + PPSR |= 0x80; + else + PPSR &= ~0x80; + PPDR |= 0x80; +} + +#endif + +#define MAX_IPAQ_CS 2 /* Number of CS we are going to test */ + +#define IPAQ_MAP_INIT(X) \ + { \ + name: "IPAQ flash " X, \ + } + + +static struct map_info ipaq_map[MAX_IPAQ_CS] = { + IPAQ_MAP_INIT("bank 1"), + IPAQ_MAP_INIT("bank 2") +}; + +static struct mtd_info *my_sub_mtd[MAX_IPAQ_CS] = { + NULL, + NULL +}; + +/* + * Here are partition information for all known IPAQ-based devices. + * See include/linux/mtd/partitions.h for definition of the mtd_partition + * structure. + * + * The *_max_flash_size is the maximum possible mapped flash size which + * is not necessarily the actual flash size. It must be no more than + * the value specified in the "struct map_desc *_io_desc" mapping + * definition for the corresponding machine. + * + * Please keep these in alphabetical order, and formatted as per existing + * entries. Thanks. + */ + +#ifdef CONFIG_IPAQ_HANDHELD +static unsigned long h3xxx_max_flash_size = 0x04000000; +static struct mtd_partition h3xxx_partitions[] = { + { + name: "H3XXX boot firmware", +#ifndef CONFIG_LAB + size: 0x00040000, +#else + size: 0x00080000, +#endif + offset: 0, +#ifndef CONFIG_LAB + mask_flags: MTD_WRITEABLE, /* force read-only */ +#endif + }, + { + name: "H3XXX root jffs2", +#ifndef CONFIG_LAB + size: 0x2000000 - 2*0x40000, /* Warning, this is fixed later */ + offset: 0x00040000, +#else + size: 0x2000000 - 0x40000 - 0x80000, /* Warning, this is fixed later */ + offset: 0x00080000, +#endif + }, + { + name: "asset", + size: 0x40000, + offset: 0x2000000 - 0x40000, /* Warning, this is fixed later */ + mask_flags: MTD_WRITEABLE, /* force read-only */ + } +}; + +#ifndef CONFIG_MTD_CONCAT +static struct mtd_partition h3xxx_partitions_bank2[] = { + /* this is used only on 2 CS machines when concat is not present */ + { + name: "second H3XXX root jffs2", + size: 0x1000000 - 0x40000, /* Warning, this is fixed later */ + offset: 0x00000000, + }, + { + name: "second asset", + size: 0x40000, + offset: 0x1000000 - 0x40000, /* Warning, this is fixed later */ + mask_flags: MTD_WRITEABLE, /* force read-only */ + } +}; +#endif + +static spinlock_t ipaq_vpp_lock = SPIN_LOCK_UNLOCKED; + +static void h3xxx_set_vpp(struct map_info *map, int vpp) +{ + static int nest = 0; + + spin_lock(&ipaq_vpp_lock); + if (vpp) + nest++; + else + nest--; + if (nest) + assign_h3600_egpio(IPAQ_EGPIO_VPP_ON, 1); + else + assign_h3600_egpio(IPAQ_EGPIO_VPP_ON, 0); + spin_unlock(&ipaq_vpp_lock); +} + +#endif + +#if defined(CONFIG_SA1100_JORNADA56X) || defined(CONFIG_SA1100_JORNADA720) +static unsigned long jornada_max_flash_size = 0x02000000; +static struct mtd_partition jornada_partitions[] = { + { + name: "Jornada boot firmware", + size: 0x00040000, + offset: 0, + mask_flags: MTD_WRITEABLE, /* force read-only */ + }, { + name: "Jornada root jffs2", + size: MTDPART_SIZ_FULL, + offset: 0x00040000, + } +}; +#endif + + +static struct mtd_partition *parsed_parts; +static struct mtd_info *mymtd; + +static unsigned long cs_phys[] = { +#ifdef CONFIG_ARCH_SA1100 + SA1100_CS0_PHYS, + SA1100_CS1_PHYS, + SA1100_CS2_PHYS, + SA1100_CS3_PHYS, + SA1100_CS4_PHYS, + SA1100_CS5_PHYS, +#else + PXA_CS0_PHYS, + PXA_CS1_PHYS, + PXA_CS2_PHYS, + PXA_CS3_PHYS, + PXA_CS4_PHYS, + PXA_CS5_PHYS, +#endif +}; + +static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL }; + +static int __init h1900_special_case(void); + +int __init ipaq_mtd_init(void) +{ + struct mtd_partition *parts = NULL; + int nb_parts = 0; + int parsed_nr_parts = 0; + const char *part_type; + int i; /* used when we have >1 flash chips */ + unsigned long tot_flashsize = 0; /* used when we have >1 flash chips */ + + /* Default flash bankwidth */ + // ipaq_map.bankwidth = (MSC0 & MSC_RBW) ? 2 : 4; + + if (machine_is_h1900()) + { + /* For our intents, the h1900 is not a real iPAQ, so we special-case it. */ + return h1900_special_case(); + } + + if (machine_is_h3100() || machine_is_h1900()) + for(i=0; isize); + + /* do we really need this debugging? --joshua 20030703 */ + // printk("my_sub_mtd[%d]=%p\n", i, my_sub_mtd[i]); + my_sub_mtd[i]->owner = THIS_MODULE; + tot_flashsize += my_sub_mtd[i]->size; + } +#ifdef CONFIG_MTD_CONCAT + /* fix the asset location */ +# ifdef CONFIG_LAB + h3xxx_partitions[1].size = tot_flashsize - 0x40000 - 0x80000 /* extra big boot block */; +# else + h3xxx_partitions[1].size = tot_flashsize - 2 * 0x40000; +# endif + h3xxx_partitions[2].offset = tot_flashsize - 0x40000; + /* and concat the devices */ + mymtd = mtd_concat_create(&my_sub_mtd[0], i, + "ipaq"); + if (!mymtd) { + printk("Cannot create iPAQ concat device\n"); + return -ENXIO; + } +#else + mymtd = my_sub_mtd[0]; + + /* + *In the very near future, command line partition parsing + * will use the device name as 'mtd-id' instead of a value + * passed to the parse_cmdline_partitions() routine. Since + * the bootldr says 'ipaq', make sure it continues to work. + */ + mymtd->name = "ipaq"; + + if ((machine_is_h3600())) { +# ifdef CONFIG_LAB + h3xxx_partitions[1].size = my_sub_mtd[0]->size - 0x80000; +# else + h3xxx_partitions[1].size = my_sub_mtd[0]->size - 0x40000; +# endif + nb_parts = 2; + } else { +# ifdef CONFIG_LAB + h3xxx_partitions[1].size = my_sub_mtd[0]->size - 0x40000 - 0x80000; /* extra big boot block */ +# else + h3xxx_partitions[1].size = my_sub_mtd[0]->size - 2*0x40000; +# endif + h3xxx_partitions[2].offset = my_sub_mtd[0]->size - 0x40000; + } + + if (my_sub_mtd[1]) { +# ifdef CONFIG_LAB + h3xxx_partitions_bank2[0].size = my_sub_mtd[1]->size - 0x80000; +# else + h3xxx_partitions_bank2[0].size = my_sub_mtd[1]->size - 0x40000; +# endif + h3xxx_partitions_bank2[1].offset = my_sub_mtd[1]->size - 0x40000; + } +#endif + } + else { + /* + * Now let's probe for the actual flash. Do it here since + * specific machine settings might have been set above. + */ + printk(KERN_NOTICE "IPAQ flash: probing %d-bit flash bus, window=%lx\n", ipaq_map[0].bankwidth*8, ipaq_map[0].virt); + mymtd = do_map_probe("cfi_probe", &ipaq_map[0]); + if (!mymtd) + return -ENXIO; + mymtd->owner = THIS_MODULE; + } + + + /* + * Dynamic partition selection stuff (might override the static ones) + */ + + i = parse_mtd_partitions(mymtd, part_probes, &parsed_parts, 0); + + if (i > 0) { + nb_parts = parsed_nr_parts = i; + parts = parsed_parts; + part_type = "dynamic"; + } + + if (!parts) { + printk(KERN_NOTICE "IPAQ flash: no partition info available, registering whole flash at once\n"); + add_mtd_device(mymtd); +#ifndef CONFIG_MTD_CONCAT + if (my_sub_mtd[1]) + add_mtd_device(my_sub_mtd[1]); +#endif + } else { + printk(KERN_NOTICE "Using %s partition definition\n", part_type); + add_mtd_partitions(mymtd, parts, nb_parts); +#ifndef CONFIG_MTD_CONCAT + if (my_sub_mtd[1]) + add_mtd_partitions(my_sub_mtd[1], h3xxx_partitions_bank2, ARRAY_SIZE(h3xxx_partitions_bank2)); +#endif + } + + return 0; +} + +static void __exit ipaq_mtd_cleanup(void) +{ + int i; + + if (mymtd) { + del_mtd_partitions(mymtd); +#ifndef CONFIG_MTD_CONCAT + if (my_sub_mtd[1]) + del_mtd_partitions(my_sub_mtd[1]); +#endif + map_destroy(mymtd); +#ifdef CONFIG_MTD_CONCAT + for(i=0; i + * Maintainer: Deepak Saxena + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +struct ixp2000_flash_info { + struct mtd_info *mtd; + struct map_info map; + struct mtd_partition *partitions; + struct resource *res; + int nr_banks; +}; + +static inline unsigned long flash_bank_setup(struct map_info *map, unsigned long ofs) +{ + unsigned long (*set_bank)(unsigned long) = + (unsigned long(*)(unsigned long))map->map_priv_2; + + return (set_bank ? set_bank(ofs) : ofs); +} + +#ifdef __ARMEB__ +/* + * Rev A0 and A1 of IXP2400 silicon have a broken addressing unit which + * causes the lower address bits to be XORed with 0x11 on 8 bit accesses + * and XORed with 0x10 on 16 bit accesses. See the spec update, erratta 44. + */ +static int errata44_workaround = 0; + +static inline unsigned long address_fix8_write(unsigned long addr) +{ + if (errata44_workaround) { + return (addr ^ 3); + } + return addr; +} +#else + +#define address_fix8_write(x) (x) +#endif + +static map_word ixp2000_flash_read8(struct map_info *map, unsigned long ofs) +{ + map_word val; + + val.x[0] = *((u8 *)(map->map_priv_1 + flash_bank_setup(map, ofs))); + return val; +} + +/* + * We can't use the standard memcpy due to the broken SlowPort + * address translation on rev A0 and A1 silicon and the fact that + * we have banked flash. + */ +static void ixp2000_flash_copy_from(struct map_info *map, void *to, + unsigned long from, ssize_t len) +{ + from = flash_bank_setup(map, from); + while(len--) + *(__u8 *) to++ = *(__u8 *)(map->map_priv_1 + from++); +} + +static void ixp2000_flash_write8(struct map_info *map, map_word d, unsigned long ofs) +{ + *(__u8 *) (address_fix8_write(map->map_priv_1 + + flash_bank_setup(map, ofs))) = d.x[0]; +} + +static void ixp2000_flash_copy_to(struct map_info *map, unsigned long to, + const void *from, ssize_t len) +{ + to = flash_bank_setup(map, to); + while(len--) { + unsigned long tmp = address_fix8_write(map->map_priv_1 + to++); + *(__u8 *)(tmp) = *(__u8 *)(from++); + } +} + + +static int ixp2000_flash_remove(struct device *_dev) +{ + struct platform_device *dev = to_platform_device(_dev); + struct flash_platform_data *plat = dev->dev.platform_data; + struct ixp2000_flash_info *info = dev_get_drvdata(&dev->dev); + + dev_set_drvdata(&dev->dev, NULL); + + if(!info) + return 0; + + if (info->mtd) { + del_mtd_partitions(info->mtd); + map_destroy(info->mtd); + } + if (info->map.map_priv_1) + iounmap((void *) info->map.map_priv_1); + + if (info->partitions) { + kfree(info->partitions); } + + if (info->res) { + release_resource(info->res); + kfree(info->res); + } + + if (plat->exit) + plat->exit(); + + return 0; +} + + +static int ixp2000_flash_probe(struct device *_dev) +{ + static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; + struct platform_device *dev = to_platform_device(_dev); + struct ixp2000_flash_data *ixp_data = dev->dev.platform_data; + struct flash_platform_data *plat; + struct ixp2000_flash_info *info; + unsigned long window_size; + int err = -1; + + if (!ixp_data) + return -ENODEV; + + plat = ixp_data->platform_data; + if (!plat) + return -ENODEV; + + window_size = dev->resource->end - dev->resource->start + 1; + dev_info(_dev, "Probe of IXP2000 flash(%d banks x %dM)\n", + ixp_data->nr_banks, ((u32)window_size >> 20)); + + if (plat->width != 1) { + dev_err(_dev, "IXP2000 MTD map only supports 8-bit mode, asking for %d\n", + plat->width * 8); + return -EIO; + } + + info = kmalloc(sizeof(struct ixp2000_flash_info), GFP_KERNEL); + if(!info) { + err = -ENOMEM; + goto Error; + } + memzero(info, sizeof(struct ixp2000_flash_info)); + + dev_set_drvdata(&dev->dev, info); + + /* + * Tell the MTD layer we're not 1:1 mapped so that it does + * not attempt to do a direct access on us. + */ + info->map.phys = NO_XIP; + + info->nr_banks = ixp_data->nr_banks; + info->map.size = ixp_data->nr_banks * window_size; + info->map.bankwidth = 1; + + /* + * map_priv_2 is used to store a ptr to to the bank_setup routine + */ + info->map.map_priv_2 = (u32) ixp_data->bank_setup; + + info->map.name = dev->dev.bus_id; + info->map.read = ixp2000_flash_read8; + info->map.write = ixp2000_flash_write8; + info->map.copy_from = ixp2000_flash_copy_from; + info->map.copy_to = ixp2000_flash_copy_to; + + info->res = request_mem_region(dev->resource->start, + dev->resource->end - dev->resource->start + 1, + dev->dev.bus_id); + if (!info->res) { + dev_err(_dev, "Could not reserve memory region\n"); + err = -ENOMEM; + goto Error; + } + + info->map.map_priv_1 = + (unsigned long) ioremap(dev->resource->start, + dev->resource->end - dev->resource->start + 1); + if (!info->map.map_priv_1) { + dev_err(_dev, "Failed to ioremap flash region\n"); + err = -EIO; + goto Error; + } + + /* + * Setup read mode for FLASH + */ + *IXP2000_SLOWPORT_FRM = 1; + +#if defined(__ARMEB__) + /* + * Enable errata 44 workaround for NPUs with broken slowport + */ + + errata44_workaround = ixp2000_has_broken_slowport(); + dev_info(_dev, "Errata 44 workaround %s\n", + errata44_workaround ? "enabled" : "disabled"); +#endif + + info->mtd = do_map_probe(plat->map_name, &info->map); + if (!info->mtd) { + dev_err(_dev, "map_probe failed\n"); + err = -ENXIO; + goto Error; + } + info->mtd->owner = THIS_MODULE; + + err = parse_mtd_partitions(info->mtd, probes, &info->partitions, 0); + if (err > 0) { + err = add_mtd_partitions(info->mtd, info->partitions, err); + if(err) + dev_err(_dev, "Could not parse partitions\n"); + } + + if (err) + goto Error; + + return 0; + +Error: + ixp2000_flash_remove(_dev); + return err; +} + +static struct device_driver ixp2000_flash_driver = { + .name = "IXP2000-Flash", + .bus = &platform_bus_type, + .probe = &ixp2000_flash_probe, + .remove = &ixp2000_flash_remove +}; + +static int __init ixp2000_flash_init(void) +{ + return driver_register(&ixp2000_flash_driver); +} + +static void __exit ixp2000_flash_exit(void) +{ + driver_unregister(&ixp2000_flash_driver); +} + +module_init(ixp2000_flash_init); +module_exit(ixp2000_flash_exit); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Deepak Saxena "); + diff -NaurbB linux-2.6.9-pre1-bk18/drivers/mtd/maps/ixp4xx.c linux-2.6.9_nbp/drivers/mtd/maps/ixp4xx.c --- linux-2.6.9-pre1-bk18/drivers/mtd/maps/ixp4xx.c 2004-08-14 07:36:11.000000000 +0200 +++ linux-2.6.9_nbp/drivers/mtd/maps/ixp4xx.c 2004-09-13 12:43:27.000000000 +0200 @@ -1,5 +1,5 @@ /* - * $Id: ixp4xx.c,v 1.3 2004/07/12 22:38:29 dwmw2 Exp $ + * $Id: ixp4xx.c,v 1.4 2004/08/31 22:55:51 dsaxena Exp $ * * drivers/mtd/maps/ixp4xx.c * @@ -88,6 +88,7 @@ struct platform_device *dev = to_platform_device(_dev); struct flash_platform_data *plat = dev->dev.platform_data; struct ixp4xx_flash_info *info = dev_get_drvdata(&dev->dev); + map_word d; dev_set_drvdata(&dev->dev, NULL); @@ -97,7 +98,8 @@ /* * This is required for a soft reboot to work. */ - ixp4xx_write16(&info->map, 0xff, 0x55 * 0x2); + d.x[0] = 0xff; + ixp4xx_write16(&info->map, d, 0x55 * 0x2); if (info->mtd) { del_mtd_partitions(info->mtd); diff -NaurbB linux-2.6.9-pre1-bk18/drivers/mtd/maps/Kconfig linux-2.6.9_nbp/drivers/mtd/maps/Kconfig --- linux-2.6.9-pre1-bk18/drivers/mtd/maps/Kconfig 2004-08-14 07:38:08.000000000 +0200 +++ linux-2.6.9_nbp/drivers/mtd/maps/Kconfig 2004-09-13 12:43:27.000000000 +0200 @@ -1,5 +1,5 @@ # drivers/mtd/maps/Kconfig -# $Id: Kconfig,v 1.30 2004/07/21 00:16:14 jwboyer Exp $ +# $Id: Kconfig,v 1.34 2004/09/02 01:27:07 eric Exp $ menu "Mapping drivers for chip access" depends on MTD!=n @@ -160,7 +160,7 @@ config MTD_ICHXROM tristate "BIOS flash chip on Intel Controller Hub 2/3/4/5" - depends on X86 && MTD_JEDECPROBE && MTD_COMPLEX_MAPPINGS + depends on X86 && MTD_JEDECPROBE help Support for treating the BIOS flash chip on ICHX motherboards as an MTD device - with this you can reprogram your BIOS. @@ -355,13 +355,21 @@ use the flash chips on it, say 'Y'. config MTD_EBONY - tristate "CFI Flash device mapped on IBM 440GP Ebony" - depends on MTD_CFI && PPC32 && 440 && EBONY + tristate "Flash devices mapped on IBM 440GP Ebony" + depends on MTD_CFI && PPC32 && 44x && EBONY help This enables access routines for the flash chips on the IBM 440GP Ebony board. If you have one of these boards and would like to use the flash chips on it, say 'Y'. +config MTD_OCOTEA + tristate "Flash devices mapped on IBM 440GX Ocotea" + depends on MTD_CFI && PPC32 && 44x && OCOTEA + help + This enables access routines for the flash chips on the IBM 440GX + Ocotea board. If you have one of these boards and would like to + use the flash chips on it, say 'Y'. + config MTD_REDWOOD tristate "CFI Flash devices mapped on IBM Redwood" depends on MTD_CFI && PPC32 && 4xx && 40x && ( REDWOOD_4 || REDWOOD_5 || REDWOOD_6 ) @@ -448,6 +456,12 @@ the SA1100 and SA1110, including the Assabet and the Compaq iPAQ. If you have such a board, say 'Y'. +config MTD_IPAQ + tristate "CFI Flash device mapped on Compaq/HP iPAQ" + depends on ARM && IPAQ_HANDHELD && MTD_CFI + help + This provides a driver for the on-board flash of the iPAQ. + config MTD_DC21285 tristate "CFI Flash device mapped on DC21285 Footbridge" depends on ARM && MTD_CFI && ARCH_FOOTBRIDGE && MTD_COMPLEX_MAPPINGS @@ -468,11 +482,20 @@ tristate "CFI Flash device mapped on Intel IXP4xx based systems" depends on ARM && MTD_CFI && MTD_COMPLEX_MAPPINGS && ARCH_IXP4XX help - This eables MTD access to flash devices on platforms based + This enables MTD access to flash devices on platforms based on Intel's IXP4xx family of network processors such as the IXDP425 and Coyote. If you have an IXP4xx based board and would like to use the flash chips on it, say 'Y'. +config MTD_IXP2000 + tristate "CFI Flash device mapped on Intel IXP2000 based systems" + depends on ARM && MTD_CFI && MTD_COMPLEX_MAPPINGS && ARCH_IXP2000 + help + This enables MTD access to flash devices on platforms based + on Intel's IXP2000 family of network processors such as the + IXDP425 and Coyote. If you have an IXP2000 based board and + would like to use the flash chips on it, say 'Y'. + config MTD_EPXA10DB tristate "CFI Flash device mapped on Epxa10db" depends on ARM && MTD_CFI && MTD_PARTITIONS && ARCH_CAMELOT diff -NaurbB linux-2.6.9-pre1-bk18/drivers/mtd/maps/lubbock-flash.c linux-2.6.9_nbp/drivers/mtd/maps/lubbock-flash.c --- linux-2.6.9-pre1-bk18/drivers/mtd/maps/lubbock-flash.c 2004-08-14 07:37:26.000000000 +0200 +++ linux-2.6.9_nbp/drivers/mtd/maps/lubbock-flash.c 2004-09-13 12:43:27.000000000 +0200 @@ -1,5 +1,5 @@ /* - * $Id: lubbock-flash.c,v 1.15 2004/07/12 21:59:44 dwmw2 Exp $ + * $Id: lubbock-flash.c,v 1.16 2004/08/26 21:09:40 nico Exp $ * * Map driver for the Lubbock developer platform. * @@ -89,9 +89,7 @@ ret = -ENOMEM; continue; } - lubbock_maps[i].cached = __ioremap(lubbock_maps[i].phys, - WINDOW_SIZE, - L_PTE_CACHEABLE, 1); + lubbock_maps[i].cached = ioremap_cached(lubbock_maps[i].phys, WINDOW_SIZE); if (!lubbock_maps[i].cached) printk(KERN_WARNING "Failed to ioremap cached %s\n", lubbock_maps[i].name); simple_map_init(&lubbock_maps[i]); diff -NaurbB linux-2.6.9-pre1-bk18/drivers/mtd/maps/Makefile linux-2.6.9_nbp/drivers/mtd/maps/Makefile --- linux-2.6.9-pre1-bk18/drivers/mtd/maps/Makefile 2004-08-14 07:36:10.000000000 +0200 +++ linux-2.6.9_nbp/drivers/mtd/maps/Makefile 2004-09-13 12:43:27.000000000 +0200 @@ -1,7 +1,7 @@ # # linux/drivers/maps/Makefile # -# $Id: Makefile.common,v 1.14 2004/07/12 16:07:31 dwmw2 Exp $ +# $Id: Makefile.common,v 1.17 2004/09/02 00:13:41 dsaxena Exp $ ifeq ($(CONFIG_MTD_COMPLEX_MAPPINGS),y) obj-$(CONFIG_MTD) += map_funcs.o @@ -31,6 +31,7 @@ obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o obj-$(CONFIG_MTD_TQM8XXL) += tqm8xxl.o obj-$(CONFIG_MTD_SA1100) += sa1100-flash.o +obj-$(CONFIG_MTD_IPAQ) += ipaq-flash.o obj-$(CONFIG_MTD_SBC_GXX) += sbc_gxx.o obj-$(CONFIG_MTD_SC520CDP) += sc520cdp.o obj-$(CONFIG_MTD_NETSC520) += netsc520.o @@ -55,6 +56,7 @@ obj-$(CONFIG_MTD_NETtel) += nettel.o obj-$(CONFIG_MTD_SCB2_FLASH) += scb2_flash.o obj-$(CONFIG_MTD_EBONY) += ebony.o +obj-$(CONFIG_MTD_OCOTEA) += ocotea.o obj-$(CONFIG_MTD_BEECH) += beech-mtd.o obj-$(CONFIG_MTD_ARCTIC) += arctic-mtd.o obj-$(CONFIG_MTD_H720X) += h720x-flash.o @@ -62,5 +64,6 @@ obj-$(CONFIG_MTD_NOR_TOTO) += omap-toto-flash.o obj-$(CONFIG_MTD_MPC1211) += mpc1211.o obj-$(CONFIG_MTD_IXP4XX) += ixp4xx.o +obj-$(CONFIG_MTD_IXP2000) += ixp2000.o obj-$(CONFIG_MTD_WRSBC8260) += wr_sbc82xx_flash.o obj-$(CONFIG_MTD_DMV182) += dmv182.o diff -NaurbB linux-2.6.9-pre1-bk18/drivers/mtd/maps/nettel.c linux-2.6.9_nbp/drivers/mtd/maps/nettel.c --- linux-2.6.9-pre1-bk18/drivers/mtd/maps/nettel.c 2005-08-15 01:14:41.000000000 +0200 +++ linux-2.6.9_nbp/drivers/mtd/maps/nettel.c 2004-09-13 12:43:27.000000000 +0200 @@ -180,7 +180,7 @@ if (mtd) { nettel_erase.mtd = mtd; nettel_erase.callback = nettel_erasecallback; - nettel_erase.callback = NULL; + nettel_erase.callback = 0; nettel_erase.addr = 0; nettel_erase.len = mtd->size; nettel_erase.priv = (u_long) &wait_q; diff -NaurbB linux-2.6.9-pre1-bk18/drivers/mtd/maps/ocotea.c linux-2.6.9_nbp/drivers/mtd/maps/ocotea.c --- linux-2.6.9-pre1-bk18/drivers/mtd/maps/ocotea.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/drivers/mtd/maps/ocotea.c 2004-09-13 13:41:20.000000000 +0200 @@ -0,0 +1,156 @@ +/* + * Mapping for Ocotea user flash + * + * Matt Porter + * + * Copyright 2002-2004 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static struct mtd_info *flash; + +static struct map_info ocotea_small_map = { + .name = "Ocotea small flash", + .size = OCOTEA_SMALL_FLASH_SIZE, + .buswidth = 1, +}; + +static struct map_info ocotea_large_map = { + .name = "Ocotea large flash", + .size = OCOTEA_LARGE_FLASH_SIZE, + .buswidth = 1, +}; + +static struct mtd_partition ocotea_small_partitions[] = { + { + .name = "pibs", + .offset = 0x0, + .size = 0x100000, + } +}; + +static struct mtd_partition ocotea_large_partitions[] = { + { + .name = "fs", + .offset = 0, + .size = 0x300000, + }, + { + .name = "firmware", + .offset = 0x300000, + .size = 0x100000, + } +}; + +#define NB_OF(x) (sizeof(x)/sizeof(x[0])) + +int __init init_ocotea(void) +{ + u8 fpga0_reg; + u8 *fpga0_adr; + unsigned long long small_flash_base, large_flash_base; + + fpga0_adr = ioremap64(OCOTEA_FPGA_ADDR, 16); + if (!fpga0_adr) + return -ENOMEM; + + fpga0_reg = readb((unsigned long)fpga0_adr); + iounmap(fpga0_adr); + + if (OCOTEA_BOOT_LARGE_FLASH(fpga0_reg)) { + small_flash_base = OCOTEA_SMALL_FLASH_HIGH; + large_flash_base = OCOTEA_LARGE_FLASH_LOW; + } + else { + small_flash_base = OCOTEA_SMALL_FLASH_LOW; + large_flash_base = OCOTEA_LARGE_FLASH_HIGH; + } + + ocotea_small_map.phys = small_flash_base; + ocotea_small_map.virt = + (unsigned long)ioremap64(small_flash_base, + ocotea_small_map.size); + + if (!ocotea_small_map.virt) { + printk("Failed to ioremap flash\n"); + return -EIO; + } + + simple_map_init(&ocotea_small_map); + + flash = do_map_probe("map_rom", &ocotea_small_map); + if (flash) { + flash->owner = THIS_MODULE; + add_mtd_partitions(flash, ocotea_small_partitions, + NB_OF(ocotea_small_partitions)); + } else { + printk("map probe failed for flash\n"); + return -ENXIO; + } + + ocotea_large_map.phys = large_flash_base; + ocotea_large_map.virt = + (unsigned long)ioremap64(large_flash_base, + ocotea_large_map.size); + + if (!ocotea_large_map.virt) { + printk("Failed to ioremap flash\n"); + return -EIO; + } + + simple_map_init(&ocotea_large_map); + + flash = do_map_probe("cfi_probe", &ocotea_large_map); + if (flash) { + flash->owner = THIS_MODULE; + add_mtd_partitions(flash, ocotea_large_partitions, + NB_OF(ocotea_large_partitions)); + } else { + printk("map probe failed for flash\n"); + return -ENXIO; + } + + return 0; +} + +static void __exit cleanup_ocotea(void) +{ + if (flash) { + del_mtd_partitions(flash); + map_destroy(flash); + } + + if (ocotea_small_map.virt) { + iounmap((void *)ocotea_small_map.virt); + ocotea_small_map.virt = 0; + } + + if (ocotea_large_map.virt) { + iounmap((void *)ocotea_large_map.virt); + ocotea_large_map.virt = 0; + } +} + +module_init(init_ocotea); +module_exit(cleanup_ocotea); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Matt Porter "); +MODULE_DESCRIPTION("MTD map and partitions for IBM 440GX Ocotea boards"); diff -NaurbB linux-2.6.9-pre1-bk18/drivers/mtd/maps/sa1100-flash.c linux-2.6.9_nbp/drivers/mtd/maps/sa1100-flash.c --- linux-2.6.9-pre1-bk18/drivers/mtd/maps/sa1100-flash.c 2004-08-14 07:36:32.000000000 +0200 +++ linux-2.6.9_nbp/drivers/mtd/maps/sa1100-flash.c 2004-09-13 12:43:27.000000000 +0200 @@ -3,7 +3,7 @@ * * (C) 2000 Nicolas Pitre * - * $Id: sa1100-flash.c,v 1.39 2004/07/12 21:59:44 dwmw2 Exp $ + * $Id: sa1100-flash.c,v 1.40 2004/08/24 18:52:05 joshua Exp $ */ #include @@ -496,6 +496,32 @@ }; #endif +#ifdef CONFIG_SA1100_JORNADA56X +static struct mtd_partition jornada56x_partitions[] = { + { + .name = "bootldr", + .size = 0x00040000, + .offset = 0, + .mask_flags = MTD_WRITEABLE, + }, { + .name = "rootfs", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND, + } +}; + +static void jornada56x_set_vpp(struct map_info *map, int vpp) +{ + if (vpp) + GPSR = GPIO_GPIO26; + else + GPCR = GPIO_GPIO26; + GPDR |= GPIO_GPIO26; +} +#else +#define jornada56x_set_vpp NULL +#endif + #ifdef CONFIG_SA1100_JORNADA720 static struct mtd_partition jornada720_partitions[] = { { @@ -822,6 +848,12 @@ nb_parts = ARRAY_SIZE(huw_webpanel_partitions); } #endif +#ifdef CONFIG_SA1100_JORNADA56X + if (machine_is_jornada56x()) { + *parts = jornada56x_partitions; + nb_parts = ARRAY_SIZE(jornada56x_partitions); + } +#endif #ifdef CONFIG_SA1100_JORNADA720 if (machine_is_jornada720()) { *parts = jornada720_partitions; @@ -1160,7 +1192,7 @@ info[0].size = SZ_16M; nr = 1; } - if (machine_is_h3xxx()) { + if (machine_is_ipaq()) { info[0].set_vpp = h3xxx_set_vpp; info[0].base = SA1100_CS0_PHYS; info[0].size = SZ_32M; @@ -1176,6 +1208,12 @@ info[0].size = SZ_32M; nr = 1; } + if (machine_is_jornada56x()) { + info[0].set_vpp = jornada56x_set_vpp; + info[0].base = SA1100_CS0_PHYS; + info[0].size = SZ_32M; + nr = 1; + } if (machine_is_jornada720()) { info[0].set_vpp = jornada720_set_vpp; info[0].base = SA1100_CS0_PHYS; diff -NaurbB linux-2.6.9-pre1-bk18/drivers/mtd/mtd_blkdevs-24.c linux-2.6.9_nbp/drivers/mtd/mtd_blkdevs-24.c --- linux-2.6.9-pre1-bk18/drivers/mtd/mtd_blkdevs-24.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/drivers/mtd/mtd_blkdevs-24.c 2004-09-13 13:41:20.000000000 +0200 @@ -0,0 +1,692 @@ +/* + * $Id: mtd_blkdevs-24.c,v 1.16 2004/08/11 15:29:24 dmarlin Exp $ + * + * (C) 2003 David Woodhouse + * + * Interface to Linux 2.4 block layer for MTD 'translation layers'. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static LIST_HEAD(blktrans_majors); + +extern struct semaphore mtd_table_mutex; +extern struct mtd_info *mtd_table[]; + +struct mtd_blkcore_priv { + devfs_handle_t devfs_dir_handle; + int blksizes[256]; + int sizes[256]; + struct hd_struct part_table[256]; + struct gendisk gd; + spinlock_t devs_lock; /* See comment in _request function */ + struct completion thread_dead; + int exiting; + wait_queue_head_t thread_wq; +}; + +static inline struct mtd_blktrans_dev *tr_get_dev(struct mtd_blktrans_ops *tr, + int devnum) +{ + struct list_head *this; + struct mtd_blktrans_dev *d; + + list_for_each(this, &tr->devs) { + d = list_entry(this, struct mtd_blktrans_dev, list); + + if (d->devnum == devnum) + return d; + } + return NULL; +} + +static inline struct mtd_blktrans_ops *get_tr(int major) +{ + struct list_head *this; + struct mtd_blktrans_ops *t; + + list_for_each(this, &blktrans_majors) { + t = list_entry(this, struct mtd_blktrans_ops, list); + + if (t->major == major) + return t; + } + return NULL; +} + +static int do_blktrans_request(struct mtd_blktrans_ops *tr, + struct mtd_blktrans_dev *dev, + struct request *req) +{ + unsigned long block, nsect; + char *buf; + int minor; + + minor = MINOR(req->rq_dev); + block = req->sector; + nsect = req->current_nr_sectors; + buf = req->buffer; + + if (block + nsect > tr->blkcore_priv->part_table[minor].nr_sects) { + printk(KERN_WARNING "Access beyond end of device.\n"); + return 0; + } + block += tr->blkcore_priv->part_table[minor].start_sect; + + switch(req->cmd) { + case READ: + for (; nsect > 0; nsect--, block++, buf += 512) + if (tr->readsect(dev, block, buf)) + return 0; + return 1; + + case WRITE: + if (!tr->writesect) + return 0; + + for (; nsect > 0; nsect--, block++, buf += 512) + if (tr->writesect(dev, block, buf)) + return 0; + return 1; + + default: + printk(KERN_NOTICE "Unknown request cmd %d\n", req->cmd); + return 0; + } +} + +static int mtd_blktrans_thread(void *arg) +{ + struct mtd_blktrans_ops *tr = arg; + struct request_queue *rq = BLK_DEFAULT_QUEUE(tr->major); + + /* we might get involved when memory gets low, so use PF_MEMALLOC */ + current->flags |= PF_MEMALLOC; + + snprintf(current->comm, sizeof(current->comm), "%sd", tr->name); + + /* daemonize() doesn't do this for us since some kernel threads + actually want to deal with signals. We can't just call + exit_sighand() since that'll cause an oops when we finally + do exit. */ + spin_lock_irq(¤t->sigmask_lock); + sigfillset(¤t->blocked); + recalc_sigpending(); + spin_unlock_irq(¤t->sigmask_lock); + + daemonize("%sd", tr->name); + + while (!tr->blkcore_priv->exiting) { + struct request *req; + struct mtd_blktrans_dev *dev; + int devnum; + int res = 0; + DECLARE_WAITQUEUE(wait, current); + + spin_lock_irq(&io_request_lock); + + if (list_empty(&rq->queue_head)) { + + add_wait_queue(&tr->blkcore_priv->thread_wq, &wait); + set_current_state(TASK_INTERRUPTIBLE); + + spin_unlock_irq(&io_request_lock); + + schedule(); + remove_wait_queue(&tr->blkcore_priv->thread_wq, &wait); + + continue; + } + + req = blkdev_entry_next_request(&rq->queue_head); + + devnum = MINOR(req->rq_dev) >> tr->part_bits; + + /* The ll_rw_blk code knows not to touch the request + at the head of the queue */ + spin_unlock_irq(&io_request_lock); + + /* FIXME: Where can we store the dev, on which + we already have a refcount anyway? We need to + lock against concurrent addition/removal of devices, + but if we use the mtd_table_mutex we deadlock when + grok_partitions is called from the registration + callbacks. */ + spin_lock(&tr->blkcore_priv->devs_lock); + dev = tr_get_dev(tr, devnum); + spin_unlock(&tr->blkcore_priv->devs_lock); + + BUG_ON(!dev); + + /* Ensure serialisation of requests */ + down(&dev->sem); + + res = do_blktrans_request(tr, dev, req); + up(&dev->sem); + + if (!end_that_request_first(req, res, tr->name)) { + spin_lock_irq(&io_request_lock); + blkdev_dequeue_request(req); + end_that_request_last(req); + spin_unlock_irq(&io_request_lock); + } + } + complete_and_exit(&tr->blkcore_priv->thread_dead, 0); +} + +static void mtd_blktrans_request(struct request_queue *rq) +{ + struct mtd_blktrans_ops *tr = rq->queuedata; + wake_up(&tr->blkcore_priv->thread_wq); +} + +int blktrans_open(struct inode *i, struct file *f) +{ + struct mtd_blktrans_ops *tr = NULL; + struct mtd_blktrans_dev *dev = NULL; + int major_nr = MAJOR(i->i_rdev); + int minor_nr = MINOR(i->i_rdev); + int devnum; + int ret = -ENODEV; + + if (is_read_only(i->i_rdev) && (f->f_mode & FMODE_WRITE)) + return -EROFS; + + down(&mtd_table_mutex); + + tr = get_tr(major_nr); + + if (!tr) + goto out; + + devnum = minor_nr >> tr->part_bits; + + dev = tr_get_dev(tr, devnum); + + if (!dev) + goto out; + + if (!tr->blkcore_priv->part_table[minor_nr].nr_sects) { + ret = -ENODEV; + goto out; + } + + if (!try_inc_mod_count(dev->mtd->owner)) + goto out; + + if (!try_inc_mod_count(tr->owner)) + goto out_tr; + + dev->mtd->usecount++; + + ret = 0; + if (tr->open && (ret = tr->open(dev))) { + dev->mtd->usecount--; + if (dev->mtd->owner) + __MOD_DEC_USE_COUNT(dev->mtd->owner); + out_tr: + if (tr->owner) + __MOD_DEC_USE_COUNT(tr->owner); + } + out: + up(&mtd_table_mutex); + + return ret; +} + +int blktrans_release(struct inode *i, struct file *f) +{ + struct mtd_blktrans_dev *dev; + struct mtd_blktrans_ops *tr; + int ret = 0; + int devnum; + + down(&mtd_table_mutex); + + tr = get_tr(MAJOR(i->i_rdev)); + if (!tr) { + up(&mtd_table_mutex); + return -ENODEV; + } + + devnum = MINOR(i->i_rdev) >> tr->part_bits; + dev = tr_get_dev(tr, devnum); + + if (!dev) { + up(&mtd_table_mutex); + return -ENODEV; + } + + if (tr->release) + ret = tr->release(dev); + + if (!ret) { + dev->mtd->usecount--; + if (dev->mtd->owner) + __MOD_DEC_USE_COUNT(dev->mtd->owner); + if (tr->owner) + __MOD_DEC_USE_COUNT(tr->owner); + } + + up(&mtd_table_mutex); + + return ret; +} + +static int mtd_blktrans_rrpart(kdev_t rdev, struct mtd_blktrans_ops *tr, + struct mtd_blktrans_dev *dev) +{ + struct gendisk *gd = &(tr->blkcore_priv->gd); + int i; + int minor = MINOR(rdev); + + if (minor & ((1<part_bits)-1) || !tr->part_bits) { + /* BLKRRPART on a partition. Go away. */ + return -ENOTTY; + } + + if (!capable(CAP_SYS_ADMIN)) + return -EACCES; + + /* We are required to prevent simultaneous open() ourselves. + The core doesn't do that for us. Did I ever mention how + much the Linux block layer sucks? Sledgehammer approach... */ + down(&mtd_table_mutex); + + for (i=0; i < (1<part_bits); i++) { + invalidate_device(MKDEV(tr->major, minor+i), 1); + gd->part[minor + i].start_sect = 0; + gd->part[minor + i].nr_sects = 0; + } + + grok_partitions(gd, minor, 1 << tr->part_bits, + tr->blkcore_priv->sizes[minor]); + up(&mtd_table_mutex); + + return 0; +} + +static int blktrans_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct mtd_blktrans_dev *dev; + struct mtd_blktrans_ops *tr; + int devnum; + + switch(cmd) { + case BLKGETSIZE: + case BLKGETSIZE64: + case BLKBSZSET: + case BLKBSZGET: + case BLKROSET: + case BLKROGET: + case BLKRASET: + case BLKRAGET: + case BLKPG: + case BLKELVGET: + case BLKELVSET: + return blk_ioctl(inode->i_rdev, cmd, arg); + } + + down(&mtd_table_mutex); + + tr = get_tr(MAJOR(inode->i_rdev)); + if (!tr) { + up(&mtd_table_mutex); + return -ENODEV; + } + + devnum = MINOR(inode->i_rdev) >> tr->part_bits; + dev = tr_get_dev(tr, devnum); + + up(&mtd_table_mutex); + + if (!dev) + return -ENODEV; + + switch(cmd) { + case BLKRRPART: + return mtd_blktrans_rrpart(inode->i_rdev, tr, dev); + + case BLKFLSBUF: + blk_ioctl(inode->i_rdev, cmd, arg); + if (tr->flush) + return tr->flush(dev); + /* The core code did the work, we had nothing to do. */ + return 0; + + case HDIO_GETGEO: + if (tr->getgeo) { + struct hd_geometry g; + struct gendisk *gd = &(tr->blkcore_priv->gd); + int ret; + + memset(&g, 0, sizeof(g)); + ret = tr->getgeo(dev, &g); + if (ret) + return ret; + + g.start = gd->part[MINOR(inode->i_rdev)].start_sect; + if (copy_to_user((void *)arg, &g, sizeof(g))) + return -EFAULT; + return 0; + } /* else */ + default: + return -ENOTTY; + } +} + +struct block_device_operations mtd_blktrans_ops = { + .owner = THIS_MODULE, + .open = blktrans_open, + .release = blktrans_release, + .ioctl = blktrans_ioctl, +}; + +int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) +{ + struct mtd_blktrans_ops *tr = new->tr; + struct list_head *this; + int last_devnum = -1; + int i; + + if (!down_trylock(&mtd_table_mutex)) { + up(&mtd_table_mutex); + BUG(); + } + + spin_lock(&tr->blkcore_priv->devs_lock); + + list_for_each(this, &tr->devs) { + struct mtd_blktrans_dev *d = list_entry(this, struct mtd_blktrans_dev, list); + if (new->devnum == -1) { + /* Use first free number */ + if (d->devnum != last_devnum+1) { + /* Found a free devnum. Plug it in here */ + new->devnum = last_devnum+1; + list_add_tail(&new->list, &d->list); + goto added; + } + } else if (d->devnum == new->devnum) { + /* Required number taken */ + spin_unlock(&tr->blkcore_priv->devs_lock); + return -EBUSY; + } else if (d->devnum > new->devnum) { + /* Required number was free */ + list_add_tail(&new->list, &d->list); + goto added; + } + last_devnum = d->devnum; + } + if (new->devnum == -1) + new->devnum = last_devnum+1; + + if ((new->devnum << tr->part_bits) > 256) { + spin_unlock(&tr->blkcore_priv->devs_lock); + return -EBUSY; + } + + init_MUTEX(&new->sem); + list_add_tail(&new->list, &tr->devs); + added: + spin_unlock(&tr->blkcore_priv->devs_lock); + + if (!tr->writesect) + new->readonly = 1; + + for (i = new->devnum << tr->part_bits; + i < (new->devnum+1) << tr->part_bits; + i++) { + set_device_ro(MKDEV(tr->major, i), new->readonly); + tr->blkcore_priv->blksizes[i] = new->blksize; + tr->blkcore_priv->sizes[i] = 0; + tr->blkcore_priv->part_table[i].nr_sects = 0; + tr->blkcore_priv->part_table[i].start_sect = 0; + } + + /* + dwmw2: BLOCK_SIZE_BITS has nothing to do with block devices + dwmw2: any code which sets blk_size[][] should be + size >> 10 /+ 2.4 and its dumb units */ + + tr->blkcore_priv->sizes[new->devnum << tr->part_bits] = + (new->size * new->blksize) >> 10; /* 2.4 and its dumb units */ + + /* But this is still in device's sectors? $DEITY knows */ + tr->blkcore_priv->part_table[new->devnum << tr->part_bits].nr_sects = new->size; + + if (tr->part_bits) { + grok_partitions(&tr->blkcore_priv->gd, new->devnum, + 1 << tr->part_bits, new->size); + } +#ifdef CONFIG_DEVFS_FS + if (!tr->part_bits) { + char name[2]; + + name[0] = '0' + new->devnum; + name[1] = 0; + + new->blkcore_priv = + devfs_register(tr->blkcore_priv->devfs_dir_handle, + name, DEVFS_FL_DEFAULT, tr->major, + new->devnum, S_IFBLK|S_IRUGO|S_IWUGO, + &mtd_blktrans_ops, NULL); + } +#endif + return 0; +} + +int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old) +{ + struct mtd_blktrans_ops *tr = old->tr; + int i; + + if (!down_trylock(&mtd_table_mutex)) { + up(&mtd_table_mutex); + BUG(); + } + +#ifdef CONFIG_DEVFS_FS + if (!tr->part_bits) { + devfs_unregister(old->blkcore_priv); + old->blkcore_priv = NULL; + } else { + devfs_register_partitions(&tr->blkcore_priv->gd, + old->devnum << tr->part_bits, 1); + } +#endif + spin_lock(&tr->blkcore_priv->devs_lock); + list_del(&old->list); + spin_unlock(&tr->blkcore_priv->devs_lock); + + for (i = (old->devnum << tr->part_bits); + i < ((old->devnum+1) << tr->part_bits); i++) { + tr->blkcore_priv->sizes[i] = 0; + tr->blkcore_priv->part_table[i].nr_sects = 0; + tr->blkcore_priv->part_table[i].start_sect = 0; + } + + return 0; +} + +void blktrans_notify_remove(struct mtd_info *mtd) +{ + struct list_head *this, *this2, *next; + + list_for_each(this, &blktrans_majors) { + struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list); + + list_for_each_safe(this2, next, &tr->devs) { + struct mtd_blktrans_dev *dev = list_entry(this2, struct mtd_blktrans_dev, list); + + if (dev->mtd == mtd) + tr->remove_dev(dev); + } + } +} + +void blktrans_notify_add(struct mtd_info *mtd) +{ + struct list_head *this; + + if (mtd->type == MTD_ABSENT) + return; + + list_for_each(this, &blktrans_majors) { + struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list); + + tr->add_mtd(tr, mtd); + } + +} + +static struct mtd_notifier blktrans_notifier = { + .add = blktrans_notify_add, + .remove = blktrans_notify_remove, +}; + +int register_mtd_blktrans(struct mtd_blktrans_ops *tr) +{ + int ret, i; + + /* Register the notifier if/when the first device type is + registered, to prevent the link/init ordering from fucking + us over. */ + if (!blktrans_notifier.list.next) + register_mtd_user(&blktrans_notifier); + + tr->blkcore_priv = kmalloc(sizeof(*tr->blkcore_priv), GFP_KERNEL); + if (!tr->blkcore_priv) + return -ENOMEM; + + memset(tr->blkcore_priv, 0, sizeof(*tr->blkcore_priv)); + + down(&mtd_table_mutex); + + ret = devfs_register_blkdev(tr->major, tr->name, &mtd_blktrans_ops); + if (ret) { + printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n", + tr->name, tr->major, ret); + kfree(tr->blkcore_priv); + up(&mtd_table_mutex); + return ret; + } + + blk_init_queue(BLK_DEFAULT_QUEUE(tr->major), &mtd_blktrans_request); + (BLK_DEFAULT_QUEUE(tr->major))->queuedata = tr; + + init_completion(&tr->blkcore_priv->thread_dead); + init_waitqueue_head(&tr->blkcore_priv->thread_wq); + + ret = kernel_thread(mtd_blktrans_thread, tr, + CLONE_FS|CLONE_FILES|CLONE_SIGHAND); + if (ret < 0) { + blk_cleanup_queue(BLK_DEFAULT_QUEUE(tr->major)); + devfs_unregister_blkdev(tr->major, tr->name); + kfree(tr->blkcore_priv); + up(&mtd_table_mutex); + return ret; + } + + tr->blkcore_priv->devfs_dir_handle = + devfs_mk_dir(NULL, tr->name, NULL); + + blksize_size[tr->major] = tr->blkcore_priv->blksizes; + blk_size[tr->major] = tr->blkcore_priv->sizes; + + tr->blkcore_priv->gd.major = tr->major; + tr->blkcore_priv->gd.major_name = tr->name; + tr->blkcore_priv->gd.minor_shift = tr->part_bits; + tr->blkcore_priv->gd.max_p = (1<part_bits) - 1; + tr->blkcore_priv->gd.part = tr->blkcore_priv->part_table; + tr->blkcore_priv->gd.sizes = tr->blkcore_priv->sizes; + tr->blkcore_priv->gd.nr_real = 256 >> tr->part_bits; + + spin_lock_init(&tr->blkcore_priv->devs_lock); + + add_gendisk(&tr->blkcore_priv->gd); + + INIT_LIST_HEAD(&tr->devs); + list_add(&tr->list, &blktrans_majors); + + for (i=0; itype != MTD_ABSENT) + tr->add_mtd(tr, mtd_table[i]); + } + up(&mtd_table_mutex); + + return 0; +} + +int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr) +{ + struct list_head *this, *next; + + down(&mtd_table_mutex); + + /* Clean up the kernel thread */ + tr->blkcore_priv->exiting = 1; + wake_up(&tr->blkcore_priv->thread_wq); + wait_for_completion(&tr->blkcore_priv->thread_dead); + + /* Remove it from the list of active majors */ + list_del(&tr->list); + + /* Remove each of its devices */ + list_for_each_safe(this, next, &tr->devs) { + struct mtd_blktrans_dev *dev = list_entry(this, struct mtd_blktrans_dev, list); + tr->remove_dev(dev); + } + + blksize_size[tr->major] = NULL; + blk_size[tr->major] = NULL; + + del_gendisk(&tr->blkcore_priv->gd); + + blk_cleanup_queue(BLK_DEFAULT_QUEUE(tr->major)); + devfs_unregister_blkdev(tr->major, tr->name); + + devfs_unregister(tr->blkcore_priv->devfs_dir_handle); + + up(&mtd_table_mutex); + + kfree(tr->blkcore_priv); + + if (!list_empty(&tr->devs)) + BUG(); + return 0; +} + +static void __exit mtd_blktrans_exit(void) +{ + /* No race here -- if someone's currently in register_mtd_blktrans + we're screwed anyway. */ + if (blktrans_notifier.list.next) + unregister_mtd_user(&blktrans_notifier); +} + +module_exit(mtd_blktrans_exit); + +EXPORT_SYMBOL_GPL(register_mtd_blktrans); +EXPORT_SYMBOL_GPL(deregister_mtd_blktrans); +EXPORT_SYMBOL_GPL(add_mtd_blktrans_dev); +EXPORT_SYMBOL_GPL(del_mtd_blktrans_dev); + +MODULE_AUTHOR("David Woodhouse "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Common interface to block layer for MTD 'translation layers'"); diff -NaurbB linux-2.6.9-pre1-bk18/drivers/mtd/mtd_blkdevs.c linux-2.6.9_nbp/drivers/mtd/mtd_blkdevs.c --- linux-2.6.9-pre1-bk18/drivers/mtd/mtd_blkdevs.c 2004-08-14 07:36:11.000000000 +0200 +++ linux-2.6.9_nbp/drivers/mtd/mtd_blkdevs.c 2004-09-26 15:54:25.000000000 +0200 @@ -1,5 +1,5 @@ /* - * $Id: mtd_blkdevs.c,v 1.22 2004/07/12 12:35:28 dwmw2 Exp $ + * $Id: mtd_blkdevs.c,v 1.23 2004/08/19 01:54:36 tpoynor Exp $ * * (C) 2003 David Woodhouse * @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -81,7 +82,7 @@ struct request_queue *rq = tr->blkcore_priv->rq; /* we might get involved when memory gets low, so use PF_MEMALLOC */ - current->flags |= PF_MEMALLOC; + current->flags |= PF_MEMALLOC | PF_NOFREEZE; daemonize("%sd", tr->name); @@ -111,6 +112,12 @@ spin_unlock_irq(rq->queue_lock); schedule(); + + if (current->flags & PF_FREEZE) { +// printk(KERN_DEBUG "mtdblockd: sleeping\n"); + refrigerator(0); + } + remove_wait_queue(&tr->blkcore_priv->thread_wq, &wait); spin_lock_irq(rq->queue_lock); diff -NaurbB linux-2.6.9-pre1-bk18/drivers/mtd/nand/au1550nd.c linux-2.6.9_nbp/drivers/mtd/nand/au1550nd.c --- linux-2.6.9-pre1-bk18/drivers/mtd/nand/au1550nd.c 2004-08-14 07:36:14.000000000 +0200 +++ linux-2.6.9_nbp/drivers/mtd/nand/au1550nd.c 2004-09-13 12:43:27.000000000 +0200 @@ -3,7 +3,7 @@ * * Copyright (C) 2004 Embedded Edge, LLC * - * $Id: au1550nd.c,v 1.5 2004/05/17 07:19:35 ppopov Exp $ + * $Id: au1550nd.c,v 1.7 2004/09/01 23:36:10 gleixner Exp $ * * 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 @@ -34,10 +34,6 @@ static volatile u32 p_nand; static int nand_width = 1; /* default, only x8 supported for now */ -/* Internal buffers. Page buffer and oob buffer for one block*/ -static u_char data_buf[512 + 16]; -static u_char oob_buf[16 * 32]; - /* * Define partitions for flash device */ @@ -254,8 +250,11 @@ int __init au1550_init (void) { struct nand_chip *this; +#ifdef CONFIG_MIPS_PB1550 u16 boot_swapboot = 0; /* default value */ u32 mem_time; +#endif + int retval = -EIO; /* Allocate memory for MTD device structure and private data */ au1550_mtd = kmalloc (sizeof(struct mtd_info) + @@ -326,13 +325,23 @@ #endif #ifdef CONFIG_MIPS_DB1550 - /* Configure RCE1 - should be done by YAMON */ - au_writel(0x00400005, MEM_STCFG1); + /* Configure RCE1 - should be done by YAMON + * tglx: stcfg1 was set to 0x00400005. I changed + * this as it does not work with all chips. + * someone should look into the correct timing + * values, as bit 8 does a clock / 4 prescale + * + * FIXME: THis is bootloader stuff. + * + */ + au_writel(0x00400105, MEM_STCFG1); au_writel(0x00007774, MEM_STTIME1); au_writel(0x12000FFF, MEM_STADDR1); #endif p_nand = (volatile struct nand_regs *)ioremap(NAND_PHYS_ADDR, 0x1000); + if (!p_nand) + goto outmem; /* Set address of hardware control function */ this->hwcontrol = au1550_hwcontrol; @@ -349,20 +358,23 @@ this->verify_buf = au1550_nand_verify_buf; this->eccmode = NAND_ECC_SOFT; - /* Set internal data buffer */ - this->data_buf = data_buf; - this->oob_buf = oob_buf; - /* Scan to find existence of the device */ if (nand_scan (au1550_mtd, 1)) { - kfree (au1550_mtd); - return -ENXIO; + retval = -ENXIO; + goto outio; } /* Register the partitions */ add_mtd_partitions(au1550_mtd, partition_info, NUM_PARTITIONS); return 0; + + outio: + iounmap ((void *)p_nand); + + outmem: + kfree (au1550_mtd); + return retval; } module_init(au1550_init); @@ -375,16 +387,14 @@ { struct nand_chip *this = (struct nand_chip *) &au1550_mtd[1]; - iounmap ((void *)p_nand); - - /* Unregister partitions */ - del_mtd_partitions(au1550_mtd); - - /* Unregister the device */ - del_mtd_device (au1550_mtd); + /* Release resources, unregister device */ + nand_release (au1550_mtd); /* Free the MTD device structure */ kfree (au1550_mtd); + + /* Unmap */ + iounmap ((void *)p_nand); } module_exit(au1550_cleanup); #endif diff -NaurbB linux-2.6.9-pre1-bk18/drivers/mtd/nand/h1910.c linux-2.6.9_nbp/drivers/mtd/nand/h1910.c --- linux-2.6.9-pre1-bk18/drivers/mtd/nand/h1910.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/drivers/mtd/nand/h1910.c 2004-09-13 13:41:20.000000000 +0200 @@ -0,0 +1,215 @@ +/* + * drivers/mtd/nand/h1910.c + * + * Copyright (C) 2003 Joshua Wise (joshua@joshuawise.com) + * + * Derived from drivers/mtd/nand/edb7312.c + * Copyright (C) 2002 Marius Gröger (mag@sysgo.de) + * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) + * + * $Id: h1910.c,v 1.2 2004/08/23 14:01:57 joshua Exp $ + * + * 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. + * + * Overview: + * This is a device driver for the NAND flash device found on the + * iPAQ h1910 board which utilizes the Samsung K9F2808 part. This is + * a 128Mibit (16MiB x 8 bits) NAND flash device. + */ + +#include +#include +#include +#include +#include +#include +#include +#include /* for CLPS7111_VIRT_BASE */ +#include +#include +#include + +/* + * MTD structure for EDB7312 board + */ +static struct mtd_info *h1910_nand_mtd = NULL; + +/* + * Module stuff + */ + +#ifdef CONFIG_MTD_PARTITIONS +/* + * Define static partitions for flash device + */ +static struct mtd_partition partition_info[] = { + { name: "h1910 NAND Flash", + offset: 0, + size: 16*1024*1024 } +}; +#define NUM_PARTITIONS 1 + +#endif + + +/* + * hardware specific access to control-lines + */ +static void h1910_hwcontrol(struct mtd_info *mtd, int cmd) +{ + struct nand_chip* this = (struct nand_chip *) (mtd->priv); + + switch(cmd) { + + case NAND_CTL_SETCLE: + this->IO_ADDR_R |= (1 << 2); + this->IO_ADDR_W |= (1 << 2); + break; + case NAND_CTL_CLRCLE: + this->IO_ADDR_R &= ~(1 << 2); + this->IO_ADDR_W &= ~(1 << 2); + break; + + case NAND_CTL_SETALE: + this->IO_ADDR_R |= (1 << 3); + this->IO_ADDR_W |= (1 << 3); + break; + case NAND_CTL_CLRALE: + this->IO_ADDR_R &= ~(1 << 3); + this->IO_ADDR_W &= ~(1 << 3); + break; + + case NAND_CTL_SETNCE: + break; + case NAND_CTL_CLRNCE: + break; + } +} + +/* + * read device ready pin + */ +#if 0 +static int h1910_device_ready(struct mtd_info *mtd) +{ + return (GPLR(55) & GPIO_bit(55)); +} +#endif + +/* + * Main initialization routine + */ +static int __init h1910_init (void) +{ + struct nand_chip *this; + const char *part_type = 0; + int mtd_parts_nb = 0; + struct mtd_partition *mtd_parts = 0; + void* nandaddr; + + if (!machine_is_h1900()) + return -ENODEV; + + nandaddr = __ioremap(0x08000000, 0x1000, 0, 1); + if (!nandaddr) + { + printk("Failed to ioremap in nand flash.\n"); + return -ENOMEM; + }; + + /* Allocate memory for MTD device structure and private data */ + h1910_nand_mtd = kmalloc(sizeof(struct mtd_info) + + sizeof(struct nand_chip), + GFP_KERNEL); + if (!h1910_nand_mtd) { + printk("Unable to allocate h1910 NAND MTD device structure.\n"); + return -ENOMEM; + } + + /* Get pointer to private data */ + this = (struct nand_chip *) (&h1910_nand_mtd[1]); + + /* Initialize structures */ + memset((char *) h1910_nand_mtd, 0, sizeof(struct mtd_info)); + memset((char *) this, 0, sizeof(struct nand_chip)); + + /* Link the private data with the MTD structure */ + h1910_nand_mtd->priv = this; + + /* + * Enable VPEN + */ + GPSR(37) = GPIO_bit(37); + + /* insert callbacks */ + this->IO_ADDR_R = (unsigned long)nandaddr; + this->IO_ADDR_W = (unsigned long)nandaddr; + this->hwcontrol = h1910_hwcontrol; + this->dev_ready = NULL; /* unknown whether that was correct or not so we will just do it like this */ + /* 15 us command delay time */ + this->chip_delay = 50; + this->eccmode = NAND_ECC_SOFT; + this->options = NAND_NO_AUTOINCR; + + /* Scan to find existence of the device */ + if (nand_scan (h1910_nand_mtd, 1)) { + printk(KERN_NOTICE "No NAND device - returning -ENXIO\n"); + kfree (h1910_nand_mtd); + return -ENXIO; + } + + /* Allocate memory for internal data buffer */ + this->data_buf = kmalloc (sizeof(u_char) * (h1910_nand_mtd->oobblock + h1910_nand_mtd->oobsize), GFP_KERNEL); + if (!this->data_buf) { + printk("Unable to allocate NAND data buffer for h1910.\n"); + kfree (h1910_nand_mtd); + return -ENOMEM; + } + +#ifdef CONFIG_MTD_CMDLINE_PARTS + mtd_parts_nb = parse_cmdline_partitions(h1910_nand_mtd, &mtd_parts, + "h1910-nand"); + if (mtd_parts_nb > 0) + part_type = "command line"; + else + mtd_parts_nb = 0; +#endif + if (mtd_parts_nb == 0) + { + mtd_parts = partition_info; + mtd_parts_nb = NUM_PARTITIONS; + part_type = "static"; + } + + /* Register the partitions */ + printk(KERN_NOTICE "Using %s partition definition\n", part_type); + add_mtd_partitions(h1910_nand_mtd, mtd_parts, mtd_parts_nb); + + /* Return happy */ + return 0; +} +module_init(h1910_init); + +/* + * Clean up routine + */ +static void __exit h1910_cleanup (void) +{ + struct nand_chip *this = (struct nand_chip *) &h1910_nand_mtd[1]; + + /* Unregister the device */ + del_mtd_device (h1910_nand_mtd); + + /* Free internal data buffer */ + kfree (this->data_buf); + + /* Free the MTD device structure */ + kfree (h1910_nand_mtd); +} +module_exit(h1910_cleanup); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Joshua Wise "); +MODULE_DESCRIPTION("NAND flash driver for iPAQ h1910"); diff -NaurbB linux-2.6.9-pre1-bk18/drivers/mtd/nand/Kconfig linux-2.6.9_nbp/drivers/mtd/nand/Kconfig --- linux-2.6.9-pre1-bk18/drivers/mtd/nand/Kconfig 2004-08-14 07:37:38.000000000 +0200 +++ linux-2.6.9_nbp/drivers/mtd/nand/Kconfig 2004-09-14 12:25:06.000000000 +0200 @@ -1,5 +1,5 @@ # drivers/mtd/nand/Kconfig -# $Id: Kconfig,v 1.17 2004/08/10 14:24:07 dwmw2 Exp $ +# $Id: Kconfig,v 1.18 2004/08/23 13:57:47 joshua Exp $ menu "NAND Flash Device Drivers" depends on MTD!=n @@ -36,6 +36,12 @@ This enables the driver for the Cirrus Logic EBD7312 evaluation board to access the onboard NAND Flash. +config MTD_NAND_H1900 + tristate "iPAQ H1900 flash" + depends on ARM && MTD_NAND && ARCH_PXA && MTD_PARTITIONS + help + This enables the driver for the iPAQ h1900 flash. + config MTD_NAND_SPIA tristate "NAND Flash device on SPIA board" depends on ARM && ARCH_P720T && MTD_NAND @@ -79,6 +85,13 @@ depends on PPCHAMELEONEVB && MTD_NAND help This enables the NAND flash driver on the PPChameleon EVB Board. +config MTD_NAND_NETBOOKPRO + tristate "NAND Flash device on Psion NetbookPro" + depends on MACH_NETBOOKPRO && MTD_NAND + help + This enables the NAND flasg driver on the Psion NetbookPro + + config MTD_NAND_DISKONCHIP tristate "DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation) (EXPERIMENTAL)" diff -NaurbB linux-2.6.9-pre1-bk18/drivers/mtd/nand/Makefile linux-2.6.9_nbp/drivers/mtd/nand/Makefile --- linux-2.6.9-pre1-bk18/drivers/mtd/nand/Makefile 2004-08-14 07:36:12.000000000 +0200 +++ linux-2.6.9_nbp/drivers/mtd/nand/Makefile 2004-09-14 12:25:06.000000000 +0200 @@ -1,7 +1,7 @@ # # linux/drivers/nand/Makefile # -# $Id: Makefile.common,v 1.9 2004/07/12 16:07:31 dwmw2 Exp $ +# $Id: Makefile.common,v 1.10 2004/08/23 13:57:48 joshua Exp $ obj-$(CONFIG_MTD_NAND) += nand.o nand_ecc.o obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o @@ -15,5 +15,7 @@ obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o obj-$(CONFIG_MTD_NAND_PPCHAMELEONEVB) += ppchameleonevb.o obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o +obj-$(CONFIG_MTD_NAND_H1900) += h1910.o +obj-$(CONFIG_MTD_NAND_NETBOOKPRO) += netbookpro.o nand-objs = nand_base.o nand_bbt.o diff -NaurbB linux-2.6.9-pre1-bk18/drivers/mtd/nand/nand_base.c linux-2.6.9_nbp/drivers/mtd/nand/nand_base.c --- linux-2.6.9-pre1-bk18/drivers/mtd/nand/nand_base.c 2004-08-14 07:38:11.000000000 +0200 +++ linux-2.6.9_nbp/drivers/mtd/nand/nand_base.c 2004-09-13 12:43:27.000000000 +0200 @@ -37,7 +37,7 @@ * The AG-AND chips have nice features for speed improvement, * which are not supported yet. Read / program 4 pages in one go. * - * $Id: nand_base.c,v 1.115 2004/08/09 13:19:45 dwmw2 Exp $ + * $Id: nand_base.c,v 1.116 2004/08/30 18:00:45 gleixner Exp $ * * 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 @@ -762,10 +762,10 @@ if (this->dev_ready) { if (this->dev_ready(mtd)) break; - } + } else { if (this->read_byte(mtd) & NAND_STATUS_READY) break; - + } spin_unlock_bh (&this->chip_lock); yield (); spin_lock_bh (&this->chip_lock); diff -NaurbB linux-2.6.9-pre1-bk18/drivers/mtd/nand/netbookpro.c linux-2.6.9_nbp/drivers/mtd/nand/netbookpro.c --- linux-2.6.9-pre1-bk18/drivers/mtd/nand/netbookpro.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/drivers/mtd/nand/netbookpro.c 2004-10-11 13:58:57.000000000 +0200 @@ -0,0 +1,393 @@ +/* drivers/mtd/nand/netbookpro.c + * + * Written by Ben Dooks + * Based on 2.4 version by Mark Whittaker + * + * (c) 2004 Simtec Electronics + * + * NAND device driver for Psion NetbookPro + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +struct nbp_mtd { + void *io_base; + void *io_sync; + unsigned long tmp; + struct mtd_info *mtd; + struct mtd_info mtd_info; + struct nand_chip nand_chip; +}; + + +static struct mtd_partition partition_info[] = { + { name: "NetbookPro nand 1", + offset: 0x00400000, + size: 0x03800000 }, + { name: "NetbookPro nand 2", + offset: 0x03c00000, + size: 0x00400000 } +}; + +struct nbp_mtd *nbp_mtd = NULL; + +#define nbp_nand_getmymtd(x) nbp_mtd + +/* nbp_nand_dosync() + * + * needed due to bus-reordering within the PXA itself (see section on + * I/O ordering in PXA manual (section 2.3, p35) +*/ + +static void +nbp_nand_dosync(struct nbp_mtd *my_mtd) +{ + unsigned long tmp; + + /* this should generate the read, followed by something that + * depends on the read + */ + tmp = readl(my_mtd->io_sync); + + /* generate instruction dependant on the value of the read */ + __asm__ __volatile__("mov %1, %0\n" : "=r" (tmp) : "r" (tmp)); +} + +static void +nbp_nand_hwcontrol(struct mtd_info *mtd, int cmd) +{ + nbp_nand_dosync(nbp_nand_getmymtd(mtd)); + + switch (cmd) { + case NAND_CTL_SETCLE: + GPSR(21) = GPIO_bit(21); + break; + + case NAND_CTL_CLRCLE: + GPCR(21) = GPIO_bit(21); + break; + + case NAND_CTL_SETALE: + GPSR(22) = GPIO_bit(22); + break; + + case NAND_CTL_CLRALE: + GPCR(22) = GPIO_bit(22); + break; + + case NAND_CTL_SETNCE: + GPCR(62) = GPIO_bit(62); + break; + + case NAND_CTL_CLRNCE: + GPSR(62) = GPIO_bit(62); + break; + + case NAND_CTL_SETWP: + GPCR(61) = GPIO_bit(61); + break; + + case NAND_CTL_CLRWP: + GPSR(61) = GPIO_bit(61); + break; + + default: + printk(KERN_ERR "%s: unknown cmd %d\n", __FUNCTION__, cmd); + } + + nbp_nand_dosync(nbp_nand_getmymtd(mtd)); +} + + +static void +nbp_nand_writebyte(struct mtd_info *mtd, u_char byte) +{ + struct nbp_mtd *my_mtd = nbp_nand_getmymtd(mtd); + + writeb(byte, my_mtd->io_base); + nbp_nand_dosync(my_mtd); +} + +static void +nbp_nand_writeword(struct mtd_info *mtd, u16 word) +{ + struct nbp_mtd *my_mtd = nbp_nand_getmymtd(mtd); + + writew(word, my_mtd->io_base); + nbp_nand_dosync(my_mtd); +} + +/* not sure if we need to have sync on the reads, but to be + * safe for the moment, we'll put this in here +*/ + +static u_char +nbp_nand_readbyte(struct mtd_info *mtd) +{ + struct nbp_mtd *my_mtd = nbp_nand_getmymtd(mtd); + u_char res; + + res = readb(my_mtd->io_base); + nbp_nand_dosync(my_mtd); + + return res; +} + +static u16 +nbp_nand_readword(struct mtd_info *mtd) +{ + struct nbp_mtd *my_mtd = nbp_nand_getmymtd(mtd); + u16 res; + + res = readw(my_mtd->io_base); + nbp_nand_dosync(my_mtd); + + return res; +} + +#define is_aligned(b) (!((unsigned long)(b) & 1)) + +static void +nbp_nand_writebuf(struct mtd_info *mtd, const u_char *buf, int len) +{ + struct nbp_mtd *my_mtd = nbp_nand_getmymtd(mtd); + struct nand_chip *this = mtd->priv; + + nbp_nand_dosync(my_mtd); + if (is_aligned(buf)) { + writesw(this->IO_ADDR_W, buf, len>>1); + } else { + int i; + u16 *ptr = (u16 *)buf; + + for (i = 0; i < len; i+=2, ptr++) { + writew(*ptr, this->IO_ADDR_W); + } + } + nbp_nand_dosync(my_mtd); +} + +static void +nbp_nand_readbuf(struct mtd_info *mtd, u_char *buf, int len) +{ + struct nbp_mtd *my_mtd = nbp_nand_getmymtd(mtd); + struct nand_chip *this = mtd->priv; + + nbp_nand_dosync(my_mtd); + + if (is_aligned(buf)) { + readsw(this->IO_ADDR_R, buf, len>>1); + } else { + int i; + u16 *ptr = (u16 *)buf; + + for (i = 0; i < len; i+=2, ptr++) { + *ptr = readw(this->IO_ADDR_R); + } + } + + nbp_nand_dosync(my_mtd); +} + +static int +nbp_nand_verifybuf(struct mtd_info *mtd, const u_char *buf, int len) +{ + struct nbp_mtd *my_mtd = nbp_nand_getmymtd(mtd); + struct nand_chip *this = mtd->priv; + int i; + int err = 0; + u16 *p = (u16 *) buf; + u16 read; + len >>= 1; + + nbp_nand_dosync(my_mtd); + + for (i = 0; i < len; i++) { + read = readw(this->IO_ADDR_R); + if (read != p[i]) { + printk(KERN_DEBUG "%s: err at %d (read %04x vs %04x)\n", + __FUNCTION__, i, read, p[i]); + err = -EFAULT; + } + } + + nbp_nand_dosync(my_mtd); + return err; +} + + +static int +nbp_nand_devready(struct mtd_info *mtd) +{ + /* not sure wether we really need to sync here, but it shouldn't + cost us too much to check wether this is ok? */ + + nbp_nand_dosync( nbp_nand_getmymtd(mtd)); + + return (GPLR(20) & GPIO_bit(20)); +} + +static void nbp_nand_cleanup(void) +{ + if (nbp_mtd == NULL) + return; + + if (nbp_mtd->mtd != NULL) { + nand_release(nbp_mtd->mtd); + nbp_mtd->mtd = NULL; + } + + nbp_nand_hwcontrol(NULL, NAND_CTL_SETWP); + + iounmap(nbp_mtd->io_base); + iounmap(nbp_mtd->io_sync); + + kfree (nbp_mtd); +} + +static int netbookpro_nand_remove(struct device *dev) +{ + nbp_nand_cleanup(); + return 0; +} + +static int netbookpro_nand_probe(struct device *dev) +{ + struct nbp_mtd *my_mtd; + struct nand_chip *this; + size_t data_size; + + printk(KERN_INFO "NetbookPro NAND driver, (c) 2004 Simtec Electronics\n"); + + nbp_mtd = my_mtd = kmalloc(sizeof(*my_mtd), GFP_KERNEL); + if (my_mtd == NULL) { + printk(KERN_ERR "netbookpro: failed to create NAND MTD\n"); + return -ENOMEM; + } + + memset(my_mtd, 0, sizeof(*my_mtd)); + + this = &my_mtd->nand_chip; + + my_mtd->io_base = ioremap(0x04000000, 2); + my_mtd->io_sync = ioremap(0x08000000, 4); + + printk(KERN_INFO "netbookpro: MTD registers at %p, sync %p\n", + my_mtd->io_base, my_mtd->io_sync); + + this->IO_ADDR_R = (int)my_mtd->io_base; + this->IO_ADDR_W = (int)my_mtd->io_base; + this->eccmode = NAND_ECC_SOFT; + this->options |= (NAND_NO_AUTOINCR | NAND_BUSWIDTH_16); + this->chip_delay = 15; /* 15 us command delay time */ + + /* install our routines */ + + this->hwcontrol = nbp_nand_hwcontrol; + this->write_byte = nbp_nand_writebyte; + this->write_word = nbp_nand_writeword; + this->read_byte = nbp_nand_readbyte; + this->read_word = nbp_nand_readword; + this->verify_buf = nbp_nand_verifybuf; + this->read_buf = nbp_nand_readbuf; + this->write_buf = nbp_nand_writebuf; + this->dev_ready = nbp_nand_devready; + + nbp_nand_hwcontrol(NULL, NAND_CTL_CLRWP); + + /* set the ready/busy pin to input */ + pxa_gpio_mode(20 | GPIO_IN); + + /* set the mtd private data for the nand driver */ + my_mtd->mtd_info.priv = this; + + if (nand_scan (&my_mtd->mtd_info, 1)) { + printk(KERN_ERR "netbookpro: no nand chips found?\n"); + nbp_nand_cleanup(); + return -ENXIO; + } + + my_mtd->mtd = &my_mtd->mtd_info; + + data_size = my_mtd->mtd_info.oobblock + my_mtd->mtd_info.oobsize; + this->data_buf = kmalloc(data_size, GFP_KERNEL); + if (this->data_buf == NULL) { + printk(KERN_ERR "%s: cannot get %d bytes for data_buf \n", + __FUNCTION__, data_size); + nbp_nand_cleanup(); + return -ENOMEM; + } + + if (my_mtd->mtd_info.size <= 32*1024*1024) + partition_info[0].size -= 0x2000000; + + printk(KERN_INFO "Adding partition at %08x, size %08x\n", + partition_info[0].offset, partition_info[0].size); + + add_mtd_partitions(&my_mtd->mtd_info, partition_info, + ARRAY_SIZE(partition_info)); + + return 0; +} + +#ifdef CONFIG_PM +static int netbookpro_nand_suspend(struct device *dev, u32 state, u32 level) +{ + return 0; +} + +static int netbookpro_nand_resume(struct device *dev, u32 level) +{ + if (level == RESUME_ENABLE) { + nbp_nand_hwcontrol(NULL, NAND_CTL_CLRWP); + pxa_gpio_mode(20 | GPIO_IN); + } + + return 0; +} +#else +#define netbookpro_nand_suspend NULL +#define netbookpro_nand_resume NULL +#endif + +static struct device_driver netbookpro_nand_driver= { + .name = "netbookpro-nand", + .bus = &platform_bus_type, + .probe = netbookpro_nand_probe, + .remove = netbookpro_nand_remove, + .suspend = netbookpro_nand_suspend, + .resume = netbookpro_nand_resume, +}; + +void __exit nbp_exit(void) +{ + driver_unregister(&netbookpro_nand_driver); + nbp_nand_cleanup(); +} + +int __init nbp_nand_init(void) +{ + return driver_register(&netbookpro_nand_driver); +} + + +module_init(nbp_nand_init); +module_exit(nbp_nand_cleanup); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ben Dooks "); +MODULE_DESCRIPTION("NAND Driver for NetbookPro"); diff -NaurbB linux-2.6.9-pre1-bk18/drivers/mtd/nftlmount.c linux-2.6.9_nbp/drivers/mtd/nftlmount.c --- linux-2.6.9-pre1-bk18/drivers/mtd/nftlmount.c 2005-08-15 01:14:41.000000000 +0200 +++ linux-2.6.9_nbp/drivers/mtd/nftlmount.c 2004-09-13 12:43:26.000000000 +0200 @@ -269,8 +269,7 @@ static int check_free_sectors(struct NFTLrecord *nftl, unsigned int address, int len, int check_oob) { - int i; - size_t retlen; + int i, retlen; u8 buf[SECTORSIZE + nftl->mbd.mtd->oobsize]; for (i = 0; i < len; i += SECTORSIZE) { @@ -367,8 +366,7 @@ { unsigned int block, i, status; struct nftl_bci bci; - int sectors_per_block; - size_t retlen; + int sectors_per_block, retlen; sectors_per_block = nftl->EraseSize / SECTORSIZE; block = first_block; diff -NaurbB linux-2.6.9-pre1-bk18/drivers/mtd/ssfdc.c linux-2.6.9_nbp/drivers/mtd/ssfdc.c --- linux-2.6.9-pre1-bk18/drivers/mtd/ssfdc.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/drivers/mtd/ssfdc.c 2004-09-13 13:41:20.000000000 +0200 @@ -0,0 +1,1132 @@ +/* + * drivers/mtd/ssfdc.c + * + * Copyright (C) 2003 Simon Haynes (simon@baydel.con) + * Baydel Ltd + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. + * + * This module provides a translation layer, via mtd, for smart + * media card access. It essentially enables the possibility + * of using cards on a hardware which does not have a hardware translation + * layer and interchanging them with hardware that does ie: PC card readers + * + * I had to write this module for a specific task and in a short timeframe + * for this reason I have imposed some restricions to make the job easier. + * + * To build an compile the driver I added the following lines + * to mtd/Config.in + * + * dep_tristate ' SSFDC support' CONFIG_SSFDC $CONFIG_MTD + * + * to /mtd/Makefile + * + * obj-$(CONFIG_SSFDC) += ssfdc.o + * + * and compiled the kernel via the usual methods. + * + * I am sure that there are many problems I don't know about but here are + * some that I know of + * + * Currently the driver uses MAJOR number 44 which I think is FTL or NFTL + * I did this because I wanted a static number and I didn't know + * how to go about getting a new one. This needs addressing + * The dev nodes required are like standard. I only use minor 0 + * (/dev/ssfdca), and minor 1 (/dev/ssfdca1). + * You should be able to run fdisk on /dev/ssfdca and the first partition + * is /dev/ssfdca1. There is no working code in the module for changing the + * SMC and rebuilding the maps so the card should not be changed once the + * module is loaded. At present I only look for 1 partition. But this is a + * small commented hack. + * + * There is no support cards which do not have a 512 byte page size with 16 + * bytes of oob and an erase size of 16K. + * There are no checks for this at present. In addition the MTD reported size + * must be 16M or a multiple. + * + * Code to handle multiple partitions or multiple cards is incomplete + * Need to allocate data buffer and oob buffer on a per partition basis. + * As I am only concerned with one partition I will do this if I ever need to. + * The cached physical address variable also needs this attention. + * + * Recently I have started to work on media changes. Some of this is specific + * to my hardware and you will see references to pt_ssfdc_smc and smc_status. + * This code is incomplete and does not work. I have commented it for the moment + * but it should give an indication of what I think is required. Maybe there is + * something it mtd that can help + * + * 17th August 2004 MHB + * + * Following updating CVS I noticed some single bit data corruption. I believe + * that this was down to the fact that I was using mtd->read instead of mtd->read_ecc + * and that mtd->read was applying it's own error corretion from the wrong ecc bytes + * I have now corrected this. + * + * During this time I noticed that while in allocate new I only seem to look for blocks + * in 1 zone. So this limits the partition size to 16MB with all the other SMC size + * restrictions + + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +#if (LINUX_VERSION_CODE >= 0x20100) +#include +#endif +#if (LINUX_VERSION_CODE >= 0x20303) +#include +#endif + +#include + +#define SSFDC_FORMAT 1 + +#define PDEBUG(fmt, args...) + +#define BLK_INC_USE_COUNT MOD_INC_USE_COUNT +#define BLK_DEC_USE_COUNT MOD_DEC_USE_COUNT + +#if (LINUX_VERSION_CODE < 0x20320) +#define BLK_DEFAULT_QUEUE(n) blk_dev[n].request_fn +#define blk_init_queue(q, req) q = (req) +#define blk_cleanup_queue(q) q = NULL +#define request_arg_t void +#else +#define request_arg_t request_queue_t *q +#endif + +#define TRUE 1 +#define FALSE 0 + +#define SSFDC_MAJOR 44 + +#define MAJOR_NR SSFDC_MAJOR +#define DEVICE_NAME "ssfdc" +#define DEVICE_REQUEST do_ssfdc_request +#define DEVICE_ON(device) +#define DEVICE_OFF(device) + +#include + +#include "/home/simon/ebony/dbwhatu/dbwhatu/smccontrol.h" + + + +#define ZONE_SIZE (16 * 1024 * 1024) +#define SMC_BLOCK_SIZE (16 * 1024) +#define SECTOR_SIZE 512 +#define SECTORS_PER_ZONE (ZONE_SIZE / SECTOR_SIZE) +#define BLOCKS_PER_ZONE (ZONE_SIZE / SMC_BLOCK_SIZE) +#define SECTORS_PER_BLOCK (SMC_BLOCK_SIZE / SECTOR_SIZE) +#define OOB_SIZE 16 + + +#define MAX_DEVICES 4 +#define MAX_PARTITIONS 8 +#define PARTITION_BITS 3 +#define MAX_ZONES 8 + + +int ssfdc_major = SSFDC_MAJOR; +unsigned int ssfdc_cached = 0xFFFFFFFF; +static unsigned char ssfdc_scratch[16384]; +static unsigned char ssfdc_buffer[16]; +static unsigned char ssfdc_ffoob_buf[OOB_SIZE * SECTORS_PER_BLOCK]; +static unsigned char ssfdc_oob_buf[OOB_SIZE * SECTORS_PER_BLOCK]; + + +static struct nand_oobinfo ssfdc_ffoob_info = { + .useecc = 0, +}; + + +typedef struct minor_t { + atomic_t open; + int cached; + unsigned char * pt_data; + unsigned char * pt_oob; +} minor_t; + + + +typedef struct partition_t { + int type; + struct mtd_info *mtd; + int count; + unsigned int *zone; + unsigned int zoneCount; + minor_t minor[MAX_PARTITIONS]; + unsigned int last_written[MAX_ZONES]; +} partition_t; + +partition_t SMCParts[MAX_DEVICES]; + + +static unsigned char ssfdc_ecc[] = {14, 13, 15, 9, 8, 10}; + +static struct hd_struct ssfdc_hd[MAX_DEVICES * MAX_PARTITIONS]; +static int ssfdc_sizes[MAX_DEVICES * MAX_PARTITIONS]; +static int ssfdc_blocksizes[MAX_DEVICES * MAX_PARTITIONS]; +smc_control * pt_ssfdc_smc; + + +static struct gendisk ssfdc_gendisk = { + major: SSFDC_MAJOR, + major_name: "ssfdc", + minor_shift: PARTITION_BITS, + max_p: MAX_PARTITIONS, + part: ssfdc_hd, + sizes: ssfdc_sizes, +}; + + +static int ssfdc_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg); +static int ssfdc_open(struct inode *inode, struct file *file); +static int ssfdc_close(struct inode *inode, struct file *file); +static int ssfdc_write(partition_t *part, caddr_t buffer, u_long sector, u_long nblocks); +static int ssfdc_read(partition_t *part, caddr_t buffer, u_long sector, u_long nblocks); +static int ssfdc_physical(partition_t * pt_smcpart, int zone, int block); +static int ssfdc_erase(partition_t *pt_smcpart, unsigned int offset); +static int ssfdc_read_partitions(partition_t * pt_smcpart); +static void ssfdc_notify_add(struct mtd_info *mtd); +static void ssfdc_notify_remove(struct mtd_info *mtd); +static void ssfdc_tables(partition_t * pt_smcpart); +static int ssfdc_sector_blank(partition_t * pt_smcpart, int sc); +static int ssfdc_allocate_new(partition_t * pt_smcpart, int zone); +int ssfdc_parity(int number); +static void ssfdc_erase_callback(struct erase_info *erase); + + + +static DECLARE_WAIT_QUEUE_HEAD(ssfdc_wq); + + +static struct mtd_notifier ssfdc_notifier = { + add: ssfdc_notify_add, + remove: ssfdc_notify_remove, +}; + + + +static struct block_device_operations ssfdc_fops = { + open: ssfdc_open, + release: ssfdc_close, + ioctl: ssfdc_ioctl, +}; + +static struct semaphore ssfdc_semaphore; + +static void ssfdc_notify_add(struct mtd_info *mtd) { + + + + + if(mtd->index >= 1) return; // Hack to limit SSFDC to 1 partition + + if( ((mtd->size % ZONE_SIZE) != 0) && (mtd->size < (ZONE_SIZE * MAX_ZONES)) ){ + PDEBUG("ssfdc_notify_add : mtd partition %d is not modulus 16M, not SSFDC\n", mtd->index); + } + else { + memset((void *)&SMCParts[mtd->index].type, 0, sizeof(partition_t)); + SMCParts[mtd->index].mtd = mtd; + SMCParts[mtd->index].count = mtd->index; + SMCParts[mtd->index].type = 1; + SMCParts[mtd->index].zoneCount = mtd->size / ZONE_SIZE; + SMCParts[mtd->index].zone = kmalloc(SMCParts[mtd->index].zoneCount * 8192, GFP_KERNEL); + + + if(!SMCParts[mtd->index].zone) { + printk(KERN_NOTICE "ssfdc_notify_add : mtd partition %d, failed to allocate mapping table\n", mtd->index); + SMCParts[mtd->index].type = 0; + } + else { + memset((void *)SMCParts[mtd->index].zone, 0xFF, SMCParts[mtd->index].zoneCount * 8192); + } + + ssfdc_read_partitions((partition_t *)&SMCParts[mtd->index].type); + } + return; + +} +static int ssfdc_read_partitions(partition_t * pt_smcpart) { + + int whole, i, j, size; + +//=printk("ssfdc_read_partitions : start\n"); + + for(i=0; iminor[i].open) > 1)) { +//=printk("ssfdc_read_partitions : part %d busy\n", i); + + return -EBUSY; + } + + +//=printk("ssfdc_read_partitions : tables start\n"); + ssfdc_tables(pt_smcpart); +//=printk("ssfdc_read_partitions : tables end\n"); + + whole = pt_smcpart->count << PARTITION_BITS; + + + j = MAX_PARTITIONS - 1; + while (j-- > 0) { + if (ssfdc_hd[whole+j].nr_sects > 0) { + kdev_t rdev = MKDEV(SSFDC_MAJOR, whole+j); + invalidate_device(rdev, 1); + } + ssfdc_hd[whole+j].start_sect = 0; + ssfdc_hd[whole+j].nr_sects = 0; + } + + + size = (((pt_smcpart->mtd->size / 16384) * 1000) / 1024) * 32; + size /= (0x8 * 0x20); + size = size * (0x8 * 0x20); + +//=printk("ssfdc_read_partitions : register start\n"); + + register_disk(&ssfdc_gendisk, whole >> PARTITION_BITS, MAX_PARTITIONS, + &ssfdc_fops, size); + +//=printk("ssfdc_read_partitions : register end\n"); + + + return 0; +} + + +static void ssfdc_notify_remove(struct mtd_info *mtd) { +int i, j, whole; + + i=mtd->index; + whole = i << PARTITION_BITS; + if(SMCParts[i].mtd == mtd) { + if(SMCParts[i].zone)kfree(SMCParts[i].zone); + memset((void *)&SMCParts[i].type, 0, sizeof(partition_t)); + for (j = 0; j < MAX_PARTITIONS; j++) { + if (ssfdc_hd[whole+j].nr_sects > 0) { + ssfdc_hd[whole+j].start_sect = 0; + ssfdc_hd[whole+j].nr_sects=0; + } + } + return; + } + return; +} + + + +static int ssfdc_ioctl(struct inode *inode, struct file *file, + u_int cmd, u_long arg) { + + int minor = MINOR(inode->i_rdev); + int ret = -EINVAL; + partition_t * pt_smcpart = (partition_t *)&SMCParts[(minor & ~(MAX_PARTITIONS -1)) >> PARTITION_BITS].type; + struct hd_geometry geo; + int size; +/* + unsigned char smc_status; + + smc_status = in_8((void *)&pt_ssfdc_smc->smc_status); + if(!(smc_status & SMC_PRESENT)) { + printk("ssfdc : media not present\n"); + ret = 1; + goto ssfdc_ioctl_error; + } + + if(smc_status & SMC_CHANGED) { + out_8((void *)&pt_ssfdc_smc->smc_status, smc_status); + if(minor & ((1<< PARTITION_BITS) - 1)) return -ENOTTY; + ssfdc_read_partitions(pt_smcpart); + printk("ssfdc : media change\n"); + } +*/ + switch(cmd) { + + case HDIO_GETGEO: + memset(&geo, 0, sizeof(geo)); + size = (((pt_smcpart->mtd->size / 16384) * 1000) / 1024) * 32; + size /= (0x8 * 0x20); + geo.heads = 0x8; + geo.sectors = 0x20; + geo.cylinders = size; + geo.start = ssfdc_hd[minor].start_sect; +// printk(KERN_WARNING "ssfdc : HDIO_GETGEO heads %d, sectors %d, cylinders %d, start %lu\n", +// geo.heads, geo.sectors, geo.cylinders, geo.start); + copy_to_user((void *)arg, &geo, sizeof(geo)); + ret = 0; + break; + + case BLKGETSIZE64: + case BLKGETSIZE: + size = (((pt_smcpart->mtd->size / 16384) * 1000) / 1024) * 32; + //=printk(KERN_WARNING "ssfdc : BLKGETSIZE %d, minor %d\n", size, minor); + ret = copy_to_user((unsigned long *)arg, &size, sizeof(size)); + break; + case BLKSSZGET: + size = 512; + ret = copy_to_user((unsigned long *)arg, &size, sizeof(size)); + break; + break; + + case BLKRRPART: + if(minor & ((1<< PARTITION_BITS) - 1)) return -ENOTTY; + ssfdc_read_partitions(pt_smcpart); + ret=0; + break; + case BLKFLSBUF: + printk(KERN_WARNING "ssfdc : block ioctl 0x%x\n", cmd); + break; + + default: + printk(KERN_WARNING "ssfdc: unknown ioctl 0x%x\n", cmd); + } + +//ssfdc_ioctl_error: + return(ret); + +} +static int ssfdc_open(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + partition_t *pt_smcpart; + int index; + + if (minor >= MAX_MTD_DEVICES) + return -ENODEV; + + index = (minor & ~(MAX_PARTITIONS -1)) >> PARTITION_BITS; + + + if(SMCParts[index].type != SSFDC_FORMAT) + return -ENXIO; + + pt_smcpart = &SMCParts[index]; + + + if(!pt_smcpart->zone) + return -ENXIO; + + + BLK_INC_USE_COUNT; + + if (!get_mtd_device(pt_smcpart->mtd, -1)) { + BLK_DEC_USE_COUNT; + return -ENXIO; + } + + if ((file->f_mode & 2) && !(pt_smcpart->mtd->flags & MTD_CLEAR_BITS) ) { + put_mtd_device(pt_smcpart->mtd); + BLK_DEC_USE_COUNT; + return -EROFS; + } + + + atomic_inc(&pt_smcpart->minor[minor & ~(MAX_PARTITIONS -1)].open); + + PDEBUG("ssfdc_open : device %d\n", minor); + + return(0); +} + +static void ssfdc_tables(partition_t * pt_smcpart) { + + int * logical, * physical; + int offset = 0; + int zone, block; + int i, retlen; + int block_address, parity; + int h, l; + + for(zone=0; zonezoneCount; zone++) { + logical = pt_smcpart->zone + (2048 * zone); + memset((void *)logical, 0xFF, 1024 * sizeof(int)); + physical = pt_smcpart->zone + (2048 * zone) + 1024; + memset((void *)physical, 0xFF, 1024 * sizeof(int)); + + for(block=0; block < 1024; block++) { + offset = (zone * ZONE_SIZE) + (block * SMC_BLOCK_SIZE); + pt_smcpart->mtd->read_oob(pt_smcpart->mtd, offset, sizeof(ssfdc_buffer), &retlen, ssfdc_buffer); + if(retlen != sizeof(ssfdc_buffer)) { + printk(KERN_WARNING "ssfdc_tables : failed to read OOB\n"); + pt_smcpart->type = 0; + return; + } + + l = (ssfdc_buffer[7] & 0xFF); + h = (ssfdc_buffer[6] & 0xFF); + block_address = l + (h << 8L); + + if((block_address & ~0x7FF) != 0x1000) { + continue; + } + + parity = block_address & 0x01; + + block_address &= 0x7FF; + block_address >>= 1; + + + if(ssfdc_parity(block_address) != parity) { + printk(KERN_WARNING "ssfdc_tables : parity error offset 0x%x, block 0x%x, parity 0x%x\nOOB : " + , offset, block_address, parity); + for(i=0; i<16; i++) { + printk("0x%02x ", (unsigned char)ssfdc_buffer[i]); + } + printk("\n"); + pt_smcpart->type = 0; + return; + } + + + /* Ok we have a valid block number so insert it */ + *(logical + block_address) = (offset/SMC_BLOCK_SIZE); + PDEBUG("ssfdc_tables : logical 0x%x + 0x%x = 0x%x\n", + (unsigned int)logical, block_address, (offset/SMC_BLOCK_SIZE)); + *(physical + block) = block_address; + PDEBUG("ssfdc_tables : physical 0x%x + 0x%x = 0x%x\n", (unsigned int)physical, block, block_address); + + + } + } + return; +} +int ssfdc_parity(int number) { + int i; + int parity = 1; // the 0x1000 bit + + for(i=0; i<10; i++) { + parity += ((number >> i) & 1); + } + PDEBUG("ssfdc_parity : number 0x%x, parity 0x%x\n", number, parity); + return(parity % 2); +} +static int ssfdc_physical(partition_t * pt_smcpart, int zone, int block) { + + unsigned int * logical; + + logical = pt_smcpart->zone + (zone * 2048); + + logical += block; + + if(*logical == 0xFFFFFFFF) { + PDEBUG("ssfdc_physical : physical for zone %d, block %d invalid\n", zone, block); + return(-1); + } + + PDEBUG("ssfdc_physical : physical for zone %d, block %d, 0x%x\n", zone, block, (*logical * SMC_BLOCK_SIZE)); + return(*logical * SMC_BLOCK_SIZE); +} + +static int ssfdc_close(struct inode *inode, struct file *file) +{ + int minor = MINOR(inode->i_rdev); + partition_t *pt_smcpart; + int index = (minor & ~(MAX_PARTITIONS -1)) >> PARTITION_BITS; + + if (minor >= MAX_MTD_DEVICES) + return -ENODEV; + + if(SMCParts[index].type != SSFDC_FORMAT) + return -ENXIO; + + pt_smcpart = &SMCParts[index]; + atomic_dec(&pt_smcpart->minor[minor & ~(MAX_PARTITIONS -1)].open); + put_mtd_device(pt_smcpart->mtd); + BLK_DEC_USE_COUNT; + + return(0); +} + + +static void do_ssfdc_request(request_arg_t) +{ + int ret, minor; + partition_t *pt_smcpart; + int index; + do { + + INIT_REQUEST; + + + + minor = MINOR(CURRENT->rq_dev); + index = (minor & ~(MAX_PARTITIONS -1)) >> PARTITION_BITS; + + pt_smcpart = &SMCParts[index]; + if (pt_smcpart->type == SSFDC_FORMAT) { + ret = 0; + switch (CURRENT->cmd) { + case READ: + ret = ssfdc_read(pt_smcpart, CURRENT->buffer, + CURRENT->sector + ssfdc_hd[minor].start_sect, + CURRENT->current_nr_sectors); + break; + + case WRITE: + ret = ssfdc_write(pt_smcpart, CURRENT->buffer, + CURRENT->sector + ssfdc_hd[minor].start_sect, + CURRENT->current_nr_sectors); + break; + + default: + panic("do_ssfdc_request : unknown block command!\n"); + } + + } else { + ret = 1; + PDEBUG("not ssfdc partition type\n"); + } + + if (!ret) { + CURRENT->sector += CURRENT->current_nr_sectors; + } + + end_request((ret == 0) ? 1 : 0); + } while (1); +} + +static int ssfdc_write(partition_t *pt_smcpart, caddr_t buffer, + u_long sector, u_long nblocks) +{ + int zone, block, offset; + int sectors_written = 0; + int physical; + int * pt_logical; + int * pt_physical; + int new = -1; + int size; + int retlen; + int i; + int sc; + int ptr_done = 0; + unsigned char * ptr = (unsigned char *)buffer; + unsigned char ecc_code[6], ecc_calc[6]; + int do_erase; +// unsigned char smc_status; + + + + offset = (sector % SECTORS_PER_ZONE) % SECTORS_PER_BLOCK ; + + PDEBUG("write device %d, sector %d, count %d\n", + pt_smcpart->count, sector, nblocks); +/* + smc_status = in_8((void *)&pt_ssfdc_smc->smc_status); + if(!(smc_status & SMC_PRESENT)) { + printk("ssfdc : media not present\n"); + return -ENXIO; + } + + if(smc_status & SMC_CHANGED) { + out_8((void *)&pt_ssfdc_smc->smc_status, smc_status); + ssfdc_read_partitions(pt_smcpart); + printk("ssfdc : media change\n"); + } +*/ + while(sectors_written < nblocks) { + + new = -1; + do_erase = FALSE; + + zone = (sector + sectors_written) / SECTORS_PER_ZONE; + block = ((sector + sectors_written) % SECTORS_PER_ZONE) / SECTORS_PER_BLOCK ; + offset = ((sector + sectors_written) % SECTORS_PER_ZONE) % SECTORS_PER_BLOCK ; + + pt_logical = pt_smcpart->zone + (zone * 2048); + pt_physical = pt_smcpart->zone + (zone * 2048) + 1024; + + size = ((SECTORS_PER_BLOCK - offset) < (nblocks - sectors_written)) ? + (SECTORS_PER_BLOCK - offset) : (nblocks - sectors_written); + size *= SECTOR_SIZE; + + PDEBUG("write device %d, sector %d, count %d, zone %d, block %d, offset %d, done %d, size %d, address 0x%x\n", + pt_smcpart->count, sector, nblocks, zone, block, offset, sectors_written, size, (unsigned int)ptr); + + physical = ssfdc_physical(pt_smcpart, zone, block); + + + if(physical >= 0) { + if(ssfdc_cached != physical) { + pt_smcpart->mtd->read_ecc(pt_smcpart->mtd, physical, SMC_BLOCK_SIZE, &retlen, ssfdc_scratch, + ssfdc_oob_buf, &ssfdc_ffoob_info); + if(retlen != SMC_BLOCK_SIZE) { + printk(KERN_WARNING "ssfdc_write : failed to read physical\n"); + return -ENXIO; + } + + for(sc=0; scmtd->read_oob(pt_smcpart->mtd, physical + (sc * SECTOR_SIZE), sizeof(ssfdc_buffer), &retlen, ssfdc_buffer); + if(retlen != sizeof(ssfdc_buffer)) { + printk(KERN_WARNING "ssfdc_write : failed to read physical oob\n"); + return -ENXIO; + } + + nand_calculate_ecc (pt_smcpart->mtd, &ssfdc_scratch[sc * SECTOR_SIZE], &ecc_calc[0]); + nand_calculate_ecc (pt_smcpart->mtd, &ssfdc_scratch[(sc * SECTOR_SIZE) + 256], &ecc_calc[3]); + for(i=0; i<6; i++) ecc_code[i] = ssfdc_buffer[ssfdc_ecc[i]]; + nand_correct_data(pt_smcpart->mtd, &ssfdc_scratch[sc * SECTOR_SIZE], &ecc_code[0], &ecc_calc[0]); + nand_correct_data(pt_smcpart->mtd, &ssfdc_scratch[(sc * SECTOR_SIZE) + 256], &ecc_code[3], &ecc_calc[3]); + } + + } + + for(sc=0; sc sc) { + PDEBUG("offset %d, sector %d\n", offset, sc); + continue; + } + pt_smcpart->mtd->read_oob(pt_smcpart->mtd, physical + (sc * SECTOR_SIZE), sizeof(ssfdc_buffer), &retlen, ssfdc_buffer); + if(retlen != sizeof(ssfdc_buffer)) { + printk(KERN_WARNING "ssfdc_write : failed to read physical oob\n"); + return -ENXIO; + } + + nand_calculate_ecc (pt_smcpart->mtd, &ssfdc_scratch[sc * SECTOR_SIZE], &ecc_calc[0]); + nand_calculate_ecc (pt_smcpart->mtd, &ssfdc_scratch[(sc * SECTOR_SIZE) + 256], &ecc_calc[3]); + for(i=0; i<6; i++) ecc_code[i] = ssfdc_buffer[ssfdc_ecc[i]]; + nand_correct_data(pt_smcpart->mtd, &ssfdc_scratch[sc * SECTOR_SIZE], &ecc_code[0], &ecc_calc[0]); + nand_correct_data(pt_smcpart->mtd, &ssfdc_scratch[(sc * SECTOR_SIZE) + 256], &ecc_code[3], &ecc_calc[3]); + + /* find out if the block is being used */ + + + if(ssfdc_sector_blank(pt_smcpart, sc)) { + PDEBUG("ssfdc_write : zone %d, block %d, sector %d, lbn %d, blank, physical 0x%x\n", + zone, block, sc, sector, physical); + memcpy(&ssfdc_scratch[(sc * SECTOR_SIZE)], ptr+ptr_done, SECTOR_SIZE); + nand_calculate_ecc (pt_smcpart->mtd, (ptr + ptr_done), &ecc_calc[0]); + nand_calculate_ecc (pt_smcpart->mtd, (ptr + ptr_done + 256), &ecc_calc[3]); + for(i=0; i<6; i++) ssfdc_buffer[ssfdc_ecc[i]] = ecc_calc[i]; + i = (block << 1) | 0x1000; + i |= ssfdc_parity(block); + ssfdc_buffer[7] = ssfdc_buffer[12] = i & 0xFF; + ssfdc_buffer[6] = ssfdc_buffer[11] = (i & 0xFF00) >> 0x08; + + pt_smcpart->mtd->write_ecc(pt_smcpart->mtd, physical + (sc * SECTOR_SIZE), SECTOR_SIZE, &retlen, + ptr + ptr_done, ssfdc_buffer, &ssfdc_ffoob_info); + if(retlen != SECTOR_SIZE) { + printk(KERN_WARNING "ssfdc_write : failed to write physical 0x%x, sector 0x%x, blank, retlen %d\n" + , physical, sc, retlen); + return -ENXIO; + } + + ptr_done += SECTOR_SIZE; + if(ptr_done >= size) break; + } + else { + new = ssfdc_allocate_new(pt_smcpart, zone); + /* erase the old block */ + *(pt_physical + ((physical % ZONE_SIZE) / SMC_BLOCK_SIZE)) = 0xFFFFFFFF; + + PDEBUG("ssfdc_write : physical 0x%x + 0x%x = 0x%x\n", + (unsigned int)pt_physical, ((physical % ZONE_SIZE) / SMC_BLOCK_SIZE), 0xFFFFFFFF); + do_erase = TRUE; + PDEBUG("ssfdc_write : zone %d, block %d, sector %d, lbn %d, written, physical 0x%x, new 0x%x\n", + zone, block, sc, sector, physical, new); + break; + } + } + } + else { + ssfdc_cached = 0xFFFFFFFF; + memset(ssfdc_scratch, 0xFF, sizeof(ssfdc_scratch)); + new = ssfdc_allocate_new(pt_smcpart, zone); + PDEBUG("ssfdc_write : zone %d, block %d, lbn %d, physical 0x%x, unallocated, new 0x%x\n", + zone, block, sector, physical, new); + } + + + + if(new != -1) { + + + memcpy(&ssfdc_scratch[(offset * SECTOR_SIZE)], ptr, size); + PDEBUG("ssfdc_write : new 0x%x, offset 0x%x, size 0x%x, block 0x%x\n", new, offset, size, block); + for(sc=0; scmtd, &ssfdc_scratch[sc * SECTOR_SIZE], &ecc_calc[0]); + nand_calculate_ecc (pt_smcpart->mtd, &ssfdc_scratch[(sc * SECTOR_SIZE) + 256], &ecc_calc[3]); + for(i=0; i<6; i++) ssfdc_buffer[ssfdc_ecc[i]] = ecc_calc[i]; + i = (block << 1) | 0x1000; + i |= ssfdc_parity(block); + ssfdc_buffer[7] = ssfdc_buffer[12] = i & 0xFF; + ssfdc_buffer[6] = ssfdc_buffer[11] = (i & 0xFF00) >> 0x08; + memcpy(&ssfdc_oob_buf[sc * OOB_SIZE], ssfdc_buffer, OOB_SIZE); + } + + + pt_smcpart->mtd->write_ecc(pt_smcpart->mtd, new, SMC_BLOCK_SIZE, &retlen, ssfdc_scratch, + ssfdc_oob_buf, &ssfdc_ffoob_info); + if(retlen != SMC_BLOCK_SIZE) { + printk(KERN_WARNING "ssfdc_write : failed to write block, physical 0x%x, returned 0x%x\n", new, retlen); + return -ENXIO; + } + /* change the mapping table to reflect the new block placement */ + + *(pt_logical + block) = (new % ZONE_SIZE) / SMC_BLOCK_SIZE; + PDEBUG("ssfdc_write : logical 0x%x + 0x%x = 0x%x\n", + (unsigned int)pt_logical, block, (new % ZONE_SIZE) / SMC_BLOCK_SIZE); + + *(pt_physical + ((new % ZONE_SIZE) / SMC_BLOCK_SIZE)) = block; + PDEBUG("ssfdc_write : physical 0x%x + 0x%x = 0x%x\n", + (unsigned int)pt_physical, ((new % ZONE_SIZE) / SMC_BLOCK_SIZE), block); + + + ssfdc_cached = new; + } + + + ptr += size; + ptr_done = 0; + sectors_written += (size / SECTOR_SIZE); + if(do_erase) ssfdc_erase(pt_smcpart, physical); + + } + + + + + return(0); +} +static int ssfdc_sector_blank(partition_t * pt_smcpart, int sc) { +int b; + + for(b=0; blast_written[zone] + 1; + int * pt_physical; + int physical; + int block; + int retlen; + unsigned char oob[16]; + + + if(new >= BLOCKS_PER_ZONE) new = 0; + + + while (new != pt_smcpart->last_written[zone]) { + block = new % BLOCKS_PER_ZONE; + pt_physical = pt_smcpart->zone + (zone * 2048) + 1024 + block; + physical = (zone * ZONE_SIZE) + (block * SMC_BLOCK_SIZE); + + PDEBUG("ssfdc_allocate_new : zone %d, block %d, address 0x%08x, data 0x%08x\n", + zone, block, (unsigned int)pt_physical, *pt_physical); + if(*pt_physical == 0xFFFFFFFF) { + PDEBUG("ssfdc_allocate_new : physical 0x%x = 0x%x\n", (unsigned int)pt_physical, *pt_physical); + memset(oob, 0, OOB_SIZE); + pt_smcpart->mtd->read_oob(pt_smcpart->mtd, physical, OOB_SIZE, &retlen, oob); + if((oob[5] == 0xFF) && (retlen == OOB_SIZE)) { // If not a bad block + pt_smcpart->last_written[zone] = new; + return((new * SMC_BLOCK_SIZE) + (zone * ZONE_SIZE)); + } + else { + PDEBUG("ssfdc_allocate_new : new 0x%x, physical 0x%x, block status 0x%x, oob length 0x%x\n", new, physical, oob[5], retlen); + } + } + new++; + if(new >= BLOCKS_PER_ZONE) new = 0; + } + + panic("ssfdc_allocate_new : cant find free block\n"); + +} + + + +static int ssfdc_read(partition_t *pt_smcpart, caddr_t buffer, + u_long sector, u_long nblocks) +{ + int zone, block, offset; + int sectors_read = 0; + int physical; + int size; + int retlen; + int i; + int sc; + unsigned char * ptr = (unsigned char *)buffer; + unsigned char ecc_code[6], ecc_calc[6]; +/* + unsigned char smc_status; + + smc_status = in_8((void *)&pt_ssfdc_smc->smc_status); + if(!(smc_status & SMC_PRESENT)) { + printk("ssfdc : media not present\n"); + return -ENXIO; + } + + + + if(smc_status & SMC_CHANGED) { + out_8((void *)&pt_ssfdc_smc->smc_status, smc_status); + ssfdc_read_partitions(pt_smcpart); + printk("ssfdc : media change\n"); + } +*/ + while(sectors_read < nblocks) { + + zone = (sector + sectors_read) / SECTORS_PER_ZONE; + block = ((sector + sectors_read) % SECTORS_PER_ZONE) / SECTORS_PER_BLOCK ; + offset = ((sector + sectors_read) % SECTORS_PER_ZONE) % SECTORS_PER_BLOCK ; + + + if(offset) { + size = ((SECTORS_PER_BLOCK - offset) < (nblocks - sectors_read)) ? + (SECTORS_PER_BLOCK - offset) : (nblocks - sectors_read); + } + else { + size = (SECTORS_PER_BLOCK < (nblocks - sectors_read)) ? SECTORS_PER_BLOCK : nblocks - sectors_read; + } + size *= SECTOR_SIZE; + + PDEBUG("ssfdc_read : device %d, sector %d, count %d, zone %d, block %d, offset %d, done %d, size %d, address 0x%x\n", + pt_smcpart->count, sector, nblocks, zone, block, offset, sectors_read, size, (unsigned int)ptr); + + + physical = ssfdc_physical(pt_smcpart, zone, block); + if(physical >= 0) { + if(ssfdc_cached != physical) { + pt_smcpart->mtd->read_ecc(pt_smcpart->mtd, physical, SMC_BLOCK_SIZE, &retlen, ssfdc_scratch, + ssfdc_oob_buf, &ssfdc_ffoob_info); + if(retlen != SMC_BLOCK_SIZE) { + printk(KERN_WARNING "ssfdc_read : failed to read physical\n"); + return -ENXIO; + } + for(sc=0; scmtd->read_oob(pt_smcpart->mtd, physical + (sc * SECTOR_SIZE), sizeof(ssfdc_buffer), &retlen, ssfdc_buffer); + if(retlen != sizeof(ssfdc_buffer)) { + printk(KERN_WARNING "ssfdc_read : failed to read physical oob\n"); + return -ENXIO; + } + nand_calculate_ecc (pt_smcpart->mtd, &ssfdc_scratch[sc * SECTOR_SIZE], &ecc_calc[0]); + nand_calculate_ecc (pt_smcpart->mtd, &ssfdc_scratch[(sc * SECTOR_SIZE) + 256], &ecc_calc[3]); + for(i=0; i<3; i++) ecc_code[i] = ssfdc_buffer[ssfdc_ecc[i]]; + for(i=3; i<6; i++) ecc_code[i] = ssfdc_buffer[ssfdc_ecc[i]]; + nand_correct_data(pt_smcpart->mtd, &ssfdc_scratch[sc * SECTOR_SIZE], &ecc_code[0], &ecc_calc[0]); + nand_correct_data(pt_smcpart->mtd, &ssfdc_scratch[(sc * SECTOR_SIZE) + 256], &ecc_code[3], &ecc_calc[3]); + } + + /* Get the ecc bytes and check that they are ok */ + + + } + ssfdc_cached = physical; + + + } + else { + memset(ssfdc_scratch, 0xFF, sizeof(ssfdc_scratch)); + ssfdc_cached = 0xFFFFFFFF; + } + + + memcpy(ptr, &ssfdc_scratch[(offset * SECTOR_SIZE)], size); + ptr += size; + sectors_read += (size / SECTOR_SIZE); + } + + + + return(0); +} + +static void ssfdc_erase_callback(struct erase_info *erase) { + + PDEBUG("ssfdc_erase_callback : wake erase\n"); + up(&ssfdc_semaphore); + PDEBUG("ssfdc_erase_callback : woken erase\n"); +} + +static int ssfdc_erase(partition_t *pt_smcpart, unsigned int offset) +{ + int ret = 0; + struct erase_info *erase; + unsigned char * junk; + unsigned char * oob; + int retlen; + int b, sc; + + + PDEBUG("ssfdc_erase : offset 0x%08x\n", offset); + + erase=kmalloc(sizeof(struct erase_info), GFP_KERNEL); + junk=kmalloc(pt_smcpart->mtd->erasesize + 16, GFP_KERNEL); + oob = junk + pt_smcpart->mtd->erasesize; + + if (!erase) + return -ENOMEM; + if (!junk) + return -ENOMEM; + + erase->addr = offset; + erase->len = pt_smcpart->mtd->erasesize; + erase->callback = ssfdc_erase_callback; + ret = pt_smcpart->mtd->erase(pt_smcpart->mtd, erase); + if(ret) { + printk(KERN_WARNING "ssfdc_erase : failed status 0x%x\n", ret); + goto end; + + } + + down(&ssfdc_semaphore); + + pt_smcpart->mtd->read_ecc(pt_smcpart->mtd, offset, SMC_BLOCK_SIZE, &retlen, junk, + ssfdc_oob_buf, &ssfdc_ffoob_info); + if(retlen != SMC_BLOCK_SIZE) { + printk(KERN_WARNING "ssfdc_erase : offset 0x%x, read returned length %d\n", offset, retlen); + goto end; + } + + + for(sc=0; sc < SECTORS_PER_BLOCK; sc++) { + for(b=0; bmtd->read_oob(pt_smcpart->mtd, offset + (sc * SECTOR_SIZE), OOB_SIZE, &retlen, oob); + if(retlen != OOB_SIZE) { + printk(KERN_WARNING "ssfdc_erase : offset 0x%x, read oob returned length %d\n", offset, retlen); + goto end; + } + for(b=0; bsmc_status); +*/ + memset(ssfdc_ffoob_buf, 0xFF, sizeof(ssfdc_ffoob_buf)); + + for (i = 0; i < MAX_DEVICES*MAX_PARTITIONS; i++) { + ssfdc_hd[i].nr_sects = 0; + ssfdc_hd[i].start_sect = 0; + ssfdc_blocksizes[i] = 4096; + } + blksize_size[SSFDC_MAJOR] = ssfdc_blocksizes; + ssfdc_gendisk.major = SSFDC_MAJOR; + + + memset(ssfdc_scratch, 0xFF, sizeof(ssfdc_scratch)); + + result = register_blkdev(ssfdc_major, "ssfdc", &ssfdc_fops); + if(result != 0) { + printk(KERN_WARNING "ssfdc : failed to get a major number\n"); + return(result); + } +// if(ssfdc_major == 0) ssfdc_major = result; + + blk_init_queue(BLK_DEFAULT_QUEUE(ssfdc_major), &do_ssfdc_request); + + add_gendisk(&ssfdc_gendisk); + + + + register_mtd_user(&ssfdc_notifier); + + + init_MUTEX_LOCKED(&ssfdc_semaphore); + + + + return 0; +} + +static void __exit cleanup_ssfdc(void) +{ + int i; + + for(i=0; i"); +MODULE_DESCRIPTION("SSFDC translation layer support for MTD"); + + + + diff -NaurbB linux-2.6.9-pre1-bk18/drivers/net/irda/Kconfig linux-2.6.9_nbp/drivers/net/irda/Kconfig --- linux-2.6.9-pre1-bk18/drivers/net/irda/Kconfig 2004-08-14 07:37:37.000000000 +0200 +++ linux-2.6.9_nbp/drivers/net/irda/Kconfig 2004-10-14 15:57:25.000000000 +0200 @@ -382,6 +382,22 @@ config SA1100_FIR tristate "SA1100 Internal IR" depends on ARCH_SA1100 && IRDA + help + Say Y here if you want to build support for the internal FIR + controller on the SA1100 embedded microprocessor + + To compile it as a module, choose M here: the module will be called + sa1100-ir. + +config PXA_FIR + tristate "PXA Internal IR" + depends on ARCH_PXA && IRDA + help + Say Y here if you want to build support for the internal FIR + controller on the PXA embedded microprocessor range + + To compile it as a module, choose M here: the module will be called + pxa-ir. config VIA_FIR tristate "VIA VT8231/VT1211 SIR/MIR/FIR" diff -NaurbB linux-2.6.9-pre1-bk18/drivers/net/irda/Makefile linux-2.6.9_nbp/drivers/net/irda/Makefile --- linux-2.6.9-pre1-bk18/drivers/net/irda/Makefile 2004-08-14 07:36:57.000000000 +0200 +++ linux-2.6.9_nbp/drivers/net/irda/Makefile 2004-10-14 15:57:25.000000000 +0200 @@ -13,6 +13,7 @@ obj-$(CONFIG_NSC_FIR) += nsc-ircc.o obj-$(CONFIG_WINBOND_FIR) += w83977af_ir.o obj-$(CONFIG_SA1100_FIR) += sa1100_ir.o +obj-$(CONFIG_PXA_FIR) += pxa_ir.o obj-$(CONFIG_TOSHIBA_FIR) += donauboe.o obj-$(CONFIG_SMC_IRCC_FIR) += smsc-ircc2.o obj-$(CONFIG_ALI_FIR) += ali-ircc.o diff -NaurbB linux-2.6.9-pre1-bk18/drivers/net/irda/pxa_ir.c linux-2.6.9_nbp/drivers/net/irda/pxa_ir.c --- linux-2.6.9-pre1-bk18/drivers/net/irda/pxa_ir.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/drivers/net/irda/pxa_ir.c 2004-10-18 13:00:09.000000000 +0200 @@ -0,0 +1,1445 @@ +/* + * linux/drivers/net/irda/pxa_ir.c + * + * Author: + * Alexey Lugovskoy RTSoft. + * lugovskoy@rtsoft.msk.ru + * + * Dmitrij Frasenyak RTSoft. + * sed@mipt.sw.ru + * + * 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. + * + * Infra-red SIR and FIR driver for the PXA 210/250 embedded microprocessors + * Based on linux/drivers/net/irda/sa1100_ir.c + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +/* + * Our netdevice. There is only ever one of these. + */ + +static int max_rate = 4000000; + +struct pxa_irda { + unsigned char open; + unsigned char isr; /* configured value of STISR */ + + int speed; + int newspeed; + + struct sk_buff *txskb; + struct sk_buff *rxskb; + + /* => FIR */ + unsigned long fir_irq; + int txdma_ch; + int rxdma_ch; + dma_addr_t txbuf_dma; + dma_addr_t rxbuf_dma; + void *txbuf_dma_virt; + void *rxbuf_dma_virt; + /* <= FIR */ + + struct net_device_stats stats; + struct device *dev; + struct irlap_cb *irlap; + struct qos_info qos; + + /* => SIR */ + iobuff_t tx_buff; + iobuff_t rx_buff; + /* <= SIR */ +}; + +#define IS_FIR(si) ((si)->speed >= 4000000) + +#define HPSIR_MAX_RXLEN 2050 +#define HPSIR_MAX_TXLEN 2050 +#define TXBUFF_MAX_SIZE HPSIR_MAX_TXLEN + +/* + * If you want to disable debug information + * please uncomment line bellow + */ + +#define PXA_FIR_DUMP_ENABLE +#undef PXA_FIR_DUMP_ENABLE + +#define PXA_FIR_DEBUG_ENABLE +#undef PXA_FIR_DEBUG_ENABLE + +#define PXA_FIR_IRQ_DEBUG_ENABLE +#undef PXA_FIR_IRQ_DEBUG_ENABLE + +#ifdef PXA_FIR_DEBUG_ENABLE +#define __ECHO_IN printk(KERN_ERR "%s: enter\n",__FUNCTION__); +#define __ECHO_OUT printk(KERN_ERR "%s: exit\n",__FUNCTION__); +#define DBG(args...) printk(KERN_ERR __FUNCTION__"():"args); +#else +#define __ECHO_IN +#define __ECHO_OUT +#define DBG(args...) +#endif + +#ifdef PXA_FIR_IRQ_DEBUG_ENABLE +#define DBG_IRQ(args...) printk(KERN_ERR __FUNCTION__"():"args); +#else +#define DBG_IRQ(args...) +#endif + +static int pxa250_irda_set_speed(struct net_device *dev, int speed); +static void pxa250_start_rx_dma(struct net_device *dev); + +/************************************************************************** + * Misc FIR/SIR functions * + **************************************************************************/ +/* + * Allocate the receive buffer, unless it is already allocated. + */ +static int pxa250_irda_rx_alloc(struct pxa_irda *si) +{ + __ECHO_IN; + + if (si->rxskb) + return 0; + + si->rxskb = alloc_skb(HPSIR_MAX_RXLEN + 1, GFP_ATOMIC); + + if (!si->rxskb) { + printk(KERN_ERR "pxa250_ir: out of memory for RX SKB\n"); + return -ENOMEM; + } + + /* + * Align any IP headers that may be contained + * within the frame. + */ + skb_reserve(si->rxskb, 1); + + __ECHO_OUT; + + return 0; +} + +/* + * Transceiver state support + */ +static inline void pxa2xx_irda_transceiver_init(struct net_device *dev) +{ + if (machine_is_netbookpro()) { + /* + * Set the direction bits + */ + GPDR(58) |= GPIO_bit(58); + GPDR(59) |= GPIO_bit(59); + } +} + +static inline void pxa2xx_irda_transceiver_disable(struct net_device *dev) +{ + if (machine_is_netbookpro()) { + GPSR(58) = GPIO_bit(58); + } +} + +static inline void pxa2xx_irda_transceiver_enable(struct net_device *dev) +{ + if (machine_is_netbookpro()) { + GPCR(58) = GPIO_bit(58); + } +} + +static inline void pxa2xx_irda_transceiver_mode(struct net_device *dev, int fir) +{ + if (machine_is_lubbock()) { + if (fir) + LUB_MISC_WR |= 1 << 4; + else + LUB_MISC_WR &= ~(1 << 4); + } + if (machine_is_netbookpro()) { + /* + * Set the tranceiver mode + */ + if (fir) + GPSR(59) = GPIO_bit(59); + else + GPCR(59) = GPIO_bit(59); + } +} + +/************************************************************************** + * FIR * + **************************************************************************/ + +static inline void pxa250_dma_stop(int ch) +{ + __ECHO_IN; + + DCSR(ch) &= ~DCSR_RUN; + + __ECHO_OUT; +} + +static void pxa250_ficp_rx_start(void) +{ + ICCR0 = 0; + ICCR2 = 1 << 2 | 0 << 3; + ICCR0 = ICCR0_ITR; + ICCR0 |= ICCR0_RIE | ICCR0_RXE; +} + +/* + * Change Alternative Function encoding + * Enable ICP unit + * Disabe STUART unit + * Enable IRQ unit clock; + * Configure direction of GPIO used by ICP + */ +static void pxa250_do_fir_GPIO_config(struct net_device *dev) +{ + /* + * Modify GPIO 46 and 47 Alternate Function + */ + __ECHO_IN; + + /* Switch AF */ + pxa_gpio_mode(GPIO46_ICPRXD_MD); + pxa_gpio_mode(GPIO47_ICPTXD_MD); + + pxa2xx_irda_transceiver_mode(dev, 1); + + /* init clock */ + pxa_set_cken(CKEN13_FICP, 1); + + __ECHO_OUT; +} + +/* + * Low level hardware configuration and startup. + */ +static int pxa250_fir_irda_startup(struct net_device *dev) +{ + __ECHO_IN; + + /* + * Disable STUART + */ + STIER &= ~IER_UUE; + + /* Disable STUART FIFO */ + STFCR = 0; + + /* + * Do low level configuration for HW AF and clock + */ + pxa250_do_fir_GPIO_config(dev); + + __ECHO_OUT; + return 0; +} + +/* + * Aieeeeee .. we should never get here :( + */ +static void pxa250_irda_rxdma_irq(int ch, void *id, struct pt_regs *regs) +{ + struct net_device *dev = id; + struct pxa_irda *si = netdev_priv(dev); + u_int dcsr; + + __ECHO_IN; + + /* + * Make sure that irq is our. + */ + if (ch != si->rxdma_ch) + /* just */ + return; + + /* + * Check status + */ + dcsr = DCSR(ch); + + DBG("DCSR=%x\n", dcsr); + + if (dcsr & DCSR_STOPSTATE) { + DBG_IRQ("Chanel %d in stop state\n", ch); + } + + if (dcsr & DCSR_BUSERR) { + /* + * BUS Error we must restart reception + */ + DBG("PXA IrDA: bus error interrupt on channel %d\n", ch); + DCSR(ch) |= DCSR_BUSERR; + } + + if (dcsr & DCSR_ENDINTR) { + DBG("PXA IrDA: Normal end of dma channel %d - packet to big\n", + ch); + DCSR(ch) |= DCSR_ENDINTR; + } + + /* no mater what restart rx */ + pxa250_start_rx_dma(dev); + + return; +} + +static void pxa250_irda_txdma_irq(int ch, void *id, struct pt_regs *regs) +{ + struct net_device *dev = id; + struct pxa_irda *si = netdev_priv(dev); + struct sk_buff *skb = si->txskb; + u_int dcsr; + + __ECHO_IN; + DBG_IRQ("transmit\n"); + + /* + * Make sure that irq is our. + */ + if (ch != si->txdma_ch) + /* just */ + return; + + /* + * Check status + */ + dcsr = DCSR(ch); + + DBG("DCSR=%x", dcsr); + + if (dcsr & DCSR_STOPSTATE) { + DBG("Chanel %d in stop state\n", ch); + } + + if (dcsr & DCSR_BUSERR) { + DBG("PXA IrDA: bus error interrupt on channel %d\n", ch); + DCSR(ch) |= DCSR_BUSERR; + si->txskb = NULL; + } + + if (dcsr & DCSR_ENDINTR) { + DBG("PXA IrDA: Normal end of dma channel %d\n", ch); + DCSR(ch) |= DCSR_ENDINTR; + si->txskb = NULL; + } + + /* + * Account and free the packet. + */ + if (skb) { + si->stats.tx_packets++; + si->stats.tx_bytes += skb->len; + dev_kfree_skb_irq(skb); + } + + /* Disable transceiver and enable receiver */ + if (si->newspeed) { + pxa250_irda_set_speed(dev, si->newspeed); + si->newspeed = 0; + } + + while (ICSR1 & ICSR1_TBY) + udelay(1); + + ICCR0 &= ~ICCR0_TXE; + + enable_irq(si->fir_irq); + + ICCR0 |= ICCR0_RXE; + + /* + * Make sure that the TX queue is available for sending + * (for retries). TX has priority over RX at all times. + */ + netif_wake_queue(dev); + + __ECHO_OUT; +} + +static void pxa250_start_rx_dma(struct net_device *dev) +{ + struct pxa_irda *si = netdev_priv(dev); + int ch = si->rxdma_ch; + + if (!si->rxskb) { + DBG("rx buffer went missing\n"); +/* return; */ + } + + DCSR(ch) = 0; + DCSR(ch) = DCSR_NODESC; + DSADR(ch) = __PREG(ICDR); + DTADR(ch) = si->rxbuf_dma; /* phisical address */ ; + + /* We should never do END_IRQ. !!! */ + DCMD(ch) = + DCMD_ENDIRQEN | DCMD_INCTRGADDR | DCMD_FLOWSRC | DCMD_BURST8 | + DCMD_WIDTH1 | HPSIR_MAX_RXLEN; + + /* + * All right information will be available as soon as we set RXE flag + */ + DCSR(ch) = DCSR_ENDINTR | DCSR_BUSERR; + DCSR(ch) = DCSR_RUN | DCSR_NODESC; +} + +static int pxa250_get_rx_len(struct pxa_irda *si) +{ + /* + * DMA have to be stoped here + */ + if (!(DCSR(si->rxdma_ch) & DCSR_STOPSTATE)) + printk("warning dma have to be stoped befor counting len\n"); + + return (HPSIR_MAX_RXLEN - (DCMD(si->rxdma_ch) & DCMD_LENGTH)); +} + +static void pxa250_irda_fir_error(struct net_device *dev) +{ + struct pxa_irda *si = netdev_priv(dev); + struct sk_buff *skb = si->rxskb; + int len; + int stat, data; + + __ECHO_IN; + + if (!skb) { + printk("pxa250 fir_error: SKB is NULL!\n"); + return; + } + + /* + * Get the current data position. + */ + len = pxa250_get_rx_len(si); + DBG("RXLEN=%d\n", len); + memcpy(skb->data, si->rxbuf_dma_virt, len); + + do { + /* + * Read Status, and then Data. + */ + stat = ICSR1; + rmb(); + data = ICDR; + if (stat & (ICSR1_CRE | ICSR1_ROR)) { + si->stats.rx_errors++; + if (stat & ICSR1_CRE) + si->stats.rx_crc_errors++; + if (stat & ICSR1_ROR) + si->stats.rx_frame_errors++; + } else + skb->data[len++] = data; + + /* + * If we hit the end of frame, there's + * no point in continuing. + */ + if (stat & ICSR1_EOF) + break; + } while (ICSR0 & ICSR0_EIF); + + if (stat & ICSR1_EOF) { + si->rxskb = NULL; + + skb_put(skb, len); + skb->dev = dev; + skb->mac.raw = skb->data; + skb->protocol = htons(ETH_P_IRDA); + si->stats.rx_packets++; + si->stats.rx_bytes += len; + + /* + * Before we pass the buffer up, allocate a new one. + */ + si->rxskb = alloc_skb(HPSIR_MAX_RXLEN + 1, GFP_ATOMIC); + + if (!si->rxskb) { + printk(KERN_ERR + "pxa250_ir: out of memory for RX SKB\n"); + return; + } + + /* + * Align any IP headers that may be contained + * within the frame. + */ + skb_reserve(si->rxskb, 1); + + netif_rx(skb); + } +} + +/* + * FIR format interrupt service routine. We only have to + * handle RX events; transmit events go via the TX DMA irq handler. + * + * No matter what, we disable RX, process, and then restart RX. + */ +static irqreturn_t pxa250_irda_fir_irq(int irq, void *dev_id, + struct pt_regs *regs) +{ + struct net_device *dev = dev_id; + struct pxa_irda *si = netdev_priv(dev); + int status; + + /* + * Stop RX + */ + __ECHO_IN; + + pxa250_dma_stop(si->rxdma_ch); + + /* + * Framing error - we throw away the packet completely. + * Clearing RXE flushes the error conditions and data + * from the fifo. + */ + status = ICSR0; + + if (status & (ICSR0_FRE | ICSR0_RAB)) { + DBG_IRQ("Framing error or RAB\n"); + + si->stats.rx_errors++; + + if (ICSR0 & ICSR0_FRE) + si->stats.rx_frame_errors++; + + /* Clear RX fifo + * DMA will be cleared when we restart RX + * Should we check RNE after that? + */ + ICCR0 &= ~ICCR0_RXE; + + /* + * Clear selected status bits now, so we + * don't miss them next time around. + */ + ICSR0 = status & (ICSR0_FRE | ICSR0_RAB); + } + + /* + * Deal with any receive errors. The any of the lowest + * 8 bytes in the FIFO may contain an error. We must read + * them one by one. The "error" could even be the end of + * packet! + */ + if (ICSR0 & ICSR0_EIF) + pxa250_irda_fir_error(dev); + + /* + * No matter what happens, we must restart reception. + */ + ICCR0 = 0; + pxa250_start_rx_dma(dev); + pxa250_ficp_rx_start(); + __ECHO_OUT; + + return IRQ_HANDLED; +} + +static int pxa2xx_irda_fir_shutdown(struct net_device *dev) +{ + struct pxa_irda *si = netdev_priv(dev); + unsigned long flags; + + local_irq_save(flags); + + pxa250_dma_stop(si->txdma_ch); + pxa250_dma_stop(si->rxdma_ch); + + if (si->txskb) + dev_kfree_skb_irq(si->txskb); + + ICCR0 &= ~(ICCR0_RXE | ICCR0_TXE); + disable_irq(si->fir_irq); + pxa_set_cken(CKEN13_FICP, 0); + + local_irq_restore(flags); + + return 0; +} + +/************************************************************************** + * SIR * + **************************************************************************/ +/* + * Handle HP-SIR transmission completion. + */ +static void pxa2xx_irda_tx_complete(struct net_device *dev, unsigned int len) +{ + struct pxa_irda *si = netdev_priv(dev); + struct sk_buff *skb = si->txskb; + + WARN_ON(!skb); + if (!skb) + return; + + si->txskb = NULL; + si->stats.tx_packets++; + si->stats.tx_bytes += len; + + /* + * Clear any unwanted garbage from the receiver FIFO. + */ + STFCR = FCR_TRFIFOE | FCR_RESETRF | FCR_ITL_1; + STFCR = FCR_TRFIFOE | FCR_ITL_1; + + /* + * Switch to receive mode and enable receiver interrupts. + */ + STISR = STISR_RCVEIR | si->isr; + STIER = IER_RAVIE | IER_UUE | IER_RTIOE; + + if (si->newspeed) { + pxa250_irda_set_speed(dev, si->newspeed); + si->newspeed = 0; + } + + /* + * I'm hungry! + */ + netif_wake_queue(dev); + + dev_kfree_skb_irq(skb); +} + +static irqreturn_t pxa250_irda_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + struct net_device *dev = (struct net_device *)dev_id; + struct pxa_irda *si = netdev_priv(dev); + unsigned int stat = STLSR; + + /* + * Only read the LSR once per data byte - the LSR has + * status bits which clear upon read. + */ + while (stat & LSR_DR) { + unsigned int data = STRBR; + + if (stat & (LSR_FE | LSR_OE | LSR_PE)) { + si->stats.rx_errors++; + if (stat & LSR_FE) + si->stats.rx_frame_errors++; + if (stat & LSR_OE) + si->stats.rx_fifo_errors++; + } else { + si->stats.rx_bytes++; + async_unwrap_char(dev, &si->stats, &si->rx_buff, data); + dev->last_rx = jiffies; + } + + stat = STLSR; + } + + /* + * If STISR == si->isr, we are in transmission mode. + */ + if (si->isr == STISR) { + while (stat & LSR_TDRQ) { + if (si->tx_buff.len == 0) + goto end_transmit; + + STTHR = *si->tx_buff.data++; + si->tx_buff.len -= 1; + + stat = LSR_DR; + } + } + return IRQ_HANDLED; + + end_transmit: + /* + * We need to ensure that the transmitter has finished. + */ + do { + udelay(1); + } while (!(STLSR & LSR_TEMT)); + + pxa2xx_irda_tx_complete(dev, si->tx_buff.data - si->tx_buff.head); + return IRQ_HANDLED; +} + +/* + * Start HP-SIR transmit. + */ +static void pxa2xx_irda_sir_tx(struct sk_buff *skb, struct net_device *dev) +{ + struct pxa_irda *si = netdev_priv(dev); + unsigned long flags; + + si->tx_buff.data = si->tx_buff.head; + si->tx_buff.len = async_wrap_skb(skb, si->tx_buff.data, + si->tx_buff.truesize); + + /* + * Disable STUART interrupts and switch to transmit mode. + */ + local_irq_save(flags); + STISR = si->isr; + STIER = IER_UUE | IER_TIE; + local_irq_restore(flags); + + dev->trans_start = jiffies; +} + +/* + * Shutdown the SIR mode port. We may have a transmit packet + * in flight, so ensure we free it. + */ +static void pxa2xx_irda_sir_shutdown(struct net_device *dev) +{ + struct pxa_irda *si = netdev_priv(dev); + + STIER = 0; + STFCR = 0; + STISR = 0; + + if (si->txskb) { + si->stats.tx_aborted_errors++; + si->stats.tx_errors++; + + dev_kfree_skb_irq(si->txskb); + si->txskb = NULL; + } + + pxa_set_cken(CKEN5_STUART, 0); +} + +/************************************************************************************/ + +/* Low level init/uninstall function PM control and IrDA protocol stack registration */ + +/* + * Set the IrDA communications speed. + * Interrupt have to be disabled here. + */ +static int pxa250_irda_startup(struct net_device *dev) +{ + struct pxa_irda *si = netdev_priv(dev); + + __ECHO_IN; + + /* + * Ensure that the ports for this device are setup correctly. + */ + pxa_gpio_mode(GPIO46_STRXD_MD); + pxa_gpio_mode(GPIO47_STTXD_MD); + + STMCR = MCR_OUT2; + STLCR = LCR_WLS1 | LCR_WLS0; + + STISR = STISR_RCVEIR | si->isr; + pxa_set_cken(CKEN5_STUART, 1); + + /* reset FIFO */ + +/* STFCR = FCR_TRFIFOE | FCR_RESETTF | FCR_RESETRF;// | FCR_ITL_16; + +STIER = IER_UUE | IER_RAVIE | IER_RTOIE; +*/ + + pxa2xx_irda_transceiver_enable(dev); + pxa2xx_irda_transceiver_mode(dev, 0); + + __ECHO_OUT; + + return 0; +} + +static int pxa250_irda_hard_xmit(struct sk_buff *skb, struct net_device *dev) +{ + struct pxa_irda *si = netdev_priv(dev); + int speed = irda_get_next_speed(skb); + int mtt; + + __ECHO_IN; + + /* + * Does this packet contain a request to change the interface + * speed? If so, remember it until we complete the transmission + * of this frame. + */ + if (speed != si->speed && speed != -1) + si->newspeed = speed; + + /* + * If this is an empty frame, we can bypass a lot. + */ + if (skb->len == 0) { + if (si->newspeed) { + si->newspeed = 0; + pxa250_irda_set_speed(dev, speed); + } + dev_kfree_skb(skb); + return 0; + } + + DBG("stop queue\n"); + netif_stop_queue(dev); + + /* + * We must not be transmitting... + */ + if (si->txskb) + BUG(); + + si->txskb = skb; + + if (!IS_FIR(si)) { + pxa2xx_irda_sir_tx(skb, dev); + return 0; + } else { /* FIR */ + DBG("Enter FIR transmit\n"); + disable_irq(si->fir_irq); + + netif_stop_queue(dev); + DBG("queue stoped\n"); + + /* we could not just map so we'll need some triks */ + /* skb->data may be not DMA capable -Sed- */ + if (skb->len > TXBUFF_MAX_SIZE) { + printk(KERN_ERR "skb data too large\n"); + printk(KERN_ERR "len=%d", skb->len); + BUG(); + } + + DBG("gonna copy %d bytes to txbuf\n", skb->len); + + memcpy(si->txbuf_dma_virt, skb->data, skb->len); + + /* Actual sending ;must not be receiving !!! */ + /* Write data and source address */ + DBG("ICSR1 & RNE =%d\n", (ICSR1 & ICSR1_RNE) ? 1 : 0); + + /* Disable receiver and enable transifer */ + ICCR0 &= ~ICCR0_RXE; + + if (ICSR1 & ICSR1_TBY) + BUG(); + + ICCR0 |= ICCR0_TXE; + + DBG("FICP status %x\n", ICSR0); + + if (0) { + int i; + + DBG("sending packet\n"); + for (i = 0; i < skb->len; i++) + (i % 64) ? printk("%2x ", + skb-> + data[i]) : printk("%2x \n", + skb-> + data[i]); + DBG(" done\n"); + + } + + /* + * If we have a mean turn-around time, impose the specified + * specified delay. We could shorten this by timing from + * the point we received the packet. + */ + mtt = irda_get_mtt(skb); + if (mtt) + udelay(mtt); + + DCSR(si->txdma_ch) = 0; + DCSR(si->txdma_ch) = DCSR_NODESC; + DSADR(si->txdma_ch) = si->txbuf_dma; /* phisic address */ + DTADR(si->txdma_ch) = __PREG(ICDR); + + DCMD(si->txdma_ch) = + DCMD_ENDIRQEN | DCMD_INCSRCADDR | DCMD_FLOWTRG | DCMD_BURST8 + | DCMD_WIDTH1 | skb->len; + + DCSR(si->txdma_ch) = DCSR_ENDINTR | DCSR_BUSERR; + DCSR(si->txdma_ch) = DCSR_RUN | DCSR_NODESC; + + DBG("FICP status %x\n", ICSR0); + + return 0; + } +} + +static int +pxa250_irda_ioctl(struct net_device *dev, struct ifreq *ifreq, int cmd) +{ + struct if_irda_req *rq = (struct if_irda_req *)ifreq; + struct pxa_irda *si = netdev_priv(dev); + int ret = -EOPNOTSUPP; + + __ECHO_IN; + + switch (cmd) { + case SIOCSBANDWIDTH: + if (capable(CAP_NET_ADMIN)) { + /* + * We are unable to set the speed if the + * device is not running. + */ + if (si->open) { + ret = pxa250_irda_set_speed(dev, + rq->ifr_baudrate); + } else { + printk + ("pxa250_irda_ioctl: SIOCSBANDWIDTH: !netif_running\n"); + ret = 0; + } + } + break; + + case SIOCSMEDIABUSY: + ret = -EPERM; + if (capable(CAP_NET_ADMIN)) { + irda_device_set_media_busy(dev, TRUE); + ret = 0; + } + break; + + case SIOCGRECEIVING: + rq->ifr_receiving = IS_FIR(si) ? 0 + : si->rx_buff.state != OUTSIDE_FRAME; + break; + + default: + break; + } + + __ECHO_OUT; + + return ret; +} + +static struct net_device_stats *pxa250_irda_stats(struct net_device *dev) +{ + struct pxa_irda *si = netdev_priv(dev); + return &si->stats; +} + +static int pxa250_irda_set_speed(struct net_device *dev, int speed) +{ + struct pxa_irda *si = netdev_priv(dev); + int brd, ret = -EINVAL; + static int last_fir_speed = 0; + + __ECHO_IN; + + switch (speed) { + case 9600: + case 19200: + case 38400: + case 57600: + case 115200: + /* Baud rate fixed - Clo */ + /* + * FIXME + */ + if (last_fir_speed) { + pxa2xx_irda_fir_shutdown(dev); + pxa_gpio_mode(GPIO46_STRXD_MD); + pxa_gpio_mode(GPIO47_STTXD_MD); + + netif_wake_queue(dev); + last_fir_speed = 0; + } + + brd = 14745600 / (16 * speed); + + STLCR |= LCR_DLAB; + + STDLH = brd >> 8; /* Clo: set Divisor Latch High */ + STDLL = brd & 0xFF; /* Clo: set Devisor Latch Low */ + STLCR &= ~LCR_DLAB; /* Clo: clear DLAB bit */ + STMCR = MCR_OUT2; + + pxa_set_cken(CKEN5_STUART, 1); + + STLCR = LCR_WLS1 | LCR_WLS0; + + STISR = STISR_RCVEIR | si->isr; + + STFCR = FCR_TRFIFOE | FCR_RESETTF | FCR_RESETRF | FCR_ITL_1; // | FCR_ITL_16; + + STIER = IER_UUE | IER_RAVIE | IER_RTIOE; + + /* + * Indicate to the IRDA controller that we are SIR + */ + pxa2xx_irda_transceiver_mode(dev, 0); + + si->speed = speed; + + ret = 0; + break; + + case 4000000: + if (last_fir_speed) + goto speed_out; + + pxa2xx_irda_sir_shutdown(dev); + pxa250_fir_irda_startup(dev); + pxa250_irda_rx_alloc(si); + ICCR0 = 0; + pxa250_start_rx_dma(dev); + pxa250_ficp_rx_start(); + + enable_irq(si->fir_irq); + DBG("enable FIR \n"); + si->speed = speed; + + netif_wake_queue(dev); + last_fir_speed = 1; + speed_out: + + ret = 0; + + break; + + default: + break; + } + __ECHO_OUT; + + return ret; +} + +static int pxa250_irda_start(struct net_device *dev) +{ + struct pxa_irda *si = netdev_priv(dev); + int err; + unsigned long flags; + + __ECHO_IN; + si->speed = 9600; + + STIER = 0; + + local_irq_save(flags); + + err = request_irq(si->fir_irq, pxa250_irda_fir_irq, 0, dev->name, dev); + if (err) + goto err_fir_irq; + + err = request_irq(dev->irq, pxa250_irda_irq, 0, dev->name, dev); + if (err) + goto err_irq; + + /* + * The interrupt must remain disabled for now. + */ + disable_irq(si->fir_irq); + + local_irq_restore(flags); + + /* Allocate DMA channel for receiver (not used) */ + err = pxa_request_dma("IrDA receive", DMA_PRIO_LOW, + pxa250_irda_rxdma_irq, dev); + if (err < 0) + goto err_rx_dma; + si->rxdma_ch = err; + + DRCMRRXICDR = DRCMR_MAPVLD | si->rxdma_ch; + + /* Allocate DMA channel for transmit */ + err = pxa_request_dma("IrDA transmit", DMA_PRIO_LOW, + pxa250_irda_txdma_irq, dev); + if (err < 0) + goto err_tx_dma; + + si->txdma_ch = err; + + /* + * Make sure that ICP will be able + * to assert the transmit dma request bit + * through the peripherals request bus (PREQ) + */ + DRCMRTXICDR = DRCMR_MAPVLD | si->txdma_ch; + + DBG("rx(not used) channel=%d tx channel=%d\n", si->rxdma_ch, + si->txdma_ch); + + /* allocate consistent buffers for dma access + * buffers have to be aligned and situated in dma capable memory region; + */ + si->rxbuf_dma_virt = dma_alloc_coherent(NULL, HPSIR_MAX_RXLEN, + &si->rxbuf_dma, GFP_KERNEL); + if (!si->rxbuf_dma_virt) + goto err_rxbuf_dma; + + si->txbuf_dma_virt = dma_alloc_coherent(NULL, HPSIR_MAX_TXLEN, + &si->txbuf_dma, GFP_KERNEL); + if (!si->txbuf_dma_virt) + goto err_txbuf_dma; + + /* Alocate skb for receiver */ + err = pxa250_irda_rx_alloc(si); + if (err) + goto err_rx_alloc; + + /* + * Setup the serial port for the specified config. + */ + err = pxa250_irda_startup(dev); + if (err) + goto err_startup; + + pxa250_irda_set_speed(dev, si->speed = 9600); + + /* + * Open a new IrLAP layer instance. + */ + si->irlap = irlap_open(dev, &si->qos, "pxa250"); + err = -ENOMEM; + if (!si->irlap) + goto err_irlap; + + /* + * Now enable the interrupt and start the queue + */ + si->open = 1; + netif_start_queue(dev); + return 0; + + err_irlap: + si->open = 0; + pxa2xx_irda_sir_shutdown(dev); + err_startup: + dev_kfree_skb(si->rxskb); + err_rx_alloc: + dma_free_coherent(NULL, HPSIR_MAX_TXLEN, si->txbuf_dma_virt, + si->txbuf_dma); + err_txbuf_dma: + dma_free_coherent(NULL, HPSIR_MAX_RXLEN, si->rxbuf_dma_virt, + si->rxbuf_dma); + err_rxbuf_dma: + pxa_free_dma(si->txdma_ch); + err_tx_dma: + pxa_free_dma(si->rxdma_ch); + err_rx_dma: + free_irq(dev->irq, dev); + err_irq: + free_irq(si->fir_irq, dev); + err_fir_irq: + return err; +} + +static int pxa250_irda_stop(struct net_device *dev) +{ + struct pxa_irda *si = netdev_priv(dev); + + printk(KERN_ERR "Irda stop... RX = %ld TX = %ld\n", si->stats.rx_bytes, si->stats.tx_bytes); + + netif_stop_queue(dev); + + pxa2xx_irda_sir_shutdown(dev); + + disable_irq(si->fir_irq); + + /* + * If we have been doing DMA receive, make sure we + * tidy that up cleanly. + */ + if (si->rxskb) { + dev_kfree_skb(si->rxskb); + si->rxskb = NULL; + } + + /* Stop IrLAP */ + if (si->irlap) { + irlap_close(si->irlap); + si->irlap = NULL; + } + + si->open = 0; + + /* + * Free resources + */ + dma_free_coherent(NULL, HPSIR_MAX_TXLEN, si->txbuf_dma_virt, + si->txbuf_dma); + dma_free_coherent(NULL, HPSIR_MAX_RXLEN, si->rxbuf_dma_virt, + si->rxbuf_dma); + pxa_free_dma(si->txdma_ch); + pxa_free_dma(si->rxdma_ch); + free_irq(dev->irq, dev); + free_irq(si->fir_irq, dev); + + return 0; +} + +static int pxa_irda_init_iobuf(iobuff_t * io, int size) +{ + io->head = kmalloc(size, GFP_KERNEL | GFP_DMA); + if (io->head != NULL) { + io->truesize = size; + io->in_frame = FALSE; + io->state = OUTSIDE_FRAME; + io->data = io->head; + } + return io->head ? 0 : -ENOMEM; +} + +static int pxa_irda_probe(struct device *_dev) +{ + struct platform_device *pdev = to_platform_device(_dev); + struct net_device *dev; + struct pxa_irda *si; + unsigned int baudrate_mask; + int err; + + /* STUART */ + err = request_mem_region(__PREG(STRBR), 0x24, "IrDA") ? 0 : -EBUSY; + if (err) + goto err_mem_1; + /* FIR */ + err = request_mem_region(__PREG(ICCR0), 0x1c, "IrDA") ? 0 : -EBUSY; + if (err) + goto err_mem_2; + + dev = alloc_irdadev(sizeof(struct pxa_irda)); + if (!dev) + goto err_mem_3; + + si = netdev_priv(dev); + si->dev = &pdev->dev; + si->fir_irq = IRQ_ICP; + si->isr = STISR_XMITIR | STISR_XMODE; + + /* + * Initialise the HP-SIR buffers + */ + err = pxa_irda_init_iobuf(&si->rx_buff, 14384); + if (err) + goto err_mem_4; + err = pxa_irda_init_iobuf(&si->tx_buff, 4000); + if (err) + goto err_mem_4; + + dev->hard_start_xmit = pxa250_irda_hard_xmit; + dev->open = pxa250_irda_start; + dev->stop = pxa250_irda_stop; + dev->do_ioctl = pxa250_irda_ioctl; + dev->get_stats = pxa250_irda_stats; + dev->irq = IRQ_STUART; + + irda_init_max_qos_capabilies(&si->qos); + + /* + * We support original IRDA up to 115k2. (we don't currently + * support 4Mbps). Min Turn Time set to 1ms or greater. + */ + baudrate_mask = IR_9600; + + switch (max_rate) { + case 4000000: + baudrate_mask |= IR_4000000 << 8; + case 115200: + baudrate_mask |= IR_115200; + case 57600: + baudrate_mask |= IR_57600; + case 38400: + baudrate_mask |= IR_38400; + case 19200: + baudrate_mask |= IR_19200; + } + + si->qos.baud_rate.bits &= baudrate_mask; + si->qos.min_turn_time.bits = 7; + + irda_qos_bits_to_value(&si->qos); + + pxa2xx_irda_transceiver_init(dev); + pxa2xx_irda_transceiver_disable(dev); + + err = register_netdev(dev); + if (err == 0) + dev_set_drvdata(&pdev->dev, dev); + + if (err) { + err_mem_4: + kfree(si->tx_buff.head); + kfree(si->rx_buff.head); + free_netdev(dev); + err_mem_3: + release_mem_region(__PREG(ICCR0), 0x1c); + err_mem_2: + release_mem_region(__PREG(STRBR), 0x24); + } + err_mem_1: + return err; +} + +static int pxa_irda_remove(struct device *_dev) +{ + struct net_device *dev = dev_get_drvdata(_dev); + + if (dev) { + struct pxa_irda *si = netdev_priv(dev); + unregister_netdev(dev); + kfree(si->tx_buff.head); + kfree(si->rx_buff.head); + free_netdev(dev); + } + + release_mem_region(__PREG(ICCR0), 0x1c); + release_mem_region(__PREG(STRBR), 0x24); + + return 0; +} + +#ifdef CONFIG_PM +/* + * Suspend the IrDA interface. + */ +static int pxa_irda_suspend(struct device *_dev, u32 state, u32 level) +{ + struct net_device *dev = dev_get_drvdata(_dev); + struct pxa_irda *si; + + if (!dev || level != SUSPEND_DISABLE) + return 0; + + si = netdev_priv(dev); + if (si && si->open) { + /* + * Stop the transmit queue + */ + netif_stop_queue(dev); + disable_irq(dev->irq); + disable_irq(si->fir_irq); + if (IS_FIR(si)) { + pxa2xx_irda_fir_shutdown(dev); + } else { + pxa2xx_irda_sir_shutdown(dev); + } + pxa2xx_irda_transceiver_disable(dev); + } + + return 0; +} + +/* + * Resume the IrDA interface. + */ +static int pxa_irda_resume(struct device *_dev, u32 level) +{ + struct net_device *dev = dev_get_drvdata(_dev); + struct pxa_irda *si; + + if (!dev || level != RESUME_ENABLE) + return 0; + + pxa2xx_irda_transceiver_init(dev); + pxa2xx_irda_transceiver_disable(dev); + + si = netdev_priv(dev); + if (si && si->open) { + /* + * If we missed a speed change, initialise at the new speed + * directly. It is debatable whether this is actually + * required, but in the interests of continuing from where + * we left off it is desireable. The converse argument is + * that we should re-negotiate at 9600 baud again. + */ + if (si->newspeed) { + si->speed = si->newspeed; + si->newspeed = 0; + } + + pxa250_irda_startup(dev); + enable_irq(dev->irq); + + /* + * This automatically wakes up the queue + */ + pxa250_irda_set_speed(dev, si->speed = 9600); + netif_wake_queue(dev); + } + + return 0; +} + +#else +#define sa1100_irda_suspend NULL +#define sa1100_irda_resume NULL +#endif + +static struct device_driver pxair_driver = { + .name = "pxa-ir", + .bus = &platform_bus_type, + .probe = pxa_irda_probe, + .remove = pxa_irda_remove, + .suspend = pxa_irda_suspend, + .resume = pxa_irda_resume, +}; + +static struct platform_device pxair_device = { + .name = "pxa-ir", + .id = 0, +}; + +static int __init pxa_irda_init(void) +{ + int ret; + + ret = driver_register(&pxair_driver); + if (ret == 0) { + ret = platform_device_register(&pxair_device); + if (ret) + driver_unregister(&pxair_driver); + } + return ret; +} + +static void __exit pxa_irda_exit(void) +{ + driver_unregister(&pxair_driver); + platform_device_unregister(&pxair_device); +} + +module_init(pxa_irda_init); +module_exit(pxa_irda_exit); +module_param(max_rate, int, 0); + +MODULE_AUTHOR("Alexey Lugovskoy Frasenyak Dmitrij"); +MODULE_DESCRIPTION("PXA250 SIR/FIR"); +MODULE_LICENSE("GPL"); +MODULE_PARM_DESC(max_rate, + "Maximum baud rate (4000000, 115200, 57600, 38400, 19200, 9600)"); diff -NaurbB linux-2.6.9-pre1-bk18/drivers/pcmcia/cistpl.c linux-2.6.9_nbp/drivers/pcmcia/cistpl.c --- linux-2.6.9-pre1-bk18/drivers/pcmcia/cistpl.c 2005-08-15 01:14:41.000000000 +0200 +++ linux-2.6.9_nbp/drivers/pcmcia/cistpl.c 2004-10-22 22:20:42.000000000 +0200 @@ -329,6 +329,14 @@ list_del(&cis->node); kfree(cis); } + + /* + * If there was a fake CIS, destroy that as well. + */ + if (s->fake_cis) { + kfree(s->fake_cis); + s->fake_cis = NULL; + } } /*====================================================================== diff -NaurbB linux-2.6.9-pre1-bk18/drivers/pcmcia/cs.c linux-2.6.9_nbp/drivers/pcmcia/cs.c --- linux-2.6.9-pre1-bk18/drivers/pcmcia/cs.c 2005-08-15 01:14:41.000000000 +0200 +++ linux-2.6.9_nbp/drivers/pcmcia/cs.c 2004-10-22 22:20:42.000000000 +0200 @@ -364,10 +364,6 @@ s->irq.AssignedIRQ = s->irq.Config = 0; s->lock_count = 0; destroy_cis_cache(s); - if (s->fake_cis) { - kfree(s->fake_cis); - s->fake_cis = NULL; - } #ifdef CONFIG_CARDBUS cb_free(s); #endif @@ -621,10 +617,18 @@ * FIXME: need a better check here for cardbus cards. */ if (verify_cis_cache(skt) != 0) { + cs_dbg(skt, 4, "cis mismatch - different card\n"); socket_remove_drivers(skt); destroy_cis_cache(skt); + /* + * Workaround: Give DS time to schedule removal. + * Remove me once the 100ms delay is eliminated + * in ds.c + */ + msleep(200); send_event(skt, CS_EVENT_CARD_INSERTION, CS_EVENT_PRI_LOW); } else { + cs_dbg(skt, 4, "cis matches cache\n"); send_event(skt, CS_EVENT_PM_RESUME, CS_EVENT_PRI_LOW); } } else { @@ -1594,7 +1598,7 @@ return CS_CONFIGURATION_LOCKED; /* Do power control. We don't allow changes in Vcc. */ - if (s->socket.Vcc != req->Vcc) + if (!(s->features & SS_CAP_NOPOWER) && s->socket.Vcc != req->Vcc) return CS_BAD_VCC; if (req->Vpp1 != req->Vpp2) return CS_BAD_VPP; diff -NaurbB linux-2.6.9-pre1-bk18/drivers/pcmcia/Makefile linux-2.6.9_nbp/drivers/pcmcia/Makefile --- linux-2.6.9-pre1-bk18/drivers/pcmcia/Makefile 2004-08-14 07:37:38.000000000 +0200 +++ linux-2.6.9_nbp/drivers/pcmcia/Makefile 2004-09-14 12:25:06.000000000 +0200 @@ -50,4 +50,4 @@ pxa2xx_cs-$(CONFIG_ARCH_LUBBOCK) += pxa2xx_lubbock.o sa1111_generic.o pxa2xx_cs-$(CONFIG_MACH_MAINSTONE) += pxa2xx_mainstone.o - +pxa2xx_cs-$(CONFIG_MACH_NETBOOKPRO) += pxa2xx_netbookpro.o diff -NaurbB linux-2.6.9-pre1-bk18/drivers/pcmcia/pxa2xx_netbookpro.c linux-2.6.9_nbp/drivers/pcmcia/pxa2xx_netbookpro.c --- linux-2.6.9-pre1-bk18/drivers/pcmcia/pxa2xx_netbookpro.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/drivers/pcmcia/pxa2xx_netbookpro.c 2004-10-22 11:48:09.000000000 +0200 @@ -0,0 +1,214 @@ +/* + * linux/drivers/pcmica/pxa2xx_netbookpro.c + * + * Based on: linux/drivers/pcmcia/pxa2xx_mainstone.c + * + * NetBookPro PCMCIA specific routines. + * + * Modifications for NetBookPro by: + * Author: Ben Dooks + * Modified: Jun 08, 2004 + * Copyright: Simtec Electronics + * + * Original: + * Created: May 12, 2004 + * Author: Nicolas Pitre + * Copyright: MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#include "soc_common.h" + +#define PFX "nbp-pcmia: " + +#define dbg(x...) do { } while (0) + +/* GPIO Lines + * Socket: 0 1 + * CardDetect 75 73 + * StatusChange 76 72 + * Ready/IRQ 77 71 + * Reset 74 70 + */ + +/* IRQ lines for our Slots */ + +struct our_socket { + unsigned long irq; /* irq line for socket */ + unsigned long detect; /* gpio bit for detect */ + unsigned long ready; /* gpio bit for ready/irq */ + unsigned long reset; /* gpio bit for card reset */ +}; + +/* define the IO lines for each socket */ + +static struct our_socket sockets[2] = { + [1] = { + .irq = IRQ_GPIO(77), + .detect = 1 << (75 - 64), + .ready = 1 << (77 - 64), + .reset = 1 << (74 - 64), + }, + [0] = { + .irq = IRQ_GPIO(71), + .detect = 1 << (73 - 64), + .ready = 1 << (71 - 64), + .reset = 1 << (70 - 64), + } +}; + +static struct pcmcia_irqs irqs[] = { + { 1, IRQ_GPIO(75), "PCMCIA Change Detect" }, + { 0, IRQ_GPIO(73), "CF Change Detect" }, + { 1, IRQ_GPIO(76), "PCMCIA Status Change" }, + { 0, IRQ_GPIO(72), "CF Status Change" }, +}; + +static int nbp_pcmcia_hw_init(struct soc_pcmcia_socket *skt) +{ + struct our_socket *oursock = &sockets[skt->nr]; + + dbg(PFX "initialising socket %d, irq %ld\n", + skt->nr, oursock->irq); + + skt->irq = oursock->irq; + + return soc_pcmcia_request_irqs(skt, irqs, ARRAY_SIZE(irqs)); +} + +static void nbp_pcmcia_hw_shutdown(struct soc_pcmcia_socket *skt) +{ + soc_pcmcia_free_irqs(skt, irqs, ARRAY_SIZE(irqs)); +} + +static void nbp_pcmcia_socket_state(struct soc_pcmcia_socket *skt, + struct pcmcia_state *state) +{ + struct our_socket *oursock = &sockets[skt->nr]; + unsigned long gpio; + + gpio = GPLR2; + + state->detect = (gpio & oursock->detect) ? 0 : 1; + state->ready = (gpio & oursock->ready) ? 1 : 0; + + /* none of these are available on this implementation */ + state->bvd1 = 1; + state->bvd2 = 1; + state->vs_3v = 1; + state->vs_Xv = 0; + state->wrprot = 0; + + dbg(PFX "skt[%d]: gpio=%08lx, detect=%d, ready=%d\n", + skt->nr, gpio, state->detect, state->ready); +} + +static int nbp_pcmcia_configure_socket(struct soc_pcmcia_socket *skt, + const socket_state_t *state) +{ + struct our_socket *oursock = &sockets[skt->nr]; + int ret = 0; + + dbg(PFX "configure_socket %d:\n", skt->nr); + + if (skt->nr != 0 && skt->nr != 1) { + printk("nbp_pcmcia_configure_socket: invalid socket %d\n", skt->nr); + return -1; + } + + /* we currently haven't got the code sorted out to + * send the PCon chip any configuration info for the + * socket + */ + + if (state->flags & SS_RESET) { + /* assert reset for socket */ + dbg(PFX "socket %d: reset on\n", skt->nr); + GPCR2 = oursock->reset; + + } else { + /* de-assert reset */ + + dbg(PFX "socket %d: reset off\n", skt->nr); + GPSR2 = oursock->reset; + } + + return ret; +} + +static void nbp_pcmcia_socket_init(struct soc_pcmcia_socket *skt) +{ + soc_pcmcia_enable_irqs(skt, irqs, ARRAY_SIZE(irqs)); +} + +static void nbp_pcmcia_socket_suspend(struct soc_pcmcia_socket *skt) +{ + soc_pcmcia_disable_irqs(skt, irqs, ARRAY_SIZE(irqs)); +} + +static struct pcmcia_low_level nbp_pcmcia_ops = { + .owner = THIS_MODULE, + .hw_init = nbp_pcmcia_hw_init, + .hw_shutdown = nbp_pcmcia_hw_shutdown, + .socket_state = nbp_pcmcia_socket_state, + .configure_socket = nbp_pcmcia_configure_socket, + .socket_init = nbp_pcmcia_socket_init, + .socket_suspend = nbp_pcmcia_socket_suspend, + .nr = 2, + .features = SS_CAP_NOPOWER, +}; + +static struct platform_device *nbp_pcmcia_device; + +static int __init nbp_pcmcia_init(void) +{ + int ret; + + printk("NetBookPro: installing PCMICA driver device\n"); + + nbp_pcmcia_device = kmalloc(sizeof(*nbp_pcmcia_device), GFP_KERNEL); + if (!nbp_pcmcia_device) + return -ENOMEM; + + memset(nbp_pcmcia_device, 0, sizeof(*nbp_pcmcia_device)); + + nbp_pcmcia_device->name = "pxa2xx-pcmcia"; + nbp_pcmcia_device->dev.platform_data = &nbp_pcmcia_ops; + + ret = platform_device_register(nbp_pcmcia_device); + if (ret) + kfree(nbp_pcmcia_device); + + return ret; +} + +static void __exit nbp_pcmcia_exit(void) +{ + /* + * This call is supposed to free our nbp_pcmcia_device. + * Unfortunately platform_device don't have a free method, and + * we can't assume it's free of any reference at this point so we + * can't free it either. + */ + platform_device_unregister(nbp_pcmcia_device); +} + +module_init(nbp_pcmcia_init); +module_exit(nbp_pcmcia_exit); + +MODULE_LICENSE("GPL"); diff -NaurbB linux-2.6.9-pre1-bk18/drivers/pcmcia/soc_common.c linux-2.6.9_nbp/drivers/pcmcia/soc_common.c --- linux-2.6.9-pre1-bk18/drivers/pcmcia/soc_common.c 2005-08-15 01:14:41.000000000 +0200 +++ linux-2.6.9_nbp/drivers/pcmcia/soc_common.c 2004-10-22 11:48:09.000000000 +0200 @@ -752,7 +752,8 @@ if (ret) goto out_err_6; - skt->socket.features = SS_CAP_STATIC_MAP|SS_CAP_PCCARD; + skt->socket.features = SS_CAP_STATIC_MAP | SS_CAP_PCCARD | + ops->features; skt->socket.irq_mask = 0; skt->socket.map_size = PAGE_SIZE; skt->socket.pci_irq = skt->irq; diff -NaurbB linux-2.6.9-pre1-bk18/drivers/pcmcia/soc_common.h linux-2.6.9_nbp/drivers/pcmcia/soc_common.h --- linux-2.6.9-pre1-bk18/drivers/pcmcia/soc_common.h 2004-08-14 07:37:26.000000000 +0200 +++ linux-2.6.9_nbp/drivers/pcmcia/soc_common.h 2004-10-22 11:48:09.000000000 +0200 @@ -111,6 +111,8 @@ */ int (*frequency_change)(struct soc_pcmcia_socket *, unsigned long, struct cpufreq_freqs *); #endif + + unsigned int features; }; diff -NaurbB linux-2.6.9-pre1-bk18/drivers/serial/Kconfig linux-2.6.9_nbp/drivers/serial/Kconfig --- linux-2.6.9-pre1-bk18/drivers/serial/Kconfig 2005-08-15 01:14:41.000000000 +0200 +++ linux-2.6.9_nbp/drivers/serial/Kconfig 2004-09-26 14:18:36.000000000 +0200 @@ -346,6 +346,15 @@ If you have a machine based on an Intel XScale PXA2xx CPU you can enable its onboard serial ports by enabling this option. +config SERIAL_PXA_ALTMAJOR + bool "PXA serial port uses ttySA instead of ttyS" + depends on ARM && ARCH_PXA && SERIAL_PXA + help + Moves the PXA serial ports to ttySA to allow the 8250 driver + to register the serial ports it wants as ttySx. If you do not + do this, then the serial_cs driver and others than rely on + 8250 support will not work. + config SERIAL_PXA_CONSOLE bool "Console on PXA serial port" depends on SERIAL_PXA diff -NaurbB linux-2.6.9-pre1-bk18/drivers/serial/pxa.c linux-2.6.9_nbp/drivers/serial/pxa.c --- linux-2.6.9-pre1-bk18/drivers/serial/pxa.c 2004-08-14 07:37:58.000000000 +0200 +++ linux-2.6.9_nbp/drivers/serial/pxa.c 2004-10-14 15:57:25.000000000 +0200 @@ -705,7 +705,11 @@ } static struct console serial_pxa_console = { +#ifdef CONFIG_SERIAL_PXA_ALTMAJOR + .name = "ttySA", +#else .name = "ttyS", +#endif .write = serial_pxa_console_write, .device = uart_console_device, .setup = serial_pxa_console_setup, @@ -777,7 +781,11 @@ .ops = &serial_pxa_pops, .line = 1, }, - }, { /* STUART */ + }, + +#ifndef CONFIG_PXA_FIR + /* do *not* use STUART when its being used for FIR */ + { /* STUART */ .name = "STUART", .cken = CKEN5_STUART, .port = { @@ -792,15 +800,23 @@ .line = 2, }, } +#endif }; static struct uart_driver serial_pxa_reg = { .owner = THIS_MODULE, .driver_name = "PXA serial", +#ifdef CONFIG_SERIAL_PXA_ALTMAJOR + .devfs_name = "ttsa/", + .dev_name = "ttySA", + .major = 204, + .minor = 5, +#else .devfs_name = "tts/", .dev_name = "ttyS", .major = TTY_MAJOR, .minor = 64, +#endif .nr = ARRAY_SIZE(serial_pxa_ports), .cons = PXA_CONSOLE, }; diff -NaurbB linux-2.6.9-pre1-bk18/drivers/serial/serial_core.c linux-2.6.9_nbp/drivers/serial/serial_core.c --- linux-2.6.9-pre1-bk18/drivers/serial/serial_core.c 2004-08-14 07:36:32.000000000 +0200 +++ linux-2.6.9_nbp/drivers/serial/serial_core.c 2004-10-25 18:12:04.000000000 +0200 @@ -58,6 +58,12 @@ #define uart_console(port) (0) #endif +#ifdef CONFIG_DEBUG_LL +#define uart_lldebug(port) (uart_console(port)) +#else +#define uart_lldebug(port) (0) +#endif + static void uart_change_speed(struct uart_state *state, struct termios *old_termios); static void uart_wait_until_sent(struct tty_struct *tty, int timeout); static void uart_change_pm(struct uart_state *state, int pm_state); @@ -146,7 +152,7 @@ unsigned long page; int retval = 0; - if (info->flags & UIF_INITIALIZED) + if (info->flags & (UIF_INITIALIZED|UIF_SUSPENDED)) return 0; /* @@ -213,7 +219,7 @@ struct uart_port *port = state->port; if (!(info->flags & UIF_INITIALIZED)) - return; + goto out; /* * Turn off DTR and RTS early. @@ -256,7 +262,8 @@ if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); - info->flags &= ~UIF_INITIALIZED; + out: + info->flags &= ~(UIF_INITIALIZED | UIF_SUSPENDED); } /** @@ -1896,6 +1903,9 @@ { struct uart_state *state = drv->state + port->line; + if (uart_lldebug(port)) + return 0; + down(&state->sem); if (state->info && state->info->flags & UIF_INITIALIZED) { @@ -1917,6 +1927,9 @@ set_current_state(TASK_RUNNING); ops->shutdown(port); + + state->info->flags |= UIF_SUSPENDED; + state->info->flags &= ~UIF_INITIALIZED; } /* @@ -1948,7 +1961,7 @@ console_start(port->cons); } - if (state->info && state->info->flags & UIF_INITIALIZED) { + if (state->info && state->info->flags & UIF_SUSPENDED) { struct uart_ops *ops = port->ops; ops->set_mctrl(port, 0); @@ -1958,6 +1971,9 @@ ops->set_mctrl(port, port->mctrl); ops->start_tx(port, 0); spin_unlock_irq(&port->lock); + + state->info->flags &= ~UIF_SUSPENDED; + state->info->flags |= UIF_INITIALIZED; } up(&state->sem); diff -NaurbB linux-2.6.9-pre1-bk18/drivers/usb/core/hcd.c linux-2.6.9_nbp/drivers/usb/core/hcd.c --- linux-2.6.9-pre1-bk18/drivers/usb/core/hcd.c 2005-08-15 01:14:41.000000000 +0200 +++ linux-2.6.9_nbp/drivers/usb/core/hcd.c 2004-11-21 23:46:50.000000000 +0100 @@ -1265,7 +1265,9 @@ */ if (!hcd->saw_irq) { dev_warn (hcd->self.controller, "Unlink after no-IRQ? " +#ifdef CONFIG_PCI "Different ACPI or APIC settings may help." +#endif "\n"); hcd->saw_irq = 1; } diff -NaurbB linux-2.6.9-pre1-bk18/drivers/usb/core/hcd.h linux-2.6.9_nbp/drivers/usb/core/hcd.h --- linux-2.6.9-pre1-bk18/drivers/usb/core/hcd.h 2005-08-15 01:14:41.000000000 +0200 +++ linux-2.6.9_nbp/drivers/usb/core/hcd.h 2004-09-14 12:25:06.000000000 +0200 @@ -79,7 +79,7 @@ int irq; /* irq allocated */ void *regs; /* device memory/io */ -#ifdef CONFIG_PCI +#if defined(CONFIG_PCI) || defined(CONFIG_USB_PXA) int region; /* pci region for regs */ u32 pci_state [16]; /* for PM state save */ #endif @@ -219,7 +219,7 @@ extern void usb_bus_init (struct usb_bus *bus); extern int usb_rh_status_dequeue (struct usb_hcd *hcd, struct urb *urb); -#ifdef CONFIG_PCI +#if defined(CONFIG_PCI) struct pci_dev; struct pci_device_id; extern int usb_hcd_pci_probe (struct pci_dev *dev, @@ -232,7 +232,17 @@ #endif /* CONFIG_PM */ #endif /* CONFIG_PCI */ +#if defined(CONFIG_USB_PXA) +extern int usb_hcd_pci_probe(struct hc_driver *drv, struct device *dev); +extern void usb_hcd_pci_remove(struct device *dev); + +#ifdef CONFIG_PM +extern int usb_hcd_pci_suspend(struct device *dev, u32 state); +extern int usb_hcd_pci_resume(struct device *dev); +#endif /* CONFIG_PM */ + +#endif /* pci-ish (pdev null is ok) buffer alloc/mapping support */ int hcd_buffer_create (struct usb_hcd *hcd); void hcd_buffer_destroy (struct usb_hcd *hcd); diff -NaurbB linux-2.6.9-pre1-bk18/drivers/usb/core/hub.c linux-2.6.9_nbp/drivers/usb/core/hub.c --- linux-2.6.9-pre1-bk18/drivers/usb/core/hub.c 2005-08-15 01:14:41.000000000 +0200 +++ linux-2.6.9_nbp/drivers/usb/core/hub.c 2004-11-21 23:46:50.000000000 +0100 @@ -2011,7 +2011,7 @@ hdev->bus->b_hnp_enable = 0; } - retval = clear_port_feature(hdev, port, USB_PORT_FEAT_SUSPEND); + retval = clear_port_feature(hdev, port + 1, USB_PORT_FEAT_SUSPEND); if (retval < 0 && retval != -EPIPE) dev_dbg(&udev->dev, "can't clear suspend; %d\n", retval); diff -NaurbB linux-2.6.9-pre1-bk18/drivers/usb/core/message.c linux-2.6.9_nbp/drivers/usb/core/message.c --- linux-2.6.9-pre1-bk18/drivers/usb/core/message.c 2005-08-15 01:14:41.000000000 +0200 +++ linux-2.6.9_nbp/drivers/usb/core/message.c 2004-11-22 18:30:14.000000000 +0100 @@ -239,14 +239,16 @@ // BUG (); } - if (urb->status && urb->status != -ECONNRESET) { + if (io->status == 0 && urb->status && urb->status != -ECONNRESET) { int i, found, status; io->status = urb->status; /* the previous urbs, and this one, completed already. * unlink pending urbs so they won't rx/tx bad data. + * careful: unlink can sometimes be synchronous... */ + spin_unlock (&io->lock); for (i = 0, found = 0; i < io->entries; i++) { if (!io->urbs [i] || !io->urbs [i]->dev) continue; @@ -259,6 +261,7 @@ } else if (urb == io->urbs [i]) found = 1; } + spin_lock (&io->lock); } urb->dev = NULL; @@ -520,6 +523,7 @@ int i; io->status = -ECONNRESET; + spin_unlock (&io->lock); for (i = 0; i < io->entries; i++) { int retval; @@ -530,6 +534,7 @@ dev_warn (&io->dev->dev, "%s, unlink --> %d\n", __FUNCTION__, retval); } + spin_lock (&io->lock); } spin_unlock_irqrestore (&io->lock, flags); } diff -NaurbB linux-2.6.9-pre1-bk18/drivers/usb/core/usb.c linux-2.6.9_nbp/drivers/usb/core/usb.c --- linux-2.6.9-pre1-bk18/drivers/usb/core/usb.c 2005-08-15 01:14:33.000000000 +0200 +++ linux-2.6.9_nbp/drivers/usb/core/usb.c 2004-09-14 12:25:06.000000000 +0200 @@ -948,6 +948,8 @@ return -1; } +EXPORT_SYMBOL(usb_new_device); + /** * usb_buffer_alloc - allocate dma-consistent buffer for URB_NO_xxx_DMA_MAP * @dev: device the buffer will be used with diff -NaurbB linux-2.6.9-pre1-bk18/drivers/usb/host/hc_sl811.c linux-2.6.9_nbp/drivers/usb/host/hc_sl811.c --- linux-2.6.9-pre1-bk18/drivers/usb/host/hc_sl811.c 2005-08-15 01:14:33.000000000 +0200 +++ linux-2.6.9_nbp/drivers/usb/host/hc_sl811.c 2004-09-14 12:25:06.000000000 +0200 @@ -40,6 +40,8 @@ #include #include "../core/hcd.h" +#include + #undef HC_URB_TIMEOUT #undef HC_SWITCH_INT #undef HC_ENABLE_ISOC @@ -1205,7 +1207,7 @@ } usb_deregister_bus (hci->bus); - usb_put_bus (hci->bus); + //usb_put_bus (hci->bus); // cannot find definition in kernel? list_del_init (&hci->hci_hcd_list); @@ -1226,8 +1228,15 @@ *****************************************************************/ void init_irq (void) { +#ifdef CONFIG_ARCH_KARO GPDR &= ~(1 << 13); - set_GPIO_IRQ_edge (1 << 13, GPIO_RISING_EDGE); + set_GPIO_IRQ_edge (1 << 13, GPIO_RISING_EDG); +#endif + +#ifdef CONFIG_MACH_NETBOOKPRO + set_irq_type(IRQ_GPIO(2), IRQT_RISING); +#endif + } /***************************************************************** diff -NaurbB linux-2.6.9-pre1-bk18/drivers/usb/host/Kconfig linux-2.6.9_nbp/drivers/usb/host/Kconfig --- linux-2.6.9-pre1-bk18/drivers/usb/host/Kconfig 2005-08-15 01:14:41.000000000 +0200 +++ linux-2.6.9_nbp/drivers/usb/host/Kconfig 2004-11-21 23:46:50.000000000 +0100 @@ -82,14 +82,16 @@ To compile this driver as a module, choose M here: the module will be called uhci-hcd. -config USB_SL811HS - tristate "SL811HS support" - depends on ARM && USB +config USB_SL811_HCD + tristate "SL811HS HCD support" + depends on USB + default N help - Say Y here if you have a SL811HS USB host controller in your system. - - If you do not know what this is, please say N. + The SL811HS is a single-port USB controller that supports either + host side or peripheral side roles. Enable this option if your + board has this chip, and you want to use it as a host controller. + If unsure, say N. To compile this driver as a module, choose M here: the - module will be called hc_sl811. + module will be called sl811-hcd. diff -NaurbB linux-2.6.9-pre1-bk18/drivers/usb/host/Makefile linux-2.6.9_nbp/drivers/usb/host/Makefile --- linux-2.6.9-pre1-bk18/drivers/usb/host/Makefile 2004-08-14 07:38:07.000000000 +0200 +++ linux-2.6.9_nbp/drivers/usb/host/Makefile 2004-11-21 23:46:50.000000000 +0100 @@ -6,5 +6,4 @@ obj-$(CONFIG_USB_EHCI_HCD) += ehci-hcd.o obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o - -obj-$(CONFIG_USB_SL811HS) += hc_sl811.o +obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o diff -NaurbB linux-2.6.9-pre1-bk18/drivers/usb/host/ohci-emu.c linux-2.6.9_nbp/drivers/usb/host/ohci-emu.c --- linux-2.6.9-pre1-bk18/drivers/usb/host/ohci-emu.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/drivers/usb/host/ohci-emu.c 2004-09-14 12:25:06.000000000 +0200 @@ -0,0 +1,1092 @@ +#include + +#define HcControl (ohci->regs->control) +#define HcCmdStatus (ohci->regs->cmdstatus) +#define HcFmRemaining (ohci->regs->fmremaining) +#define HcFmNumber (ohci->regs->fmnumber) +#define HcFmInterval (ohci->regs->fminterval) +#define HcPeriodicStart (ohci->regs->periodicstart) +#define HcLsThreshold (ohci->regs->lsthresh) +#define HcRhStatus (ohci->regs->roothub.status) +#define HcRhPortStatus (ohci->regs->roothub.portstatus) +#define HcIntrEnable (ohci->regs->intrenable) +#define HcIntrStatus (ohci->regs->intrstatus) +#define HcControlHeadED (ohci->regs->ed_controlhead) +#define HcBulkHeadED (ohci->regs->ed_bulkhead) +#define HcDoneHead (ohci->regs->donehead) +#define HcControlCurrentED (ohci->regs->ed_controlcurrent) +#define HcBulkCurrentED (ohci->regs->ed_bulkcurrent) +#define HcPeriodCurrentED (ohci->regs->ed_periodcurrent) +#define HccaDoneHead (ohci->hcca->done_head) +#define HccaFrameNumber (ohci->hcca->frame_no) +#define HccaInterruptTable (ohci->hcca->int_table) + +static inline void hc_sl811_write_regs(struct hc_sl811_dev *dev, u8 regno, const void *buf, u8 count) +{ + int i; + const u8 *data = buf; + + HC_SL811_WRITE_ADDR(dev, regno); + for (i = 0; i < count; i++) { + HC_SL811_WRITE_DATA(dev, data[i]); + } +} + +static inline void hc_sl811_read_regs(struct hc_sl811_dev *dev, u8 regno, void *buf, u8 count) +{ + int i; + u8 *data = buf; + + HC_SL811_WRITE_ADDR(dev, regno); + for (i = 0; i < count; i++) { + data[i] = HC_SL811_READ_DATA(dev); + } +} + +#if 0 +static inline u32 ohci_read_fm_remaining(struct ohci_hcd *ohci) +{ + u32 val; + struct usb_hcd *hcd = ohci_to_hcd(ohci); + struct hc_sl811_dev *dev = hcd_to_sl811_dev(hcd); + + val = hc_sl811_read_reg(dev, SL11H_CTLREG2) * 64; + + return val; +} +#endif + +static inline u32 TD_BUF_LEN(struct td *td) +{ + u32 len = 0; + u32 cbp = td->hwCBP; + + if (cbp != 0) { + u32 be = td->hwBE; + + if (unlikely((cbp ^ be) & PAGE_MASK)) { + // CBP and BE in different pages + // length till end of first page + length in second page + len = (PAGE_SIZE - (cbp & ~PAGE_MASK)) + (be & ~PAGE_MASK) + 1; + DPRINTK("%s: CBP=%08x, BE=%08x, len=%08x\n", __FUNCTION__, + cbp, be, len); + print_debug_buffer(); + } else { + len = be - cbp + 1; + } + } + + return len; +} + +#if DEBUG_BUF_SIZE > 0 +static inline int TD_BUF_OFFSET(struct td *td) +{ + int offset = 0; + u32 cbp = td->hwCBP; + u32 be = td->hwBE; + + if (cbp != 0) { + if (unlikely((cbp ^ td->data_dma) & PAGE_MASK)) { + offset = PAGE_SIZE + (be & ~PAGE_MASK); + DPRINTK("%s: cbp=%08x, be=%08x, td->data_dma=%08x, offset=%08x\n", + __FUNCTION__, cbp, be, td->data_dma, offset); + print_debug_buffer(); + } else { + offset = cbp - td->data_dma; + } + } + + return offset; +} +#endif + +#define ED_MPS (((1 << 10) - 1) << 16) +#define ed_pid(info) (((info) & (ED_IN | ED_OUT)) >> 11) +#define td_pid(info) (((info) & TD_DP) >> 19) +#define ed_mps(i) (((i) & ED_MPS) >> 16) +#define ed_fa(info) ((info) & ((1 << 7) - 1)) +#define ed_en(info) (((info) >> 7) & ((1 << 4) - 1)) + +static const u8 ed_td_to_pid[4][4] = { + { PID_SETUP, PID_OUT, PID_IN, 0 }, // PID from TD + { PID_OUT, PID_OUT, PID_OUT, PID_OUT }, // PID from ED + { PID_IN, PID_IN, PID_IN, PID_IN }, // PID from ED + { PID_SETUP, PID_OUT, PID_IN, 0 }, // PID from TD +}; + +static inline u32 DMA_XFER_SIZE(u32 addr, u32 len) +{ + u32 count = len; + + if (unlikely(((addr & ~PAGE_MASK) + len) > PAGE_SIZE)) { + count = PAGE_ALIGN(addr + len) - addr; + DPRINTK("%s: addr: %08x, len: %08x count: %08x\n", __FUNCTION__, + addr, len, count); + print_debug_buffer(); + } + + return count; +} + +#define DO_DMA(ch,len) \ + DCMD(ch) = DMA_FLAGS | (DCMD_LENGTH & len); \ + DCSR(ch) |= DCSR_RUN; \ + while (!(DCSR(ch) & DCSR_STOPSTATE)); \ + DCSR(ch) = DCSR_NODESC + +#ifdef USE_DMA +static inline void sl811_dma_recv_data(struct usb_hcd *hcd, struct td *td, + struct hc_sl811_dev *dev, int count) +{ + const unsigned int DMA_FLAGS = DCMD_INCTRGADDR | DCMD_WIDTH1 | DCMD_BURST32; + u32 dma_buf = td->hwCBP; + int align = dma_buf & 7; + int dma_ch = dev->dma; + int len = count; + + WARN_ON(dma_buf == 0); + + if (dma_ch < 0) { + printk(KERN_ERR "%s: DMA unavailable\n", __FUNCTION__); + return; + } + + DCSR(dma_ch) = DCSR_NODESC; + + HC_SL811_WRITE_ADDR(dev, SL11H_DATA_START); + while (len > 0) { + u32 dma_len = DMA_XFER_SIZE(dma_buf, len); + + DSADR(dma_ch) = dev->dma_addr; + DTADR(dma_ch) = dma_buf; // FIXME: work around DMA buffer alignment issue + + DO_DMA(dma_ch, dma_len); + + len -= dma_len; + if (likely(len == 0)) { + break; + } else { + dma_buf = td->hwBE & ~PAGE_MASK; + } + } + dma_sync_single(hcd->self.controller, dma_buf, count, DMA_FROM_DEVICE); +} + +static inline void sl811_dma_send_data(struct usb_hcd *hcd, struct td *td, + struct hc_sl811_dev *dev, int count) +{ + const unsigned int DMA_FLAGS = DCMD_INCSRCADDR | DCMD_WIDTH1 | DCMD_BURST32; + u32 dma_buf = td->hwCBP; + int align = dma_buf & 7; + int dma_ch = dev->dma; + int len = count; + + if (dma_ch < 0) { + printk(KERN_ERR "%s: DMA unavailable\n", __FUNCTION__); + return; + } + + dma_sync_single(hcd->self.controller, dma_buf, count, DMA_TO_DEVICE); + DCSR(dma_ch) = DCSR_NODESC; + + HC_SL811_WRITE_ADDR(dev, SL11H_DATA_START); + while (len > 0) { + u32 dma_len = DMA_XFER_SIZE(dma_buf, len); + + DSADR(dma_ch) = dma_buf; // FIXME: work around DMA buffer alignment issue + DTADR(dma_ch) = dev->dma_addr; + + DO_DMA(dma_ch, dma_len); + + len -= dma_len; + if (likely(len == 0)) { + break; + } else { + dma_buf = td->hwBE & ~PAGE_MASK; + } + } +} +#else +#if 0 +static inline void dma_move_data(int chan, u32 src, u32 dst, u32 dma_len) +{ + const unsigned int DMA_FLAGS = DCMD_INCTRGADDR | DCMD_INCSRCADDR | DCMD_BURST32; + + if (chan < 0) { + return; + } + + DPRINTK("%s: Moving %d byte from %08x to %08x\n", __FUNCTION__, + dma_len, src, dst); + DCSR(chan) = DCSR_NODESC; + DSADR(chan) = src; + DTADR(chan) = dst; + DCMD(chan) = DMA_FLAGS | (DCMD_LENGTH & dma_len); + DPRINTK("%s: DMA started: DCSR=%08x, DSADR=%08x, DTADR=%08x, DCMD=%08x\n", __FUNCTION__, + DCSR(chan), DSADR(chan), DTADR(chan), DCMD(chan)); + DCSR(chan) |= DCSR_RUN; + while (!(DCSR(chan) & DCSR_STOPSTATE)); + DPRINTK("%s: DMA stopped: DCSR=%08x, DSADR=%08x, DTADR=%08x, DCMD=%08x\n", __FUNCTION__, + DCSR(chan), DSADR(chan), DTADR(chan), DCMD(chan)); + DCSR(chan) = DCSR_NODESC; +} + +static inline void sl811_dma_recv_data(struct usb_hcd *hcd, struct td *td, + struct hc_sl811_dev *dev, int count) +{ + int dma_ch = dev->dma; + char *buf = dev->dma_buf; + dma_addr_t dma_buf = td->hwCBP; + int align = dma_buf & 7; + int len = TD_BUF_LEN(td); +#if DEBUG_BUF_SIZE > 0 + int offset = TD_BUF_OFFSET(td); +#endif + u8 sl811_start; + int i; + + DPRINTK("%s: data_dma=%08x, cbp=%08x\n", __FUNCTION__, td->data_dma, td->hwCBP); + + BUG_ON(dma_ch < 0); + + if (len == 0) { + // nothing to to, if no transfer buffer given + DEBUG_STORE(0, 0x0d); + return; + } + + sl811_start = hc_sl811_read_reg(dev, SL11H_BUFADDRREG); + + if (count > len || count > (SL811_MAP_SIZE - sl811_start)) { + DPRINTK("%s: count=%d, len=%d, start=0x%02x\n", __FUNCTION__, + count, len, sl811_start); + BUG(); + } + + hc_sl811_read_regs(dev, sl811_start, buf + align, count); + consistent_sync(buf, count + align, PCI_DMA_TODEVICE); + DEBUG_STORE((offset << 8) | count, 0x0d); + for (i = 0; i < count; i++) { + DEBUG_STORE(buf[i], 0x0c); + } + + if (align) { + DPRINTK("%s: Copying %d byte from %08x to %08x\n", __FUNCTION__, + align, dma_buf - align, dev->dma_bus_addr); + dma_move_data(dma_ch, dma_buf - align, dev->dma_bus_addr, align); + } + dma_move_data(dma_ch, dev->dma_bus_addr, dma_buf, count + align); +} + +static inline void sl811_dma_send_data(struct usb_hcd *hcd, struct td *td, + struct hc_sl811_dev *dev, int count) +{ + int dma_ch = dev->dma; + char *buf = dev->dma_buf; + dma_addr_t dma_buf = td->hwCBP; + int align = dma_buf & 7; + int len = TD_BUF_LEN(td); +#if DEBUG_BUF_SIZE > 0 + int offset = TD_BUF_OFFSET(td); +#endif + int i; + + BUG_ON(dma_ch < 0); + + if (len == 0) { + // nothing to to, if no transfer buffer given + DEBUG_STORE(0, 0x0e); + return; + } + + BUG_ON(count > len || count > SL811_BUF_SIZE); + dma_move_data(dma_ch, dma_buf - align, dev->dma_bus_addr, count + align); + consistent_sync(buf, count + align, PCI_DMA_FROMDEVICE); + + DEBUG_STORE((offset << 8) | count, 0x0e); + for (i = 0; i < count; i++) { + DEBUG_STORE(buf[i + align], 0x0c); + } + hc_sl811_write_regs(dev, SL11H_DATA_START, buf + align, count); +} +#else +static inline void sl811_dma_recv_data(struct usb_hcd *hcd, struct td *td, + struct hc_sl811_dev *dev, int count) +{ + char *buf = bus_to_virt(le32_to_cpu(td->hwCBP)); + int len = TD_BUF_LEN(td); +#if DEBUG_BUF_SIZE > 0 + int offset = TD_BUF_OFFSET(td); +#endif + u8 sl811_start = hc_sl811_read_reg(dev, SL11H_BUFADDRREG); + int i; + + if (len == 0) { + // nothing to to, if no transfer buffer given + DEBUG_STORE(0, 0x0d); + return; + } + + BUG_ON(count > len || count > (SL811_MAP_SIZE - sl811_start)); + BUG_ON(buf == NULL); + + DEBUG_STORE((offset << 8) | count, 0x0d); + hc_sl811_read_regs(dev, sl811_start, buf, count); + dma_sync_single(hcd->self.controller, td->hwCBP, count, DMA_TO_DEVICE); + for (i = 0; i < count; i++) { + DEBUG_STORE(buf[i], 0x0c); + } +} + +static inline void sl811_dma_send_data(struct usb_hcd *hcd, struct td *td, + struct hc_sl811_dev *dev, int count) +{ + char *buf = bus_to_virt(le32_to_cpu(td->hwCBP)); + int len = TD_BUF_LEN(td); +#if DEBUG_BUF_SIZE > 0 + int offset = TD_BUF_OFFSET(td); +#endif + int i; + + if (len == 0) { + // nothing to to, if no transfer buffer given + DEBUG_STORE(0, 0x0e); + return; + } + + BUG_ON(count > len || count > SL811_BUF_SIZE); + + DEBUG_STORE((offset << 8) | count, 0x0e); + for (i = 0; i < count; i++) { + DEBUG_STORE(buf[i], 0x0c); + } + hc_sl811_write_regs(dev, SL11H_DATA_START, buf, count); +} +#endif // 1 +#endif // USE_DMA + +static inline void start_xfer(struct usb_hcd *hcd, struct hc_sl811_dev *dev, + struct xfer_buf *buf, struct td *td) +{ + if (buf->hc & SL11H_HCTLMASK_WRITE) { + // transfer data from TD buffer to SL811 buffer memory + sl811_dma_send_data(hcd, td, dev, buf->len); + } + hc_sl811_write_reg(dev, SL11H_BUFADDRREG, SL11H_DATA_START); + hc_sl811_write_regs(dev, SL11H_BUFLNTHREG, &buf->len, 3); + //hc_sl811_set_mask(dev, SL11H_INTENBLREG, SL11H_INTMASK_XFERDONE); + hc_sl811_write_reg(dev, SL11H_HOSTCTLREG, buf->hc); + + DEBUG_STORE((((((buf->len << 8) | buf->pid_ep) << 8) | buf->dev_addr) << 8) | buf->hc, 0xd0); + dev->last_td = td; +} + +static inline int process_td(struct ohci_hcd *ohci, struct td *td) +{ + struct hc_sl811_dev *dev = ohci_to_sl811_dev(ohci); + struct ed *ed = td->ed; + u8 pid; + struct xfer_buf *buf; + u32 ed_flags = ed->hwINFO; + u32 td_flags = td->hwINFO; + u8 hc_flags = SL11H_HCTLMASK_ARM | SL11H_HCTLMASK_ENBLEP; + u32 max_packet_size = ed_mps(ed->hwINFO); + u32 buf_len = TD_BUF_LEN(td); + int len; +#if DEBUG_BUF_SIZE > 0 + struct usb_hcd *hcd = ohci_to_hcd(ohci); +#endif + + buf = &dev->xfer_buf; + pid = ed_td_to_pid[ed_pid(ed_flags)][td_pid(td_flags)]; + //DEBUG_STORE((((ed_pid(ed_flags) << 8) | td_pid(td_flags)) << 8) | pid, 0xcc); + WARN_ON(pid == 0); + len = max_packet_size; + if (pid != PID_IN) { + hc_flags |= SL11H_HCTLMASK_WRITE; + } + if (len > buf_len) { + len = buf_len; + } + + if (ed_flags & ED_LOWSPEED) { + if (HcFmRemaining < HcLsThreshold) { + return 0; + } + if (!(HcRhPortStatus[0] & RH_PS_LSDA)) { + hc_flags |= SL11H_HCTLMASK_PREAMBLE; + } + } + if (ed_flags & ED_ISO) { + BUG(); + hc_flags |= SL11H_HCTLMASK_ISOCH; + } + if (pid != PID_SETUP) { + if ((td_flags & TD_T) == TD_T_DATA1) { + hc_flags |= SL11H_HCTLMASK_SEQ; + } else if ((td_flags & TD_T) != TD_T_DATA0) { + hc_flags |= ((ed->hwHeadP & ED_C) ? SL11H_HCTLMASK_SEQ : 0); + } + } + buf->hc = hc_flags; + buf->len = len; + buf->pid_ep = (pid << 4) | ed_en(ed_flags); + buf->dev_addr = ed_fa(ed_flags); + + DPRINTK( "%s: pid=%d, len=%d\n", __func__, pid, len ); + + start_xfer(ohci_to_hcd(ohci), dev, buf, td); + + return 1; +} + +#define ED_HEADP(ed) (ed->hwHeadP & ~0xf) +#define ED_TAILP(ed) (ed->hwTailP & ~0xf) +#define ED_NEXTED(ed) (ed->hwNextED & ~0xf) + +static inline struct td *find_td(struct ohci_hcd *ohci, struct hc_sl811_dev *dev, struct ed *ed) +{ + struct td *td = NULL; + + for (td = dma_to_td(ohci, ED_HEADP(ed)); td != NULL; td = dma_to_td(ohci, td->hwNextTD)) { + if (ED_TAILP(ed) == td->td_dma) { + // TD queue empty + td = NULL; + break; + } + if (td->hwINFO & TD_DONE) { + printk("%s: TD @ %08x already done\n", __FUNCTION__, (u32)td); + } else { + break; + } + } + + return td; +} + +/* + * process_ed_list + * + * generic ED list processing routine + * + * Return codes: 1 TD found and processed + * 0 no TD needed processing + * <0 Error + */ +#define ED_HALTED(ed) ((ed)->hwHeadP & ED_H) +#define EP_SKIP(ed) ((ed) & ED_SKIP) +#define EP_ISO(ed) ((ed) & ED_ISO) +static int process_ed_list(struct ohci_hcd *ohci, u32 *head_reg) +{ + int ret = 0; + int done = 0; + struct td *td = NULL; + struct ed *ed = (struct ed *)(*head_reg); + struct hc_sl811_dev *dev = ohci_to_sl811_dev(ohci); + + // walk ED list and find TD that needs processing + do { + u32 ed_flags = ed->hwINFO; + + if (!EP_SKIP(ed_flags) && !ED_HALTED(ed) && + !(EP_ISO(ed_flags) && !(HcControl & OHCI_CTRL_IE))) { + td = find_td(ohci, dev, ed); + } + ed = ed->ed_next; + *head_reg = (u32)ed; + if (td != NULL) { + done = process_td(ohci, td); + ret = done; + } + done |= (ed == NULL); + } while (!done); + + return ret; +} + +/* + * process_ctrl_bulk_lists + * + * try to find a TD that needs processing in the ctrl or bulk ED list + * according to cbsr and cbc + * + * Return codes: 1 TD found and processed + * 0 no TD needed processing + * <0 Error + * + */ +static int process_ctrl_bulk_lists(struct ohci_hcd *ohci) +{ + struct hc_sl811_dev *dev = ohci_to_sl811_dev(ohci); + //struct ohci_hcd *ohci = hcd_to_ohci(hcd); + int ret = 0; + int cbsr = HcControl & OHCI_CTRL_CBSR; // ctrl/bulk ratio + int bulk_ena = (HcControl & OHCI_CTRL_BLE) && (HcBulkCurrentED != 0); +#if DEBUG_BUF_SIZE > 0 + struct usb_hcd *hcd = ohci_to_hcd(ohci); +#endif + // reset ctrl/bulk ratio counter, if bulk processing disabled or + // no bulk descriptors to process + if (!bulk_ena) { + dev->cbc = 0; + } + // process ctrl EDs until cbsr limit reached + if ((HcControl & OHCI_CTRL_CLE) && (dev->cbc <= cbsr)) { + if (HcControlCurrentED != 0) { + DDPRINTK( "%s: process HcControlCurrentED\n", __func__ ); + //DEBUG_STORE(HcControlCurrentED, 0xf0); + ret = process_ed_list(ohci, &HcControlCurrentED); + if (ret > 0) { + dev->cbc++; + HcCmdStatus |= OHCI_CLF; + return ret; + } + if (ret != 0) { + return ret; + } + } + } + + if (bulk_ena) { + //DEBUG_STORE(HcBulkCurrentED, 0xf1); + DDPRINTK( "%s: process HcBulkCurrentED\n", __func__ ); + ret = process_ed_list(ohci, &HcBulkCurrentED); + if (ret > 0) { + HcCmdStatus |= OHCI_BLF; + dev->cbc = 0; + } + if (ret != 0) { + return ret; + } + } + + return ret; +} + +#define OHCI_FR ((1 << 14) - 1) +#define OHCI_FRT (1 << 31) +static inline u32 update_fm_remaining(struct ohci_hcd *ohci) +{ + struct hc_sl811_dev *dev = ohci_to_sl811_dev(ohci); + + u32 fmremaining = hc_sl811_read_reg(dev, SL11H_CTLREG2) << 6; + HcFmRemaining = (HcFmRemaining & ~OHCI_FR) | fmremaining; + + return fmremaining; +} + +/* + * process_ed_lists + * + * try to find a TD that needs processing in the appropriate ED list + * depending on frame number and HcFmRemaining... + * + * Return codes: 1 TD found and processed + * 0 no TD needed processing + * <0 Error + * + */ +#define OHCI_PS ((1 << 14) - 1) +static int process_ed_lists(struct ohci_hcd *ohci) +{ + int ret = 0; + int periodicstart = HcPeriodicStart & OHCI_PS; // limit for ctrl/bulk processing + u32 fmremaining = update_fm_remaining(ohci); + + DDPRINTK( "%s: fmrem=%08x\n", __func__, fmremaining ); + + /* + * process cbsr control desc. + * process bulk desc. + */ + // do control/bulk processing until periodicstart reached + if (fmremaining > periodicstart) { + DDPRINTK( "%s: BULK\n", __func__ ); + ret = process_ctrl_bulk_lists(ohci); + if (ret != 0) { + return ret; + } + fmremaining = update_fm_remaining(ohci); + } + DDPRINTK( "%s: fmrem=%08x\n", __func__, fmremaining ); + + // do interrupt/iso processing + if ((HcControl & OHCI_CTRL_PLE) && (HcPeriodCurrentED != 0)) { + DDPRINTK( "%s: ISO\n", __func__ ); + ret = process_ed_list(ohci, &HcPeriodCurrentED); + if (ret != 0) { + return ret; + } + fmremaining = update_fm_remaining(ohci); + } + DDPRINTK( "%s: fmrem=%08x\n", __func__, fmremaining ); + + if (fmremaining > 0) { + // do control/bulk processing again until end of frame + DDPRINTK( "%s: CTRL/BULK\n", __func__ ); + ret = process_ctrl_bulk_lists(ohci); + } + + return ret; +} + +static inline void retire_td(struct usb_hcd *hcd, struct td *td, struct ed *ed) +{ + struct hc_sl811_dev *dev = hcd_to_sl811_dev(hcd); + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + u32 ed_flags = ed->hwINFO; + u32 td_flags = td->hwINFO; + u32 td_dma = td->td_dma; + + td->hwINFO |= TD_DONE; + if (ed_flags & ED_ISO) { + ed->hwHeadP = td->hwNextTD; + } else if ((td_flags & TD_T) == TD_T_DATA0) { + ed->hwHeadP = td->hwNextTD & ~ED_C; + } else if ((td_flags & TD_T) == TD_T_DATA1) { + ed->hwHeadP = td->hwNextTD | ED_C; + } + DEBUG_STORE(ed->hwHeadP, 0xc2); + td->hwNextTD = HcDoneHead; + HcDoneHead = td_dma; + + if (!(HcIntrStatus & OHCI_INTR_WDH)) { + u32 int_delay = (td_flags & TD_DI) >> 21; + u32 done_head; + + if (int_delay == 7) { + return; + } + if (int_delay < dev->intrdelay) { + dev->intrdelay = int_delay; + } + if (dev->intrdelay > 0) { + return; + } + done_head = HcDoneHead; + done_head |= ((HcIntrStatus & ~OHCI_INTR_WDH) ? 1 : 0); + + HccaDoneHead = done_head; + HcDoneHead = 0; + + HcIntrStatus |= OHCI_INTR_WDH; + } +} + +static inline void update_data_toggle(struct usb_hcd *hcd, struct td *td) +{ + u32 td_flags = td->hwINFO; + u32 ed_carry = td->ed->hwHeadP & ED_C; + + if ((td_flags & TD_T) == TD_T_TOGGLE) { + DPRINTK("%s: Using Data toggle %d from ED\n", __FUNCTION__, ed_carry); + td_flags |= (ed_carry ? TD_T_DATA0 : TD_T_DATA1); + DEBUG_STORE((u32)td | ed_carry, 0x42); + } else if ((td_flags & TD_T) == TD_T_DATA0) { + td_flags = (td_flags & ~TD_T) | TD_T_DATA1; + DEBUG_STORE((u32)td, 0x41); + } else { + td_flags = (td_flags & ~TD_T) | TD_T_DATA0; + DEBUG_STORE((u32)td, 0x40); + } + td->hwINFO = td_flags; +} + +static inline int check_data_toggle(struct td *td, struct xfer_buf *done_buf, struct usb_hcd *hcd) +{ + int ret = 0; + int pkt_stat = done_buf->pkt_stat; + u32 td_flags = td->hwINFO & TD_T; + u32 ed_carry = td->ed->hwHeadP & ED_C; + int data1 = pkt_stat & SL11H_STATMASK_SEQ; + int err = (pkt_stat & SL811_PKT_ERR_MASK) ^ data1; + + if (err) { + if (pkt_stat != SL11H_STATMASK_NAK) { + DEBUG_STORE(0xffffff00 | pkt_stat, 0xc0); + } + return -1; + } + if (td_flags == TD_T_TOGGLE) { + ret = ((ed_carry && data1) || (!ed_carry && !data1)); + } else { + ret = (((td_flags == TD_T_DATA0) && !data1) || ((td_flags == TD_T_DATA1) && data1)); + } + DEBUG_STORE((((((ret << 8) | ed_carry) << 8) | (td_flags >> 24)) << 8) | pkt_stat, 0xc0); + if (!ret) { + DPRINTK("%s: pkt_stat=%02x, ret=%d ed_c=%d, td_d=%d\n", __FUNCTION__, + pkt_stat, ret, ed_carry, td_flags >> 24); + print_debug_buffer(); + } + + return ret; +} + +static inline int update_cbp(struct td *td, int len) +{ + u32 cbp = td->hwCBP; + u32 be = td->hwBE; + int finished = 0; + + if (cbp == 0) { + return 1; + } + if (TD_BUF_LEN(td) == len) { + cbp = 0; + finished = 1; + } else { + u32 chunk_size = DMA_XFER_SIZE(cbp, len); + + DPRINTK("%s: len=%d, chunk_size=%d buf_len=%d\n", __FUNCTION__, + len, chunk_size, TD_BUF_LEN(td)); + + if (chunk_size == len) { + cbp += len; + } else { + DPRINTK("%s: len=%04x chunk_size=%04x buf_len=%04x cbp=%08x be=%08x\n", + __FUNCTION__, len, chunk_size, TD_BUF_LEN(td), cbp, be); + cbp = (be & ~PAGE_MASK) | (len - chunk_size); + print_debug_buffer(); + } + finished = cbp == be; + } + td->hwCBP = cbp; + + return finished; +} + +#define TD_CC_SET(info, cc) (info) = ((info) & ~TD_CC) | (((cc) << 28) & TD_CC) + +#define TD_EC_GET(info) (((info) & TD_EC) >> 26) +#define TD_EC_SET(info, ec) (info) = ((info) & ~TD_EC) | (((ec) << 26) & TD_EC) + +static inline u32 update_td_status(struct usb_hcd *hcd, struct xfer_buf *buf, struct td *td) +{ + u32 ohci_int_status = 0; + struct ed *ed = td->ed; + struct hc_sl811_dev *dev = hcd_to_sl811_dev(hcd); + u32 td_flags = td->hwINFO; + u8 pkt_stat = buf->pkt_stat; + u32 ed_flags = ed->hwINFO; + int iso = ed_flags & ED_ISO; + int td_finished = 0; + int td_halt = 0; + u32 cc = TD_CC_NOERROR; + + if (pkt_stat & (SL811_PKT_ERR_MASK & ~SL11H_STATMASK_NAK)) { + DEBUG_STORE((u32)td, 0xc3); + } + if (unlikely((pkt_stat & SL811_PKT_ERR_MASK) != 0)) { + if (pkt_stat & SL11H_STATMASK_NAK) { + return 0; + } else if (pkt_stat & SL11H_STATMASK_STALL) { + cc = TD_CC_STALL; + td_halt = 1; + } else if (pkt_stat & (SL11H_STATMASK_TMOUT | + SL11H_STATMASK_ERROR | + SL11H_STATMASK_SEQ)) { + int err_count = TD_EC_GET(td_flags) + 1; + + if (pkt_stat & SL11H_STATMASK_TMOUT) { + cc = TD_DEVNOTRESP; + } else if (pkt_stat & SL11H_STATMASK_SEQ) { + cc = TD_CC_DATATOGGLEM; + } else { + cc = TD_CC_CRC; + } + + DEBUG_STORE((u32)td | err_count, 0xe0); + TD_EC_SET(td->hwINFO, err_count); + if (err_count == 3) { + td_halt = 1; + } + } else if (pkt_stat & SL11H_STATMASK_OVF) { + cc = TD_DATAOVERRUN; + } + } else { + TD_EC_SET(td->hwINFO, 0); + td_finished = update_cbp(td, buf->len); + if (!td_finished && buf->len < ed_mps(ed_flags)) { + td_halt = 1; + if ((td_flags & TD_R)) { + char *pkt_types[] = { + "UNDEF", "OUT", "ACK", "DATA0", "PING", "SOF", "NYET", "DATA2", + "SPLIT", "IN", "NAK", "DATA1", "PREAMBLE/ERR", "SETUP", "STALL", "MDATA"}; + DPRINTK("%s: Short %s packet: %d of %d byte\n", __FUNCTION__, + pkt_types[ed_td_to_pid[ed_pid(ed_flags)][td_pid(td_flags)] & 0x0f], buf->len, ed_mps(ed_flags)); + if (likely((td->hwBE & PAGE_MASK) == ((td->hwCBP + buf->len - 1) & PAGE_MASK))) { + td->hwBE = td->hwCBP + buf->len - 1; + } else { + DPRINTK("%s: Adjusting hwBE %08x + %08x -> %08lx\n", + __FUNCTION__, td->hwBE, buf->len, + (td->hwBE & ~PAGE_MASK) + ((td->hwCBP + buf->len - 1) & ~PAGE_MASK)); + td->hwBE = (td->hwBE & ~PAGE_MASK) + ((td->hwCBP + buf->len - 1) & ~PAGE_MASK); + } + td->hwCBP = 0; + td_finished = 1; + } else { + DPRINTK("%s: Data underrun: %d byte transferred, %d byte requested\n", + __FUNCTION__, buf->len, ed_mps(ed_flags)); + cc = TD_DATAUNDERRUN; + } + } + } + TD_CC_SET(td->hwINFO, cc); + + if (pkt_stat & SL11H_STATMASK_ACK) { + update_data_toggle(hcd, td); + } + + if (td_halt && !td_finished) { + print_debug_buffer(); + } + if (td_halt && !iso) { + ed->hwHeadP |= ED_H; + td_finished = 1; + } + + if (td_finished) { + if (cc != TD_CC_NOERROR) { + dev->intrdelay = 0; + } + retire_td(hcd, td, ed); + } + + return ohci_int_status; +} + +static inline void kill_td(struct usb_hcd *hcd, struct td *td) +{ + retire_td(hcd, td, td->ed); +} + +static u32 finish_xfer(struct usb_hcd *hcd, struct hc_sl811_dev *dev, struct xfer_buf *done_buf) +{ + u32 int_status = 0; + struct td *td; + + if (dev->last_td != NULL) { + td = dev->last_td; + dev->last_td = NULL; +#if DEBUG_BUF_SIZE > 0 + u8 pid = done_buf->pid_ep; + u8 addr = done_buf->dev_addr; +#endif + hc_sl811_read_regs(dev, SL11H_PKTSTATREG, &done_buf->pkt_stat, 2); + + DPRINTK( "%s: td=%p, len=%d, stat=%04x\n", __func__, td, done_buf->count, done_buf->pkt_stat ); + + done_buf->len -= done_buf->count; + DEBUG_STORE((((((done_buf->len << 8) | pid) << 8) | addr) << 8) | done_buf->pkt_stat, 0xc1); + if (!(done_buf->hc & SL11H_HCTLMASK_WRITE)) { + //if (check_data_toggle(td, done_buf->pkt_stat, hcd)) { + if (check_data_toggle(td, done_buf, hcd)) { + done_buf->pkt_stat &= ~SL11H_STATMASK_SEQ; + } else { + done_buf->pkt_stat |= SL11H_STATMASK_SEQ; + } + //if (likely(done_buf->pkt_stat & SL811_PKT_ERR_MASK) == 0) { + if (likely((done_buf->pkt_stat & (SL811_PKT_ERR_MASK & ~SL11H_STATMASK_SEQ)) == 0)) { + // transfer data from SL811 buffer memory to TD buffer + if (done_buf->len > 0) { + sl811_dma_recv_data(hcd, td, dev, done_buf->len); + } + } + } else { + done_buf->pkt_stat &= ~SL11H_STATMASK_SEQ; + } + int_status = update_td_status(hcd, done_buf, td); + } + + return int_status; +} + +/* + * Interrupt routine: + * + * XFER_DONE interrupt: + * - do outstanding data transfer of current TD + * - prepare next TD, if any + * - postprocess last completed TD + * - find next TD to process + * + * SOF interrupt: + * - check current td pointer + * - kickstart an ED list processing, if NULL + * + * + */ +static irqreturn_t ohci_sl811_interrupt(int irq, void *data, struct pt_regs *pt_regs) +{ + int handled = 0; + struct usb_hcd *hcd = data; + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + struct hc_sl811_dev *dev = hcd_to_sl811_dev(hcd); + const int max_loops = 5; + int loop = 0; + u8 int_mask; + u8 int_status; + u8 addr_save = HC_SL811_READ_ADDR(dev); + + do { + u8 svc_mask; + + int_status = hc_sl811_read_reg(dev, SL11H_INTSTATREG); + int_mask = hc_sl811_read_reg(dev, SL11H_INTENBLREG); + svc_mask = int_status & int_mask; + + SET_GPIO(5); + if (loop == 0) { + DEBUG_STORE((((((int_status << 8) | int_mask) << 8) | svc_mask) << 8) | !!dev->last_td, 0x0f); + } + if (svc_mask == 0) { + if (!handled) { + DPRINTK("Spurious interrupt: status:mask=%02x:%02x\n", int_status, + int_mask); + print_debug_buffer(); + } + break; + } + handled = 1; + DDPRINTK("%s: Servicing: %02x(%02x:%02x)\n", __FUNCTION__, svc_mask, int_status, int_mask); +#ifdef SOF_B + if (svc_mask & SL11H_INTMASK_XFERDONE_B) { + DPRINTK( "%s: XFERDONE_B\n", __func__ ); + svc_mask &= ~SL11H_INTMASK_XFERDONE_B; + hc_sl811_clr_mask(dev, SL11H_INTENBLREG, SL11H_INTMASK_XFERDONE_B); + } +#endif + if (svc_mask & SL11H_INTMASK_XFERDONE) { + svc_mask &= ~SL11H_INTMASK_XFERDONE; + DPRINTK( "%s: XFERDONEB\n", __func__ ); + + if (dev->last_td != NULL) { + HcIntrStatus |= finish_xfer(hcd, dev, &dev->xfer_buf); + process_ed_lists(ohci); + } else { + //hc_sl811_clr_mask(dev, SL11H_INTENBLREG, SL11H_INTMASK_XFERDONE); + } + } + +#if 0 + if (svc_mask & SL11H_INTMASK_USBRESET) { + u8 ctl1 = hc_sl811_read_reg(dev, SL11H_CTLREG1) & SL11H_CTL1MASK_SUSPEND; + svc_mask &= ~SL11H_INTMASK_USBRESET; + + hc_sl811_clr_mask(dev, SL11H_INTENBLREG, SL11H_INTMASK_USBRESET); + if (ctl1) { + DPRINTK("%s: Resume Interrupt occured\n", __FUNCTION__); + if (HcRhStatus & RH_HS_DRWE) { + HcIntrStatus |= OHCI_INTR_RD; + } + } else { + int connected = hc_sl811_handle_insrmv(hcd); + + DPRINTK("%s: Device Present Interrupt occured: %d\n", __FUNCTION__, + connected); + hc_sl811_start_sof(hcd, connected); + } + } +#endif + if (svc_mask & SL11H_INTMASK_INSRMV) { + int connected; + + svc_mask &= ~SL11H_INTMASK_INSRMV; + int_mask &= ~SL11H_INTMASK_INSRMV; + + DPRINTK("%s: Device Insert/Remove occured\n", __FUNCTION__); + if (dev->last_td) { + DPRINTK("%s: Nuking last_td: %08x\n", __FUNCTION__, + (u32)dev->last_td); + kill_td(hcd, dev->last_td); + dev->last_td = NULL; + } + + hc_sl811_usb_reset(hcd, -1); + connected = hc_sl811_handle_insrmv(hcd); + DPRINTK("%s: Device %spresent\n", __FUNCTION__, connected ? "" : "not "); + hc_sl811_start_sof(hcd, connected); + } + + if (svc_mask & SL11H_INTMASK_SOFINTR) { + u16 frame_no = HcFmNumber + 1; + int int_no = frame_no & 0x1f; // index into interrupt table + + svc_mask &= ~SL11H_INTMASK_SOFINTR; + + if (dev->intrdelay > 0 && dev->intrdelay < 7) { + dev->intrdelay--; + if (dev->intrdelay == 0) { + HcIntrStatus |= OHCI_INTR_WDH; + } + } + HcFmRemaining = HcFmInterval & (OHCI_FR | OHCI_FRT); + HccaFrameNumber = HcFmNumber = frame_no; + if ((frame_no & 0x7fff) == 0) { + //DPRINTK("%s: Frame number overflow\n", __FUNCTION__); + HcIntrStatus |= OHCI_INTR_FNO; + } + HcIntrStatus |= OHCI_INTR_SF; + if (HcPeriodCurrentED != 0) { + int soc = (HcCmdStatus & OHCI_SOC) + (1 << 16); + + dev->last_td = NULL; + // scheduling overrun + DPRINTK("%s: Scheduling overrun at frame %04x\n", __FUNCTION__, + frame_no); + HcIntrStatus |= OHCI_INTR_SO; + HcCmdStatus = (HcCmdStatus & ~(OHCI_SOC | OHCI_CLF | OHCI_BLF)) | + (soc & OHCI_SOC); + HcControlCurrentED = 0; + HcBulkCurrentED = 0; + HcPeriodCurrentED = 0; + update_fm_remaining(ohci); + } + if ((HcControl & OHCI_CTRL_PLE) && (HccaInterruptTable[int_no] != 0)) { + HcPeriodCurrentED = (u32)ohci->periodic[int_no]; + } + if ((HcControl & OHCI_CTRL_CLE) && (HcControlCurrentED == 0)) { + if (HcCmdStatus & OHCI_CLF) { + HcCmdStatus &= ~OHCI_CLF; + HcControlCurrentED = HcControlHeadED; + } + } + if ((HcControl & OHCI_CTRL_BLE) && (HcBulkCurrentED == 0)) { + if (HcCmdStatus & OHCI_BLF) { + HcCmdStatus &= ~OHCI_BLF; + HcBulkCurrentED = HcBulkHeadED; + } + } + process_ed_lists(ohci); + } + + if (svc_mask) { + DPRINTK("%s: Unhandled SL811 interrupt %02x\n", __FUNCTION__, + svc_mask); + hc_sl811_clr_mask(dev, SL11H_INTENBLREG, svc_mask); + } + // acknowledge all interrupts we have serviced right now + + //DPRINTK("%s: Acknowledging interrupts %02x(svc:%02x,int:%02x,msk:%02x)\n", __FUNCTION__, int_status & int_mask, svc_mask, int_status, int_mask); + hc_sl811_write_reg(dev, SL11H_INTSTATREG, int_status & int_mask); + DEBUG_STORE(((loop << 8) | (int_status & int_mask)), 0x0b); + PULSE_GPIO(4); + } while (++loop < max_loops); + + CLR_GPIO(5); + HC_SL811_WRITE_ADDR(dev, addr_save); + if (HcIntrEnable & OHCI_INTR_MIE) { + if (HcIntrStatus & HcIntrEnable) { + u32 ohci_intr_status = HcIntrStatus & HcIntrEnable; + + //DPRINTK("%s: Calling ohci_irq(%08x)\n", __FUNCTION__, ohci_intr_status); + if (usb_hcd_irq(hcd->irq, hcd, pt_regs) != IRQ_HANDLED) { + DPRINTK("%s: Spurious OHCI Interrupt: %08x\n", __FUNCTION__, + ohci_intr_status); + } + } + } + return IRQ_RETVAL(handled); + //return IRQ_RETVAL(1); +} diff -NaurbB linux-2.6.9-pre1-bk18/drivers/usb/host/ohci-hcd.c linux-2.6.9_nbp/drivers/usb/host/ohci-hcd.c --- linux-2.6.9-pre1-bk18/drivers/usb/host/ohci-hcd.c 2005-08-15 01:14:41.000000000 +0200 +++ linux-2.6.9_nbp/drivers/usb/host/ohci-hcd.c 2004-09-19 13:17:49.000000000 +0200 @@ -103,11 +103,36 @@ #include /* needed by ohci-mem.c when no PCI */ #include +#include #include #include #include #include +#ifdef CONFIG_ARCH_KARO +# define KARO_DEBUG_OFF +# include +#endif + +#undef VERBOSE_DEBUG +#ifndef DPRINTK +# ifdef DEBUG +# define DPRINTK( fmt, args... ) printk( KERN_DEBUG "%s[%d]: " fmt, __FILE__, __LINE__, ## args ) +# else +# define DPRINTK( fmt, args... ) do { } while(0) +# endif +# ifdef VERBOSE_DEBUG +# define DDPRINTK( fmt, args... ) printk( KERN_DEBUG "%s[%d]: " fmt, __FILE__, __LINE__, ## args ) +# else +# define DDPRINTK( fmt, args... ) do { } while(0) +# endif +#endif + + +#if 0 +#undef DDPRINTK +# define DDPRINTK( fmt, args... ) printk( KERN_INFO "%s[%d]: " fmt, __FILE__, __LINE__, ## args ) +#endif #define DRIVER_VERSION "2004 Feb 02" #define DRIVER_AUTHOR "Roman Weissgaerber, David Brownell" @@ -128,6 +153,7 @@ #include "ohci.h" +#include "ohci-regs.h" #include "ohci-hub.c" #include "ohci-dbg.c" #include "ohci-mem.c" @@ -136,9 +162,10 @@ /* Some boards don't support per-port power switching */ static int power_switching = 0; +#ifndef CONFIG_ARCH_KARO module_param (power_switching, bool, 0); MODULE_PARM_DESC (power_switching, "true (not default) to switch port power"); - +#endif /*-------------------------------------------------------------------------*/ /* @@ -411,7 +438,7 @@ * On PA-RISC, PDC can leave IR set incorrectly; ignore it there. */ #ifndef __hppa__ - if (ohci_readl (&ohci->regs->control) & OHCI_CTRL_IR) { + if (ohci_read_control (ohci) & OHCI_CTRL_IR) { ohci_dbg (ohci, "USB HC TakeOver from BIOS/SMM\n"); /* this timeout is arbitrary. we make it long, so systems @@ -420,9 +447,9 @@ */ temp = 500; /* arbitrary: five seconds */ - writel (OHCI_INTR_OC, &ohci->regs->intrenable); - writel (OHCI_OCR, &ohci->regs->cmdstatus); - while (ohci_readl (&ohci->regs->control) & OHCI_CTRL_IR) { + ohci_write_intrenable (ohci, OHCI_INTR_OC); + ohci_write_cmdstatus (ohci, OHCI_OCR); + while (ohci_read_control (ohci) & OHCI_CTRL_IR) { msleep (10); if (--temp == 0) { ohci_err (ohci, "USB HC TakeOver failed!\n"); @@ -433,36 +460,35 @@ #endif /* Disable HC interrupts */ - writel (OHCI_INTR_MIE, &ohci->regs->intrdisable); + ohci_write_intrdisable (ohci, OHCI_INTR_MIE); ohci_dbg (ohci, "reset, control = 0x%x\n", - ohci_readl (&ohci->regs->control)); + ohci_read_control (ohci)); /* Reset USB (needed by some controllers); RemoteWakeupConnected * saved if boot firmware (BIOS/SMM/...) told us it's connected * (for OHCI integrated on mainboard, it normally is) */ - ohci->hc_control = ohci_readl (&ohci->regs->control); + ohci->hc_control = ohci_read_control (ohci); ohci->hc_control &= OHCI_CTRL_RWC; /* hcfs 0 = RESET */ if (ohci->hc_control) ohci->hcd.can_wakeup = 1; - writel (ohci->hc_control, &ohci->regs->control); + ohci_write_control (ohci, ohci->hc_control); if (power_switching) { unsigned ports = roothub_a (ohci) & RH_A_NDP; /* power down each port */ for (temp = 0; temp < ports; temp++) - writel (RH_PS_LSDA, - &ohci->regs->roothub.portstatus [temp]); + ohci_write_roothub_portstatus(ohci, RH_PS_LSDA, temp); } // flush those pci writes - (void) ohci_readl (&ohci->regs->control); + (void) ohci_read_control (ohci); msleep (50); /* HC Reset requires max 10 us delay */ - writel (OHCI_HCR, &ohci->regs->cmdstatus); + ohci_write_cmdstatus(ohci, OHCI_HCR); temp = 30; /* ... allow extra time */ - while ((ohci_readl (&ohci->regs->cmdstatus) & OHCI_HCR) != 0) { + while ((ohci_read_cmdstatus (ohci) & OHCI_HCR) != 0) { if (--temp == 0) { ohci_err (ohci, "USB HC reset timed out!\n"); return -1; @@ -477,9 +503,9 @@ * (SiS, OPTi ...), so reset again instead. SiS doesn't need * this if we write fmInterval after we're OPERATIONAL. */ - writel (ohci->hc_control, &ohci->regs->control); + ohci_write_control (ohci, ohci->hc_control); // flush those pci writes - (void) ohci_readl (&ohci->regs->control); + (void) ohci_read_control (ohci); return 0; } @@ -500,8 +526,8 @@ /* Tell the controller where the control and bulk lists are * The lists are empty now. */ - writel (0, &ohci->regs->ed_controlhead); - writel (0, &ohci->regs->ed_bulkhead); + ohci_clr_ed_controlhead(ohci); + ohci_clr_ed_bulkhead(ohci); /* a reset clears this */ writel ((u32) ohci->hcca_dma, &ohci->regs->hcca); @@ -520,7 +546,7 @@ /* start controller operations */ ohci->hc_control &= OHCI_CTRL_RWC; ohci->hc_control |= OHCI_CONTROL_INIT | OHCI_USB_OPER; - writel (ohci->hc_control, &ohci->regs->control); + ohci_write_control(ohci, ohci->hc_control); ohci->hcd.state = USB_STATE_RUNNING; /* wake on ConnectStatusChange, matching external hubs */ @@ -528,8 +554,8 @@ /* Choose the interrupts we care about now, others later on demand */ mask = OHCI_INTR_INIT; - writel (mask, &ohci->regs->intrstatus); - writel (mask, &ohci->regs->intrenable); + ohci_write_intrstatus (ohci, mask); + ohci_write_intrenable (ohci, mask); /* handle root hub init quirks ... */ tmp = roothub_a (ohci); @@ -550,11 +576,11 @@ */ tmp |= RH_A_NPS; } - writel (tmp, &ohci->regs->roothub.a); - writel (RH_HS_LPSC, &ohci->regs->roothub.status); - writel (power_switching ? RH_B_PPCM : 0, &ohci->regs->roothub.b); + ohci_write_roothub_a (ohci, tmp); + ohci_write_roothub_status (ohci, RH_HS_LPSC); + ohci_write_roothub_b (ohci, power_switching ? RH_B_PPCM : 0); // flush those pci writes - (void) ohci_readl (&ohci->regs->control); + (void) ohci_read_control (ohci); // POTPGT delay is bits 24-31, in 2 ms units. mdelay ((roothub_a (ohci) >> 23) & 0x1fe); @@ -571,7 +597,7 @@ if (!udev) { disable (ohci); ohci->hc_control &= ~OHCI_CTRL_HCFS; - writel (ohci->hc_control, &ohci->regs->control); + ohci_write_control (ohci, ohci->hc_control); return -ENOMEM; } @@ -580,7 +606,7 @@ usb_put_dev (udev); disable (ohci); ohci->hc_control &= ~OHCI_CTRL_HCFS; - writel (ohci->hc_control, &ohci->regs->control); + ohci_write_control (ohci, ohci->hc_control); return -ENODEV; } @@ -604,13 +630,15 @@ ints = OHCI_INTR_WDH; /* cardbus/... hardware gone before remove() */ - } else if ((ints = ohci_readl (®s->intrstatus)) == ~(u32)0) { + } else if ((ints = ohci_read_intrstatus (ohci)) == ~(u32)0) { disable (ohci); ohci_dbg (ohci, "device removed!\n"); return IRQ_HANDLED; /* interrupt for some other device? */ - } else if ((ints &= ohci_readl (®s->intrenable)) == 0) { + } else if ((ints &= ohci_read_intrenable (ohci)) == 0) { + DDPRINTK("%s: Not my business: %08x:%08x\n", __FUNCTION__, ints, + ohci_read_intrenable (ohci)); return IRQ_NONE; } @@ -630,12 +658,12 @@ if (ints & OHCI_INTR_WDH) { if (HCD_IS_RUNNING(hcd->state)) - writel (OHCI_INTR_WDH, ®s->intrdisable); + ohci_write_intrdisable (ohci, OHCI_INTR_WDH); spin_lock (&ohci->lock); dl_done_list (ohci, ptregs); spin_unlock (&ohci->lock); if (HCD_IS_RUNNING(hcd->state)) - writel (OHCI_INTR_WDH, ®s->intrenable); + ohci_write_intrenable (ohci, OHCI_INTR_WDH); } /* could track INTR_SO to reduce available PCI/... bandwidth */ @@ -649,14 +677,14 @@ ptregs); if ((ints & OHCI_INTR_SF) != 0 && !ohci->ed_rm_list && HCD_IS_RUNNING(ohci->hcd.state)) - writel (OHCI_INTR_SF, ®s->intrdisable); + ohci_write_intrdisable (ohci, OHCI_INTR_SF); spin_unlock (&ohci->lock); if (HCD_IS_RUNNING(ohci->hcd.state)) { - writel (ints, ®s->intrstatus); - writel (OHCI_INTR_MIE, ®s->intrenable); + ohci_write_intrstatus (ohci, ints); + ohci_write_intrenable (ohci, OHCI_INTR_MIE); // flush those pci writes - (void) ohci_readl (&ohci->regs->control); + (void) ohci_read_control (ohci); } return IRQ_HANDLED; @@ -810,10 +838,20 @@ #include "ohci-lh7a404.c" #endif +#ifdef CONFIG_ARCH_KARO +#include "ohci-sl811.c" +#endif + +#ifdef CONFIG_MACH_NETBOOKPRO +#include "ohci-sl811.c" +#endif + #if !(defined(CONFIG_PCI) \ || defined(CONFIG_SA1111) \ || defined(CONFIG_ARCH_OMAP) \ - || defined (CONFIG_ARCH_LH7A404) \ + || defined(CONFIG_ARCH_LH7A404) \ + || defined(CONFIG_ARCH_KARO) \ + || defined(CONFIG_MACH_NETBOOKPRO) \ ) #error "missing bus glue for ohci-hcd" #endif diff -NaurbB linux-2.6.9-pre1-bk18/drivers/usb/host/ohci-hub.c linux-2.6.9_nbp/drivers/usb/host/ohci-hub.c --- linux-2.6.9-pre1-bk18/drivers/usb/host/ohci-hub.c 2005-08-15 01:14:41.000000000 +0200 +++ linux-2.6.9_nbp/drivers/usb/host/ohci-hub.c 2004-09-14 12:25:06.000000000 +0200 @@ -19,6 +19,7 @@ * The erratum (#4) description is incorrect. AMD's workaround waits * till some bits (mostly reserved) are clear; ok for all revs. */ +#ifdef CONFIG_PCI #define read_roothub(hc, register, mask) ({ \ u32 temp = ohci_readl (&hc->regs->roothub.register); \ if (temp == -1) \ @@ -36,7 +37,12 @@ { return ohci_readl (&hc->regs->roothub.status); } static u32 roothub_portstatus (struct ohci_hcd *hc, int i) { return read_roothub (hc, portstatus [i], 0xffe0fce0); } - +#else +#define roothub_a(hc) ohci_read_roothub_a(hc) +#define roothub_b(hc) ohci_read_roothub_a(hc) +#define roothub_status(hc) ohci_read_roothub_status(hc) +#define roothub_portstatus(hc,i) ohci_read_roothub_portstatus(hc, i) +#endif /*-------------------------------------------------------------------------*/ #define dbg_port(hc,label,num,value) \ @@ -332,7 +338,7 @@ if (!HCD_IS_RUNNING(ohci->hcd.state)) return -ESHUTDOWN; ohci_err (ohci, "bogus NDP=%d, rereads as NDP=%d\n", - ports, ohci_readl (&ohci->regs->roothub.a) & RH_A_NDP); + ports, ohci_read_roothub_a (ohci) & RH_A_NDP); /* retry later; "should not happen" */ return 0; } @@ -533,9 +539,10 @@ switch (typeReq) { case ClearHubFeature: + DPRINTK("%s: %10u ClearHubFeature %04x\n", __FUNCTION__, OSCR, wValue); switch (wValue) { case C_HUB_OVER_CURRENT: - writel (RH_HS_OCIC, &ohci->regs->roothub.status); + ohci_write_roothub_status(ohci, RH_HS_OCIC); case C_HUB_LOCAL_POWER: break; default: @@ -543,6 +550,8 @@ } break; case ClearPortFeature: + DPRINTK("%s: %10u ClearPortFeature[%d] %04x\n", __FUNCTION__, OSCR, + wIndex, wValue); if (!wIndex || wIndex > ports) goto error; wIndex--; @@ -578,8 +587,11 @@ default: goto error; } - writel (temp, &ohci->regs->roothub.portstatus [wIndex]); - // ohci_readl (&ohci->regs->roothub.portstatus [wIndex]); + + DPRINTK("%s: %10u changing roothub portstatus %08x -> %08x\n", __FUNCTION__, OSCR, + ohci_read_roothub_portstatus(ohci, wIndex), temp); + ohci_write_roothub_portstatus(ohci, wIndex, temp); + //ohci_read_roothub_portstatus(ohci, wIndex); break; case GetHubDescriptor: ohci_hub_descriptor (ohci, (struct usb_hub_descriptor *) buf); @@ -593,6 +605,7 @@ goto error; wIndex--; temp = roothub_portstatus (ohci, wIndex); + DPRINTK("%s: %10u Get port status[%d] %08x\n", __FUNCTION__, OSCR, wIndex, temp); *(u32 *) buf = cpu_to_le32 (temp); #ifndef OHCI_VERBOSE_DEBUG @@ -601,6 +614,7 @@ dbg_port (ohci, "GetStatus", wIndex, temp); break; case SetHubFeature: + DPRINTK("%s: %10u Set HUB feature %08x\n", __FUNCTION__, OSCR, wValue); switch (wValue) { case C_HUB_OVER_CURRENT: // FIXME: this can be cleared, yes? @@ -611,6 +625,8 @@ } break; case SetPortFeature: + DPRINTK("%s: %10u Set port feature[%d] %08x\n", __FUNCTION__, OSCR, + wIndex, wValue); if (!wIndex || wIndex > ports) goto error; wIndex--; @@ -622,12 +638,13 @@ start_hnp(ohci); else #endif - writel (RH_PS_PSS, - &ohci->regs->roothub.portstatus [wIndex]); + DPRINTK("%s: %10u Suspending port %d\n", __FUNCTION__, OSCR, wIndex); + ohci_write_roothub_portstatus(ohci, wIndex, RH_PS_PSS); break; case USB_PORT_FEAT_POWER: - writel (RH_PS_PPS, - &ohci->regs->roothub.portstatus [wIndex]); + DPRINTK("%s: %10u Enabling power on port %d\n", __FUNCTION__, OSCR, + wIndex); + ohci_write_roothub_portstatus(ohci, wIndex, RH_PS_PPS); break; case USB_PORT_FEAT_RESET: root_port_reset (ohci, wIndex); diff -NaurbB linux-2.6.9-pre1-bk18/drivers/usb/host/ohci-q.c linux-2.6.9_nbp/drivers/usb/host/ohci-q.c --- linux-2.6.9-pre1-bk18/drivers/usb/host/ohci-q.c 2005-08-15 01:14:41.000000000 +0200 +++ linux-2.6.9_nbp/drivers/usb/host/ohci-q.c 2004-09-14 12:25:06.000000000 +0200 @@ -78,7 +78,7 @@ if (hcd_to_bus (&ohci->hcd)->bandwidth_isoc_reqs == 0 && hcd_to_bus (&ohci->hcd)->bandwidth_int_reqs == 0) { ohci->hc_control &= ~(OHCI_CTRL_PLE|OHCI_CTRL_IE); - writel (ohci->hc_control, &ohci->regs->control); + ohci_write_control(ohci, ohci->hc_control); } } @@ -193,7 +193,7 @@ case PIPE_CONTROL: if (ohci->ed_controltail == NULL) { WARN_ON (ohci->hc_control & OHCI_CTRL_CLE); - writel (ed->dma, &ohci->regs->ed_controlhead); + ohci_write_ed_controlhead(ohci, ed); } else { ohci->ed_controltail->ed_next = ed; ohci->ed_controltail->hwNextED = cpu_to_le32 (ed->dma); @@ -202,8 +202,8 @@ if (!ohci->ed_controltail && !ohci->ed_rm_list) { wmb(); ohci->hc_control |= OHCI_CTRL_CLE; - writel (0, &ohci->regs->ed_controlcurrent); - writel (ohci->hc_control, &ohci->regs->control); + ohci_clr_ed_controlcurrent(ohci); + ohci_write_control(ohci, ohci->hc_control); } ohci->ed_controltail = ed; break; @@ -211,7 +211,7 @@ case PIPE_BULK: if (ohci->ed_bulktail == NULL) { WARN_ON (ohci->hc_control & OHCI_CTRL_BLE); - writel (ed->dma, &ohci->regs->ed_bulkhead); + ohci_write_ed_bulkhead(ohci, ed); } else { ohci->ed_bulktail->ed_next = ed; ohci->ed_bulktail->hwNextED = cpu_to_le32 (ed->dma); @@ -220,8 +220,8 @@ if (!ohci->ed_bulktail && !ohci->ed_rm_list) { wmb(); ohci->hc_control |= OHCI_CTRL_BLE; - writel (0, &ohci->regs->ed_bulkcurrent); - writel (ohci->hc_control, &ohci->regs->control); + ohci_clr_ed_bulkcurrent(ohci); + ohci_write_control(ohci, ohci->hc_control); } ohci->ed_bulktail = ed; break; @@ -320,11 +320,12 @@ if (ed->ed_prev == NULL) { if (!ed->hwNextED) { ohci->hc_control &= ~OHCI_CTRL_CLE; - writel (ohci->hc_control, &ohci->regs->control); - // a ohci_readl() later syncs CLE with the HC - } else - writel (le32_to_cpup (&ed->hwNextED), - &ohci->regs->ed_controlhead); + ohci_write_control(ohci, ohci->hc_control); + ohci_clr_ed_controlcurrent(ohci); + // post those pci writes + (void)ohci_read_control(ohci); + } + ohci_write_ed_controlhead_next(ohci, ed); } else { ed->ed_prev->ed_next = ed->ed_next; ed->ed_prev->hwNextED = ed->hwNextED; @@ -344,11 +345,12 @@ if (ed->ed_prev == NULL) { if (!ed->hwNextED) { ohci->hc_control &= ~OHCI_CTRL_BLE; - writel (ohci->hc_control, &ohci->regs->control); - // a ohci_readl() later syncs BLE with the HC - } else - writel (le32_to_cpup (&ed->hwNextED), - &ohci->regs->ed_bulkhead); + ohci_write_control(ohci, ohci->hc_control); + ohci_clr_ed_bulkcurrent(ohci); + // post those pci writes + (void) ohci_read_control(ohci); + } + ohci_write_ed_bulkhead_next(ohci, ed); } else { ed->ed_prev->ed_next = ed->ed_next; ed->ed_prev->hwNextED = ed->hwNextED; @@ -478,10 +480,12 @@ ohci->ed_rm_list = ed; /* enable SOF interrupt */ - writel (OHCI_INTR_SF, &ohci->regs->intrstatus); - writel (OHCI_INTR_SF, &ohci->regs->intrenable); - // flush those writes, and get latest HCCA contents - (void) ohci_readl (&ohci->regs->control); + if (HCD_IS_RUNNING (ohci->hcd.state)) { + ohci_write_intrstatus(ohci, OHCI_INTR_SF); + ohci_write_intrenable(ohci, OHCI_INTR_SF); + // flush those pci writes + (void) ohci_read_control(ohci); + } /* SF interrupt might get delayed; record the frame counter value that * indicates when the HC isn't looking at it, so concurrent unlinks @@ -555,6 +559,9 @@ td->hwNextTD = cpu_to_le32 (td_pt->td_dma); /* append to queue */ + DPRINTK("%s: %10u Adding TD %08x (%08x) urb %08x data %08x len %d to ed %08x(%08x) head %08x tail %08x\n", + __FUNCTION__, OSCR, (u32)td, td->hwINFO, (u32)td->urb, (u32)data, len, + (u32)td->ed, td->ed->hwINFO, td->ed->hwHeadP, td->ed->hwTailP); list_add_tail (&td->td_list, &td->ed->td_list); /* hash it for later reverse mapping */ @@ -642,7 +649,7 @@ /* maybe kickstart bulk list */ if (urb_priv->ed->type == PIPE_BULK) { wmb (); - writel (OHCI_BLF, &ohci->regs->cmdstatus); + ohci_write_cmdstatus(ohci, OHCI_BLF); } break; @@ -664,7 +671,7 @@ td_fill (ohci, info, data, 0, urb, cnt++); /* maybe kickstart control list */ wmb (); - writel (OHCI_CLF, &ohci->regs->cmdstatus); + ohci_write_cmdstatus(ohci, OHCI_CLF); break; /* ISO has no retransmit, so no toggle; and it uses special TDs. @@ -693,7 +700,7 @@ if (periodic) { wmb (); ohci->hc_control |= OHCI_CTRL_PLE|OHCI_CTRL_IE; - writel (ohci->hc_control, &ohci->regs->control); + ohci_write_control(ohci, ohci->hc_control); } // ASSERT (urb_priv->length == cnt); @@ -710,6 +717,10 @@ { u32 tdINFO = le32_to_cpup (&td->hwINFO); int cc = 0; + static unsigned long nr_tds = 0; + + DPRINTK("%s: removing TD @ %08x from td_list\n", __FUNCTION__, (u32)td); + nr_tds++; list_del (&td->td_list); @@ -764,12 +775,17 @@ /* count all non-empty packets except control SETUP packet */ if ((type != PIPE_CONTROL || td->index != 0) && tdBE != 0) { - if (td->hwCBP == 0) + if (td->hwCBP == 0) { urb->actual_length += tdBE - td->data_dma + 1; - else + DPRINTK("%s: Got last chunk of %d byte (%d)\n", __FUNCTION__, + tdBE - td->data_dma + 1, urb->actual_length); + } else { urb->actual_length += le32_to_cpup (&td->hwCBP) - td->data_dma; + DPRINTK("%s: Got chunk of %d byte (%d)\n", __FUNCTION__, + le32_to_cpup(&td->hwCBP) - td->data_dma, urb->actual_length); + } } if (cc != TD_CC_NOERROR && cc < 0x0E) @@ -877,7 +893,7 @@ td = dma_to_td (ohci, td_dma); if (!td) { - ohci_err (ohci, "bad entry %8x\n", td_dma); + ohci_err (ohci, "bad entry %08x\n", td_dma); break; } @@ -1014,24 +1030,24 @@ command |= OHCI_CLF; if (!(ohci->hc_control & OHCI_CTRL_CLE)) { control |= OHCI_CTRL_CLE; - writel (0, &ohci->regs->ed_controlcurrent); + ohci_clr_ed_controlcurrent(ohci); } } if (ohci->ed_bulktail) { command |= OHCI_BLF; if (!(ohci->hc_control & OHCI_CTRL_BLE)) { control |= OHCI_CTRL_BLE; - writel (0, &ohci->regs->ed_bulkcurrent); + ohci_clr_ed_bulkcurrent(ohci); } } /* CLE/BLE to enable, CLF/BLF to (maybe) kickstart */ if (control) { ohci->hc_control |= control; - writel (ohci->hc_control, &ohci->regs->control); + ohci_write_control(ohci, ohci->hc_control); } if (command) - writel (command, &ohci->regs->cmdstatus); + ohci_write_cmdstatus(ohci, command); } } @@ -1078,12 +1094,10 @@ /* ... hc may need waking-up */ switch (ed->type) { case PIPE_CONTROL: - writel (OHCI_CLF, - &ohci->regs->cmdstatus); + ohci_write_cmdstatus(ohci, OHCI_CLF); break; case PIPE_BULK: - writel (OHCI_BLF, - &ohci->regs->cmdstatus); + ohci_write_cmdstatus(ohci, OHCI_BLF); break; } } diff -NaurbB linux-2.6.9-pre1-bk18/drivers/usb/host/ohci-regs.h linux-2.6.9_nbp/drivers/usb/host/ohci-regs.h --- linux-2.6.9-pre1-bk18/drivers/usb/host/ohci-regs.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/drivers/usb/host/ohci-regs.h 2004-09-14 12:25:07.000000000 +0200 @@ -0,0 +1,502 @@ +/* + * drivers/usb/host/ohci-regs.h + * + * Macros for OHCI register access to facilitate OHCI emulation + * on non-OHCI compliant hardware + * + */ + +#ifdef CONFIG_PCI +#define ohci_read_reg(hc,n) readl(&hc->regs->n) +#define ohci_read_control(hc) ohci_read_reg(hc,control) +#define ohci_read_cmdstatus(hc) ohci_read_reg(hc,cmdstatus) +#define ohci_read_intstatus(hc) readl(&hc->regs->intrstatus) +#define ohci_read_intrenable(hc) readl(&hc->regs->intrenable) +#define ohci_read_ed_bulkhead(hc) ohci_read_reg(hc,ed_bulkhead) +#define ohci_read_ed_bulkcurrent(hc) ohci_read_reg(hc,ed_bulkcurrent) +#define ohci_read_ed_controlhead(hc) ohci_read_reg(hc,ed_controlhead) +#define ohci_read_ed_controlcurrent(hc) ohci_read_reg(hc,ed_controlcurrent) +#define ohci_read_ed_periodcurrent(hc) ohci_read_reg(hc,ed_periodcurrent) +#define ohci_read_ed_(hc) ohci_read_reg(hc,ed_) +#define ohci_write_control(v,hc) writel(v, &hc->regs->control) +#define ohci_write_cmdstatus(cmd,hc) writel(cmd, &hc->regs->cmdstatus) +#define ohci_write_intrstatus(mask,hc) writel(mask, &hc->regs->intrstatus) +#define ohci_write_intrenable(mask,hc) writel(mask, &hc->regs->intrenable) +#define ohci_write_intrdisable(mask,hc) writel(mask, &hc->regs->intrdisable) +#define ohci_clr_ed_controlhead(hc) writel(0, &hc->regs->ed_controlhead) +#define ohci_write_ed_controlhead(hc,ed) writel(ed->dma, &hc->regs->ed_controlhead) +#define ohci_write_ed_controlhead_next(hc,ed) writel(le32_to_cpup(&ed->hwNextED), &hc->regs->ed_controlhead) +#define ohci_clr_ed_bulkhead(hc) writel(0, &hc->regs->ed_bulkhead) +#define ohci_write_ed_bulkhead(hc,ed) writel(ed->dma, &hc->regs->ed_bulkhead) +#define ohci_write_ed_bulkhead_next(hc,ed) writel(le32_to_cpup(&ed->hwNextED), &hc->regs->ed_bulkhead) + +#else +#include "ohci-sl811.h" + +#define hcd_to_sl811_dev(hcd) ((struct hc_sl811_dev *)dev_get_drvdata(hcd->self.controller)) +#define ohci_to_sl811_dev(ohci) ((struct hc_sl811_dev *)dev_get_drvdata(ohci->hcd.self.controller)) + +/* + * OHCI emulation + */ +static void sl811_reset_hc(struct usb_hcd *hcd, int cold); +//static void ohci_sl811_do_port_reset(struct ohci_hcd *ohci, int port); + +#define ohci_sl811_update_reg(hc,n,s,c) \ +{ \ + u32 *reg = &ohci->regs->n; \ + DPRINTK("%s: Updating %s @ %08x: %08x->%08x\n", \ + __FUNCTION__, #n, (u32)reg, \ + *reg, (*reg & ~(c)) | (s)); \ + \ + *reg = (*reg & ~(c)) | (s); \ +} + +#define ohci_sl811_update_control(hc,s,c) ohci_sl811_update_reg(hc,control,s,c); +#define ohci_sl811_update_cmdstatus(hc,s,c) ohci_sl811_update_reg(hc,cmdstatus,s,c); +#define ohci_sl811_update_intrstatus(hc,s,c) ohci_sl811_update_reg(hc,intrstatus,s,c); +#define ohci_sl811_update_ed_controlhead(hc,s,c) ohci_sl811_update_reg(hc,ed_controlhead,s,c); +#define ohci_sl811_update_ed_controlcurrent(hc,s,c) ohci_sl811_update_reg(hc,ed_controlcurrent,s,c); +#define ohci_sl811_update_ed_bulkhead(hc,s,c) ohci_sl811_update_reg(hc,ed_bulkhead,s,c); +#define ohci_sl811_update_ed_bulkcurrent(hc,s,c) ohci_sl811_update_reg(hc,ed_bulkcurrent,s,c); + +static void ohci_sl811_update_portstatus(struct ohci_hcd *ohci, int port, u32 set, u32 clr) +{ + unsigned long flags; + u32 stat; + u32 new; + u32 chg; + + if (port < 0 || port >= MAX_ROOT_PORTS) { + printk("%s: Invalid port number %d\n", __FUNCTION__, port); + return; + } + local_irq_save(flags); + stat = ohci->regs->roothub.portstatus[port]; + new = ((stat & 0xffff) | set) & ~clr; + chg = (stat ^ new) & 0xffff; + DPRINTK("%s: old: %08x new: %08x chg: %04x\n", __FUNCTION__, stat, new, chg); + if (chg) { + chg |= stat >> 16; + DDPRINTK("%s: Changed port_status from %08x to: %08x\n", __FUNCTION__, + ohci->regs->roothub.portstatus[port], new | (chg << 16)); + ohci->regs->roothub.portstatus[port] = new | (chg << 16); + ohci->regs->intrstatus |= OHCI_INTR_RHSC; + } else { + DPRINTK("%s: Leaving portstatus unchanged: %08x\n", __FUNCTION__, + ohci->regs->roothub.portstatus[port]); + } + local_irq_restore(flags); +} + +/* + * Software API + */ +#define ohci_read_reg(hc,n) \ +({ \ + u32 *reg = &hc->regs->n; \ + u32 val; \ + \ + val = *reg; \ + DPRINTK("%s: Read %08x from %s reg @ %08x\n", __FUNCTION__, \ + val, #n, (u32)reg); \ + val; \ +}) + +#define ohci_sl811_wake_thread() \ + if (changed) { \ + if (waitqueue_active(&dev->task_wait)) { \ + DDPRINTK("%s: Waking up kernel thread\n", __FUNCTION__); \ + wake_up_interruptible(&dev->task_wait); \ + } else { \ + DDPRINTK("%s: Waitqueue is not active\n", __FUNCTION__); \ + } \ + } + +#define _ohci_write_reg(hc,n,v) \ +({ \ + u32 *reg = &ohci->regs->n; \ + \ + DPRINTK("%s: Writing %08x to %s reg @ %08x\n", __FUNCTION__, \ + v, #n, (u32)reg); \ + *reg = v; \ +}) + +#define ohci_write_reg(hc,n,v) \ +({ \ + unsigned long flags; \ + u32 *reg = &ohci->regs->n; \ + \ + DPRINTK("%s: Writing %08x to %s reg @ %08x\n", __FUNCTION__, \ + v, #n, (u32)reg); \ + local_irq_save(flags); \ + *reg = v; \ + local_irq_restore(flags); \ +}) + +#define ohci_read_roothub_reg(hc,n) \ + ohci_read_reg(hc,roothub.n) + +#define ohci_write_roothub_reg(hc,n,v) \ +({ \ + unsigned long flags; \ + u32 *reg = &ohci->regs->roothub.n; \ + \ + DPRINTK("%s: Writing %08x to roothub.%s reg @ %08x\n", __FUNCTION__, \ + v, #n, (u32)reg); \ + local_irq_save(flags); \ + *reg = v; \ + local_irq_restore(flags); \ +}) + +#define ohci_read_control(hc) ohci_read_reg(hc,control) +#define ohci_read_cmdstatus(hc) ohci_read_reg(hc,cmdstatus) + +#define ohci_read_roothub_a(hc) ohci_read_roothub_reg(hc,a) +#define ohci_read_roothub_b(hc) ohci_read_roothub_reg(hc,b) +//#define ohci_read_roothub_status(hc) ohci_read_roothub_reg(hc,status) + +#define ohci_write_roothub_a(hc,v) ohci_write_roothub_reg(hc,a,v) +#define ohci_write_roothub_b(hc,v) ohci_write_roothub_reg(hc,b,v) + +static inline void ohci_write_control(struct ohci_hcd *ohci, u32 ctrl) +{ + unsigned long flags; + struct usb_hcd *hcd = &ohci->hcd; + struct hc_sl811_dev *dev = hcd_to_sl811_dev(hcd); + int old_state; + int new_state = ctrl & OHCI_CTRL_HCFS; + + local_irq_save(flags); + old_state = ohci->regs->control & OHCI_CTRL_HCFS; + if (old_state != new_state) { + DDPRINTK("%s: Changing state from %08x to %08x\n", __FUNCTION__, old_state, new_state); + switch (new_state) { + case OHCI_USB_RESET: + DDPRINTK("%s: Asserting USB reset\n", __FUNCTION__); + hc_sl811_usb_reset(hcd, 1); + break; + case OHCI_USB_RESUME: + if (old_state == OHCI_USB_SUSPEND) { + DDPRINTK("%s: Deactivating SUSPEND mode\n", __FUNCTION__); + hc_sl811_set_mask(dev, SL11H_CTLREG1, SL11H_CTL1MASK_SOFENA); + } + break; + case OHCI_USB_SUSPEND: + if (old_state == OHCI_USB_OPER) { + DDPRINTK("%s: Activating SUSPEND mode\n", __FUNCTION__); + hc_sl811_clr_mask(dev, SL11H_CTLREG1, SL11H_CTL1MASK_SOFENA); + } + break; + case OHCI_USB_OPER: + switch (old_state) { + case OHCI_USB_RESET: + DDPRINTK("%s: Deasserting USB reset\n", __FUNCTION__); + hc_sl811_usb_reset(hcd, 0); + break; + case OHCI_USB_RESUME: + DDPRINTK("%s: Disabling USB Resume Interrupt\n", __FUNCTION__); + hc_sl811_clr_mask(dev, SL11H_INTENBLREG, SL11H_INTMASK_USBRESET); + hc_sl811_clr_mask(dev, SL11H_CTLREG1, SL11H_CTL1MASK_SUSPEND); + break; + case OHCI_USB_SUSPEND: + hc_sl811_set_mask(dev, SL11H_CTLREG1, SL11H_CTL1MASK_SUSPEND); + DDPRINTK("%s: Enabling USB Resume Interrupt\n", __FUNCTION__); + hc_sl811_set_mask(dev, SL11H_INTENBLREG, SL11H_INTMASK_USBRESET); + break; + } + break; + } + } + ohci->regs->control = ctrl; + local_irq_restore(flags); +} + +static inline void ohci_write_cmdstatus(struct ohci_hcd *ohci, u32 cmd) +{ + unsigned long flags; + struct usb_hcd *hcd = &ohci->hcd; + + if (cmd & OHCI_HCR) { + sl811_reset_hc(hcd, 0); + cmd &= ~OHCI_HCR; + } + + local_irq_save(flags); + if ((ohci->regs->ed_controlcurrent == 0) && (cmd & OHCI_CLF)) { + DPRINTK("%s: Got CLF command\n", __FUNCTION__); + + ohci->regs->ed_controlcurrent = ohci->regs->ed_controlhead; + } + if ((ohci->regs->ed_bulkcurrent == 0) && (cmd & OHCI_BLF)) { + DPRINTK("%s: Got BLF command\n", __FUNCTION__); + + ohci->regs->ed_bulkcurrent = ohci->regs->ed_bulkhead; + } + ohci->regs->cmdstatus = cmd; + local_irq_restore(flags); +} + +#if 0 +static inline u32 ohci_read_roothub_a(struct ohci_hcd *ohci) +{ + u32 status = ohci->regs->roothub.a; + return status; +} + +static inline u32 ohci_read_roothub_b(struct ohci_hcd *ohci) +{ + u32 status = ohci->regs->roothub.b; + return status; +} +#endif + +static inline u32 ohci_read_roothub_status(struct ohci_hcd *ohci) +{ + int oci = 0; + u32 status = ohci->regs->roothub.status; + +#ifdef CONFIG_ARCH_KARO_ + oci = karo_pcf8574_tst(FMsk(KARO_PCF8574_USBOVL)); + DDPRINTK("%s: Read %08x from PCF8574\n", __FUNCTION__, oci); + + if (oci < 0) { + DDPRINTK("%s: Failed to read Overcurrent status from PCF8574: %d\n", __FUNCTION__, + oci); + } else { + oci = !oci; + } +#endif + if ((status & RH_HS_OCI) ^ ((oci > 0) ? RH_HS_OCI : 0)) { + status |= RH_HS_OCIC; + status = (status & ~RH_HS_OCI) | ((oci > 0) ? RH_HS_OCI : 0); + } + + return status; +} + +static inline void ohci_write_roothub_status(struct ohci_hcd *ohci, u32 mask) +{ + struct usb_hcd *hcd = ohci_to_hcd(ohci); + struct hc_sl811_dev *dev = hcd_to_sl811_dev(hcd); + unsigned long flags; + u32 *reg = &ohci->regs->roothub.status; + u32 status = 0; + + DPRINTK("%s: Writing %08x to HcRhStatus\n", __FUNCTION__, mask); + local_irq_save(flags); + if (mask & RH_HS_LPS) { + DPRINTK("%s: Power control not supported on SL811\n", __FUNCTION__); + } + if (mask & RH_HS_DRWE) { + status |= RH_HS_DRWE; + hc_sl811_set_mask(dev, SL11H_CTLREG1, SL11H_CTL1MASK_SUSPEND); + } + if (mask & RH_HS_CRWE) { + status &= ~RH_HS_DRWE; + hc_sl811_clr_mask(dev, SL11H_CTLREG1, SL11H_CTL1MASK_SUSPEND); + } + if (mask & RH_HS_LPSC) { + DPRINTK("%s: Power control not supported on SL811\n", __FUNCTION__); + status &= ~RH_HS_LPSC; + } + if (mask & RH_HS_OCIC) { + status &= ~RH_HS_OCIC; + } + + if (*reg != status) { + ohci->regs->intrstatus |= OHCI_INTR_RHSC; + } + *reg = status; + + local_irq_restore(flags); +} + +static inline u32 ohci_read_roothub_portstatus(struct ohci_hcd *ohci, int port) +{ + struct ohci_roothub_regs *rh_regs = &ohci->regs->roothub; + u32 status = RH_PS_PPS; + + if (port < 0 || port >= MAX_ROOT_PORTS) { + DPRINTK("%s: Invalid port number %d\n", __FUNCTION__, port); + return -1; + } + status |= rh_regs->portstatus[port]; + + return status; +} + +static inline void ohci_write_roothub_portstatus(struct ohci_hcd *ohci, int port, u32 mask) +{ + unsigned long flags; + struct ohci_roothub_regs *rh_regs = &ohci->regs->roothub; + u32 status; + + if (port < 0 || port >= MAX_ROOT_PORTS) { + printk("%s: Invalid port number %d\n", __FUNCTION__, port); + return; + } +#ifdef DEBUG + status = rh_regs->portstatus[port]; + DDPRINTK("%s: Writing %08x to portstatus[%d]: %08x\n", __FUNCTION__, mask, port, status); +#endif + local_irq_save(flags); + status = rh_regs->portstatus[port]; + if (mask & RH_PS_PESC) { + status &= ~RH_PS_PESC; + } + if (mask & RH_PS_PSSC) { + status &= ~RH_PS_PSSC; + } + if (mask & RH_PS_OCIC) { + status &= ~RH_PS_OCIC; + } + if (mask & RH_PS_PRSC) { + status &= ~RH_PS_PRSC; + } + if (mask & RH_PS_CSC) { + status &= ~RH_PS_CSC; + } + if (status & RH_PS_CCS) { + if (mask & RH_PS_CCS) { + // clear PES, if device is removable + if (rh_regs->b & (1 << port)) { + status &= ~RH_PS_PES; + } + } + if (mask & RH_PS_PES) { + status |= RH_PS_PES; + } + if (mask & RH_PS_PSS) { + status |= RH_PS_PSS; + } + if (mask & RH_PS_POCI) { + status &= ~RH_PS_PSS; + } + if (mask & RH_PS_PRS) { + status |= RH_PS_PRS; + } + if (mask & RH_PS_PPS) { + // PPS enabled: set PPS + } + if (mask & RH_PS_LSDA) { + // PPS enabled: clear PPS + } + } else { + if (mask & (RH_PS_PRS | RH_PS_PES | RH_PS_PSS)) { + status |= RH_PS_CSC; + } + } + if (status != rh_regs->portstatus[port]) { + DDPRINTK("%s: status of port %d changed from %08x to %08x\n", __FUNCTION__, + port, rh_regs->portstatus[port], status); + } + rh_regs->portstatus[port] = status; + + if (status & RH_PS_PRS) { + hc_sl811_usb_reset(ohci_to_hcd(ohci), -1); + } + local_irq_restore(flags); +} + +static inline void ohci_write_intrstatus(struct ohci_hcd *ohci, u32 ohci_mask) +{ + unsigned long flags; + + DPRINTK("%s: Acknowledging Interrupts: %08x\n", __FUNCTION__, ohci_mask); + local_irq_save(flags); + ohci->regs->intrstatus &= ~ohci_mask; + local_irq_restore(flags); +} + +static inline void ohci_write_intrenable(struct ohci_hcd *ohci, u32 ohci_mask) +{ + struct usb_hcd *hcd = ohci_to_hcd(ohci); + struct hc_sl811_dev *dev = hcd_to_sl811_dev(hcd); + unsigned long flags; + u32 chg = ohci->regs->intrenable ^ ohci_mask; + + DPRINTK("%s: Enabling OHCI Interrupts: %08x\n", __FUNCTION__, ohci_mask); + + local_irq_save(flags); + ohci->regs->intrenable |= ohci_mask; + if (chg & OHCI_INTR_MIE) { + if (ohci->regs->intrenable & OHCI_INTR_MIE) { + DPRINTK("%s: Enabling SL811 SOF interrupts\n", __FUNCTION__); + hc_sl811_set_mask(dev, SL11H_INTENBLREG, SL11H_INTMASK_SOFINTR); + } else { + DPRINTK("%s: Disabling SL811 SOF interrupts\n", __FUNCTION__); + hc_sl811_clr_mask(dev, SL11H_INTENBLREG, SL11H_INTMASK_SOFINTR); + } + } + local_irq_restore(flags); +} + +static inline void ohci_write_intrdisable(struct ohci_hcd *ohci, u32 ohci_mask) +{ + unsigned long flags; + + DPRINTK("%s: Disabling OHCI Interrupts: %08x\n", __FUNCTION__, ohci_mask); + + local_irq_save(flags); + ohci->regs->intrenable &= ~ohci_mask; + local_irq_restore(flags); +} + +static inline u32 ohci_read_intrstatus(struct ohci_hcd *ohci) +{ + u32 ohci_status = ohci->regs->intrstatus; + + DPRINTK("%s: Int status: %02x\n", __FUNCTION__, ohci_status); + + return ohci_status; +} + +static inline u32 ohci_read_intrenable(struct ohci_hcd *ohci) +{ + u32 ohci_int_mask = ohci->regs->intrenable; + + return ohci_int_mask; +} +#define ohci_read_intrdisable ohci_read_intrenable + +static inline void ohci_clr_ed_controlhead(struct ohci_hcd *ohci) +{ + _ohci_write_reg(ohci, ed_controlhead, 0); +} + +static inline void ohci_clr_ed_controlcurrent(struct ohci_hcd *ohci) +{ + _ohci_write_reg(ohci, ed_controlcurrent, 0); +} + +static inline void ohci_write_ed_controlhead(struct ohci_hcd *ohci, struct ed *ed) +{ + _ohci_write_reg(ohci, ed_controlhead, (u32)ed); +} + +static inline void ohci_write_ed_controlhead_next(struct ohci_hcd *ohci, struct ed *ed) +{ + _ohci_write_reg(ohci, ed_controlhead, (u32)ed->ed_next); +} + +static inline void ohci_clr_ed_bulkhead(struct ohci_hcd *ohci) +{ + _ohci_write_reg(ohci, ed_bulkhead, 0); +} + +static inline void ohci_clr_ed_bulkcurrent(struct ohci_hcd *ohci) +{ + _ohci_write_reg(ohci, ed_bulkcurrent, 0); +} + +static inline void ohci_write_ed_bulkhead(struct ohci_hcd *ohci, struct ed *ed) +{ + _ohci_write_reg(ohci, ed_bulkhead, (u32)ed); +} + +static inline void ohci_write_ed_bulkhead_next(struct ohci_hcd *ohci, struct ed *ed) +{ + _ohci_write_reg(ohci, ed_bulkhead, (u32)ed->ed_next); +} +#endif diff -NaurbB linux-2.6.9-pre1-bk18/drivers/usb/host/ohci-sl811.c linux-2.6.9_nbp/drivers/usb/host/ohci-sl811.c --- linux-2.6.9-pre1-bk18/drivers/usb/host/ohci-sl811.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/drivers/usb/host/ohci-sl811.c 2004-09-14 12:25:07.000000000 +0200 @@ -0,0 +1,1320 @@ +/* + * OHCI HCD (Host Controller Driver) for USB. + * + * (C) Copyright 1999 Roman Weissgaerber + * (C) Copyright 2000-2002 David Brownell + * (C) Copyright 2002 Hewlett-Packard Company + * + * SL811 Bus Glue + * + * Written by Christopher Hoover + * Based on fragments of previous driver by Rusell King et al. + * + * This file is licenced under the GPL. + */ + +#include +#include + +#include +#include +#include + +#include + +#define SOF_B + +#ifdef USE_TST_GPIOS +static inline void SET_GPIO(int n) +{ + BUG_ON(n >= ARRAY_SIZE(tst_gpios)); + gpio_set_bit(tst_gpios[n]); +} + +static inline void CLR_GPIO(int n) +{ + BUG_ON(n >= ARRAY_SIZE(tst_gpios)); + gpio_clr_bit(tst_gpios[n]); +} + +static inline void PULSE_GPIO(int n) +{ + SET_GPIO(n); + CLR_GPIO(n); +} +#else +#define SET_GPIO(n) do {} while (0) +#define CLR_GPIO(n) do {} while (0) +#define PULSE_GPIO(n) do {} while (0) +#endif +#include + + + +//static int hc_sl811_usb_reset(struct usb_hcd *hcd); + +//static void hcd_sl811_release(struct usb_bus *bus); + +#include "ohci-emu.c" + +extern int usb_disabled(void); +#ifdef KERNEL_THREAD +static int ohci_sl811_thread(void *data); +#endif + +static inline void hc_sl811_set_drvdata(struct hc_sl811_dev *dev, struct usb_hcd *data) +{ + dev_set_drvdata(&dev->dev, data); +} + +static inline struct usb_hcd* hc_sl811_get_drvdata(struct hc_sl811_dev *dev) +{ + struct usb_hcd *hcd = dev_get_drvdata(&dev->dev); + + return hcd; +} + +#if 0 +static int usb_hcd_sl811_remove(struct hc_sl811_dev *dev); +#else +static int usb_hcd_sl811_remove(struct usb_hcd *hcd, struct hc_sl811_dev *dev); +#endif +/*-------------------------------------------------------------------------*/ + +//#define TEST_DMA +#ifdef TEST_DMA +static inline void _dma_move_data(int chan, u32 src, u32 dst, u32 dma_len) +{ + const unsigned int DMA_FLAGS = DCMD_INCTRGADDR | DCMD_INCSRCADDR | DCMD_BURST32; + + if (chan < 0) { + return; + } + + DDPRINTK("%s: Moving %d byte from %08x to %08x\n", __FUNCTION__, + dma_len, src, dst); + DCSR(chan) = DCSR_NODESC; + DSADR(chan) = src; + DTADR(chan) = dst; + DCMD(chan) = DMA_FLAGS | (DCMD_LENGTH & dma_len); + DDPRINTK("%s: DMA started: DCSR=%08x, DSADR=%08x, DTADR=%08x, DCMD=%08x\n", __FUNCTION__, + DCSR(chan), DSADR(chan), DTADR(chan), DCMD(chan)); + DCSR(chan) |= DCSR_RUN; + while (!(DCSR(chan) & DCSR_STOPSTATE)); + DDPRINTK("%s: DMA stopped: DCSR=%08x, DSADR=%08x, DTADR=%08x, DCMD=%08x\n", __FUNCTION__, + DCSR(chan), DSADR(chan), DTADR(chan), DCMD(chan)); + DCSR(chan) = DCSR_NODESC; +} + +static inline void _dma_copy_data(int chan, u32 src, u32 dst, u32 dma_len) +{ + void *dma_buf; + int src_align = src & 7; + int dst_align = dst & 7; + + DDPRINTK("%s: Copying %d byte from %08x(%d) to %08x(%d)\n", __FUNCTION__, + dma_len, src, src_align, dst, dst_align); + if ((src_align | dst_align) == 0) { + _dma_move_data(chan, src, dst, dma_len); + } else { + dma_addr_t dma_addr; + + dma_buf = pci_alloc_consistent(NULL, dma_len + 8, &dma_addr); + DDPRINTK("%s: temporary buffer allocated @ %08x:%08x\n", __FUNCTION__, + (u32)dma_buf, dma_addr); + _dma_move_data(chan, src - src_align, dma_addr, dma_len + src_align); + consistent_sync(dma_buf, SL811_BUF_SIZE, PCI_DMA_FROMDEVICE); + if (src_align != dst_align) { + DDPRINTK("%s: Shifting %d byte from %08x to %08x\n", __FUNCTION__, + dma_len, (u32)dma_buf + src_align, (u32)dma_buf + dst_align); + _memdmp(dma_buf, dma_len, dma_addr); + memmove(dma_buf + dst_align, dma_buf + src_align, dma_len); + _memdmp(dma_buf, dma_len + dst_align, dma_addr); + consistent_sync(dma_buf, SL811_BUF_SIZE, PCI_DMA_TODEVICE); + } + _dma_move_data(chan, dst - dst_align, dma_addr, dst_align); + _memdmp(dma_buf, dst_align, dma_addr); + _dma_move_data(chan, dma_addr, dst - dst_align, dma_len + dst_align); + _memdmp(dma_buf, dma_len + dst_align, dma_addr); + consistent_sync(dma_buf, SL811_BUF_SIZE, PCI_DMA_FROMDEVICE); + pci_free_consistent(NULL, dma_len + 8, dma_buf, dma_addr); + } +} +#endif + +static int sl811_start_hc(struct hc_sl811_dev *dev) +{ + int i; + int ret = 0; +#ifdef TEST_DMA + struct usb_hcd *hcd = hc_sl811_get_drvdata(dev); + u8 *src_buf = kmalloc(SL811_BUF_SIZE, GFP_DMA); + u8 *dst_buf = kmalloc(SL811_BUF_SIZE, GFP_DMA); + u32 src; + u32 dst; +#endif + + DDPRINTK("%s: Starting SL811 OHCI USB Controller\n", __FUNCTION__); + +#if 0 + for (i = 0; i < ARRAY_SIZE(tst_gpios); i++) { + CLR_GPIO(i); + pxa_gpio_mode(tst_gpios[i] | GPIO_OUT); + } +#endif +#if 0 + for (i = 0; i < (1 << (ARRAY_SIZE(tst_gpios) - 1)); i++) { + int bit; + int carry = 1; + + for (bit = 0; carry && (bit < ARRAY_SIZE(tst_gpios)); bit++) { + if (gpio_tst_bit(tst_gpios[bit])) { + CLR_GPIO(bit); + carry = 1; + } else { + SET_GPIO(bit); + carry = 0; + } + } + } +#endif + /* + * Configure the power sense and control lines. Place the USB + * host controller in reset. + */ +#ifdef CONFIG_ARCH_KARO + karo_pcf8574_clr(FMsk(KARO_PCF8574_USBRST)); +#endif +#ifdef CONFIG_MACH_NETBOOKPRO + GPCR(14) = GPIO_bit(14); +#endif + + /* + * Now, carefully enable the USB clock, and take + * the USB host controller out of reset. + */ +#ifdef CONFIG_ARCH_KARO + karo_pcf8574_set(FMsk(KARO_PCF8574_USBRST)); +#endif +#ifdef CONFIG_MACH_NETBOOKPRO + GPSR(14) = GPIO_bit(14); +#endif + + + DDPRINTK("%s: Testing SL811 register access\n", __FUNCTION__); +#if DEBUG_BUF_SIZE > 0 + debug_on = 0; +#endif + for (i = 0; i < SL811_BUF_SIZE; i++) { + hc_sl811_write_reg(dev, SL11H_DATA_START + i, ~i); + } + + HC_SL811_WRITE_ADDR(dev, SL11H_DATA_START); + for (i = 0; i < SL811_BUF_SIZE; i++) { + u8 val = HC_SL811_READ_DATA(dev); + u8 ref = ~i; + if (val != ref) { + DPRINTK("%s: SL811 register test failed @ %02x: wr: %02x, rd %02x\n", + __FUNCTION__, i, ref, val); + ret = 1; + break; + } + } + + DDPRINTK("%s: Clearing SL811 buffer\n", __FUNCTION__); + for (i = 0; i < SL811_BUF_SIZE; i++) { + hc_sl811_write_reg(dev, SL11H_DATA_START + i, 0); + } + ENABLE_DEBUG; + + dev->hw_rev = hc_sl811_read_reg(dev, SL11H_HWREVREG) >> 4; + if (dev->hw_rev == 0) { + DPRINTK("%s: Bad HW rev\n", __FUNCTION__); + return -ENODEV; + } + // enable Host mode + hc_sl811_write_reg(dev, SL11H_CTLREG2, SL11H_CTL2MASK_HOSTMODE); + +#ifdef TEST_DMA + DDPRINTK("%s: Testing DMA capabilities\n", __FUNCTION__); + src = dma_map_single(hcd->self.controller, src_buf, SL811_BUF_SIZE, DMA_TO_DEVICE); + dst = dma_map_single(hcd->self.controller, dst_buf, SL811_BUF_SIZE, DMA_FROM_DEVICE); + if (src == 0 || dst == 0) { + DDPRINTK("%s: Failed to map DMA buffers\n", __FUNCTION__); + } else { + for (i = 0; i < SL811_BUF_SIZE; i++) { + src_buf[i] = i; + } + memset(dst_buf, 0xee, SL811_BUF_SIZE); + dma_sync_single(hcd->self.controller, src, SL811_BUF_SIZE, DMA_TO_DEVICE); + _dma_copy_data(0, src, dst, SL811_BUF_SIZE); + if (memcmp(src_buf, dst_buf, SL811_BUF_SIZE) == 0) { + DDPRINTK("%s: DMA move succeeded\n", __FUNCTION__); + } else { + DPRINTK("%s: DMA move failed:\n", __FUNCTION__); + _memdmp(dst_buf, SL811_BUF_SIZE, dst); + _memdmp(src_buf, SL811_BUF_SIZE, src); + } + } + dma_unmap_single(hcd->self.controller, src, SL811_BUF_SIZE, DMA_TO_DEVICE); + dma_unmap_single(hcd->self.controller, dst, SL811_BUF_SIZE, DMA_FROM_DEVICE); + kfree(src_buf); + kfree(dst_buf); + return -ENODEV; +#endif + +#ifdef KERNEL_THREAD_ + DDPRINTK("%s: Starting kernel thread\n", __FUNCTION__); + ret = kernel_thread(ohci_sl811_thread, dev, 0); + if (ret <= 0) { + printk("%s: Failed to start kernel thread %d\n", __FUNCTION__, ret); + if (ret == 0) { + ret = -ENOMEM; + } + } else { + wait_for_completion(&dev->complete); + DDPRINTK("%s: Kernel thread started\n", __FUNCTION__); + ret = 0; + } +#endif + return ret; +} + +static void sl811_stop_hc(struct hc_sl811_dev *dev) +{ + DPRINTK("%s: stopping SL811 OHCI USB Controller\n", __FUNCTION__); + + /* + * Put the USB host controller into reset. + */ +#ifdef CONFIG_ARCH_KARO + karo_pcf8574_clr(FMsk(KARO_PCF8574_USBRST)); +#endif +#ifdef CONFIG_MACH_NETBOOKRP + GPCR(14) = GPIO_bit(14); +#endif + +#if DEBUG_BUF_SIZE > 0 + print_debug_buffer(); +#endif + /* + * Stop the USB clock. + */ + // No way to do it on KARO boards +#ifdef KERNEL_THREAD + if (dev->task) { + dev->flags.b.shutdown = 1; + if (waitqueue_active(&dev->task_wait)) { + DDPRINTK("%s: Waking up kernel thread\n", __FUNCTION__); + wake_up_interruptible(&dev->task_wait); + } else { + DDPRINTK("%s: Wait queue not active\n", __FUNCTION__); + } + DDPRINTK("%s: Waiting for kernel thread to terminate\n", __FUNCTION__); + wait_for_completion(&dev->complete); + DDPRINTK("%s: kernel thread terminated\n", __FUNCTION__); + } else { + DDPRINTK("%s: Kernel thread not yet started\n", __FUNCTION__); + } +#endif +} + +/* + * initialize the emulated OHCI registers + */ +static void sl811_reset_hc(struct usb_hcd *hcd, int cold) +{ + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + struct ohci_regs *regs = hcd->regs; + struct hc_sl811_dev *dev = hcd_to_sl811_dev(hcd); + const u8 nr_ports = 1; + const u8 pwr_on_delay = 100; + + DDPRINTK("%s: Performing %s reset of HC SL811, regs=%08x->%08x\n", __FUNCTION__, + cold ? "cold" : "warm", (u32)ohci->regs, (u32)regs); + ohci->regs = regs; + + DDPRINTK("%s: Disabling SL811 interrupts\n", __FUNCTION__); + hc_sl811_write_reg(dev, SL11H_INTENBLREG, 0); + DDPRINTK("%s: Resetting SL811 chip\n", __FUNCTION__); + hc_sl811_write_reg(dev, SL11H_CTLREG1, SL11H_CTL1VAL_RESET); + + if (cold) { + memzero(regs, sizeof(struct ohci_regs)); + regs->revision = 0x10; // OHCI Rev. + regs->control = OHCI_USB_RESET; + regs->roothub.a = RH_A_PSM | RH_A_NOCP | nr_ports | (pwr_on_delay << 24); + regs->roothub.status = 0; + regs->roothub.portstatus[0] = RH_PS_PPS; + } else { + regs->control = OHCI_USB_SUSPEND; + regs->ed_controlcurrent = 0; + regs->ed_controlhead = 0; + regs->ed_bulkcurrent = 0; + regs->ed_bulkhead = 0; + } +} + +static void sl811_dma_irq(int dma, void *dummy, struct pt_regs *regs) +{ + printk(KERN_WARNING "%s: Ooops! How did we get **HERE**?\n", __FUNCTION__); + DPRINTK("%s: Stopping DMA channel %d\n", __FUNCTION__, dma); + DCSR(dma) = 0; +} + +#define RESET_WIDTH 50 + +/* + INSRMV interrupt + enable SL11H_INTMASK_USBRESET +*/ +static void hc_sl811_start_sof(struct usb_hcd *hcd, int connected) +{ + struct hc_sl811_dev *dev = hcd_to_sl811_dev(hcd); + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + u16 sof_ctr = (ohci->regs->fminterval & OHCI_FR) + 1; + u8 ctrl2 = SL11H_CTL2MASK_HOSTMODE | (sof_ctr >> 8); + u8 ctrl1 = SL11H_CTL1MASK_SOFENA; + + DPRINTK( "%s: connected=%d\n", __func__, connected ); + + switch (connected) { + case 0: + DPRINTK("%s: Disabling SOF\n", __FUNCTION__); + hc_sl811_clr_mask(dev, SL11H_CTLREG1, SL11H_CTL1MASK_SOFENA); + break; + case 1: + ctrl1 |= SL11H_CTL1MASK_NSPD; + ctrl2 |= SL11H_CTL2MASK_DSWAP; + //hc_sl811_write_reg(dev, SL11H_CTLREG1, SL11H_CTL1VAL_FORCE_J); + // fall through + case 2: + DPRINTK("%s: Enabling SOF %02x\n", __FUNCTION__, ctrl2); +#ifdef SOF_B + hc_sl811_write_reg(dev, SL11H_BUFLNTHREG_B, 0); //zero lenth + hc_sl811_write_reg(dev, SL11H_PIDEPREG_B, 0x50); //send SOF to EP0 + hc_sl811_write_reg(dev, SL11H_DEVADDRREG_B, 0x01); //address0 +#else + hc_sl811_write_reg(dev, SL11H_BUFLNTHREG, 0); //zero lenth + hc_sl811_write_reg(dev, SL11H_PIDEPREG, 0x50); //send SOF to EP0 + hc_sl811_write_reg(dev, SL11H_DEVADDRREG, 0x01); //address0 +#endif + hc_sl811_write_reg(dev, SL11H_SOFLOWREG, (u8)sof_ctr); // start sending SOF tokens + +#ifdef SOF_B + hc_sl811_set_mask(dev, SL11H_INTENBLREG, SL11H_INTMASK_XFERDONE_B); +#else + hc_sl811_set_mask(dev, SL11H_INTENBLREG, SL11H_INTMASK_XFERDONE); +#endif + hc_sl811_write_reg(dev, SL11H_CTLREG2, ctrl2); + hc_sl811_write_reg(dev, SL11H_CTLREG1, ctrl1); +#ifdef SOF_B + hc_sl811_write_reg(dev, SL11H_HOSTCTLREG_B, SL11H_HCTLMASK_ARM); +#else + hc_sl811_write_reg(dev, SL11H_HOSTCTLREG, SL11H_HCTLMASK_ARM); +#endif + } + //hc_sl811_write_reg(dev, SL11H_INTSTATREG, 0xff); +} + +static int hc_sl811_handle_insrmv(struct usb_hcd *hcd) +{ + int ret = 0; + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + struct hc_sl811_dev *dev = hcd_to_sl811_dev(hcd); + u8 sl811_stat; + u8 int_mask; + + DDPRINTK("%s: \n", __FUNCTION__); + + hc_sl811_set_mask(dev, SL11H_INTENBLREG, SL11H_INTMASK_USBRESET); + hc_sl811_write_reg(dev, SL11H_INTSTATREG, SL11H_INTMASK_USBRESET | SL11H_INTMASK_INSRMV); + mdelay(10); + sl811_stat = hc_sl811_read_reg(dev, SL11H_INTSTATREG); + + if (sl811_stat & SL11H_INTMASK_USBRESET) { + DDPRINTK("%s: Device removed: %02x\n", __FUNCTION__, sl811_stat); + ohci_sl811_update_portstatus(ohci, 0, 0, RH_PS_CCS | RH_PS_PES); + int_mask = SL11H_INTMASK_INSRMV; + } else { + int_mask = SL11H_INTMASK_INSRMV | SL11H_INTMASK_SOFINTR | SL11H_INTMASK_XFERDONE; + if (sl811_stat & SL11H_INTMASK_DSTATE) { + DDPRINTK("%s: Full speed device attached: %02x\n", __FUNCTION__, sl811_stat); + ret = 2; + ohci_sl811_update_portstatus(ohci, 0, RH_PS_CCS | RH_PS_PES, RH_PS_PRS); + } else { + DDPRINTK("%s: Low speed device attached: %02x\n", __FUNCTION__, sl811_stat); + ret = 1; + ohci_sl811_update_portstatus(ohci, 0, RH_PS_CCS | RH_PS_PES | RH_PS_LSDA, RH_PS_PRS); + } + } + DDPRINTK("%s: Enabling interrupts: %02x\n", __FUNCTION__, int_mask); + hc_sl811_write_reg(dev, SL11H_INTENBLREG, int_mask); + + return ret; +} + +static void hc_sl811_usb_reset(struct usb_hcd *hcd, int assert) +{ + struct hc_sl811_dev *dev = hcd_to_sl811_dev(hcd); + +#if DEBUG_BUF_SIZE > 0 + print_debug_buffer(); +#endif + SET_GPIO(0); + if (assert) { + DDPRINTK("%s: %sing reset @ %12u\n", __FUNCTION__, + assert > 0 ? "assert" : "puls", OSCR); + SET_GPIO(1); + hc_sl811_write_reg(dev, SL11H_INTENBLREG, 0); + hc_sl811_write_reg(dev, SL11H_CTLREG1, SL11H_CTL1VAL_RESET); + DDPRINTK("%s: Clearing interrupt status %02x\n", __FUNCTION__, + hc_sl811_read_reg(dev, SL11H_INTSTATREG)); + hc_sl811_write_reg(dev, SL11H_INTSTATREG, 0xff); + } + + if (assert < 0) { + if (in_interrupt()) { + mdelay(RESET_WIDTH); + } else { + schedule_timeout((RESET_WIDTH * HZ) / 1000); + } + } + + if (assert <= 0) { + DDPRINTK("%s: deasserting reset @ %12u\n", __FUNCTION__, OSCR); + CLR_GPIO(1); + DDPRINTK("%s: Clearing interrupt status %02x\n", __FUNCTION__, + hc_sl811_read_reg(dev, SL11H_INTSTATREG)); + hc_sl811_write_reg(dev, SL11H_INTSTATREG, 0xff); + hc_sl811_write_reg(dev, SL11H_CTLREG1, 0); + + hc_sl811_write_reg(dev, SL11H_INTENBLREG, SL11H_INTMASK_INSRMV); + } + CLR_GPIO(0); + print_debug_buffer(); +} + +#ifdef KERNEL_THREAD +static int hc_sl811_handle_resume(struct usb_hcd *hcd) +{ + return 0; +} + +static int hc_sl811_do_suspend(struct usb_hcd *hcd) +{ + return 0; +} + +static void timer_event(unsigned long data) +{ + struct hc_sl811_dev *dev = (void*)data; + + dev->flags.b.timer = 1; + if (waitqueue_active(&dev->task_wait)) { + wake_up_interruptible(&dev->task_wait); + } +} + +/* + * state machine to keep track of device insertion/removal events + * and guarantee the required reset timing + * + */ +typedef enum { + ST_INIT, + ST_IDLE, + ST_RESET, + ST_TIMER, + ST_CONNECT, + ST_INSRMV, + ST_RESUME, + ST_SUSPEND, +} st_t; + +/* + State Event Action NewState + ST_INIT Reset Assert Reset, Activate Timer ST_TIMER + ST_TIMER Timer Deassert Reset ST_IDLE + ST_IDLE +*/ + +static int ohci_sl811_thread(void *data) +{ + int ret = 0; + struct task_struct *tsk = current; + struct hc_sl811_dev *dev = data; + struct usb_hcd *hcd = hc_sl811_get_drvdata(dev); + //struct ohci_hcd *ohci = hcd_to_ohci(hcd); + int running = 1; + st_t state = ST_INIT; + st_t new_state = ST_INIT; + st_t last_state; + static struct timer_list timer; + + dev->task = current; + + current->policy = SCHED_FIFO; + current->rt_priority = 1; + daemonize("kSL811d"); + + /* only want to receive SIGKILL */ + spin_lock_irq(&tsk->sighand->siglock); + siginitsetinv(&tsk->blocked, sigmask(SIGKILL)); + recalc_sigpending(); + spin_unlock_irq(&tsk->sighand->siglock); + + sl811_reset_hc(hcd, 1); + + complete(&dev->complete); + + while (running) { + state = new_state; + switch (state) { + case ST_INIT: + init_timer(&timer); + timer.function = timer_event; + timer.data = (unsigned long)dev; + new_state = ST_IDLE; + state = new_state; + // fall through + case ST_IDLE: + wait_event_interruptible(dev->task_wait, dev->flags.i); + break; + case ST_TIMER: + del_timer(&timer); + new_state = ST_IDLE; + break; + case ST_INSRMV: + if (hc_sl811_handle_insrmv(hcd)) { + new_state = ST_CONNECT; + } else { + break; + } + // fall through if connected + case ST_CONNECT: + hc_sl811_start_sof(hcd, 1); + new_state = ST_IDLE; + break; + case ST_RESET: + new_state = ST_TIMER; + break; + case ST_RESUME: + hc_sl811_handle_resume(hcd); + break; + case ST_SUSPEND: + hc_sl811_do_suspend(hcd); + break; + default: + DPRINTK("%s: Invalid state %d\n", __FUNCTION__, state); + } + last_state = state; + + if (signal_pending(tsk)) { + DDPRINTK("%s: Taking emergency exit\n", __FUNCTION__); + break; + } + + if (dev->flags.b.shutdown) { + DDPRINTK("%s: Shutting down kernel thread\n", __FUNCTION__); + running = 0; + break; + } else if (dev->flags.b.portreset) { + dev->flags.b.portreset = 0; + + DDPRINTK("%s: Resetting port 1\n", __FUNCTION__); + hc_sl811_usb_reset(hcd, -1); + add_timer(&timer); + timer.expires = jiffies + ((RESET_DELAY * HZ) / 1000); + new_state = ST_TIMER; + } else if (dev->flags.b.timer) { + dev->flags.b.timer = 0; + DDPRINTK("%s: Timer expired\n", __FUNCTION__); + new_state = ST_TIMER; + } + if (dev->flags.b.roothub_portstatus) { + dev->flags.b.roothub_portstatus = 0; + } + if (dev->flags.b.roothub_status) { + u32 status = ohci->regs->roothub.status; + + DPRINTK("%s: rh_status changed to %08x\n", __FUNCTION__, + status); + if (status & RH_HS_CRWE) { + status &= ~(RH_HS_CRWE | RH_HS_DRWE); + hc_sl811_clr(dev, SL11H_CTLREG1, SL11H_CTL1MASK_SUSPEND); + } + if (status & RH_HS_DRWE) { + hc_sl811_set(dev, SL11H_CTLREG1, SL11H_CTL1MASK_SUSPEND); + } + if (status & RH_HS_OCIC) { + status &= ~RH_HS_OCIC; + } + regs->roothub.status = status; + ohci->regs->roothub.status = status; + dev->flags.b.roothub_status = 0; + } + if (dev->flags.b.roothub_a) { + dev->flags.b.roothub_a = 0; + } + if (dev->flags.b.roothub_b) { + dev->flags.b.roothub_b = 0; + } + } + + dev->task = NULL; + DDPRINTK("%s: Finishing\n", __FUNCTION__); + complete_and_exit(&dev->complete, ret); + + return ret; +} +#endif + +/*-------------------------------------------------------------------------*/ + +/* configure so an HC device and id are always provided */ +/* always called with process context; sleeping is OK */ + + +/** + * usb_hcd_sl811_probe - initialize HC-SL811-based HCDs + * Context: !in_interrupt() + * + * Allocates basic resources for this USB host controller, and + * then invokes the start() method for the HCD associated with it + * through the hotplug entry's driver_data. + * + * Store this function in the HCD's struct pci_driver as probe(). + */ +static int usb_hcd_sl811_probe(struct hc_driver *driver, struct usb_hcd **hcd_out, struct hc_sl811_dev *dev) +{ + int retval; + struct usb_hcd *hcd = NULL; + struct device *parent = dev->dev.parent; + //struct platform_device *pdev = to_platform_device(parent); + const char *chip_names[] = { + "SL11H", + "SL811HS Rev 1.2", + "SL811HS Rev 1.5", + "Unknown"}; + +#ifdef KERNEL_THREAD + init_completion(&dev->complete); + init_waitqueue_head(&dev->task_wait); + + //init_waitqueue_head(&dev->update); + //init_MUTEX(&dev->hw_lock); +#endif + + DDPRINTK("%s: Allocating hcd struct\n", __FUNCTION__); + hcd = driver->hcd_alloc(); + if (hcd == NULL) { + DPRINTK("hcd_alloc failed\n"); + retval = -ENOMEM; + goto out; + } + + DDPRINTK("%s: Allocating hcd register struct\n", __FUNCTION__); + hcd->regs = kmalloc(sizeof(struct ohci_regs), GFP_KERNEL); + if (hcd->regs == NULL) { + retval = -ENOMEM; + goto err1; + } + hc_sl811_set_drvdata(dev, hcd); + + if (sl811_start_hc(dev) != 0) { + DPRINTK("%s: Failed to start SL811 USB host controller\n", __FUNCTION__); + retval = -ENODEV; + goto err2; + } + + hcd->driver = driver; + hcd->description = driver->description; + hcd->self.bus_name = "sl811"; // ?? + if (hcd->product_desc == NULL) { + hcd->product_desc = "HC-SL811 OHCI"; + } + hcd->self.controller = parent; + DDPRINTK("%s: controller=%08x(%08x)\n", __FUNCTION__, (u32)parent, (u32)&dev->dev); + + DDPRINTK("%s: Setting dma mask of device %08x\n", __FUNCTION__, (u32)hcd->self.controller); + retval = dma_set_mask(hcd->self.controller, (u64)0x0fffffff8); + if (retval) { + printk("%s: Failed to set dma_mask to %08llx\n", __FUNCTION__, + (u64)0x0fffffff8); + goto err2; + } + //hcd->self.controller->dma_mask = &(u64)0x0fffffff8; + DDPRINTK("%s: dma_mask=%08llx\n", __FUNCTION__, *hcd->self.controller->dma_mask); + + // done by sl811_reset_hc: memzero(hcd->regs, sizeof(struct ohci_regs)); +#if 1 +#ifdef KERNEL_THREAD + DDPRINTK("%s: Starting kernel thread\n", __FUNCTION__); + retval = kernel_thread(ohci_sl811_thread, dev, 0); + if (retval <= 0) { + printk("%s: Failed to start kernel thread %d\n", __FUNCTION__, retval); + if (retval == 0) { + retval = -ENOMEM; + goto err2; + } + } else { + wait_for_completion(&dev->complete); + DDPRINTK("%s: Kernel thread started\n", __FUNCTION__); + retval = 0; + } +#else + sl811_reset_hc(hcd, 1); +#endif +#endif + DDPRINTK("%s: Creating hcd buffer pool\n", __FUNCTION__); + retval = hcd_buffer_create(hcd); + if (retval != 0) { + DPRINTK("pool alloc failed\n"); + goto err2; + } + + DDPRINTK("%s: Resetting USB controller\n", __FUNCTION__); + /* till now HC has been in an indeterminate state ... */ + if (driver->reset && (retval = driver->reset(hcd)) < 0) { + dev_err(hcd->self.controller, "can't reset\n"); + goto err3; + } + hcd->state = USB_STATE_HALT; + + DDPRINTK("%s: Requesting IRQ %d (GPIO%d)\n", __FUNCTION__, dev->irq, IRQ_TO_GPIO(dev->irq)); + retval = request_irq(dev->irq, ohci_sl811_interrupt, SA_INTERRUPT, hcd->description, hcd); + if (retval != 0) { + DPRINTK("request_irq failed\n"); + retval = -EBUSY; + goto err3; + } + hcd->irq = dev->irq; + set_irq_type(hcd->irq, IRQT_RISING); + + DDPRINTK("%s: Requesting DMA channel\n", __FUNCTION__); + retval = pxa_request_dma((char*)hcd_name, DMA_PRIO_HIGH, sl811_dma_irq, NULL); + if (retval < 0) { + DPRINTK("request_dma failed: %d\n", retval); + goto err4; + } + dev->dma = retval; + +#ifdef CONSISTENT_ALLOC + dev->dma_buf = consistent_alloc(GFP_DMA, SL811_BUF_SIZE, &dev->dma_bus_addr, 0); + if (dev->dma_buf == NULL) { + DPRINTK("%s: Failed to allocate DMA buffer\n", __FUNCTION__); + retval = -ENOMEM; + goto err4; + } +#endif + info("%s (HC-%s) at 0x%p, irq %d DMA %d\n", hcd->description, + chip_names[dev->hw_rev & 0x0f], hcd->regs, hcd->irq, dev->dma); + + usb_bus_init(&hcd->self); + hcd->self.op = &usb_hcd_operations; + hcd->self.hcpriv = hcd; + //hcd->self.release = hcd_sl811_release; + + INIT_LIST_HEAD(&hcd->dev_list); + + DDPRINTK("adding hdc->self=%p, hcd->self->controller=%p\n", + &hcd->self, hcd->self.controller); + + usb_register_bus(&hcd->self); + + DDPRINTK("calling driver->start(%p)\n", hcd); + + if ((retval = driver->start(hcd)) < 0) { + usb_hcd_sl811_remove(hcd, dev); + return retval; + } + + DDPRINTK("setting hcd_out (%p) to %p\n", hcd_out, hcd); + *hcd_out = hcd; + + return 0; + + err4: + if (dev->dma >= 0) { + pxa_free_dma(dev->dma); + dev->dma = -1; + } + free_irq(dev->irq, hcd); + err3: + hcd_buffer_destroy(hcd); + err2: + sl811_stop_hc(dev); + err1: + kfree(hcd->regs); + driver->hcd_free(hcd); + out: + + return retval; +} + +/* may be called without controller electrically present */ +/* may be called with controller, bus, and devices active */ + +/** + * usb_hcd_sl811_remove - shutdown processing for HC-SL811-based HCDs + * @dev: USB Host Controller being removed + * Context: !in_interrupt() + * + * Reverses the effect of usb_hcd_sl811_probe(), first invoking + * the HCD's stop() method. It is always called from a thread + * context, normally "rmmod", "apmd", or something similar. + * + */ +static int usb_hcd_sl811_remove(struct usb_hcd *hcd, struct hc_sl811_dev *dev) +{ + struct usb_device *hub; + + dev_info(hcd->self.controller, "remove, state %x\n", hcd->state); + + BUG_ON(in_interrupt()); + + hub = hcd->self.root_hub; + if (HCD_IS_RUNNING (hcd->state)) { + hcd->state = USB_STATE_QUIESCING; + } + + DDPRINTK("%s: roothub graceful disconnect\n", hcd->self.bus_name); + usb_disconnect(&hub); + + DDPRINTK("%s: Stopping driver\n", __FUNCTION__); + hcd->driver->stop(hcd); + DDPRINTK("%s: Destroying buffers\n", __FUNCTION__); + hcd_buffer_destroy(hcd); + hcd->state = USB_STATE_HALT; + + hc_sl811_set_drvdata(dev, NULL); + + if (dev->dma >= 0) { + DDPRINTK("%s: Freeing DMA channel %d\n", __FUNCTION__, dev->dma); + pxa_free_dma(dev->dma); + } + + DDPRINTK("%s: Freeing IRQ %d\n", __FUNCTION__, hcd->irq); + free_irq(hcd->irq, hcd); + + DPRINTK("%s: Deregistering bus\n", __FUNCTION__); + usb_deregister_bus(&hcd->self); + + hcd->driver->hcd_free(hcd); //done in hcd_sl811_release + + sl811_stop_hc(dev); + + return 0; +} + +/* + * perform OHCI (PCI) bus reset + */ +static int ohci_sl811_reset(struct usb_hcd *hcd) +{ + int ret; + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + + DDPRINTK("%s: Resetting HC SL811 regs=%08x->%08x\n", __FUNCTION__, + (u32)ohci->regs, (u32)hcd->regs); + ohci->regs = hcd->regs; + + ret = hc_reset(ohci); + + return ret; +} + +static int __devinit ohci_sl811_start(struct usb_hcd *hcd) +{ + struct ohci_hcd *ohci = hcd_to_ohci(hcd); + int ret; + + DPRINTK("%s: hcd=%08x\n", __FUNCTION__, (u32)hcd); + + ohci->hcca = pci_alloc_consistent(NULL, sizeof(*ohci->hcca), &ohci->hcca_dma); + if (!ohci->hcca) { + return -ENOMEM; + } + + DDPRINTK("%s: Clearing HCCA @ %08x\n", __FUNCTION__, (u32)ohci->hcca); + memzero(ohci->hcca, sizeof(struct ohci_hcca)); + + DDPRINTK("%s: Initializing ohci memory\n", __FUNCTION__); + ret = ohci_mem_init(ohci); + if (ret < 0) { + ohci_stop(hcd); + return ret; + } + + DDPRINTK("%s: Starting OHCI host controller\n", __FUNCTION__); + if (hc_start(ohci) < 0) { + err("can't start %s", ohci->hcd.self.bus_name); + ohci_stop(hcd); + return -EBUSY; + } + create_debug_files(ohci); + +//#ifdef DEBUG + ohci_dump(ohci, 1); +//#endif + return 0; +} + +/*-------------------------------------------------------------------------*/ +#ifdef CONFIG_PM +static int ohci_sl811_suspend(struct usb_hcd *hcd, u32 state) +{ + DDPRINTK("%s: \n", __FUNCTION__); + return 0; +} + +static int ohci_sl811_resume(struct usb_hcd *hcd) +{ + DDPRINTK("%s: \n", __FUNCTION__); + + hc_restart(hcd_to_ohci(hcd)); + + return 0; +} +#endif + +static struct hc_driver ohci_sl811_hc_driver = { + .description = hcd_name, + + /* + * generic hardware linkage + */ + .irq = ohci_irq, + .flags = HCD_MEMORY | HCD_USB11, + + /* + * basic lifecycle operations + */ + .reset = ohci_sl811_reset, + .start = ohci_sl811_start, +#ifdef CONFIG_PM + .suspend = ohci_sl811_suspend, + .resume = ohci_sl811_resume, +#endif + .stop = ohci_stop, + + /* + * memory lifecycle (except per-request) + */ + .hcd_alloc = ohci_hcd_alloc, + .hcd_free = ohci_hcd_free, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = ohci_urb_enqueue, + .urb_dequeue = ohci_urb_dequeue, + .endpoint_disable = ohci_endpoint_disable, + + /* + * scheduling support + */ + .get_frame_number = ohci_get_frame, + + /* + * root hub support + */ + .hub_status_data = ohci_hub_status_data, + .hub_control = ohci_hub_control, +}; + +/*-------------------------------------------------------------------------*/ +#define res_len(p) ((p)->end - (p)->start + 1) +static int ohci_hcd_sl811_drv_probe(struct hc_sl811_dev *dev) +{ + struct usb_hcd *hcd = NULL; + struct platform_device *pdev = to_platform_device(dev->dev.parent); + int req = -1; + int ret; + + if (usb_disabled()) { + DDPRINTK("%s: USB is disabled\n", __FUNCTION__); + return -ENODEV; + } + + DDPRINTK("%s: Got platform dev for %s @ %08x with %d resources\n", __FUNCTION__, + pdev->name, (u32)pdev, pdev->num_resources); + if (pdev->num_resources < 2) { + return -ENODEV; + } + + if (pdev->num_resources > 2) { + dev->dma_addr = pdev->resource[1].start; + } else { + dev->dma_addr = pdev->resource[0].start + res_len(&pdev->resource[0] - 1); + } + + for (req = 0; req < pdev->num_resources - 1; req++) { + struct resource *res = &pdev->resource[req]; + + DDPRINTK("%s: Requesting memory region %08lx..%08lx\n", __FUNCTION__, + res->start, res->start + res_len(res) - 1); + if (!request_mem_region(res->start, res_len(res), hcd_name)) { + DDPRINTK("%s: request_mem_region %08lx:%08lx failed\n", __FUNCTION__, + res->start, res->start + res_len(res) - 1); + ret = -EBUSY; + goto out; + } + } + dev->irq = pdev->resource[req].start; + + DDPRINTK("%s: Remapping memory region %08lx\n", __FUNCTION__, pdev->resource[0].start); + dev->addr_reg = ioremap(pdev->resource[0].start, res_len(&pdev->resource[0])); + if (dev->addr_reg == NULL) { + DDPRINTK("%s: Remapping memory region %08lx failed\n", __FUNCTION__, + pdev->resource[0].start); + ret = -ENOMEM; + goto out; + } + + if (pdev->num_resources > 2) { + DDPRINTK("%s: Remapping memory region %08lx\n", __FUNCTION__, pdev->resource[1].start); + dev->data_reg = ioremap(pdev->resource[1].start, res_len(&pdev->resource[1])); + if (dev->data_reg == NULL) { + DDPRINTK("%s: Remapping memory region %08lx failed\n", __FUNCTION__, + pdev->resource[1].start); + ret = -ENOMEM; + goto out; + } + } else { + dev->data_reg = dev->addr_reg + res_len(&pdev->resource[0]); + } + + DDPRINTK("%s: Probing for HC_SL811 @ %08x\n", __FUNCTION__, (u32)&ohci_sl811_hc_driver); + ret = usb_hcd_sl811_probe(&ohci_sl811_hc_driver, &hcd, dev); + + DDPRINTK("%s: usb_hcd_sl811_probe returned %d\n", __FUNCTION__, ret); + if (ret == 0) { + hc_sl811_set_drvdata(dev, hcd); + } + + return 0; + + out: + if (dev->addr_reg) { + DDPRINTK("%s: Unmapping memory @ %08x\n", __FUNCTION__, (u32)dev->addr_reg); + iounmap(dev->addr_reg); + dev->addr_reg = NULL; + if ((pdev->num_resources > 2) && (dev->data_reg)) { + DDPRINTK("%s: Unmapping memory @ %08x\n", __FUNCTION__, (u32)dev->data_reg); + iounmap(dev->data_reg); + dev->data_reg = NULL; + } + } + for (; req >= 0; req--) { + struct resource *res = &pdev->resource[req]; + + DDPRINTK("%s: Releasing memory region %08lx..%08lx\n", __FUNCTION__, + res->start, res->start + res_len(res) - 1); + release_mem_region(res->start, res_len(res)); + } + + return ret; +} + +static int ohci_hcd_sl811_drv_remove(struct hc_sl811_dev *dev) +{ + struct usb_hcd *hcd = hc_sl811_get_drvdata(dev); + struct platform_device *pdev = to_platform_device(dev->dev.parent); + int i; + + DDPRINTK("%s: Removing HC-SL811 driver for hc_sl811_dev @ %08x\n", __FUNCTION__, (u32)dev); + usb_hcd_sl811_remove(hcd, dev); + + hc_sl811_set_drvdata(dev, NULL); + + DDPRINTK("%s: Unmapping memory @ %08x\n", __FUNCTION__, (u32)dev->addr_reg); + iounmap(dev->addr_reg); + + if (pdev->num_resources > 2) { + DDPRINTK("%s: Unmapping memory @ %08x\n", __FUNCTION__, (u32)dev->data_reg); + iounmap(dev->data_reg); + } + + for (i = 0; i < pdev->num_resources - 1; i++) { + struct resource *res = &pdev->resource[i]; + + DDPRINTK("%s: Releasing memory region %08lx..%08lx\n", __FUNCTION__, + res->start, res->start + res_len(res) - 1); + release_mem_region(res->start, res_len(res)); + } + + return 0; +} +#if 0 +static void hcd_sl811_release(struct usb_bus *bus) +{ + struct usb_hcd *hcd = bus->hcpriv; + + DDPRINTK("%s: bus=%08x\n", __FUNCTION__, (u32)bus); + if (hcd != NULL) { + DDPRINTK("%s: Freeing hcd @ %08x\n", __FUNCTION__, (u32)hcd); + hcd->driver->hcd_free(hcd); + } +} +#endif + +static void __sl811_release(struct device *dev) +{ + struct hc_sl811_dev *sl811_dev = dev_get_drvdata(dev); + + DDPRINTK("%s: dev=%08x\n", __FUNCTION__, (u32)dev); + if (sl811_dev != NULL) { + DDPRINTK("%s: Freeing hc_sl811_dev @ %08x\n", __FUNCTION__, (u32)sl811_dev); +#ifdef CONSISTENT_ALLOC + consistent_free(sl811_dev->dma_buf, SL811_MAP_SIZE, sl811_dev->dma_bus_addr); +#endif + kfree(sl811_dev); + } +} + +static int __sl811_probe(struct device *dev, struct resource *mem, struct resource *irq) +{ + int ret; + struct hc_sl811_dev *sl811_dev; + + sl811_dev = kmalloc(sizeof(struct hc_sl811_dev), GFP_KERNEL); + if (sl811_dev == NULL) { + return -ENOMEM; + } + memzero(sl811_dev, sizeof(struct hc_sl811_dev)); + DDPRINTK("%s: sl811_dev allocated @ %08x\n", __FUNCTION__, (u32)sl811_dev); + + sl811_dev->dma = -1; + sl811_dev->irq = NO_IRQ; + + sl811_dev->dev.parent = dev; + sl811_dev->dev.bus = &platform_bus_type; + sl811_dev->dev.release = __sl811_release; + sl811_dev->res.start = mem->start; + sl811_dev->res.end = mem->end; + sl811_dev->res.name = sl811_dev->dev.bus_id; + sl811_dev->res.flags = IORESOURCE_MEM; + + dev_set_drvdata(dev, sl811_dev); + + ret = ohci_hcd_sl811_drv_probe(sl811_dev); + + if (ret != 0) { + dev_set_drvdata(dev, NULL); + } + + if (machine_is_netbookpro()) { + printk("NetbookPro: enabling USB Power\n"); + nbp_pcon_command(I2C_SETUSBHOSTPOWER, 1, PCON_FLG_ASYNC, NULL); + printk("USB Power Enabled\n"); + } + + return ret; +} + +static struct hc_sl811_driver ohci_hcd_sl811_driver = { + .drv = { + .name = "sl811-ohci", + .bus = &platform_bus_type, + }, + .devid = 0, +#if 0 + .probe = ohci_hcd_sl811_drv_probe, + .remove = usb_hcd_sl811_remove, +#else + .probe = ohci_hcd_sl811_drv_probe, + .remove = ohci_hcd_sl811_drv_remove, +#endif +#ifdef CONFIG_PM + .suspend = NULL, + .resume = NULL, +#endif +}; + +static int sl811_bus_probe(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct resource *mem, *irq = NULL; + int ret = -ENODEV; + + DPRINTK("%s: platform_device=%08x\n", __FUNCTION__, (u32)pdev); + + mem = &pdev->resource[0]; + if (pdev->num_resources == 2) { + irq = &pdev->resource[1]; + } + ret = __sl811_probe(dev, mem, irq); + + return ret; +} + +static int sl811_bus_remove(struct device *dev) +{ + int ret = 0; + struct hc_sl811_dev *sl811_dev = dev_get_drvdata(dev); + struct hc_sl811_driver *drv = SL811_DRV(dev->driver); + + DDPRINTK("%s: removing driver %08x\n", __FUNCTION__, (u32)drv); + if (drv->remove) { + DPRINTK("%s: Removing driver @ %08x from dev %08x\n", __FUNCTION__, (u32)drv, + (u32)sl811_dev); + ret = drv->remove(sl811_dev); + } + return ret; +} + +static int hc_sl811_driver_register(struct hc_sl811_driver *driver) +{ + int ret; + + WARN_ON(driver->drv.suspend || driver->drv.resume || driver->drv.probe || driver->drv.remove); + DDPRINTK("%s: Registering hc_sl811_driver @ %08x\n", __FUNCTION__, (u32)driver); + driver->drv.probe = sl811_bus_probe; + driver->drv.remove = sl811_bus_remove; + + DDPRINTK("%s: Registering platform bus driver @ %08x\n", __FUNCTION__, (u32)&driver->drv); + ret = driver_register(&driver->drv); + DDPRINTK("%s: driver_register returned %d\n", __FUNCTION__, ret); + + return ret; +} + +static void hc_sl811_driver_unregister(struct hc_sl811_driver *driver) +{ + DDPRINTK("%s: Unregistering hc_sl811_driver @ %08x\n", __FUNCTION__, (u32)driver); + driver_unregister(&driver->drv); +} + +static int __init ohci_hcd_sl811_init(void) +{ + int ret = 0; + +#if DEBUG_BUF_SIZE > 0 + debug_buffer = kmalloc(DEBUG_BUF_SIZE * sizeof(struct debug_buffer), GFP_KERNEL); +#endif + printk(DRIVER_INFO " (HC-SL811)\n"); + + DDPRINTK("%s: Registering hc_driver @ %08x\n", __FUNCTION__, + (u32)&ohci_hcd_sl811_driver); + ret = hc_sl811_driver_register(&ohci_hcd_sl811_driver); + DDPRINTK("%s: driver_register returned %d\n", __FUNCTION__, ret); + + return ret; +} + +static void __exit ohci_hcd_sl811_cleanup(void) +{ +#if DEBUG_BUF_SIZE > 0 + print_debug_buffer(); + kfree(debug_buffer); + debug_buffer = NULL; +#endif + DDPRINTK("%s: Unregistering hc_driver @ %08x\n", __FUNCTION__, + (u32)&ohci_hcd_sl811_driver); + hc_sl811_driver_unregister(&ohci_hcd_sl811_driver); +} + +module_init(ohci_hcd_sl811_init); +module_exit(ohci_hcd_sl811_cleanup); diff -NaurbB linux-2.6.9-pre1-bk18/drivers/usb/host/ohci-sl811.h linux-2.6.9_nbp/drivers/usb/host/ohci-sl811.h --- linux-2.6.9-pre1-bk18/drivers/usb/host/ohci-sl811.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/drivers/usb/host/ohci-sl811.h 2004-09-14 12:25:07.000000000 +0200 @@ -0,0 +1,371 @@ +/* + * SL811HS HCD (Host Controller Driver) for USB. + * + * COPYRIGHT (C) by CYPRESS SEMICONDUCTOR INC + * + * + */ + +//#define USE_DMA //Can't use DMA because of SL811 register address alignment :( +//#define CONSISTENT_ALLOC +//#define KERNEL_THREAD +//#define DEBUG_BUF_SIZE 4096 +#if DEBUG_BUF_SIZE > 0 +static struct debug_buffer { + u32 tag; + u32 data; + u32 frame_no; + u32 timestamp; +} *debug_buffer; +static int debug_on = -1; +static int debug_level = 0x10; +static unsigned int debug_buf_wr = 0; +static unsigned int debug_buf_cnt = 0; +static unsigned int debug_buf_rd = 0; + +#define print_debug_buffer() _print_debug_buffer(__FUNCTION__, __LINE__) +static void _print_debug_buffer(const char *func, int line) +{ + BUG_ON(debug_buf_cnt > DEBUG_BUF_SIZE); + BUG_ON(debug_buf_wr > DEBUG_BUF_SIZE); + BUG_ON(debug_buf_rd > DEBUG_BUF_SIZE); + if (debug_buffer != NULL) { + printk(KERN_WARNING "%s: %s @ %d: cnt=%d, rd=%d, wr=%d\n", __FUNCTION__, + func, line, debug_buf_cnt, debug_buf_rd, debug_buf_wr); + while (debug_buf_cnt > 0) { + struct debug_buffer *dbg = &debug_buffer[debug_buf_rd]; + static u32 last_time = 0; + + printk(KERN_DEBUG "dbg: %02x: %08x frame: %04x rem: %06x time: %10u(%10u)\n", + (u8)dbg->tag, dbg->data, dbg->frame_no, dbg->tag >> 8, + dbg->timestamp, dbg->timestamp - last_time); + last_time = dbg->timestamp; + debug_buf_rd++; + debug_buf_cnt--; + if (debug_buf_rd >= DEBUG_BUF_SIZE) { + debug_buf_rd -= DEBUG_BUF_SIZE; + } +#if 0 + if ((dbg->tag & 0xff) == 0xc0) { + printk("\n"); + } +#endif + } + } +} + +#define DEBUG_STORE(data, tag) _debug_store(hcd ? hcd_to_ohci(hcd)->regs : NULL, data, tag) +static inline void _debug_store(struct ohci_regs *regs, u32 data, u8 tag) { + if (debug_on && tag >= debug_level) { + if (likely(debug_buffer != NULL)) { + struct debug_buffer *dbg; + + BUG_ON(debug_buf_cnt > DEBUG_BUF_SIZE); + BUG_ON(debug_buf_wr > DEBUG_BUF_SIZE); + BUG_ON(debug_buf_rd > DEBUG_BUF_SIZE); + if (debug_buf_cnt == DEBUG_BUF_SIZE) { +#if 0 + print_debug_buffer(); +#endif + } + dbg = &debug_buffer[debug_buf_wr++]; + dbg->data = data; + if (regs != NULL) { + dbg->tag = tag | (regs->fmremaining << 8); + dbg->frame_no = regs->fmnumber; + } else { + dbg->tag = tag; + dbg->frame_no = -1; + } + dbg->timestamp = OSCR; + if (unlikely(debug_buf_wr >= DEBUG_BUF_SIZE)) { + debug_buf_wr -= DEBUG_BUF_SIZE; + } + if (debug_buf_cnt < DEBUG_BUF_SIZE) { + debug_buf_cnt++; + } else { + debug_buf_rd++; + if (unlikely(debug_buf_rd >= DEBUG_BUF_SIZE)) { + debug_buf_rd -= DEBUG_BUF_SIZE; + } + } + } + } +} +#define ENABLE_DEBUG if (debug_on >= 0) debug_on++ +#define DISABLE_DEBUG if (debug_on > 0) debug_on-- +#define DEBUG_LVL(n) debug_level = (n) +#else +#define print_debug_buffer() do {} while(0) +#define debug_on 0 +#define ENABLE_DEBUG +#define DISABLE_DEBUG +#define DEBUG_LVL(n) +#define DEBUG_STORE(data, tag) do { (hcd) = (hcd); } while(0) +#endif + +#define DATA0_WR 0x07 // (Arm+Enable+tranmist to Host+DATA0) +#define DATA1_WR 0x47 // (Arm+Enable+tranmist to Host on DATA1) +#define ZDATA0_WR 0x05 // (Arm+Transaction Ignored+tranmist to Host+DATA0) +#define ZDATA1_WR 0x45 // (Arm+Transaction Ignored+tranmist to Host+DATA1) +#define DATA0_RD 0x03 // (Arm+Enable+received from Host+DATA0) +#define DATA1_RD 0x43 // (Arm+Enable+received from Host+DATA1) + +#define PID_SETUP 0x2d // USB Specification 1.1 Standard Definition +#define PID_SOF 0xA5 +#define PID_IN 0x69 +#define PID_OUT 0xe1 + +#define SL11H_HOSTCTLREG 0 +#define SL11H_BUFADDRREG 1 +#define SL11H_BUFLNTHREG 2 +#define SL11H_PKTSTATREG 3 /* read */ +#define SL11H_PIDEPREG 3 /* write */ +#define SL11H_XFERCNTREG 4 /* read */ +#define SL11H_DEVADDRREG 4 /* write */ +#define SL11H_CTLREG1 5 +#define SL11H_INTENBLREG 6 + +#define SL11H_HOSTCTLREG_B 8 +#define SL11H_BUFADDRREG_B 9 +#define SL11H_BUFLNTHREG_B 0x0A +#define SL11H_PKTSTATREG_B 0x0B /* read */ +#define SL11H_PIDEPREG_B 0x0B /* write */ +#define SL11H_XFERCNTREG_B 0x0C /* read */ +#define SL11H_DEVADDRREG_B 0x0C /* write */ + +#define SL11H_INTSTATREG 0x0D /* write clears bitwise */ +#define SL11H_HWREVREG 0x0E /* read */ +#define SL11H_SOFLOWREG 0x0E /* write */ +#define SL11H_SOFTMRREG 0x0F /* read */ +#define SL11H_CTLREG2 0x0F /* write */ +#define SL11H_DATA_START 0x10 + +/* Host control register bits (addr 0) */ +#define SL11H_HCTLMASK_ARM 0x01 +#define SL11H_HCTLMASK_ENBLEP 0x02 +#define SL11H_HCTLMASK_WRITE 0x04 +#define SL11H_HCTLMASK_ISOCH 0x10 +#define SL11H_HCTLMASK_AFTERSOF 0x20 +#define SL11H_HCTLMASK_SEQ 0x40 +#define SL11H_HCTLMASK_PREAMBLE 0x80 + +/* Packet status register bits (addr 3) */ +#define SL11H_STATMASK_ACK 0x01 +#define SL11H_STATMASK_ERROR 0x02 +#define SL11H_STATMASK_TMOUT 0x04 +#define SL11H_STATMASK_SEQ 0x08 +#define SL11H_STATMASK_SETUP 0x10 +#define SL11H_STATMASK_OVF 0x20 +#define SL11H_STATMASK_NAK 0x40 +#define SL11H_STATMASK_STALL 0x80 + +#define SL811_PKT_ERR_MASK (SL11H_STATMASK_ERROR | \ + SL11H_STATMASK_TMOUT | \ + SL11H_STATMASK_OVF | \ + SL11H_STATMASK_NAK | \ + SL11H_STATMASK_STALL | \ + SL11H_STATMASK_SEQ) + +/* Control register 1 bits (addr 5) */ +#define SL11H_CTL1MASK_SOFENA (1 << 0) +#define SL11H_CTL1MASK_NOTXEOF2 (1 << 2) /* ??? */ +#define SL11H_CTL1MASK_DSTATE 0x18 +#define SL11H_CTL1MASK_NSPD 0x20 +#define SL11H_CTL1MASK_SUSPEND 0x40 +#define SL11H_CTL1MASK_CLK12 0x80 + +#define SL11H_CTL1VAL_NORMAL 0x00 +#define SL11H_CTL1VAL_RESET 0x08 +#define SL11H_CTL1VAL_FORCE_J 0x10 +#define SL11H_CTL1VAL_FORCE_K 0x18 + +/* Interrupt enable (addr 6) and interrupt status register bits (addr 0xD) */ +#define SL11H_INTMASK_XFERDONE 0x01 +#define SL11H_INTMASK_XFERDONE_B 0x02 +#define SL11H_INTMASK_SOFINTR 0x10 +#define SL11H_INTMASK_INSRMV 0x20 +#define SL11H_INTMASK_USBRESET 0x40 +#define SL11H_INTMASK_DSTATE 0x80 /* only in status reg */ + +#define SL811_INT_MASK (SL11H_INTMASK_SOFINTR | SL11H_INTMASK_INSRMV) + +/* HW rev and SOF lo register bits (addr 0xE) */ +#define SL11H_HWRMASK_HWREV 0xF0 + +/* SOF counter and control reg 2 (addr 0xF) */ +#define SL11H_CTL2MASK_SOFHI 0x3F +#define SL11H_CTL2MASK_DSWAP 0x40 +#define SL11H_CTL2MASK_HOSTMODE 0x80 + +#define SL811_MAP_SIZE 256 +#define SL811_BUF_SIZE (SL811_MAP_SIZE - SL11H_DATA_START) + +#define ohci_to_hcd(ohci) (&(ohci)->hcd) + +struct xfer_buf { + u8 len; + union { + u8 pid_ep; + u8 pkt_stat; + } __attribute__((packed)); + union { + u8 dev_addr; + u8 count; + } __attribute__((packed)); + u8 hc; +} __attribute__((packed)); + +struct hc_sl811_dev { + struct device dev; + struct resource res; + //unsigned long phys_addr; + //unsigned long mem_size; + unsigned long dma_addr; + void *addr_reg; + void *data_reg; + int dma; + int irq; +#if defined(CONSISTENT_ALLOC) || defined(TEST_DMA) + char *dma_buf; + dma_addr_t dma_bus_addr; +#endif + //struct semaphore hw_lock; +#ifdef KERNEL_THREAD + struct completion complete; + struct task_struct *task; + wait_queue_head_t task_wait; + //wait_queue_head_t update; +#endif +#ifdef DEV_REGS + struct ohci_regs ohci_regs; +#endif + struct td *last_td; + struct xfer_buf xfer_buf; + int cbc; + int intrdelay; + int hw_rev; +#ifdef KERNEL_THREAD + union { + int i; + struct { + int irq:1; + int sof:1; + int shutdown:1; + int portreset:1; +#if 0 + int control:1; + int cmdstatus:1; + int intrenable:1; + int intrstatus:1; + int ed_controlhead:1; + int ed_controlcurrent:1; + int ed_bulkhead:1; + int ed_bulkcurrent:1; + int roothub_a:1; + int roothub_b:1; + int roothub_status:1; + int roothub_portstatus:1; +#endif + } b; + } flags; +#endif +}; + +struct hc_sl811_driver { + struct device_driver drv; + unsigned int devid; + int (*probe)(struct hc_sl811_dev *dev); + int (*remove)(struct hc_sl811_dev *dev); + int (*suspend)(struct hc_sl811_dev *dev, u32 state); + int (*resume)(struct hc_sl811_dev *dev); +}; + +#define SL811_DRV(_d) container_of((_d), struct hc_sl811_driver, drv) + +/* + * SL811 HW Interface + */ + +#define hc_sl811_addr_reg (dev->addr_reg) +#define hc_sl811_data_reg (dev->data_reg) + +static inline void HC_SL811_WRITE_ADDR(struct hc_sl811_dev *dev, int reg) +{ + void *hcd = NULL; + + writeb(reg, hc_sl811_addr_reg); + DEBUG_STORE(reg, 0x01); +} + +static inline void HC_SL811_WRITE_DATA(struct hc_sl811_dev *dev, int val) +{ + void *hcd = NULL; + + writeb(val, hc_sl811_data_reg); + DEBUG_STORE(val, 0x03); +} + +static inline u8 HC_SL811_READ_ADDR(struct hc_sl811_dev *dev) +{ + u8 reg; + void *hcd = NULL; + + reg = readb(hc_sl811_addr_reg); + DEBUG_STORE(reg, 0x00); + + return reg; +} + +static inline u8 HC_SL811_READ_DATA(struct hc_sl811_dev *dev) +{ + u8 val; + void *hcd = NULL; + + val = readb(hc_sl811_data_reg); + DEBUG_STORE(val, 0x02); + + return val; +} + +static inline u8 hc_sl811_read_reg(struct hc_sl811_dev *dev, int reg) +{ + u8 val; + + HC_SL811_WRITE_ADDR(dev, reg); + val = HC_SL811_READ_DATA(dev); + DDPRINTK("%s: %02x<%02x\n", __FUNCTION__, val, reg); + + return val; +} + +static inline void hc_sl811_write_reg(struct hc_sl811_dev *dev, int reg, u8 val) +{ + HC_SL811_WRITE_ADDR(dev, reg); + HC_SL811_WRITE_DATA(dev, val); + DDPRINTK("%s: %02x>%02x\n", __FUNCTION__, val, reg); +} + +static inline void hc_sl811_set_mask(struct hc_sl811_dev *dev, int reg, u8 mask) +{ + u8 val = hc_sl811_read_reg(dev, reg); + if ((val | mask) != val) { + DDPRINTK("%s: %02x->%02x\n", __FUNCTION__, val, val | mask); + } + val |= mask; + hc_sl811_write_reg(dev, reg, val); +} + +static inline void hc_sl811_clr_mask(struct hc_sl811_dev *dev, int reg, u8 mask) +{ + u8 val = hc_sl811_read_reg(dev, reg); + if ((val & ~mask) != val) { + DDPRINTK("%s: %02x->%02x\n", __FUNCTION__, val, val & ~mask); + } + val &= ~mask; + hc_sl811_write_reg(dev, reg, val); +} + +static int hc_sl811_handle_insrmv(struct usb_hcd *hcd); +static void hc_sl811_usb_reset(struct usb_hcd *hcd, int assert); +static void hc_sl811_start_sof(struct usb_hcd *hcd, int lowspeed); diff -NaurbB linux-2.6.9-pre1-bk18/drivers/usb/host/sl811.h linux-2.6.9_nbp/drivers/usb/host/sl811.h --- linux-2.6.9-pre1-bk18/drivers/usb/host/sl811.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/drivers/usb/host/sl811.h 2004-12-02 22:18:34.000000000 +0100 @@ -0,0 +1,273 @@ +/* + * SL811HS register declarations and HCD data structures + * + * Copyright (C) 2004 Psion Teklogix + * Copyright (C) 2004 David Brownell + * Copyright (C) 2001 Cypress Semiconductor Inc. + */ + +/* + * SL811HS has transfer registers, and control registers. In host/master + * mode one set of registers is used; in peripheral/slave mode, another. + * - SL11H only has some "A" transfer registers from 0x00-0x04 + * - SL811HS also has "B" registers from 0x08-0x0c + * - SL811S (or HS in slave mode) has four A+B sets, at 00, 10, 20, 30 + */ + +#define SL811_EP_A(base) ((base) + 0) +#define SL811_EP_B(base) ((base) + 8) + +#define SL811_HOST_BUF 0x00 +#define SL811_PERIPH_EP0 0x00 +#define SL811_PERIPH_EP1 0x10 +#define SL811_PERIPH_EP2 0x20 +#define SL811_PERIPH_EP3 0x30 + + +/* TRANSFER REGISTERS: host and peripheral sides are similar + * except for the control models (master vs slave). + */ +#define SL11H_HOSTCTLREG 0 +# define SL11H_HCTLMASK_ARM 0x01 +# define SL11H_HCTLMASK_ENABLE 0x02 +# define SL11H_HCTLMASK_IN 0x00 +# define SL11H_HCTLMASK_OUT 0x04 +# define SL11H_HCTLMASK_ISOCH 0x10 +# define SL11H_HCTLMASK_AFTERSOF 0x20 +# define SL11H_HCTLMASK_TOGGLE 0x40 +# define SL11H_HCTLMASK_PREAMBLE 0x80 +#define SL11H_BUFADDRREG 1 +#define SL11H_BUFLNTHREG 2 +#define SL11H_PKTSTATREG 3 /* read */ +# define SL11H_STATMASK_ACK 0x01 +# define SL11H_STATMASK_ERROR 0x02 +# define SL11H_STATMASK_TMOUT 0x04 +# define SL11H_STATMASK_SEQ 0x08 +# define SL11H_STATMASK_SETUP 0x10 +# define SL11H_STATMASK_OVF 0x20 +# define SL11H_STATMASK_NAK 0x40 +# define SL11H_STATMASK_STALL 0x80 +#define SL11H_PIDEPREG 3 /* write */ +# define SL_SETUP 0xd0 +# define SL_IN 0x90 +# define SL_OUT 0x10 +# define SL_SOF 0x50 +# define SL_PREAMBLE 0xc0 +# define SL_NAK 0xa0 +# define SL_STALL 0xe0 +# define SL_DATA0 0x30 +# define SL_DATA1 0xb0 +#define SL11H_XFERCNTREG 4 /* read */ +#define SL11H_DEVADDRREG 4 /* write */ + + +/* CONTROL REGISTERS: host and peripheral are very different. + */ +#define SL11H_CTLREG1 5 +# define SL11H_CTL1MASK_SOF_ENA 0x01 +# define SL11H_CTL1MASK_FORCE 0x18 +# define SL11H_CTL1MASK_NORMAL 0x00 +# define SL11H_CTL1MASK_SE0 0x08 /* reset */ +# define SL11H_CTL1MASK_J 0x10 +# define SL11H_CTL1MASK_K 0x18 /* resume */ +# define SL11H_CTL1MASK_LSPD 0x20 +# define SL11H_CTL1MASK_SUSPEND 0x40 +#define SL11H_IRQ_ENABLE 6 +# define SL11H_INTMASK_DONE_A 0x01 +# define SL11H_INTMASK_DONE_B 0x02 +# define SL11H_INTMASK_SOFINTR 0x10 +# define SL11H_INTMASK_INSRMV 0x20 /* to/from SE0 */ +# define SL11H_INTMASK_RD 0x40 +# define SL11H_INTMASK_DP 0x80 /* only in INTSTATREG */ +#define SL11S_ADDRESS 7 + +/* 0x08-0x0c are for the B buffer (not in SL11) */ + +#define SL11H_IRQ_STATUS 0x0D /* write to ack */ +#define SL11H_HWREVREG 0x0E /* read */ +# define SL11H_HWRMASK_HWREV 0xF0 +#define SL11H_SOFLOWREG 0x0E /* write */ +#define SL11H_SOFTMRREG 0x0F /* read */ + +/* a write to this register enables SL811HS features. + * HOST flag presumably overrides the chip input signal? + */ +#define SL811HS_CTLREG2 0x0F +# define SL811HS_CTL2MASK_SOF_MASK 0x3F +# define SL811HS_CTL2MASK_DSWAP 0x40 +# define SL811HS_CTL2MASK_HOST 0x80 + +#define SL811HS_CTL2_INIT (SL811HS_CTL2MASK_HOST | 0x2e) + + +/* DATA BUFFERS: registers from 0x10..0xff are for data buffers; + * that's 240 bytes, which we'll split evenly between A and B sides. + * Only ISO can use more than 64 bytes per packet. + * (The SL11S has 0x40..0xff for buffers.) + */ +#define H_MAXPACKET 120 /* bytes in A or B fifos */ + +#define SL11H_DATA_START 0x10 +#define SL811HS_PACKET_BUF(is_a) ((is_a) \ + ? SL11H_DATA_START \ + : (SL11H_DATA_START + H_MAXPACKET)) + +/*-------------------------------------------------------------------------*/ + +#define LOG2_PERIODIC_SIZE 5 /* arbitrary; this matches OHCI */ +#define PERIODIC_SIZE (1 << LOG2_PERIODIC_SIZE) + +struct sl811 { + struct usb_hcd hcd; + spinlock_t lock; + void __iomem *addr_reg; + void __iomem *data_reg; + struct sl811_platform_data *board; + struct proc_dir_entry *pde; + + unsigned long stat_insrmv; + unsigned long stat_wake; + unsigned long stat_sof; + unsigned long stat_a; + unsigned long stat_b; + unsigned long stat_lost; + unsigned long stat_overrun; + + unsigned long stat1, stat2, stat4, stat8, stat16; + + /* sw model */ + struct timer_list timer; + struct sl811h_ep *next_periodic; + struct sl811h_ep *next_async; + + struct sl811h_ep *active_a; + unsigned long jiffies_a; + struct sl811h_ep *active_b; + unsigned long jiffies_b; + + u32 port1; + u8 ctrl1, ctrl2, irq_enable; + u16 frame; + + /* async schedule: control, bulk */ + struct list_head async; + + /* periodic schedule: interrupt, iso */ + u16 load[PERIODIC_SIZE]; + struct sl811h_ep *periodic[PERIODIC_SIZE]; + unsigned periodic_count; +}; + +static void ALIGNSTAT(struct sl811 *sl811, void *ptr) +{ + unsigned p = (unsigned) ptr; + if (!(p & 0xf)) + sl811->stat16++; + else if (!(p & 0x7)) + sl811->stat8++; + else if (!(p & 0x3)) + sl811->stat4++; + else if (!(p & 0x1)) + sl811->stat2++; + else + sl811->stat1++; +} + +static inline struct sl811 *hcd_to_sl811(struct usb_hcd *hcd) +{ + return container_of(hcd, struct sl811, hcd); +} + +struct sl811h_ep { + struct list_head queue; + struct usb_device *udev; + + u8 defctrl; + u8 maxpacket; + u8 epnum; + u8 nextpid; + + u16 error_count; + u16 nak_count; + u16 length; /* of current packet */ + + /* periodic schedule */ + u16 period; + u16 branch; + u16 load; + struct sl811h_ep *next; + + /* async schedule */ + struct list_head schedule; +}; + +struct sl811h_req { + /* FIXME usbcore should maintain endpoints' urb queues + * directly in 'struct usb_host_endpoint' + */ + struct urb *urb; + struct list_head queue; +}; + +/*-------------------------------------------------------------------------*/ + +/* These register utilities should work for the SL811S register API too + * NOTE: caller must hold sl811->lock. + */ + +static inline u8 sl811_read(struct sl811 *sl811, int reg) +{ + writeb(reg, sl811->addr_reg); + return readb(sl811->data_reg); +} + +static inline void sl811_write(struct sl811 *sl811, int reg, u8 val) +{ + writeb(reg, sl811->addr_reg); + writeb(val, sl811->data_reg); +} + +static inline void +sl811_write_buf(struct sl811 *sl811, int addr, const void *buf, size_t count) +{ + const u8 *data = buf; + + writeb(addr, sl811->addr_reg); + while (count--) + writeb(*data++, sl811->data_reg); +} + +static inline void +sl811_read_buf(struct sl811 *sl811, int addr, void *buf, size_t count) +{ + u8 *data = buf; + + writeb(addr, sl811->addr_reg); + while (count--) + *data++ = readb(sl811->data_reg); +} + +/*-------------------------------------------------------------------------*/ + +#ifdef DEBUG +#define DBG(stuff...) printk(KERN_DEBUG "sl811: " stuff) +#else +#define DBG(stuff...) do{}while(0) +#endif + +#ifdef VERBOSE +# define VDBG DBG +#else +# define VDBG(stuff...) do{}while(0) +#endif + +#ifdef PACKET_TRACE +# define PACKET VDBG +#else +# define PACKET(stuff...) do{}while(0) +#endif + +#define ERR(stuff...) printk(KERN_ERR "sl811: " stuff) +#define WARN(stuff...) printk(KERN_WARNING "sl811: " stuff) +#define INFO(stuff...) printk(KERN_INFO "sl811: " stuff) + diff -NaurbB linux-2.6.9-pre1-bk18/drivers/usb/host/sl811-hcd.c linux-2.6.9_nbp/drivers/usb/host/sl811-hcd.c --- linux-2.6.9-pre1-bk18/drivers/usb/host/sl811-hcd.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/drivers/usb/host/sl811-hcd.c 2004-12-02 22:18:34.000000000 +0100 @@ -0,0 +1,1909 @@ +/* + * SL811HS HCD (Host Controller Driver) for USB. + * + * Copyright (C) 2004 Psion Teklogix (for NetBook PRO) + * Copyright (C) 2004 David Brownell + * + * Periodic scheduling is based on Roman's OHCI code + * Copyright (C) 1999 Roman Weissgaerber + * + * The SL811HS controller handles host side USB (like the SL11H, but with + * another register set and SOF generation) as well as peripheral side USB + * (like the SL811S). This driver version doesn't implement the Gadget API + * for the peripheral role; or OTG (that'd need much external circuitry). + * + * For documentation, see the SL811HS spec and the "SL811HS Embedded Host" + * document (providing significant pieces missing from that spec); plus + * the SL811S spec if you want peripheral side info. + */ + +/* + * Status: Basic driver functionality seems to be working; this enumerates + * over a dozen different devices, including through external hubs. Basic + * stress testing passes (several hours with no errors). Seemed to behave + * well with usb-storage. + * + * TODO: + * - more root hub work: + * + enumerating directly-connected lowspeed devices (NAK issues) + * + usb suspend/resume triggered by sl811 (with USB_SUSPEND) + * - various issues noted in the code + * - performance work; use both register banks; ... + * - use urb->iso_frame_desc[] with ISO transfers + */ + +#undef VERBOSE +#undef PACKET_TRACE + +#include + +#ifdef CONFIG_USB_DEBUG +# define DEBUG +#else +# undef DEBUG +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "../core/hcd.h" +#include "sl811.h" + + +MODULE_DESCRIPTION("SL811HS USB Host Controller Driver"); +MODULE_LICENSE("GPL"); + +#define DRIVER_VERSION "02 Dec 2004" + + +#ifndef DEBUG +# define STUB_DEBUG_FILE +#endif + +/* for now, use only one transfer register bank */ +#undef USE_B + +/* this doesn't understand urb->iso_frame_desc[], but if you had a driver + * that just queued one ISO frame per URB then iso transfers "should" work + * using the normal urb status fields. + */ +#define DISABLE_ISO + +// temporary build hacks!! +#include +#ifndef __releases +#define __releases(x) +#define __acquires(x) +#endif + + +static const char hcd_name[] = "sl811-hcd"; + +/*-------------------------------------------------------------------------*/ + +static irqreturn_t sl811h_irq(int irq, void *_sl811, struct pt_regs *regs); + +static void port_power(struct sl811 *sl811, int is_on) +{ + /* hub is inactive unless the port is powered */ + if (is_on) { + if (sl811->port1 & (1 << USB_PORT_FEAT_POWER)) + return; + + sl811->port1 = (1 << USB_PORT_FEAT_POWER); + sl811->irq_enable = SL11H_INTMASK_INSRMV; + sl811->hcd.self.controller->power.power_state = PM_SUSPEND_ON; + } else { + sl811->port1 = 0; + sl811->irq_enable = 0; + sl811->hcd.state = USB_STATE_HALT; + sl811->hcd.self.controller->power.power_state = PM_SUSPEND_DISK; + } + sl811->ctrl1 = 0; + sl811_write(sl811, SL11H_IRQ_ENABLE, 0); + sl811_write(sl811, SL11H_IRQ_STATUS, ~0); + + if (sl811->board && sl811->board->port_power) { + /* switch VBUS, at 500mA unless hub power budget gets set */ + DBG("power %s\n", is_on ? "on" : "off"); + sl811->board->port_power(sl811->hcd.self.controller, is_on); + } + + /* reset as thoroughly as we can */ + if (sl811->board && sl811->board->reset) + sl811->board->reset(sl811->hcd.self.controller); + + sl811_write(sl811, SL11H_IRQ_ENABLE, 0); + sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); + sl811_write(sl811, SL811HS_CTLREG2, SL811HS_CTL2_INIT); + sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable); + + // if !is_on, put into lowpower mode now +} + +/*-------------------------------------------------------------------------*/ + +/* This is a PIO-only HCD. Queueing appends URBs to the endpoint's queue, + * and may start I/O. Endpoint queues are scanned during completion irq + * handlers (one per packet: ACK, NAK, faults, etc) and urb cancelation. + * + * Using an external DMA engine to copy a packet at a time could work, + * though setup/teardown costs may be too big to make it worthwhile. + */ + +/* SETUP starts a new control request. Devices are not allowed to + * STALL or NAK these; they must cancel any pending control requests. + */ +static void setup_packet( + struct sl811 *sl811, + struct sl811h_ep *ep, + struct urb *urb, + u8 bank, + u8 control +) +{ + u8 addr; + u8 len; + + addr = SL811HS_PACKET_BUF(bank == 0); + len = sizeof(struct usb_ctrlrequest); + sl811_write_buf(sl811, addr, urb->setup_packet, len); + + /* autoincrementing */ + sl811_write(sl811, bank + SL11H_BUFADDRREG, addr); + writeb(len, sl811->data_reg); + writeb(SL_SETUP /* | ep->epnum */, sl811->data_reg); + writeb(usb_pipedevice(urb->pipe), sl811->data_reg); + + /* always OUT/data0 */ ; + sl811_write(sl811, bank + SL11H_HOSTCTLREG, + control | SL11H_HCTLMASK_OUT); + ep->length = 0; + PACKET("SETUP qh%p\n", ep); +} + +/* STATUS finishes control requests, often after IN or OUT data packets */ +static void status_packet( + struct sl811 *sl811, + struct sl811h_ep *ep, + struct urb *urb, + u8 bank, + u8 control +) +{ + int do_out; + + do_out = urb->transfer_buffer_length && usb_pipein(urb->pipe); + + /* autoincrementing */ + sl811_write(sl811, bank + SL11H_BUFADDRREG, 0); + writeb(0, sl811->data_reg); + writeb((do_out ? SL_OUT : SL_IN) /* | ep->epnum */, sl811->data_reg); + writeb(usb_pipedevice(urb->pipe), sl811->data_reg); + + /* always data1; sometimes IN */ + control |= SL11H_HCTLMASK_TOGGLE; + if (do_out) + control |= SL11H_HCTLMASK_OUT; + sl811_write(sl811, bank + SL11H_HOSTCTLREG, control); + ep->length = 0; + PACKET("STATUS%s/%s qh%p\n", ep->nak_count ? "/retry" : "", + do_out ? "out" : "in", ep); +} + +/* IN packets can be used with any type of endpoint. here we just + * start the transfer, data from the peripheral may arrive later. + * urb->iso_frame_desc is currently ignored here... + */ +static void in_packet( + struct sl811 *sl811, + struct sl811h_ep *ep, + struct urb *urb, + u8 bank, + u8 control +) +{ + u8 addr; + u8 len; + + /* avoid losing data on overflow */ + len = ep->maxpacket; + addr = SL811HS_PACKET_BUF(bank == 0); + if (!(control & SL11H_HCTLMASK_ISOCH) + && usb_gettoggle(urb->dev, ep->epnum, 0)) + control |= SL11H_HCTLMASK_TOGGLE; + + /* autoincrementing */ + sl811_write(sl811, bank + SL11H_BUFADDRREG, addr); + writeb(len, sl811->data_reg); + writeb(SL_IN | ep->epnum, sl811->data_reg); + writeb(usb_pipedevice(urb->pipe), sl811->data_reg); + + sl811_write(sl811, bank + SL11H_HOSTCTLREG, control); + ep->length = min((int)len, + urb->transfer_buffer_length - urb->actual_length); + PACKET("IN%s/%d qh%p len%d\n", ep->nak_count ? "/retry" : "", + !!usb_gettoggle(urb->dev, ep->epnum, 0), ep, len); +} + +/* OUT packets can be used with any type of endpoint. + * urb->iso_frame_desc is currently ignored here... + */ +static void out_packet( + struct sl811 *sl811, + struct sl811h_ep *ep, + struct urb *urb, + u8 bank, + u8 control +) +{ + void *buf; + u8 addr; + u8 len; + + buf = urb->transfer_buffer + urb->actual_length; + prefetch(buf); + + len = min((int)ep->maxpacket, + urb->transfer_buffer_length - urb->actual_length); + + if (!(control & SL11H_HCTLMASK_ISOCH) + && usb_gettoggle(urb->dev, ep->epnum, 1)) + control |= SL11H_HCTLMASK_TOGGLE; + addr = SL811HS_PACKET_BUF(bank == 0); + + sl811_write_buf(sl811, addr, buf, len); + + /* autoincrementing */ + sl811_write(sl811, bank + SL11H_BUFADDRREG, addr); + writeb(len, sl811->data_reg); + writeb(SL_OUT | ep->epnum, sl811->data_reg); + writeb(usb_pipedevice(urb->pipe), sl811->data_reg); + + sl811_write(sl811, bank + SL11H_HOSTCTLREG, + control | SL11H_HCTLMASK_OUT); + ep->length = len; + PACKET("OUT%s/%d qh%p len%d\n", ep->nak_count ? "/retry" : "", + !!usb_gettoggle(urb->dev, ep->epnum, 1), ep, len); +} + +/*-------------------------------------------------------------------------*/ + +/* caller updates on-chip enables later */ + +static inline void sofirq_on(struct sl811 *sl811) +{ + if (sl811->irq_enable & SL11H_INTMASK_SOFINTR) + return; + VDBG("sof irq on\n"); + sl811->irq_enable |= SL11H_INTMASK_SOFINTR; +} + +static inline void sofirq_off(struct sl811 *sl811) +{ + if (!(sl811->irq_enable & SL11H_INTMASK_SOFINTR)) + return; + VDBG("sof irq off\n"); + sl811->irq_enable &= ~SL11H_INTMASK_SOFINTR; +} + +/*-------------------------------------------------------------------------*/ + +/* pick the next endpoint for a transaction, and issue it. + * frames start with periodic transfers (after whatever is pending + * from the previous frame), and the rest of the time is async + * transfers, scheduled round-robin. + */ +static struct sl811h_ep *start(struct sl811 *sl811, u8 bank) +{ + struct sl811h_ep *ep; + struct sl811h_req *req; + struct urb *urb; + int fclock; + u8 control; + + /* use endpoint at schedule head */ + if (sl811->next_periodic) { + ep = sl811->next_periodic; + sl811->next_periodic = ep->next; + } else { + if (sl811->next_async) + ep = sl811->next_async; + else if (list_empty(&sl811->async)) + return NULL; + else + ep = container_of(sl811->async.next, + struct sl811h_ep, schedule); + +// FIXME make sure A and B always use different endpoints + + if (ep->schedule.next == &sl811->async) + sl811->next_async = NULL; + else + sl811->next_async = container_of(ep->schedule.next, + struct sl811h_ep, schedule); + } + + if (list_empty(&ep->queue)) { + DBG("empty %p queue?\n", ep); + return NULL; + } + + req = container_of(ep->queue.next, struct sl811h_req, queue); + urb = req->urb; + control = ep->defctrl; + + /* if this frame doesn't have enough time left to transfer this + * packet, wait till the next frame. too-simple algorithm... + */ + fclock = sl811_read(sl811, SL11H_SOFTMRREG) << 6; + fclock -= 100; /* setup takes not much time */ + if (urb->dev->speed == USB_SPEED_LOW) { + if (control & SL11H_HCTLMASK_PREAMBLE) { + /* also note erratum 1: some hubs won't work */ + fclock -= 800; + } + fclock -= ep->maxpacket << 8; + + /* erratum 2: AFTERSOF only works for fullspeed */ + if (fclock < 0) { + if (ep->period) + sl811->stat_overrun++; + sofirq_on(sl811); + return NULL; + } + } else { + fclock -= 12000 / 19; /* 19 64byte packets/msec */ + if (fclock < 0) { + if (ep->period) + sl811->stat_overrun++; + control |= SL11H_HCTLMASK_AFTERSOF; + + /* throttle bulk/control irq noise */ + } else if (ep->nak_count) + control |= SL11H_HCTLMASK_AFTERSOF; + } + + + switch (ep->nextpid) { + case USB_PID_IN: + in_packet(sl811, ep, urb, bank, control); + break; + case USB_PID_OUT: + out_packet(sl811, ep, urb, bank, control); + break; + case USB_PID_SETUP: + setup_packet(sl811, ep, urb, bank, control); + break; + case USB_PID_ACK: /* for control status */ + status_packet(sl811, ep, urb, bank, control); + break; + default: + DBG("bad ep%p pid %02x\n", ep, ep->nextpid); + ep = NULL; + } + return ep; +} + +#define MIN_JIFFIES ((msecs_to_jiffies(2) > 1) ? msecs_to_jiffies(2) : 2) + +static inline void start_transfer(struct sl811 *sl811) +{ + if (sl811->port1 & (1 << USB_PORT_FEAT_SUSPEND)) + return; + if (sl811->active_a == NULL) { + sl811->active_a = start(sl811, SL811_EP_A(SL811_HOST_BUF)); + if (sl811->active_a != NULL) + sl811->jiffies_a = jiffies + MIN_JIFFIES; + } +#ifdef USE_B + if (sl811->active_b == NULL) { + sl811->active_b = start(sl811, SL811_EP_B(SL811_HOST_BUF)); + if (sl811->active_b != NULL) + sl811->jiffies_b = jiffies + MIN_JIFFIES; + } +#endif +} + +static void finish_request( + struct sl811 *sl811, + struct sl811h_ep *ep, + struct sl811h_req *req, + struct pt_regs *regs, + int status +) __releases(sl811->lock) __acquires(sl811->lock) +{ + unsigned i; + struct urb *urb = req->urb; + + list_del(&req->queue); + kfree(req); + urb->hcpriv = NULL; + + if (usb_pipecontrol(urb->pipe)) + ep->nextpid = USB_PID_SETUP; + + spin_lock(&urb->lock); + if (urb->status == -EINPROGRESS) + urb->status = status; + spin_unlock(&urb->lock); + + spin_unlock(&sl811->lock); + usb_hcd_giveback_urb(&sl811->hcd, urb, regs); + spin_lock(&sl811->lock); + + /* take idle endpoints out of the schedule right away */ + if (!list_empty(&ep->queue)) + return; + + /* async deschedule? */ + if (!list_empty(&ep->schedule)) { + list_del_init(&ep->schedule); + if (ep == sl811->next_async) + sl811->next_async = NULL; + return; + } + + /* periodic deschedule */ + DBG("deschedule qh%d/%p branch %d\n", ep->period, ep, ep->branch); + for (i = ep->branch; i < PERIODIC_SIZE; i += ep->period) { + struct sl811h_ep *temp; + struct sl811h_ep **prev = &sl811->periodic[i]; + + while (*prev && ((temp = *prev) != ep)) + prev = &temp->next; + if (*prev) + *prev = ep->next; + sl811->load[i] -= ep->load; + } + ep->branch = PERIODIC_SIZE; + sl811->periodic_count--; + hcd_to_bus(&sl811->hcd)->bandwidth_allocated + -= ep->load / ep->period; + if (ep == sl811->next_periodic) + sl811->next_periodic = ep->next; + + /* we might turn SOFs back on again for the async schedule */ + if (sl811->periodic_count == 0) + sofirq_off(sl811); +} + +static void +done(struct sl811 *sl811, struct sl811h_ep *ep, u8 bank, struct pt_regs *regs) +{ + u8 status; + struct sl811h_req *req; + struct urb *urb; + int urbstat = -EINPROGRESS; + + if (unlikely(!ep)) + return; + + status = sl811_read(sl811, bank + SL11H_PKTSTATREG); + + req = container_of(ep->queue.next, struct sl811h_req, queue); + urb = req->urb; + + /* we can safely ignore NAKs */ + if (status & SL11H_STATMASK_NAK) { + // PACKET("...NAK_%02x qh%p\n", bank, ep); + if (!ep->period) + ep->nak_count++; + ep->error_count = 0; + + /* ACK advances transfer, toggle, and maybe queue */ + } else if (status & SL11H_STATMASK_ACK) { + struct usb_device *udev = urb->dev; + int len; + unsigned char *buf; + + /* urb->iso_frame_desc is currently ignored here... */ + + ep->nak_count = ep->error_count = 0; + switch (ep->nextpid) { + case USB_PID_OUT: + // PACKET("...ACK/out_%02x qh%p\n", bank, ep); + urb->actual_length += ep->length; + usb_dotoggle(udev, ep->epnum, 1); + if (urb->actual_length + == urb->transfer_buffer_length) { + if (usb_pipecontrol(urb->pipe)) + ep->nextpid = USB_PID_ACK; + + /* some bulk protocols terminate OUT transfers + * by a short packet, using ZLPs not padding. + */ + else if (ep->length < ep->maxpacket + || !(urb->transfer_flags + & URB_ZERO_PACKET)) + urbstat = 0; + } + break; + case USB_PID_IN: + // PACKET("...ACK/in_%02x qh%p\n", bank, ep); + buf = urb->transfer_buffer + urb->actual_length; + prefetchw(buf); + len = ep->maxpacket - sl811_read(sl811, + bank + SL11H_XFERCNTREG); + if (len > ep->length) { + len = ep->length; + urb->status = -EOVERFLOW; + } + urb->actual_length += len; + sl811_read_buf(sl811, SL811HS_PACKET_BUF(bank == 0), + buf, len); + usb_dotoggle(udev, ep->epnum, 0); + if (urb->actual_length == urb->transfer_buffer_length) + urbstat = 0; + else if (len < ep->maxpacket) { + if (urb->transfer_flags & URB_SHORT_NOT_OK) + urbstat = -EREMOTEIO; + else + urbstat = 0; + } + if (usb_pipecontrol(urb->pipe) + && (urbstat == -EREMOTEIO + || urbstat == 0)) { + + /* NOTE if the status stage STALLs (why?), + * this reports the wrong urb status. + */ + spin_lock(&urb->lock); + if (urb->status == -EINPROGRESS) + urb->status = urbstat; + spin_unlock(&urb->lock); + + req = 0; + ep->nextpid = USB_PID_ACK; + } + break; + case USB_PID_SETUP: + // PACKET("...ACK/setup_%02x qh%p\n", bank, ep); + if (urb->transfer_buffer_length == urb->actual_length) + ep->nextpid = USB_PID_ACK; + else if (usb_pipeout(urb->pipe)) { + usb_settoggle(udev, 0, 1, 1); + ep->nextpid = USB_PID_OUT; + } else { + usb_settoggle(udev, 0, 0, 1); + ep->nextpid = USB_PID_IN; + } + break; + case USB_PID_ACK: + // PACKET("...ACK/status_%02x qh%p\n", bank, ep); + urbstat = 0; + break; + } + + /* STALL stops all transfers */ + } else if (status & SL11H_STATMASK_STALL) { + PACKET("...STALL_%02x qh%p\n", bank, ep); + ep->nak_count = ep->error_count = 0; + urbstat = -EPIPE; + + /* error? retry, until "3 strikes" */ + } else if (++ep->error_count >= 3) { + if (status & SL11H_STATMASK_TMOUT) + urbstat = -ETIMEDOUT; + else if (status & SL11H_STATMASK_OVF) + urbstat = -EOVERFLOW; + else + urbstat = -EPROTO; + ep->error_count = 0; + PACKET("...3STRIKES_%02x %02x qh%p stat %d\n", + bank, status, ep, urbstat); + } + + if ((urbstat != -EINPROGRESS || urb->status != -EINPROGRESS) + && req) + finish_request(sl811, ep, req, regs, urbstat); +} + +static u8 checkdone(struct sl811 *sl811) +{ + u8 ctl; + u8 irqstat = 0; + + if (sl811->active_a && time_before_eq(sl811->jiffies_a, jiffies)) { + ctl = sl811_read(sl811, SL811_EP_A(SL11H_HOSTCTLREG)); + if (ctl & SL11H_HCTLMASK_ARM) + sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG), 0); + DBG("%s DONE_A: ctrl %02x sts %02x\n", + (ctl & SL11H_HCTLMASK_ARM) ? "timeout" : "lost", + ctl, + sl811_read(sl811, SL811_EP_A(SL11H_PKTSTATREG))); + irqstat |= SL11H_INTMASK_DONE_A; + } +#ifdef USE_B + if (sl811->active_b && time_before_eq(sl811->jiffies_b, jiffies)) { + ctl = sl811_read(sl811, SL811_EP_B(SL11H_HOSTCTLREG)); + if (ctl & SL11H_HCTLMASK_ARM) + sl811_write(sl811, SL811_EP_B(SL11H_HOSTCTLREG), 0); + DBG("%s DONE_B: ctrl %02x sts %02x\n", ctl, + (ctl & SL11H_HCTLMASK_ARM) ? "timeout" : "lost", + ctl, + sl811_read(sl811, SL811_EP_B(SL11H_PKTSTATREG))); + irqstat |= SL11H_INTMASK_DONE_A; + } +#endif + return irqstat; +} + +static irqreturn_t sl811h_irq(int irq, void *_sl811, struct pt_regs *regs) +{ + struct sl811 *sl811 = _sl811; + u8 irqstat; + irqreturn_t ret = IRQ_NONE; + unsigned retries = 5; + + spin_lock(&sl811->lock); + +retry: + irqstat = sl811_read(sl811, SL11H_IRQ_STATUS) & ~SL11H_INTMASK_DP; + if (irqstat) { +// sl811_write(sl811, SL11H_IRQ_STATUS, irqstat); + sl811_write(sl811, SL11H_IRQ_STATUS, ~0); + irqstat &= sl811->irq_enable; + } + +#if 1 + /* FIXME this shouldn't be necessary ... */ + if (irqstat == 0 && ret == IRQ_NONE) { + irqstat = checkdone(sl811); + if (irqstat && irq != ~0) + sl811->stat_lost++; + } +#endif + + /* USB packets, not necessarily handled in the order they're + * issued ... that's fine if they're different endpoints. + */ + if (irqstat & SL11H_INTMASK_DONE_A) { + done(sl811, sl811->active_a, SL811_EP_A(SL811_HOST_BUF), regs); + sl811->active_a = NULL; + sl811->stat_a++; + } +#ifdef USE_B + if (irqstat & SL11H_INTMASK_DONE_B) { + done(sl811, sl811->active_b, SL811_EP_B(SL811_HOST_BUF), regs); + sl811->active_b = NULL; + sl811->stat_b++; + } +#endif + if (irqstat & SL11H_INTMASK_SOFINTR) { + unsigned index; + + index = sl811->frame++ % (PERIODIC_SIZE - 1); + sl811->stat_sof++; + + /* be graceful about almost-inevitable periodic schedule + * overruns: continue the previous frame's transfers iff + * this one has nothing scheduled. + */ + if (sl811->next_periodic) { + // ERR("overrun to slot %d\n", index); + sl811->stat_overrun++; + } + if (sl811->periodic[index]) + sl811->next_periodic = sl811->periodic[index]; + } + + /* khubd manages debouncing and wakeup */ + if (irqstat & SL11H_INTMASK_INSRMV) { + sl811->stat_insrmv++; + + /* most stats are reset for each VBUS session */ + sl811->stat_wake = 0; + sl811->stat_sof = 0; + sl811->stat_a = 0; + sl811->stat_b = 0; + sl811->stat_lost = 0; + + sl811->ctrl1 = 0; + sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); + + sl811->irq_enable = SL11H_INTMASK_INSRMV; + sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable); + + /* usbcore nukes other pending transactions on disconnect */ + if (sl811->active_a) { + sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG), 0); + finish_request(sl811, sl811->active_a, + container_of(sl811->active_a->queue.next, + struct sl811h_req, queue), + NULL, -ESHUTDOWN); + sl811->active_a = NULL; + } +#ifdef USE_B + if (sl811->active_b) { + sl811_write(sl811, SL811_EP_B(SL11H_HOSTCTLREG), 0); + finish_request(sl811, sl811->active_b, + container_of(sl811->active_b->queue.next, + struct sl811h_req, queue), + NULL, -ESHUTDOWN); + sl811->active_b = NULL; + } +#endif + + /* port status seems wierd until after acks, so + * force the reset and make khubd clean up later. + */ + sl811->port1 |= (1 << USB_PORT_FEAT_C_CONNECTION) + | (1 << USB_PORT_FEAT_CONNECTION); + + } else if (irqstat & SL11H_INTMASK_RD) { + if (sl811->port1 & (1 << USB_PORT_FEAT_SUSPEND)) { + DBG("wakeup\n"); + sl811->port1 |= 1 << USB_PORT_FEAT_C_SUSPEND; + sl811->stat_wake++; + } else + irqstat &= ~SL11H_INTMASK_RD; + } + + if (irqstat) { + if (sl811->port1 & (1 << USB_PORT_FEAT_ENABLE)) + start_transfer(sl811); + ret = IRQ_HANDLED; + sl811->hcd.saw_irq = 1; + if (retries--) + goto retry; + } + + if (sl811->periodic_count == 0 && list_empty(&sl811->async)) + sofirq_off(sl811); + sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable); + + spin_unlock(&sl811->lock); + + return ret; +} + +/*-------------------------------------------------------------------------*/ + +/* usb 1.1 says max 90% of a frame is available for periodic transfers. + * this driver doesn't promise that much since it's got to handle an + * IRQ per packet; irq handling latencies also use up that time. + */ +#define MAX_PERIODIC_LOAD 500 /* out of 1000 usec */ + +static int balance(struct sl811 *sl811, u16 period, u16 load) +{ + int i, branch = -ENOSPC; + + /* search for the least loaded schedule branch of that period + * which has enough bandwidth left unreserved. + */ + for (i = 0; i < period ; i++) { + if (branch < 0 || sl811->load[branch] > sl811->load[i]) { + int j; + + for (j = i; j < PERIODIC_SIZE; j += period) { + if ((sl811->load[j] + load) + > MAX_PERIODIC_LOAD) + break; + } + if (j < PERIODIC_SIZE) + continue; + branch = i; + } + } + return branch; +} + +/*-------------------------------------------------------------------------*/ + +static int sl811h_urb_enqueue( + struct usb_hcd *hcd, + struct urb *urb, + int mem_flags +) { + struct sl811 *sl811 = hcd_to_sl811(hcd); + struct usb_device *udev = urb->dev; + struct hcd_dev *hdev = (struct hcd_dev *) udev->hcpriv; + unsigned int pipe = urb->pipe; + int is_out = !usb_pipein(pipe); + int type = usb_pipetype(pipe); + int epnum = usb_pipeendpoint(pipe); + struct sl811h_ep *ep = NULL; + struct sl811h_req *req; + unsigned long flags; + int i; + int retval = 0; + +#ifdef DISABLE_ISO + if (type == PIPE_ISOCHRONOUS) + return -ENOSPC; +#endif + + /* avoid all allocations within spinlocks: request or endpoint */ + urb->hcpriv = req = kmalloc(sizeof *req, mem_flags); + if (!req) + return -ENOMEM; + req->urb = urb; + + i = epnum << 1; + if (i && is_out) + i |= 1; + if (!hdev->ep[i]) + ep = kcalloc(1, sizeof *ep, mem_flags); + + spin_lock_irqsave(&sl811->lock, flags); + + /* don't submit to a dead or disabled port */ + if (!(sl811->port1 & (1 << USB_PORT_FEAT_ENABLE)) + || !HCD_IS_RUNNING(sl811->hcd.state)) { + retval = -ENODEV; + goto fail; + } + + if (hdev->ep[i]) { + kfree(ep); + ep = hdev->ep[i]; + } else if (!ep) { + retval = -ENOMEM; + goto fail; + + } else { + INIT_LIST_HEAD(&ep->queue); + INIT_LIST_HEAD(&ep->schedule); + ep->udev = usb_get_dev(udev); + ep->epnum = epnum; + ep->maxpacket = usb_maxpacket(udev, urb->pipe, is_out); + ep->defctrl = SL11H_HCTLMASK_ARM | SL11H_HCTLMASK_ENABLE; + usb_settoggle(udev, epnum, is_out, 0); + + if (type == PIPE_CONTROL) + ep->nextpid = USB_PID_SETUP; + else if (is_out) + ep->nextpid = USB_PID_OUT; + else + ep->nextpid = USB_PID_IN; + + if (ep->maxpacket > H_MAXPACKET) { + /* iso packets up to 240 bytes could work... */ + DBG("dev %d ep%d maxpacket %d\n", + udev->devnum, epnum, ep->maxpacket); + retval = -EINVAL; + goto fail; + } + + if (udev->speed == USB_SPEED_LOW) { + /* send preamble for external hub? */ + if (!(sl811->ctrl1 & SL11H_CTL1MASK_LSPD)) + ep->defctrl |= SL11H_HCTLMASK_PREAMBLE; + } + switch (type) { + case PIPE_ISOCHRONOUS: + case PIPE_INTERRUPT: + if (urb->interval > PERIODIC_SIZE) + urb->interval = PERIODIC_SIZE; + ep->period = urb->interval; + ep->branch = PERIODIC_SIZE; + if (type == PIPE_ISOCHRONOUS) + ep->defctrl |= SL11H_HCTLMASK_ISOCH; + ep->load = usb_calc_bus_time(udev->speed, !is_out, + (type == PIPE_ISOCHRONOUS), + usb_maxpacket(udev, pipe, is_out)) + / 1000; + break; + } + + hdev->ep[i] = ep; + } + + /* maybe put endpoint into schedule */ + switch (type) { + case PIPE_CONTROL: + case PIPE_BULK: + if (list_empty(&ep->schedule)) + list_add_tail(&ep->schedule, &sl811->async); + break; + case PIPE_ISOCHRONOUS: + case PIPE_INTERRUPT: + urb->interval = ep->period; + if (ep->branch < PERIODIC_SIZE) + break; + + retval = balance(sl811, ep->period, ep->load); + if (retval < 0) + goto fail; + ep->branch = retval; + retval = 0; + urb->start_frame = (sl811->frame & (PERIODIC_SIZE - 1)) + + ep->branch; + + /* sort each schedule branch by period (slow before fast) + * to share the faster parts of the tree without needing + * dummy/placeholder nodes + */ + DBG("schedule qh%d/%p branch %d\n", ep->period, ep, ep->branch); + for (i = ep->branch; i < PERIODIC_SIZE; i += ep->period) { + struct sl811h_ep **prev = &sl811->periodic[i]; + struct sl811h_ep *here = *prev; + + while (here && ep != here) { + if (ep->period > here->period) + break; + prev = &here->next; + here = *prev; + } + if (ep != here) { + ep->next = here; + *prev = ep; + } + sl811->load[i] += ep->load; + } + sl811->periodic_count++; + hcd_to_bus(&sl811->hcd)->bandwidth_allocated + += ep->load / ep->period; + sofirq_on(sl811); + } + + /* in case of unlink-during-submit */ + spin_lock(&urb->lock); + if (urb->status != -EINPROGRESS) { + spin_unlock(&urb->lock); + finish_request(sl811, ep, req, NULL, 0); + req = NULL; + retval = 0; + goto fail; + } + list_add_tail(&req->queue, &ep->queue); + ALIGNSTAT(sl811, urb->transfer_buffer); + spin_unlock(&urb->lock); + + start_transfer(sl811); + sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable); +fail: + spin_unlock_irqrestore(&sl811->lock, flags); + if (retval) + kfree(req); + return retval; +} + +static int sl811h_urb_dequeue(struct usb_hcd *hcd, struct urb *urb) +{ + struct sl811 *sl811 = hcd_to_sl811(hcd); + struct usb_device *udev = urb->dev; + struct hcd_dev *hdev = (struct hcd_dev *) udev->hcpriv; + unsigned int pipe = urb->pipe; + int is_out = !usb_pipein(pipe); + unsigned long flags; + unsigned i; + struct sl811h_ep *ep; + struct sl811h_req *req = urb->hcpriv; + int retval = 0; + + i = usb_pipeendpoint(pipe) << 1; + if (i && is_out) + i |= 1; + + spin_lock_irqsave(&sl811->lock, flags); + ep = hdev->ep[i]; + if (ep) { + /* finish right away if this urb can't be active ... + * note that some drivers wrongly expect delays + */ + if (ep->queue.next != &req->queue) { + /* not front of queue? never active */ + + /* for active transfers, we expect an IRQ */ + } else if (sl811->active_a == ep) { + if (time_before_eq(sl811->jiffies_a, jiffies)) { + /* happens a lot with lowspeed?? */ + DBG("giveup on DONE_A: ctrl %02x sts %02x\n", + sl811_read(sl811, + SL811_EP_A(SL11H_HOSTCTLREG)), + sl811_read(sl811, + SL811_EP_A(SL11H_PKTSTATREG))); + sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG), + 0); + sl811->active_a = NULL; + } else + req = NULL; +#ifdef USE_B + } else if (sl811->active_b == ep) { + if (time_before_eq(sl811->jiffies_a, jiffies)) { + /* happens a lot with lowspeed?? */ + DBG("giveup on DONE_B: ctrl %02x sts %02x\n", + sl811_read(sl811, + SL811_EP_B(SL11H_HOSTCTLREG)), + sl811_read(sl811, + SL811_EP_B(SL11H_PKTSTATREG))); + sl811_write(sl811, SL811_EP_B(SL11H_HOSTCTLREG), + 0); + sl811->active_b = NULL; + } else + req = NULL; +#endif + } else { + /* front of queue for inactive endpoint */ + } + + if (req) + finish_request(sl811, ep, req, NULL, 0); + else + VDBG("dequeue, urb %p active %s; wait4irq\n", urb, + (sl811->active_a == ep) ? "A" : "B"); + } else + retval = -EINVAL; + spin_unlock_irqrestore(&sl811->lock, flags); + return retval; +} + +static void +sl811h_endpoint_disable(struct usb_hcd *hcd, struct hcd_dev *hdev, int epnum) +{ + struct sl811 *sl811 = hcd_to_sl811(hcd); + struct sl811h_ep *ep; + unsigned long flags; + int i; + + i = (epnum & 0xf) << 1; + if (epnum && !(epnum & USB_DIR_IN)) + i |= 1; + + spin_lock_irqsave(&sl811->lock, flags); + ep = hdev->ep[i]; + hdev->ep[i] = NULL; + spin_unlock_irqrestore(&sl811->lock, flags); + + if (ep) { + /* assume we'd just wait for the irq */ + if (!list_empty(&ep->queue)) + msleep(3); + if (!list_empty(&ep->queue)) + WARN("ep %p not empty?\n", ep); + + usb_put_dev(ep->udev); + kfree(ep); + } + return; +} + +static int +sl811h_get_frame(struct usb_hcd *hcd) +{ + struct sl811 *sl811 = hcd_to_sl811(hcd); + + /* wrong except while periodic transfers are scheduled; + * never matches the on-the-wire frame; + * subject to overruns. + */ + return sl811->frame; +} + + +/*-------------------------------------------------------------------------*/ + +/* the virtual root hub timer IRQ checks for hub status */ +static int +sl811h_hub_status_data(struct usb_hcd *hcd, char *buf) +{ + struct sl811 *sl811 = hcd_to_sl811(hcd); +#if 1 + unsigned long flags; + + /* non-SMP HACK: use root hub timer as i/o watchdog */ + local_irq_save(flags); + if (!timer_pending(&sl811->timer)) { + if (sl811h_irq(~0, sl811, NULL) != IRQ_NONE) + sl811->stat_lost++; + } + local_irq_restore(flags); +#endif + + if (!(sl811->port1 & (0xffff << 16))) + return 0; + + /* tell khubd port 1 changed */ + *buf = (1 << 1); + return 1; +} + +static void +sl811h_hub_descriptor ( + struct sl811 *sl811, + struct usb_hub_descriptor *desc +) { + u16 temp = 0; + + desc->bDescriptorType = 0x29; + desc->bHubContrCurrent = 0; + + desc->bNbrPorts = 1; + desc->bDescLength = 9; + + /* per-port power switching (gang of one!), or none */ + desc->bPwrOn2PwrGood = 0; + if (sl811->board && sl811->board->port_power) { + desc->bPwrOn2PwrGood = sl811->board->potpg; + if (!desc->bPwrOn2PwrGood) + desc->bPwrOn2PwrGood = 10; + temp = 0x0001; + } else + temp = 0x0002; + + /* no overcurrent errors detection/handling */ + temp |= 0x0010; + + desc->wHubCharacteristics = cpu_to_le16(temp); + + /* two bitmaps: ports removable, and legacy PortPwrCtrlMask */ + desc->bitmap[0] = 1 << 1; + desc->bitmap[1] = ~0; +} + +static void +sl811h_timer(unsigned long _sl811) +{ + struct sl811 *sl811 = (void *) _sl811; + unsigned long flags; + u8 irqstat; + u8 signaling = sl811->ctrl1 & SL11H_CTL1MASK_FORCE; + const u32 mask = (1 << USB_PORT_FEAT_CONNECTION) + | (1 << USB_PORT_FEAT_ENABLE) + | (1 << USB_PORT_FEAT_LOWSPEED); + + spin_lock_irqsave(&sl811->lock, flags); + + /* stop special signaling */ + sl811->ctrl1 &= ~SL11H_CTL1MASK_FORCE; + sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); + udelay(3); + + irqstat = sl811_read(sl811, SL11H_IRQ_STATUS); + + switch (signaling) { + case SL11H_CTL1MASK_SE0: + DBG("end reset\n"); + sl811->port1 = (1 << USB_PORT_FEAT_C_RESET) + | (1 << USB_PORT_FEAT_POWER); + sl811->ctrl1 = 0; + /* don't wrongly ack RD */ + if (irqstat & SL11H_INTMASK_INSRMV) + irqstat &= ~SL11H_INTMASK_RD; + break; + case SL11H_CTL1MASK_K: + DBG("end resume\n"); + sl811->port1 &= ~(1 << USB_PORT_FEAT_SUSPEND); + break; + default: + DBG("odd timer signaling: %02x\n", signaling); + break; + } + sl811_write(sl811, SL11H_IRQ_STATUS, irqstat); + + if (irqstat & SL11H_INTMASK_RD) { + /* usbcore nukes all pending transactions on disconnect */ + if (sl811->port1 & (1 << USB_PORT_FEAT_CONNECTION)) + sl811->port1 |= (1 << USB_PORT_FEAT_C_CONNECTION) + | (1 << USB_PORT_FEAT_C_ENABLE); + sl811->port1 &= ~mask; + sl811->irq_enable = SL11H_INTMASK_INSRMV; + } else { + sl811->port1 |= mask; + if (irqstat & SL11H_INTMASK_DP) + sl811->port1 &= ~(1 << USB_PORT_FEAT_LOWSPEED); + sl811->irq_enable = SL11H_INTMASK_INSRMV | SL11H_INTMASK_RD; + } + + if (sl811->port1 & (1 << USB_PORT_FEAT_CONNECTION)) { + u8 ctrl2 = SL811HS_CTL2_INIT; + + sl811->irq_enable |= SL11H_INTMASK_DONE_A; +#ifdef USE_B + sl811->irq_enable |= SL11H_INTMASK_DONE_B; +#endif + if (sl811->port1 & (1 << USB_PORT_FEAT_LOWSPEED)) { + sl811->ctrl1 |= SL11H_CTL1MASK_LSPD; + ctrl2 |= SL811HS_CTL2MASK_DSWAP; + } + + /* start SOFs flowing, kickstarting with A registers */ + sl811->ctrl1 |= SL11H_CTL1MASK_SOF_ENA; + sl811_write(sl811, SL11H_SOFLOWREG, 0xe0); + sl811_write(sl811, SL811HS_CTLREG2, ctrl2); + + /* autoincrementing */ + sl811_write(sl811, SL811_EP_A(SL11H_BUFLNTHREG), 0); + writeb(SL_SOF, sl811->data_reg); + writeb(0, sl811->data_reg); + sl811_write(sl811, SL811_EP_A(SL11H_HOSTCTLREG), + SL11H_HCTLMASK_ARM); + + /* khubd provides debounce delay */ + } else { + sl811->ctrl1 = 0; + } + sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); + + /* reenable irqs */ + sl811_write(sl811, SL11H_IRQ_ENABLE, sl811->irq_enable); + spin_unlock_irqrestore(&sl811->lock, flags); +} + +static int +sl811h_hub_control( + struct usb_hcd *hcd, + u16 typeReq, + u16 wValue, + u16 wIndex, + char *buf, + u16 wLength +) { + struct sl811 *sl811 = hcd_to_sl811(hcd); + int retval = 0; + unsigned long flags; + + spin_lock_irqsave(&sl811->lock, flags); + + switch (typeReq) { + case ClearHubFeature: + case SetHubFeature: + switch (wValue) { + case C_HUB_OVER_CURRENT: + case C_HUB_LOCAL_POWER: + break; + default: + goto error; + } + break; + case ClearPortFeature: + if (wIndex != 1 || wLength != 0) + goto error; + + switch (wValue) { + case USB_PORT_FEAT_ENABLE: + sl811->port1 &= (1 << USB_PORT_FEAT_POWER); + sl811->ctrl1 = 0; + sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); + sl811->irq_enable = SL11H_INTMASK_INSRMV; + sl811_write(sl811, SL11H_IRQ_ENABLE, + sl811->irq_enable); + break; + case USB_PORT_FEAT_SUSPEND: + if (!(sl811->port1 & (1 << USB_PORT_FEAT_SUSPEND))) + break; + + /* 20 msec of resume/K signaling, other irqs blocked */ + DBG("start resume...\n"); + sl811->irq_enable = 0; + sl811_write(sl811, SL11H_IRQ_ENABLE, + sl811->irq_enable); + sl811->ctrl1 |= SL11H_CTL1MASK_K; + sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); + + mod_timer(&sl811->timer, jiffies + + msecs_to_jiffies(20)); + break; + case USB_PORT_FEAT_POWER: + port_power(sl811, 0); + break; + case USB_PORT_FEAT_C_ENABLE: + case USB_PORT_FEAT_C_SUSPEND: + case USB_PORT_FEAT_C_CONNECTION: + case USB_PORT_FEAT_C_OVER_CURRENT: + case USB_PORT_FEAT_C_RESET: + break; + default: + goto error; + } + sl811->port1 &= ~(1 << wValue); + break; + case GetHubDescriptor: + sl811h_hub_descriptor(sl811, (struct usb_hub_descriptor *) buf); + break; + case GetHubStatus: + *(__le32 *) buf = cpu_to_le32(0); + break; + case GetPortStatus: + if (wIndex != 1) + goto error; + *(__le32 *) buf = cpu_to_le32(sl811->port1); + +#ifndef VERBOSE + if (*(u16*)(buf+2)) /* only if wPortChange is interesting */ +#endif + DBG("GetPortStatus %08x\n", sl811->port1); + break; + case SetPortFeature: + if (wIndex != 1 || wLength != 0) + goto error; + switch (wValue) { + case USB_PORT_FEAT_SUSPEND: + if (sl811->port1 & (1 << USB_PORT_FEAT_RESET)) + goto error; + if (!(sl811->port1 & (1 << USB_PORT_FEAT_ENABLE))) + goto error; + + DBG("suspend...\n"); + sl811->ctrl1 &= ~SL11H_CTL1MASK_SOF_ENA; + sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); + break; + case USB_PORT_FEAT_POWER: + port_power(sl811, 1); + break; + case USB_PORT_FEAT_RESET: + if (sl811->port1 & (1 << USB_PORT_FEAT_SUSPEND)) + goto error; + if (!(sl811->port1 & (1 << USB_PORT_FEAT_POWER))) + break; + + /* 50 msec of reset/SE0 signaling, irqs blocked */ + sl811->irq_enable = 0; + sl811_write(sl811, SL11H_IRQ_ENABLE, + sl811->irq_enable); + sl811->ctrl1 = SL11H_CTL1MASK_SE0; + sl811_write(sl811, SL11H_CTLREG1, sl811->ctrl1); + sl811->port1 |= (1 << USB_PORT_FEAT_RESET); + mod_timer(&sl811->timer, jiffies + + msecs_to_jiffies(50)); + break; + default: + goto error; + } + sl811->port1 |= 1 << wValue; + break; + + default: +error: + /* "protocol stall" on error */ + retval = -EPIPE; + } + + spin_unlock_irqrestore(&sl811->lock, flags); + return retval; +} + +#ifdef CONFIG_PM + +static int +sl811h_hub_suspend(struct usb_hcd *hcd) +{ + // SOFs off + DBG("%s\n", __FUNCTION__); + return 0; +} + +static int +sl811h_hub_resume(struct usb_hcd *hcd) +{ + // SOFs on + DBG("%s\n", __FUNCTION__); + return 0; +} + +#else + +#define sl811h_hub_suspend NULL +#define sl811h_hub_resume NULL + +#endif + + +/*-------------------------------------------------------------------------*/ + +#ifdef STUB_DEBUG_FILE + +static inline void create_debug_file(struct sl811 *sl811) { } +static inline void remove_debug_file(struct sl811 *sl811) { } + +#else + +#include +#include + +static void dump_irq(struct seq_file *s, char *label, u8 mask) +{ + seq_printf(s, "%s %02x%s%s%s%s%s%s\n", label, mask, + (mask & SL11H_INTMASK_DONE_A) ? " done_a" : "", + (mask & SL11H_INTMASK_DONE_B) ? " done_b" : "", + (mask & SL11H_INTMASK_SOFINTR) ? " sof" : "", + (mask & SL11H_INTMASK_INSRMV) ? " ins/rmv" : "", + (mask & SL11H_INTMASK_RD) ? " rd" : "", + (mask & SL11H_INTMASK_DP) ? " dp" : ""); +} + +static int proc_sl811h_show(struct seq_file *s, void *unused) +{ + struct sl811 *sl811 = s->private; + struct sl811h_ep *ep; + unsigned i; + + seq_printf(s, "%s\n%s version %s\nportstatus[1] = %08x\n", + sl811->hcd.product_desc, + hcd_name, DRIVER_VERSION, + sl811->port1); + + /* collect statistics to help estimate potential win for + * DMA engines that care about alignment (PXA) + */ + seq_printf(s, "alignment: 16b/%ld 8b/%ld 4b/%ld 2b/%ld 1b/%ld\n", + sl811->stat16, sl811->stat8, sl811->stat4, + sl811->stat2, sl811->stat1); + + seq_printf(s, "insert/remove: %ld\n", sl811->stat_insrmv); + seq_printf(s, "current session: done_a %ld done_b %ld " + "wake %ld sof %ld overrun %ld lost %ld\n\n", + sl811->stat_a, sl811->stat_b, + sl811->stat_wake, sl811->stat_sof, + sl811->stat_overrun, sl811->stat_lost); + + spin_lock_irq(&sl811->lock); + + if (sl811->ctrl1 & SL11H_CTL1MASK_SUSPEND) + seq_printf(s, "(suspended)\n\n"); + else { + u8 t = sl811_read(sl811, SL11H_CTLREG1); + + seq_printf(s, "ctrl1 %02x%s%s%s%s\n", t, + (t & SL11H_CTL1MASK_SOF_ENA) ? " sofgen" : "", + ({char *s; switch (t & SL11H_CTL1MASK_FORCE) { + case SL11H_CTL1MASK_NORMAL: s = ""; break; + case SL11H_CTL1MASK_SE0: s = " se0/reset"; break; + case SL11H_CTL1MASK_K: s = " k/resume"; break; + default: s = "j"; break; + }; s; }), + (t & SL11H_CTL1MASK_LSPD) ? " lowspeed" : "", + (t & SL11H_CTL1MASK_SUSPEND) ? " suspend" : ""); + + dump_irq(s, "irq_enable", + sl811_read(sl811, SL11H_IRQ_ENABLE)); + dump_irq(s, "irq_status", + sl811_read(sl811, SL11H_IRQ_STATUS)); + seq_printf(s, "frame clocks remaining: %d\n", + sl811_read(sl811, SL11H_SOFTMRREG) << 6); + } + + seq_printf(s, "A: qh%p ctl %02x sts %02x\n", sl811->active_a, + sl811_read(sl811, SL811_EP_A(SL11H_HOSTCTLREG)), + sl811_read(sl811, SL811_EP_A(SL11H_PKTSTATREG))); + seq_printf(s, "B: qh%p ctl %02x sts %02x\n", sl811->active_b, + sl811_read(sl811, SL811_EP_B(SL11H_HOSTCTLREG)), + sl811_read(sl811, SL811_EP_B(SL11H_PKTSTATREG))); + seq_printf(s, "\n"); + list_for_each_entry (ep, &sl811->async, schedule) { + struct sl811h_req *req; + + seq_printf(s, "%s%sqh%p, ep%d%s, maxpacket %d" + " nak %d err %d\n", + (ep == sl811->active_a) ? "(A) " : "", + (ep == sl811->active_b) ? "(B) " : "", + ep, ep->epnum, + ({ char *s; switch (ep->nextpid) { + case USB_PID_IN: s = "in"; break; + case USB_PID_OUT: s = "out"; break; + case USB_PID_SETUP: s = "setup"; break; + case USB_PID_ACK: s = "status"; break; + default: s = "?"; break; + }; s;}), + ep->maxpacket, + ep->nak_count, ep->error_count); + list_for_each_entry (req, &ep->queue, queue) { + seq_printf(s, " urb%p, %d/%d\n", req->urb, + req->urb->actual_length, + req->urb->transfer_buffer_length); + } + } + if (!list_empty(&sl811->async)) + seq_printf(s, "\n"); + + seq_printf(s, "periodic size= %d\n", PERIODIC_SIZE); + + for (i = 0; i < PERIODIC_SIZE; i++) { + ep = sl811->periodic[i]; + if (!ep) + continue; + seq_printf(s, "%2d [%3d]:\n", i, sl811->load[i]); + + /* DUMB: prints shared entries multiple times */ + do { + seq_printf(s, + " %s%sqh%d/%p (%sdev%d ep%d%s max %d) " + "err %d\n", + (ep == sl811->active_a) ? "(A) " : "", + (ep == sl811->active_b) ? "(B) " : "", + ep->period, ep, + (ep->udev->speed == USB_SPEED_FULL) + ? "" : "ls ", + ep->udev->devnum, ep->epnum, + (ep->epnum == 0) ? "" + : ((ep->nextpid == USB_PID_IN) + ? "in" + : "out"), + ep->maxpacket, ep->error_count); + ep = ep->next; + } while (ep); + } + + spin_unlock_irq(&sl811->lock); + seq_printf(s, "\n"); + + return 0; +} + +static int proc_sl811h_open(struct inode *inode, struct file *file) +{ + return single_open(file, proc_sl811h_show, PDE(inode)->data); +} + +static struct file_operations proc_ops = { + .open = proc_sl811h_open, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +/* expect just one sl811 per system */ +static const char proc_filename[] = "driver/sl811h"; + +static void create_debug_file(struct sl811 *sl811) +{ + struct proc_dir_entry *pde; + + pde = create_proc_entry(proc_filename, 0, NULL); + if (pde == NULL) + return; + + pde->proc_fops = &proc_ops; + pde->data = sl811; + sl811->pde = pde; +} + +static void remove_debug_file(struct sl811 *sl811) +{ + if (sl811->pde) + remove_proc_entry(proc_filename, NULL); +} + +#endif + +/*-------------------------------------------------------------------------*/ + +static void +sl811h_stop(struct usb_hcd *hcd) +{ + struct sl811 *sl811 = hcd_to_sl811(hcd); + unsigned long flags; + + del_timer_sync(&sl811->hcd.rh_timer); + + spin_lock_irqsave(&sl811->lock, flags); + port_power(sl811, 0); + spin_unlock_irqrestore(&sl811->lock, flags); +} + +static int +sl811h_start(struct usb_hcd *hcd) +{ + struct sl811 *sl811 = hcd_to_sl811(hcd); + struct usb_device *udev; + + /* chip has been reset, VBUS power is off */ + + udev = usb_alloc_dev(NULL, &sl811->hcd.self, 0); + if (!udev) + return -ENOMEM; + + udev->speed = USB_SPEED_FULL; + hcd->state = USB_STATE_RUNNING; + + if (sl811->board) + sl811->hcd.can_wakeup = sl811->board->can_wakeup; + + if (hcd_register_root(udev, &sl811->hcd) != 0) { + usb_put_dev(udev); + sl811h_stop(hcd); + return -ENODEV; + } + + if (sl811->board && sl811->board->power) + hub_set_power_budget(udev, sl811->board->power * 2); + + return 0; +} + +/*-------------------------------------------------------------------------*/ + +static struct hc_driver sl811h_hc_driver = { + .description = hcd_name, + + /* + * generic hardware linkage + */ + .flags = HCD_USB11, + + /* + * managing i/o requests and associated device resources + */ + .urb_enqueue = sl811h_urb_enqueue, + .urb_dequeue = sl811h_urb_dequeue, + .endpoint_disable = sl811h_endpoint_disable, + + /* + * periodic schedule support + */ + .get_frame_number = sl811h_get_frame, + + /* + * root hub support + */ + .hub_status_data = sl811h_hub_status_data, + .hub_control = sl811h_hub_control, + .hub_suspend = sl811h_hub_suspend, + .hub_resume = sl811h_hub_resume, +}; + +/*-------------------------------------------------------------------------*/ + +static int __init_or_module +sl811h_remove(struct device *dev) +{ + struct sl811 *sl811 = dev_get_drvdata(dev); + struct platform_device *pdev; + struct resource *res; + + pdev = container_of(dev, struct platform_device, dev); + + if (HCD_IS_RUNNING(sl811->hcd.state)) + sl811->hcd.state = USB_STATE_QUIESCING; + + usb_disconnect(&sl811->hcd.self.root_hub); + remove_debug_file(sl811); + sl811h_stop(&sl811->hcd); + + if (!list_empty(&sl811->hcd.self.bus_list)) + usb_deregister_bus(&sl811->hcd.self); + + if (sl811->hcd.irq >= 0) + free_irq(sl811->hcd.irq, sl811); + + if (sl811->data_reg) + iounmap(sl811->data_reg); + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + release_mem_region(res->start, 1); + + if (sl811->addr_reg) + iounmap(sl811->addr_reg); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, 1); + + kfree(sl811); + return 0; +} + +#define resource_len(r) (((r)->end - (r)->start) + 1) + +static int __init +sl811h_probe(struct device *dev) +{ + struct sl811 *sl811; + struct platform_device *pdev; + struct resource *addr, *data; + int irq; + int status; + u8 tmp; + unsigned long flags; + + /* basic sanity checks first. board-specific init logic should + * have initialized this the three resources and probably board + * specific platform_data. we don't probe for IRQs, and do only + * minimal sanity checking. + */ + pdev = container_of(dev, struct platform_device, dev); + if (pdev->num_resources < 3) + return -ENODEV; + + addr = platform_get_resource(pdev, IORESOURCE_MEM, 0); + data = platform_get_resource(pdev, IORESOURCE_MEM, 1); + irq = platform_get_irq(pdev, 0); + if (!addr || !data || irq < 0) + return -ENODEV; + + /* refuse to confuse usbcore */ + if (dev->dma_mask) { + DBG("no we won't dma\n"); + return -EINVAL; + } + + if (!request_mem_region(addr->start, 1, hcd_name)) + return -EBUSY; + if (!request_mem_region(data->start, 1, hcd_name)) { + release_mem_region(addr->start, 1); + return -EBUSY; + } + + /* allocate and initialize hcd */ + sl811 = kcalloc(1, sizeof *sl811, GFP_KERNEL); + if (!sl811) + return 0; + dev_set_drvdata(dev, sl811); + + usb_bus_init(&sl811->hcd.self); + sl811->hcd.self.controller = dev; + sl811->hcd.self.bus_name = dev->bus_id; + sl811->hcd.self.op = &usb_hcd_operations; + sl811->hcd.self.hcpriv = sl811; + + // NOTE: 2.6.11 starts to change the hcd glue layer some more, + // eventually letting us eliminate struct sl811h_req and a + // lot of the boilerplate code here + + INIT_LIST_HEAD(&sl811->hcd.dev_list); + +#if KERNEL_VERSION(2,6,10) <= LINUX_VERSION_CODE + sl811->hcd.self.release = &usb_hcd_release; +#endif + + sl811->hcd.description = sl811h_hc_driver.description; + init_timer(&sl811->hcd.rh_timer); + sl811->hcd.driver = &sl811h_hc_driver; + sl811->hcd.irq = -1; + sl811->hcd.state = USB_STATE_HALT; + + spin_lock_init(&sl811->lock); + INIT_LIST_HEAD(&sl811->async); + sl811->board = dev->platform_data; + init_timer(&sl811->timer); + sl811->timer.function = sl811h_timer; + sl811->timer.data = (unsigned long) sl811; + + sl811->addr_reg = ioremap(addr->start, resource_len(addr)); + if (sl811->addr_reg == NULL) { + status = -ENOMEM; + goto fail; + } + sl811->data_reg = ioremap(data->start, resource_len(addr)); + if (sl811->data_reg == NULL) { + status = -ENOMEM; + goto fail; + } + + spin_lock_irqsave(&sl811->lock, flags); + port_power(sl811, 0); + spin_unlock_irqrestore(&sl811->lock, flags); + msleep(200); + + tmp = sl811_read(sl811, SL11H_HWREVREG); + switch (tmp >> 4) { + case 1: + sl811->hcd.product_desc = "SL811HS v1.2"; + break; + case 2: + sl811->hcd.product_desc = "SL811HS v1.5"; + break; + default: + /* reject case 0, SL11S is less functional */ + DBG("chiprev %02x\n", tmp); + status = -ENXIO; + goto fail; + } + + /* sl811s would need a different handler for this irq */ +#ifdef CONFIG_ARM + /* Cypress docs say the IRQ is IRQT_HIGH ... */ + set_irq_type(irq, IRQT_RISING); +#endif + status = request_irq(irq, sl811h_irq, SA_INTERRUPT, hcd_name, sl811); + if (status < 0) + goto fail; + sl811->hcd.irq = irq; + + INFO("%s, irq %d\n", sl811->hcd.product_desc, irq); + + status = usb_register_bus(&sl811->hcd.self); + if (status < 0) + goto fail; + status = sl811h_start(&sl811->hcd); + if (status == 0) { + create_debug_file(sl811); + return 0; + } +fail: + sl811h_remove(dev); + DBG("init error, %d\n", status); + return status; +} + +#ifdef CONFIG_PM + +/* for this device there's no useful distinction between the controller + * and its root hub, except that the root hub only gets direct PM calls + * when CONFIG_USB_SUSPEND is enabled. + */ + +static int +sl811h_suspend(struct device *dev, u32 state, u32 phase) +{ + struct sl811 *sl811 = dev_get_drvdata(dev); + int retval = 0; + + if (phase != SUSPEND_POWER_DOWN) + return retval; + + if (state <= PM_SUSPEND_MEM) + retval = sl811h_hub_suspend(&sl811->hcd); + else + port_power(sl811, 0); + if (retval == 0) + dev->power.power_state = state; + return retval; +} + +static int +sl811h_resume(struct device *dev, u32 phase) +{ + struct sl811 *sl811 = dev_get_drvdata(dev); + + if (phase != RESUME_POWER_ON) + return 0; + + /* with no "check to see if VBUS is still powered" board hook, + * let's assume it'd only be powered to enable remote wakeup. + */ + if (dev->power.power_state > PM_SUSPEND_MEM + || !sl811->hcd.can_wakeup) { + sl811->port1 = 0; + port_power(sl811, 1); + return 0; + } + + dev->power.power_state = PM_SUSPEND_ON; + return sl811h_hub_resume(&sl811->hcd); +} + +#else + +#define sl811h_suspend NULL +#define sl811h_resume NULL + +#endif + + +static struct device_driver sl811h_driver = { + .name = (char *) hcd_name, + .bus = &platform_bus_type, + + .probe = sl811h_probe, + .remove = sl811h_remove, + + .suspend = sl811h_suspend, + .resume = sl811h_resume, +}; + +/*-------------------------------------------------------------------------*/ + +static int __init sl811h_init(void) +{ + if (usb_disabled()) + return -ENODEV; + + INFO("driver %s, %s\n", hcd_name, DRIVER_VERSION); + return driver_register(&sl811h_driver); +} +module_init(sl811h_init); + +static void __exit sl811h_cleanup(void) +{ + driver_unregister(&sl811h_driver); +} +module_exit(sl811h_cleanup); diff -NaurbB linux-2.6.9-pre1-bk18/drivers/usb/Kconfig linux-2.6.9_nbp/drivers/usb/Kconfig --- linux-2.6.9-pre1-bk18/drivers/usb/Kconfig 2004-08-14 07:36:17.000000000 +0200 +++ linux-2.6.9_nbp/drivers/usb/Kconfig 2004-09-14 12:25:06.000000000 +0200 @@ -7,7 +7,7 @@ # ARM SA1111 chips have a non-PCI based "OHCI-compatible" USB host interface. config USB tristate "Support for Host-side USB" - depends on PCI || SA1111 || ARCH_OMAP1510 || ARCH_OMAP1610 || ARCH_LH7A404 + depends on PCI || SA1111 || ARCH_OMAP1510 || ARCH_OMAP1610 || ARCH_LH7A404 || MACH_NETBOOKPRO ---help--- Universal Serial Bus (USB) is a specification for a serial bus subsystem which offers higher speeds and more features than the @@ -40,6 +40,11 @@ To compile this driver as a module, choose M here: the module will be called usbcore. +config USB_PXA + tristate + default y + depends on USB && ARCH_PXA + source "drivers/usb/core/Kconfig" source "drivers/usb/host/Kconfig" diff -NaurbB linux-2.6.9-pre1-bk18/drivers/usb/Makefile linux-2.6.9_nbp/drivers/usb/Makefile --- linux-2.6.9-pre1-bk18/drivers/usb/Makefile 2004-08-14 07:38:04.000000000 +0200 +++ linux-2.6.9_nbp/drivers/usb/Makefile 2004-11-21 23:46:50.000000000 +0100 @@ -9,7 +9,7 @@ obj-$(CONFIG_USB_EHCI_HCD) += host/ obj-$(CONFIG_USB_OHCI_HCD) += host/ obj-$(CONFIG_USB_UHCI_HCD) += host/ -obj-$(CONFIG_USB_SL811HS) += host/ +obj-$(CONFIG_USB_SL811_HCD) += host/ obj-$(CONFIG_USB_ACM) += class/ obj-$(CONFIG_USB_AUDIO) += class/ diff -NaurbB linux-2.6.9-pre1-bk18/drivers/usb/misc/usbtest.c linux-2.6.9_nbp/drivers/usb/misc/usbtest.c --- linux-2.6.9-pre1-bk18/drivers/usb/misc/usbtest.c 2005-08-15 01:14:41.000000000 +0200 +++ linux-2.6.9_nbp/drivers/usb/misc/usbtest.c 2004-11-22 18:32:09.000000000 +0100 @@ -1355,7 +1355,7 @@ "iso test, %lu errors\n", ctx->errors); complete (&ctx->done); - } else + } done: spin_unlock(&ctx->lock); } @@ -1457,8 +1457,10 @@ status = usb_submit_urb (urbs [i], SLAB_ATOMIC); if (status < 0) { ERROR (dev, "submit iso[%d], error %d\n", i, status); - if (i == 0) + if (i == 0) { + spin_unlock_irq (&context.lock); goto fail; + } simple_free_urb (urbs [i]); context.pending--; diff -NaurbB linux-2.6.9-pre1-bk18/drivers/usb/storage/protocol.c linux-2.6.9_nbp/drivers/usb/storage/protocol.c --- linux-2.6.9-pre1-bk18/drivers/usb/storage/protocol.c 2005-08-15 01:14:33.000000000 +0200 +++ linux-2.6.9_nbp/drivers/usb/storage/protocol.c 2004-11-21 23:46:50.000000000 +0100 @@ -45,6 +45,7 @@ */ #include +#include #include #include #include "protocol.h" diff -NaurbB linux-2.6.9-pre1-bk18/drivers/usb/storage/scsiglue.c linux-2.6.9_nbp/drivers/usb/storage/scsiglue.c --- linux-2.6.9-pre1-bk18/drivers/usb/storage/scsiglue.c 2005-08-15 01:14:33.000000000 +0200 +++ linux-2.6.9_nbp/drivers/usb/storage/scsiglue.c 2004-11-21 23:46:50.000000000 +0100 @@ -47,6 +47,7 @@ #include #include +#include #include #include diff -NaurbB linux-2.6.9-pre1-bk18/drivers/usb/storage/transport.c linux-2.6.9_nbp/drivers/usb/storage/transport.c --- linux-2.6.9-pre1-bk18/drivers/usb/storage/transport.c 2005-08-15 01:14:42.000000000 +0200 +++ linux-2.6.9_nbp/drivers/usb/storage/transport.c 2004-11-21 23:46:50.000000000 +0100 @@ -49,6 +49,7 @@ #include #include #include +#include #include #include diff -NaurbB linux-2.6.9-pre1-bk18/drivers/usb/storage/usb.c linux-2.6.9_nbp/drivers/usb/storage/usb.c --- linux-2.6.9-pre1-bk18/drivers/usb/storage/usb.c 2005-08-15 01:14:33.000000000 +0200 +++ linux-2.6.9_nbp/drivers/usb/storage/usb.c 2004-11-21 23:46:50.000000000 +0100 @@ -50,6 +50,7 @@ #include #include #include +#include #include #include diff -NaurbB linux-2.6.9-pre1-bk18/drivers/video/Kconfig linux-2.6.9_nbp/drivers/video/Kconfig --- linux-2.6.9-pre1-bk18/drivers/video/Kconfig 2005-08-15 01:14:33.000000000 +0200 +++ linux-2.6.9_nbp/drivers/video/Kconfig 2004-09-14 12:25:07.000000000 +0200 @@ -425,6 +425,12 @@ framebuffer. Product specs at . +config FB_S1D138XX + bool "Epson S1D138XX Support" + depends on FB + help + Driver for S1D13XXX LCD/CRT controllers + config FB_RIVA tristate "nVidia Riva support" depends on FB && PCI diff -NaurbB linux-2.6.9-pre1-bk18/drivers/video/Makefile linux-2.6.9_nbp/drivers/video/Makefile --- linux-2.6.9-pre1-bk18/drivers/video/Makefile 2005-08-15 01:14:42.000000000 +0200 +++ linux-2.6.9_nbp/drivers/video/Makefile 2004-09-14 12:25:07.000000000 +0200 @@ -89,6 +89,10 @@ obj-$(CONFIG_FB_LEO) += leo.o sbuslib.o cfbimgblt.o cfbcopyarea.o \ cfbfillrect.o obj-$(CONFIG_FB_PXA) += pxafb.o cfbimgblt.o cfbcopyarea.o cfbfillrect.o + +obj-$(CONFIG_FB_S1D138XX) += s1d13xxxfb.o cfbimgblt.o cfbcopyarea.o \ + cfbfillrect.o +obj-$(CONFIG_MACH_NETBOOKPRO) += s1d13xxxfb-netbookpro.o obj-$(CONFIG_FB_VGA16) += vga16fb.o cfbfillrect.o cfbcopyarea.o \ cfbimgblt.o vgastate.o obj-$(CONFIG_FB_VESA) += vesafb.o cfbfillrect.o cfbcopyarea.o cfbimgblt.o diff -NaurbB linux-2.6.9-pre1-bk18/drivers/video/s1d13xxxfb.c linux-2.6.9_nbp/drivers/video/s1d13xxxfb.c --- linux-2.6.9-pre1-bk18/drivers/video/s1d13xxxfb.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/drivers/video/s1d13xxxfb.c 2004-09-21 16:10:23.000000000 +0200 @@ -0,0 +1,550 @@ +/* drivers/video/s1d13xxxfb.c + * + * (c) 2004 Simtec Electronics + * + * Driver for Epson S1D13xxx series framebuffer chips + * + * Note, only currently tested with S1D13806. + * + * Released under GPL +*/ + +#include +#include +#include + +#include +#include +#include +#include +#include + +#include + +#ifdef __arm__ +#include +#endif + +#include "s1d13xxxfb.h" + +#define PFX "s1d138xx: " + +#define MAX_REGS (128) + +unsigned short regsave_list[] = { + 0x0001, 0x01FC, 0x0004, 0x0005, 0x0008, 0x0009, 0x0010, + 0x0014, 0x0018, 0x001C, 0x001E, 0x0021, 0x002A, 0x002B, + 0x0020, 0x0030, 0x0031, 0x0032, 0x0034, 0x0035, 0x0036, + 0x0038, 0x0039, 0x003A, 0x003B, 0x003C, 0x0040, 0x0041, + 0x0042, 0x0043, 0x0044, 0x0046, 0x0047, 0x0048, 0x004A, + 0x004B, 0x0050, 0x0052, 0x0053, 0x0054, 0x0056, 0x0057, + 0x0058, 0x0059, 0x005A, 0x005B, 0x0060, 0x0062, 0x0063, + 0x0064, 0x0066, 0x0067, 0x0068, 0x006A, 0x006B, 0x0070, + 0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, + 0x0078, 0x007A, 0x007B, 0x007C, 0x007E, 0x0080, 0x0081, + 0x0082, 0x0083, 0x0084, 0x0085, 0x0086, 0x0087, 0x0088, + 0x008A, 0x008B, 0x008C, 0x008E, 0x0100, 0x0101, 0x0102, + 0x0103, 0x0104, 0x0105, 0x0106, 0x0108, 0x0109, 0x010A, + 0x010C, 0x010D, 0x0110, 0x0111, 0x0112, 0x0113, 0x0114, + 0x0115, 0x0118, 0x0119, 0x01E0, 0x01E2, 0x01F0, 0x01F1, + 0x01F4, 0x01FC +}; + + +struct s1dfb_regsave { + unsigned char reg_val; +}; + + +struct s1dfb { + void *disp_mem; + void *regs; + void *disp_save; /* pm: save entire screen here */ + struct s1dfb_mach *machine; + struct fb_info fbinfo; + + unsigned int pseudo_palette[16]; + struct s1dfb_regsave regsave[ARRAY_SIZE(regsave_list)+1]; +}; + +#if 0 +#define dbg(x...) do { printk(KERN_DEBUG x); } while(0) +#else +#define dbg(x...) do { } while (0) +#endif + +struct s1dfb *s1dfb; + + unsigned int +s1d13xxx_readreg(struct s1dfb *fb, int regno) +{ + //dbg("s1d13xxx_readreg: fb=%p, regno=%d\n", fb, regno); + + return readb((unsigned long)fb->regs + regno); +} + +void +s1d13xxx_writereg(struct s1dfb *fb, int regno, int value) +{ + //dbg("s1d13xxx_writereg: fb=%p, %04x, value=%02x\n", fb, regno, value); + + writeb(value, (unsigned long)fb->regs + regno); +} + +void +s1d13xxx_runinit(struct s1dfb *fb, struct s1d13xxx_reginit *inits) +{ + const struct s1d13xxx_regval *ptr; + int count; + + ptr = inits->values; + + for (count = inits->length; count > 0; count--, ptr++) { + s1d13xxx_writereg(fb, ptr->addr, ptr->value); + } +} + + +/* framebuffer control routines */ + +static int +s1d13xxx_check_var(struct fb_var_screeninfo *var, struct fb_info *info) +{ + dbg("s1d13xxx_check_var: var=%p, info=%p\n", var, info); + + + return 0; +} + +static void +s1d13xxx_setup_pseudocolour(struct fb_info *info) +{ + info->fix.visual = FB_VISUAL_PSEUDOCOLOR; + + info->var.red.length = 4; + info->var.green.length = 4; + info->var.blue.length = 4; +} + +static void +s1d13xxx_setup_truecolour(struct fb_info *info) +{ + info->fix.visual = FB_VISUAL_TRUECOLOR; + + info->var.red.length = 5; + info->var.red.offset = 11; + + info->var.green.length = 6; + info->var.green.offset = 5; + + info->var.blue.length = 5; + info->var.blue.offset = 0; +} + +static int +s1d13xxx_set_par(struct fb_info *info) +{ + unsigned int val; + + dbg("s1d13xxx_set_par: info=%p\n", info); + + dbg("s1d13xxx_set_par: bpp=%d\n", info->var.bits_per_pixel); + + val = s1d13xxx_readreg(s1dfb, 0x40); /* read colour control */ + + val &= ~7; + + switch (info->var.bits_per_pixel) { + case 4: + s1d13xxx_setup_pseudocolour(info); + val |= 2; + break; + case 8: + s1d13xxx_setup_pseudocolour(info); + val |= 3; + break; + case 16: + s1d13xxx_setup_truecolour(info); + val |= 5; + break; + + default: + dbg("bpp not supported!\n"); + return -EINVAL; + } + + dbg("writing %02x to lcd display mode register\n", val); + s1d13xxx_writereg(s1dfb, 0x40, val); + + info->fix.line_length = info->var.xres * info->var.bits_per_pixel; + info->fix.line_length /= 8; + + dbg("setting line_length to %d\n", info->fix.line_length); + + dbg("done setup\n"); + + return 0; +} + +static int +s1d13xxx_setcolreg(u_int regno, + u_int red, + u_int green, + u_int blue, + u_int transp, + struct fb_info *info) +{ + if (0) { + dbg("s1d13xxx_setcolreg: %d: rgb=%d,%d,%d, tr=%d\n", + regno, red, green, blue, transp); + } + + if (regno < 16) { + unsigned int pseudo_val; + /* deal with creating pseudo-palette entries */ + + pseudo_val = (red >> 11) << info->var.red.offset; + pseudo_val |= (green >> 10) << info->var.green.offset; + pseudo_val |= (blue >> 11) << info->var.blue.offset; + + if (0) { + dbg("s1d13xxx_setcolreg: pseudo %d, val %08x\n", + regno, pseudo_val); + } + + ((u32 *)info->pseudo_palette)[regno] = pseudo_val; + } + + if (info->var.bits_per_pixel > 8) { + return 0; + } + + s1d13xxx_writereg(s1dfb, 0x1e2, regno); + s1d13xxx_writereg(s1dfb, 0x1e4, red); + s1d13xxx_writereg(s1dfb, 0x1e4, green); + s1d13xxx_writereg(s1dfb, 0x1e4, blue); + + dbg("s1d13xxx_setcolreg: done\n"); + + return 0; +} + +static int +s1d13xxx_blank(int blank, struct fb_info *info) +{ + dbg("s1d13xxx_blank: blank=%d, info=%p\n", blank, info); + + if (s1dfb->machine->blank != NULL) + (s1dfb->machine->blank)(s1dfb, blank); + + return 0; +} + +static int +s1d13xxx_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + return 0; +} + +/* framebuffer information structures */ + + + +static struct fb_ops s1d13xxx_fbops = { + .owner = THIS_MODULE, + .fb_check_var = s1d13xxx_check_var, + .fb_set_par = s1d13xxx_set_par, + .fb_setcolreg = s1d13xxx_setcolreg, + .fb_blank = s1d13xxx_blank, + + .fb_pan_display = s1d13xxx_pan_display, + + /* to be replaced by any acceleration we can */ + .fb_fillrect = cfb_fillrect, + .fb_copyarea = cfb_copyarea, + .fb_imageblit = cfb_imageblit, + .fb_cursor = soft_cursor +}; + +static void +do_test_pattern(unsigned long base) +{ + unsigned short *sp = (unsigned short *)base; + int x, y; + int r, g, b; + + dbg("writing default pattern to screen\n"); + + for (y = 0; y < 600; y++) { + for (x = 0; x < 800; x++) { + r = x / 32; + g = y / 16; + b = 0; + + if (r > 0x1f) + r = 0x1f; + if (g > 0x1f) + g = 0x1f; + + sp[x + (y*800)] = r | (g<<5) | (r << 11); + } + } + + dbg("written pattern to screen\n"); +} + + + +static int +s1d13xxx_init_fbinfo(struct s1dfb *sdfb, struct fb_info *fbinfo) +{ + unsigned long size; + + dbg("s1d13xxx_init_fbinfo: sdfb=%p, fbinfo=%p\n", sdfb, fbinfo); + + fbinfo->var.xres_virtual = 800; + fbinfo->var.yres_virtual = 600; + fbinfo->var.xres = 800; + fbinfo->var.yres = 600; + + fbinfo->var.bits_per_pixel = 16; + fbinfo->var.activate = FB_ACTIVATE_NOW; + fbinfo->var.height = -1; + fbinfo->var.width = -1; + + size = 1280*1024; + + if (size < (800*600*2)) { + printk(KERN_ERR "size is not big enough for 800x600 16bpp\n"); + } + + fbinfo->screen_base = ioremap_nocache((u32)sdfb->disp_mem, size); + fbinfo->pseudo_palette = sdfb->pseudo_palette; + + dbg("fbinfo->screen_base = %p, size %08x\n", + fbinfo->screen_base, (int)size); + + do_test_pattern((unsigned long)fbinfo->screen_base); + + fbinfo->fix.type = FB_TYPE_PACKED_PIXELS; + fbinfo->fix.smem_start = (unsigned long)sdfb->disp_mem; + fbinfo->fix.smem_len = size; + + return 0; +} + +void +s1d13xxx_set_machine(struct s1dfb *sdfb, struct s1dfb_mach *mach) +{ + dbg("%s: machine at %p (%s)\n", __FUNCTION__, mach, mach->name); + + sdfb->machine = mach; +} + + + +/* s1d13xxx_probe() + * + * allocate resources and try and identify the framebuffer chip we + * are dealing with +*/ + +int +s1d13xxx_probe(struct s1dfb *sdfb) +{ + struct fb_info *fbinfo = &sdfb->fbinfo; + unsigned int vers; + + s1dfb = sdfb; + + vers = s1d13xxx_readreg(sdfb, S1DREG_REVCODE); + if ((vers >> 2) != 7) { + printk(KERN_ERR PFX "did not detect controller\n"); + return -ENOENT; + } + + printk(PFX "s1d13806: revision %d\n", vers & 3); + + /* ok, now let us register a framebuffer driver */ + + strcpy(fbinfo->fix.id, "s1d13xxx"); + + fbinfo->fbops = &s1d13xxx_fbops; + fbinfo->flags = FBINFO_FLAG_DEFAULT; + + s1d13xxx_init_fbinfo(sdfb, fbinfo); + + fb_alloc_cmap(&fbinfo->cmap, 256, 0); + + if (machine_is_netbookpro()) { + s1dfb_netbookpro_init(sdfb); + } + + if (register_framebuffer(fbinfo) < 0) { + printk(PFX ": failed to install framebuffer\n"); + return -EINVAL; + } + + printk(KERN_INFO "fb%d: %s framebuffer device\n", + fbinfo->node, fbinfo->fix.id); + + return 0; +} + +int +s1d13xxx_setup(const char *args) +{ + dbg("s1d13xxx_setup: args=%p\n", args); + dbg("s1313xxx_setup: '%s'\n", args); + + return 0; +} + +static void free_sdfb(struct s1dfb *sdfb) +{ + if (sdfb != NULL) { + /* TODO: unmap resources */ + + kfree(sdfb); + } +} + +static int __init s1d13xxx_devprobe(struct device *dev) +{ + struct platform_device *platdev = to_platform_device(dev); + struct resource *res; + struct s1dfb *sdfb; + int seen = 0; + int i; + + res = platdev->resource; + + dbg("probe called: device is %p\n", dev); + + sdfb = kmalloc(sizeof(*sdfb), GFP_KERNEL); + if (sdfb == NULL) { + printk(KERN_ERR PFX "failed to allocate device info\n"); + return -ENOMEM; + } + + memset(sdfb, 0, sizeof(*sdfb)); + + for (i = 0; i < platdev->num_resources; i++, res++) { + if (!(res->flags & IORESOURCE_MEM)) + continue; + + switch (seen) { + case 0: + sdfb->disp_mem = (void *)res->start; + seen++; + break; + case 1: + sdfb->regs = ioremap_nocache(res->start, 0x1000); + seen++; + break; + + default: + /* more resources than we needed? */ + dbg("extra resource %d,%p\n", i, res); + } + } + + if (seen < 2) { + printk(KERN_ERR PFX "not enough supplied resources\n"); + free_sdfb(sdfb); + return -EINVAL; + } + + if (sdfb->disp_mem != NULL && sdfb->regs != NULL) { + s1d13xxx_probe(sdfb); + dev_set_drvdata(dev, sdfb); + } else { + printk(KERN_ERR PFX "no allocate resources to probe\n"); + free_sdfb(sdfb); + return -EINVAL; + } + + return 0; +} + +static int s1d13xxx_suspend(struct device *dev, u32 state, u32 level) +{ + int i; + +#if 0 + if (s1dfb->disp_save == NULL) { + s1dfb->disp_save = kmalloc(1280*1024, GFP_KERNEL); + } + + if (s1dfb->disp_save == NULL) { + printk(KERN_ERR PFX "no memory to save screen"); + return -ENOMEM; + } + + memcpy(s1dfb->disp_save, s1dfb->fbinfo.screen_base, + 1024*1280); +#else + s1dfb->disp_save = NULL; +#endif + + for (i = 0; i < ARRAY_SIZE(regsave_list); i++) { + s1dfb->regsave[i].reg_val = s1d13xxx_readreg(s1dfb, regsave_list[i]); + } + + if (s1dfb->machine->suspend != NULL) { + (s1dfb->machine->suspend)(s1dfb); + } + + return 0; +} + +static int s1d13xxx_resume(struct device *dev, u32 level) +{ + int i; + + if (level != RESUME_ENABLE) + return 0; + + for (i = 0; i < ARRAY_SIZE(regsave_list); i++) { + s1d13xxx_writereg(s1dfb, regsave_list[i], + s1dfb->regsave[i].reg_val); + } + + if (s1dfb->disp_save != NULL) { + memcpy(s1dfb->fbinfo.screen_base, s1dfb->disp_save, + 1280*1024); + kfree(s1dfb->disp_save); + } else { + //do_test_pattern(s1dfb->fbinfo.screen_base); + } + + if (s1dfb->machine->resume != NULL) { + (s1dfb->machine->resume)(s1dfb); + } + + return 0; +} + +static struct device_driver s1d_driver = { + .name = "epson-s1d138xxx", + .bus = &platform_bus_type, + .probe = s1d13xxx_devprobe, +#ifdef CONFIG_PM + .suspend = s1d13xxx_suspend, + .resume = s1d13xxx_resume +#endif +}; + + +static int __init s1d13xxx_init(void) +{ + printk("Epson S1D13XXX FB Driver, (c) 2004 Simtec Electronics\n"); + + return driver_register(&s1d_driver); +} + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Ben Dooks, Simtec Electronics "); + +late_initcall(s1d13xxx_init); diff -NaurbB linux-2.6.9-pre1-bk18/drivers/video/s1d13xxxfb.h linux-2.6.9_nbp/drivers/video/s1d13xxxfb.h --- linux-2.6.9-pre1-bk18/drivers/video/s1d13xxxfb.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/drivers/video/s1d13xxxfb.h 2004-09-14 12:25:07.000000000 +0200 @@ -0,0 +1,41 @@ +/* drivers/video/s1d3xxxfb.h + * + * (c) 2004 Simtec Electronics + * + * Header file for Epson S1D13XXX driver code +*/ + +struct s1d13xxx_regval { + unsigned short addr; + unsigned char value; +}; + +struct s1d13xxx_reginit { + const struct s1d13xxx_regval *values; + unsigned int length; +}; + +struct s1dfb; + +struct s1dfb_mach { + const char *name; + void (*blank)(struct s1dfb *fb, int to); + void (*suspend)(struct s1dfb *fb); + void (*resume)(struct s1dfb *fb); +}; + +extern void s1d13xxx_runinit(struct s1dfb *fb, struct s1d13xxx_reginit *); +extern void s1d13xxx_set_machine(struct s1dfb *sdfb, struct s1dfb_mach *mach); + +extern void s1d13xxx_writereg(struct s1dfb *fb, int regno, int value); +extern unsigned int s1d13xxx_readreg(struct s1dfb *fb, int regno); + + + +extern int s1dfb_netbookpro_init(struct s1dfb *sdfb); + + +/* some register definitions */ + +#define S1DREG_REVCODE (0x00) +#define S1DREG_GPIO_CTRL0 (0x08) diff -NaurbB linux-2.6.9-pre1-bk18/drivers/video/s1d13xxxfb-netbookpro.c linux-2.6.9_nbp/drivers/video/s1d13xxxfb-netbookpro.c --- linux-2.6.9-pre1-bk18/drivers/video/s1d13xxxfb-netbookpro.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/drivers/video/s1d13xxxfb-netbookpro.c 2004-10-14 18:02:16.000000000 +0200 @@ -0,0 +1,370 @@ +/* drivers/video/s1d13x-netbookpro.c + * + * (c) 2004 Simtec Electronics + * + * Netbookpro specific bits for Epson 13806 + */ + + +#include +#include +#include +#include +#include + +#include + +#include + +#include "s1d13xxxfb.h" + +#define PFX "s1dfb-nbp: " + +#if 0 +extern void printascii(const char *); +#define dbg(x...) do { char buff[1024]; printascii(PFX); sprintf(buff, x); printascii(buff); } while(0) +#else +#define dbg(x...) do {} while(0) +#endif + + + + +static const struct s1d13xxx_regval init_netbookpro_vals[] = { + { 0x0001, 0x00 }, // Miscellaneous Register + { 0x01FC, 0x00 }, // Display Mode Register + { 0x0004, 0xFF }, // General IO Pins Configuration Register 0 + { 0x0005, 0x1F }, // General IO Pins Configuration Register 1 + { 0x0008, 0x7F }, // General IO Pins Control Register 0 + { 0x0009, 0x00 }, // General IO Pins Control Register 1 + + { 0x0010, 0x01 }, // Memory Clock Configuration Register + { 0x0014, 0x00 }, // LCD Pixel Clock Configuration Register + { 0x0018, 0x02 }, // CRT/TV Pixel Clock Configuration Register + { 0x001C, 0x02 }, // MediaPlug Clock Configuration Register + { 0x001E, 0x01 }, // CPU To Memory Wait State Select Register + + { 0x0021, 0x03 }, // DRAM Refresh Rate Register + { 0x002A, 0x00 }, // DRAM Timings Control Register 0 + { 0x002B, 0x01 }, // DRAM Timings Control Register 1 + { 0x0020, 0x80 }, // Memory Configuration Register + + { 0x0030, 0x25 }, // Panel Type Register + { 0x0031, 0x00 }, // MOD Rate Register + { 0x0032, 0x63 }, // LCD Horizontal Display Width Register + { 0x0034, 0x1F }, // TFT FPLINE Start Position Register + { 0x0035, 0x01 }, // TFT FPLINE Start Position Register + { 0x0036, 0x0B }, // TFT FPLINE Pulse Width Register + { 0x0038, 0x57 }, // LCD Vertical Display Height Register 0 + { 0x0039, 0x02 }, // LCD Vertical Display Height Register 1 + { 0x003A, 0x1B }, // LCD Vertical Non-Display Period Register + { 0x003B, 0x0A }, // TFT FPFRAME Start Position Register + { 0x003C, 0x01 }, // TFT FPFRAME Pulse Width Register + + { 0x0040, 0x05 }, // LCD Display Mode Register + { 0x0041, 0x00 }, // LCD Miscellaneous Register + { 0x0042, 0x00 }, // LCD Display Start Address Register 0 + { 0x0043, 0x00 }, // LCD Display Start Address Register 1 + { 0x0044, 0x00 }, // LCD Display Start Address Register 2 + { 0x0046, 0x20 }, // LCD Memory Address Offset Register 0 + { 0x0047, 0x03 }, // LCD Memory Address Offset Register 1 + { 0x0048, 0x00 }, // LCD Pixel Panning Register + { 0x004A, 0x00 }, // LCD Display FIFO High Threshold Control Register + { 0x004B, 0x00 }, // LCD Display FIFO Low Threshold Control Register + + { 0x0050, 0x00 }, // CRT/TV Horizontal Display Width Register + { 0x0052, 0x00 }, // CRT/TV Horizontal Non-Display Period Register + { 0x0053, 0x00 }, // CRT/TV HRTC Start Position Register + { 0x0054, 0x00 }, // CRT/TV HRTC Pulse Width Register + { 0x0056, 0x00 }, // CRT/TV Vertical Display Height Register 0 + { 0x0057, 0x00 }, // CRT/TV Vertical Display Height Register 1 + { 0x0058, 0x00 }, // CRT/TV Vertical Non-Display Period Register + { 0x0059, 0x00 }, // CRT/TV VRTC Start Position Register + { 0x005A, 0x00 }, // CRT/TV VRTC Pulse Width Register + { 0x005B, 0x00 }, // TV Output Control Register + + { 0x0060, 0x00 }, // CRT/TV Display Mode Register + { 0x0062, 0x00 }, // CRT/TV Display Start Address Register 0 + { 0x0063, 0x00 }, // CRT/TV Display Start Address Register 1 + { 0x0064, 0x00 }, // CRT/TV Display Start Address Register 2 + { 0x0066, 0x00 }, // CRT/TV Memory Address Offset Register 0 + { 0x0067, 0x00 }, // CRT/TV Memory Address Offset Register 1 + { 0x0068, 0x00 }, // CRT/TV Pixel Panning Register + { 0x006A, 0x00 }, // CRT/TV Display FIFO High Threshold Control Register + { 0x006B, 0x00 }, // CRT/TV Display FIFO Low Threshold Control Register + + { 0x0070, 0x00 }, // LCD Ink/Cursor Control Register + { 0x0071, 0x00 }, // LCD Ink/Cursor Start Address Register + { 0x0072, 0x00 }, // LCD Cursor X Position Register 0 + { 0x0073, 0x00 }, // LCD Cursor X Position Register 1 + { 0x0074, 0x00 }, // LCD Cursor Y Position Register 0 + { 0x0075, 0x00 }, // LCD Cursor Y Position Register 1 + { 0x0076, 0x00 }, // LCD Ink/Cursor Blue Color 0 Register + { 0x0077, 0x00 }, // LCD Ink/Cursor Green Color 0 Register + { 0x0078, 0x00 }, // LCD Ink/Cursor Red Color 0 Register + { 0x007A, 0x00 }, // LCD Ink/Cursor Blue Color 1 Register + { 0x007B, 0x00 }, // LCD Ink/Cursor Green Color 1 Register + { 0x007C, 0x00 }, // LCD Ink/Cursor Red Color 1 Register + { 0x007E, 0x00 }, // LCD Ink/Cursor FIFO Threshold Register + + { 0x0080, 0x00 }, // CRT/TV Ink/Cursor Control Register + { 0x0081, 0x00 }, // CRT/TV Ink/Cursor Start Address Register + { 0x0082, 0x00 }, // CRT/TV Cursor X Position Register 0 + { 0x0083, 0x00 }, // CRT/TV Cursor X Position Register 1 + { 0x0084, 0x00 }, // CRT/TV Cursor Y Position Register 0 + { 0x0085, 0x00 }, // CRT/TV Cursor Y Position Register 1 + { 0x0086, 0x00 }, // CRT/TV Ink/Cursor Blue Color 0 Register + { 0x0087, 0x00 }, // CRT/TV Ink/Cursor Green Color 0 Register + { 0x0088, 0x00 }, // CRT/TV Ink/Cursor Red Color 0 Register + { 0x008A, 0x00 }, // CRT/TV Ink/Cursor Blue Color 1 Register + { 0x008B, 0x00 }, // CRT/TV Ink/Cursor Green Color 1 Register + { 0x008C, 0x00 }, // CRT/TV Ink/Cursor Red Color 1 Register + { 0x008E, 0x00 }, // CRT/TV Ink/Cursor FIFO Threshold Register + + { 0x0100, 0x00 }, // BitBlt Control Register 0 + { 0x0101, 0x01 }, // BitBlt Control Register 1 + { 0x0102, 0x00 }, // BitBlt ROP Code/Color Expansion Register + { 0x0103, 0x0C }, // BitBlt Operation Register + { 0x0104, 0x00 }, // BitBlt Source Start Address Register 0 + { 0x0105, 0x00 }, // BitBlt Source Start Address Register 1 + { 0x0106, 0x00 }, // BitBlt Source Start Address Register 2 + { 0x0108, 0x28 }, // BitBlt Destination Start Address Register 0 + { 0x0109, 0xBD }, // BitBlt Destination Start Address Register 1 + { 0x010A, 0x06 }, // BitBlt Destination Start Address Register 2 + { 0x010C, 0x20 }, // BitBlt Memory Address Offset Register 0 + { 0x010D, 0x03 }, // BitBlt Memory Address Offset Register 1 + + { 0x0110, 0x09 }, // BitBlt Width Register 0 + { 0x0111, 0x00 }, // BitBlt Width Register 1 + { 0x0112, 0x0b }, // BitBlt Height Register 0 + { 0x0113, 0x00 }, // BitBlt Height Register 1 + { 0x0114, 0x00 }, // BitBlt Background Color Register 0 + { 0x0115, 0x00 }, // BitBlt Background Color Register 1 + { 0x0118, 0x00 }, // BitBlt Foreground Color Register 0 + { 0x0119, 0x00 }, // BitBlt Foreground Color Register 1 + { 0x01E0, 0x00 }, // Look-Up Table Mode Register + { 0x01E2, 0x00 }, // Look-Up Table Address Register + { 0x01F0, 0x10 }, // Power Save Configuration Register + { 0x01F1, 0x00 }, // Power Save Status Register + { 0x01F4, 0x00 }, // CPU-to-Memory Access Watchdog Timer Register + { 0x01FC, 0x01 } // Display Mode Register +}; + +struct s1d13xxx_reginit init_netbookpro = { + .values = init_netbookpro_vals, + .length = ARRAY_SIZE(init_netbookpro_vals) +}; + +static struct s1dfb *save_fb; + + +static void +netbookpro_backlight_off(struct s1dfb *fb) +{ + unsigned int value = s1d13xxx_readreg(fb, 8); + + dbg("%s:\n", __FUNCTION__); + + s1d13xxx_writereg(fb, 8, value & 0xf9); +} + +static void +netbookpro_backlight_on(struct s1dfb *fb, int level) +{ + unsigned int value = s1d13xxx_readreg(fb, 8); + + dbg("%s: fb=%p, level=%d\n", __FUNCTION__, fb, level); + + /* turning back light on with a value */ + if(level !=0) + value = (level<<3) | (value & 0x07); + + /* enable backlight power */ + s1d13xxx_writereg(fb, 8, value | 2); + udelay(5); + + /* set backlight enable and full brightness to strike tube */ + s1d13xxx_writereg(fb, 8, 0x00ff); + udelay(200); + + /* set backlight enable and value */ + s1d13xxx_writereg(fb, 8, value | 6); +} + +static int backlight_level = 31; + +static void +netbookpro_backlight_set(struct s1dfb *fb, int level) +{ + unsigned int value = s1d13xxx_readreg(fb, 8); + + dbg("%s: fb=%p, level=%d\n", __FUNCTION__, fb, level); + + value &= 7; + s1d13xxx_writereg(fb, 8, value | (level << 3)); +} + +static void +s1dfb_netbookrpo_blank(struct s1dfb *sdfb, int blank) +{ + if (blank) + netbookpro_backlight_off(sdfb); + else + netbookpro_backlight_on(sdfb, backlight_level); +} + +void +netbookpro_backlight_mod(int by) +{ + unsigned long flags; + + if (!save_fb) + return; + + local_irq_save(flags); + backlight_level += by; + if (backlight_level < 0) + backlight_level = 0; + if (backlight_level > 31) + backlight_level = 31; + local_irq_restore(flags); + + netbookpro_backlight_on(save_fb, backlight_level); + +} + +/* + * procfs file entry + * + * create and handle driver/backlight + * + */ + +#ifdef CONFIG_PROC_FS + +static struct proc_dir_entry *backlight_file; + +static int proc_read_backlight(char *page, char **start, + off_t off, int count, + int *eof, void *data) +{ + u16 value; + int len; + + value = s1d13xxx_readreg(save_fb, 8); + + if((value & 0x6)==0) + { + value=0; + } + else + { + value=(value>>3)+1; + } + len = sprintf(page, "%d\n",value); + + return len; +} + + +static int proc_write_backlight(struct file *file, + const char *buffer, + unsigned long count, + void *data) +{ + char string[12] = {'\0'}; + int level=16; + int value=s1d13xxx_readreg(save_fb, 8); + + if (count > sizeof(string) - 1) + return -EINVAL; + + if (copy_from_user(string, buffer, count)) + return -EFAULT; + + string[count] = '\0'; + + level=simple_strtoul(string, NULL, 0); + + /* level can be between 0 and 32 + * 0 is backlight off + * 1-32 is backlight level + */ + if(level < 0 ) level=0; + if(level > 32) level=32; + + backlight_level = level; /* save for later */ + + if((value & 0x6)==0) { + if(level > 0) { + /* set value, enable inverter and backlight power */ + netbookpro_backlight_on(save_fb, level - 1); + } + } else { + if(level==0) { + /* turn inverter off and disable backlight power */ + netbookpro_backlight_off(save_fb); + return count; + } + else { + netbookpro_backlight_set(save_fb, level - 1); + } + } + + return count; +} + +/* creates the proc file for backlight */ +static int create_proc_file(const char *filename) +{ + backlight_file = create_proc_entry(filename, 0666, NULL); + if(backlight_file == NULL) { + printk(KERN_ERR "Failed to crate /proc/%s\n", filename); + return -ENOMEM; + } + + backlight_file->read_proc = proc_read_backlight; + backlight_file->write_proc = proc_write_backlight; + backlight_file->owner = THIS_MODULE; + + return 0; +} +#else +#define create_proc_file(x) +#endif /* CONFIG_PROC_FS */ + +static void s1dfb_netbookpro_suspend(struct s1dfb *fb) +{ + netbookpro_backlight_off(fb); +} + +static void s1dfb_netbookpro_resume(struct s1dfb *fb) +{ + netbookpro_backlight_on(fb, backlight_level); + +} + +static struct s1dfb_mach nbp_machine = { + .name = "netbookpro", + .blank = s1dfb_netbookrpo_blank, + .suspend = s1dfb_netbookpro_suspend, + .resume = s1dfb_netbookpro_resume, +}; + +int +s1dfb_netbookpro_init(struct s1dfb *sdfb) +{ + printk(PFX "s1d13806: initialsing netbookpro settings\n"); + + save_fb = sdfb; + + s1d13xxx_set_machine(sdfb, &nbp_machine); + s1d13xxx_runinit(sdfb, &init_netbookpro); + + netbookpro_backlight_on(sdfb, 32-1); + + create_proc_file("driver/backlight"); + + return 0; +} + diff -NaurbB linux-2.6.9-pre1-bk18/fs/cifs/.cvsignoire linux-2.6.9_nbp/fs/cifs/.cvsignoire --- linux-2.6.9-pre1-bk18/fs/cifs/.cvsignoire 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/fs/cifs/.cvsignoire 2004-09-27 03:47:57.000000000 +0200 @@ -0,0 +1,3 @@ +*.o.cmd +*.ko +*.ko.cmd diff -NaurbB linux-2.6.9-pre1-bk18/fs/jffs2/build.c linux-2.6.9_nbp/fs/jffs2/build.c --- linux-2.6.9-pre1-bk18/fs/jffs2/build.c 2004-08-14 07:36:58.000000000 +0200 +++ linux-2.6.9_nbp/fs/jffs2/build.c 2004-09-28 15:52:52.000000000 +0200 @@ -297,7 +297,11 @@ c->free_size = c->flash_size; c->nr_blocks = c->flash_size / c->sector_size; +#ifndef CONFIG_JFFS2_LARGEMEM c->blocks = kmalloc(sizeof(struct jffs2_eraseblock) * c->nr_blocks, GFP_KERNEL); +#else + c->blocks = vmalloc(sizeof(struct jffs2_eraseblock) * c->nr_blocks); +#endif if (!c->blocks) return -ENOMEM; for (i=0; inr_blocks; i++) { @@ -336,7 +340,11 @@ D1(printk(KERN_DEBUG "build_fs failed\n")); jffs2_free_ino_caches(c); jffs2_free_raw_node_refs(c); +#ifdef CONFIG_JFFS2_LARGEMEM + vfree(c->blocks); +#else kfree(c->blocks); +#endif return -EIO; } diff -NaurbB linux-2.6.9-pre1-bk18/fs/jffs2/compr.c linux-2.6.9_nbp/fs/jffs2/compr.c --- linux-2.6.9-pre1-bk18/fs/jffs2/compr.c 2004-08-14 07:36:44.000000000 +0200 +++ linux-2.6.9_nbp/fs/jffs2/compr.c 2004-09-13 12:43:28.000000000 +0200 @@ -9,7 +9,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: compr.c,v 1.41 2004/06/24 09:51:38 havasi Exp $ + * $Id: compr.c,v 1.42 2004/08/07 21:56:08 dwmw2 Exp $ * */ @@ -180,6 +180,11 @@ struct jffs2_compressor *this; int ret; + /* Older code had a bug where it would write non-zero 'usercompr' + fields. Deal with it. */ + if ((comprtype & 0xff) <= JFFS2_COMPR_ZLIB) + comprtype &= 0xff; + switch (comprtype & 0xff) { case JFFS2_COMPR_NONE: /* This should be special-cased elsewhere, but we might as well deal with it */ @@ -208,7 +213,7 @@ return ret; } } - printk(KERN_WARNING "JFFS2 compression type 0x%02x not avaiable.\n", comprtype); + printk(KERN_WARNING "JFFS2 compression type 0x%02x not available.\n", comprtype); spin_unlock(&jffs2_compressor_list_lock); return -EIO; } diff -NaurbB linux-2.6.9-pre1-bk18/fs/jffs2/compr_lzari.c linux-2.6.9_nbp/fs/jffs2/compr_lzari.c --- linux-2.6.9-pre1-bk18/fs/jffs2/compr_lzari.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/fs/jffs2/compr_lzari.c 2004-09-13 13:41:20.000000000 +0200 @@ -0,0 +1,717 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2004 Patrik Kluba, + * University of Szeged, Hungary + * + * For licensing information, see the file 'LICENCE' in the + * jffs2 directory. + * + * $Id: compr_lzari.c,v 1.3 2004/06/23 16:34:39 havasi Exp $ + * + */ + +/* + Lempel-Ziv-Arithmetic coding compression module for jffs2 + Based on the LZARI source included in LDS (lossless datacompression sources) +*/ + +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ + +/* +Original copyright follows: + +************************************************************** + LZARI.C -- A Data Compression Program + (tab = 4 spaces) +************************************************************** + 4/7/1989 Haruhiko Okumura + Use, distribute, and modify this program freely. + Please send me your improved versions. + PC-VAN SCIENCE + NIFTY-Serve PAF01022 + CompuServe 74050,1022 +************************************************************** + +LZARI.C (c)1989 by Haruyasu Yoshizaki, Haruhiko Okumura, and Kenji Rikitake. +All rights reserved. Permission granted for non-commercial use. + +*/ + +/* + + 2004-02-18 pajko + Removed unused variables and fixed no return value + + 2004-02-16 pajko + Initial release + +*/ + +/* lzari.c */ + +#define N 4096 /* size of ring buffer */ +#define F 60 /* upper limit for match_length */ +#define THRESHOLD 2 /* encode string into position and length + if match_length is greater than this */ +#define NIL N /* index for root of binary search trees */ + +static unsigned char + text_buf[N + F - 1]; /* ring buffer of size N, + with extra F-1 bytes to facilitate string comparison */ +static unsigned long match_position, match_length, /* of longest match. These are + set by the InsertNode() procedure. */ + lson[N + 1], rson[N + 257], dad[N + 1]; /* left & right children & + parents -- These constitute binary search trees. */ + +static void InitTree(void) /* Initialize trees */ +{ + unsigned long i; + + /* For i = 0 to N - 1, rson[i] and lson[i] will be the right and + left children of node i. These nodes need not be initialized. + Also, dad[i] is the parent of node i. These are initialized to + NIL (= N), which stands for 'not used.' + For i = 0 to 255, rson[N + i + 1] is the root of the tree + for strings that begin with character i. These are initialized + to NIL. Note there are 256 trees. */ + + for (i = N + 1; i <= N + 256; i++) rson[i] = NIL; /* root */ + for (i = 0; i < N; i++) dad[i] = NIL; /* node */ +} + +static void InsertNode(unsigned long r) + /* Inserts string of length F, text_buf[r..r+F-1], into one of the + trees (text_buf[r]'th tree) and returns the longest-match position + and length via the global variables match_position and match_length. + If match_length = F, then removes the old node in favor of the new + one, because the old one will be deleted sooner. + Note r plays double role, as tree node and position in buffer. */ +{ + unsigned long i, p, temp; + unsigned char *key; + signed long cmp; + + cmp = 1; key = &text_buf[r]; p = N + 1 + key[0]; + rson[r] = lson[r] = NIL; match_length = 0; + for ( ; ; ) { + if (cmp >= 0) { + if (rson[p] != NIL) p = rson[p]; + else { rson[p] = r; dad[r] = p; return; } + } else { + if (lson[p] != NIL) p = lson[p]; + else { lson[p] = r; dad[r] = p; return; } + } + for (i = 1; i < F; i++) + if ((cmp = key[i] - text_buf[p + i]) != 0) break; + if (i > THRESHOLD) { + if (i > match_length) { + match_position = (r - p) & (N - 1); + if ((match_length = i) >= F) break; + } else if (i == match_length) { + if ((temp = (r - p) & (N - 1)) < match_position) + match_position = temp; + } + } + } + dad[r] = dad[p]; lson[r] = lson[p]; rson[r] = rson[p]; + dad[lson[p]] = r; dad[rson[p]] = r; + if (rson[dad[p]] == p) rson[dad[p]] = r; + else lson[dad[p]] = r; + dad[p] = NIL; /* remove p */ +} + +static void DeleteNode(unsigned long p) /* Delete node p from tree */ +{ + unsigned long q; + + if (dad[p] == NIL) return; /* not in tree */ + if (rson[p] == NIL) q = lson[p]; + else if (lson[p] == NIL) q = rson[p]; + else { + q = lson[p]; + if (rson[q] != NIL) { + do { q = rson[q]; } while (rson[q] != NIL); + rson[dad[q]] = lson[q]; dad[lson[q]] = dad[q]; + lson[q] = lson[p]; dad[lson[p]] = q; + } + rson[q] = rson[p]; dad[rson[p]] = q; + } + dad[q] = dad[p]; + if (rson[dad[p]] == p) rson[dad[p]] = q; + else lson[dad[p]] = q; + dad[p] = NIL; +} + +/********** Arithmetic Compression **********/ + +/* If you are not familiar with arithmetic compression, you should read + I. E. Witten, R. M. Neal, and J. G. Cleary, + Communications of the ACM, Vol. 30, pp. 520-540 (1987), + from which much have been borrowed. */ + +#define M 15 + +/* Q1 (= 2 to the M) must be sufficiently large, but not so + large as the unsigned long 4 * Q1 * (Q1 - 1) overflows. */ + +#define Q1 (1UL << M) +#define Q2 (2 * Q1) +#define Q3 (3 * Q1) +#define Q4 (4 * Q1) +#define MAX_CUM (Q1 - 1) + +#define N_CHAR (256 - THRESHOLD + F) + /* character code = 0, 1, ..., N_CHAR - 1 */ + +static unsigned long char_to_sym[N_CHAR], sym_to_char[N_CHAR + 1]; +static unsigned long + sym_freq[N_CHAR + 1], /* frequency for symbols */ + sym_cum[N_CHAR + 1], /* cumulative freq for symbols */ + position_cum[N + 1]; /* cumulative freq for positions */ + +static void StartModel(void) /* Initialize model */ +{ + unsigned long ch, sym, i; + + sym_cum[N_CHAR] = 0; + for (sym = N_CHAR; sym >= 1; sym--) { + ch = sym - 1; + char_to_sym[ch] = sym; sym_to_char[sym] = ch; + sym_freq[sym] = 1; + sym_cum[sym - 1] = sym_cum[sym] + sym_freq[sym]; + } + sym_freq[0] = 0; /* sentinel (!= sym_freq[1]) */ + position_cum[N] = 0; + for (i = N; i >= 1; i--) + position_cum[i - 1] = position_cum[i] + 10000 / (i + 200); + /* empirical distribution function (quite tentative) */ + /* Please devise a better mechanism! */ +} + +static void UpdateModel(unsigned long sym) +{ + unsigned long c, ch_i, ch_sym; + unsigned long i; + if (sym_cum[0] >= MAX_CUM) { + c = 0; + for (i = N_CHAR; i > 0; i--) { + sym_cum[i] = c; + c += (sym_freq[i] = (sym_freq[i] + 1) >> 1); + } + sym_cum[0] = c; + } + for (i = sym; sym_freq[i] == sym_freq[i - 1]; i--) ; + if (i < sym) { + ch_i = sym_to_char[i]; ch_sym = sym_to_char[sym]; + sym_to_char[i] = ch_sym; sym_to_char[sym] = ch_i; + char_to_sym[ch_i] = sym; char_to_sym[ch_sym] = i; + } + sym_freq[i]++; + while (--i > 0) sym_cum[i]++; + sym_cum[0]++; +} + +static unsigned long BinarySearchSym(unsigned long x) + /* 1 if x >= sym_cum[1], + N_CHAR if sym_cum[N_CHAR] > x, + i such that sym_cum[i - 1] > x >= sym_cum[i] otherwise */ +{ + unsigned long i, j, k; + + i = 1; j = N_CHAR; + while (i < j) { + k = (i + j) / 2; + if (sym_cum[k] > x) i = k + 1; else j = k; + } + return i; +} + +unsigned long BinarySearchPos(unsigned long x) + /* 0 if x >= position_cum[1], + N - 1 if position_cum[N] > x, + i such that position_cum[i] > x >= position_cum[i + 1] otherwise */ +{ + unsigned long i, j, k; + + i = 1; j = N; + while (i < j) { + k = (i + j) / 2; + if (position_cum[k] > x) i = k + 1; else j = k; + } + return i - 1; +} + +/* modified for block compression */ +/* on return, srclen will contain the number of successfully compressed bytes + and dstlen will contain completed compressed bytes */ + +static int Encode(unsigned char *srcbuf, unsigned char *dstbuf, unsigned long *srclen, + unsigned long *dstlen) +{ + unsigned long c, i, len, r, s, last_match_length, sym, range; + unsigned long low = 0; + unsigned long high = Q4; + unsigned long shifts = 0; /* counts for magnifying low and high around Q2 */ + unsigned char *ip, *op; + unsigned long written = 0; + unsigned long read = 0; + unsigned char buffer = 0; + unsigned char mask = 128; + unsigned char *srcend = srcbuf + *srclen; + unsigned char *dstend = dstbuf + *dstlen; + ip = srcbuf; + op = dstbuf; + StartModel(); + InitTree(); /* initialize trees */ + s = 0; r = N - F; + for (i = s; i < r; i++) text_buf[i] = ' '; /* Clear the buffer with + any character that will appear often. */ + for (len = 0; (len < F) && (ip < srcend); len++) + text_buf[r + len] = *(ip++); /* Read F bytes into the last F bytes of + the buffer */ + read = len; + for (i = 1; i <= F; i++) InsertNode(r - i); /* Insert the F strings, + each of which begins with one or more 'space' characters. Note + the order in which these strings are inserted. This way, + degenerate trees will be less likely to occur. */ + InsertNode(r); /* Finally, insert the whole string just read. The + global variables match_length and match_position are set. */ + do { + if (match_length > len) match_length = len; /* match_length + may be spuriously long near the end of text. */ + if (match_length <= THRESHOLD) { + match_length = 1; /* Not long enough match. Send one byte. */ + sym = char_to_sym[text_buf[r]]; + range = high - low; + high = low + (range * sym_cum[sym - 1]) / sym_cum[0]; + low += (range * sym_cum[sym ]) / sym_cum[0]; + for ( ; ; ) { + if (high <= Q2) { + if ((mask >>= 1) == 0) { + if (op >= dstend) { + *dstlen = written; + return -1; + } + *(op++) = buffer; + buffer = 0; + mask = 128; + written++; + *srclen = read; + } + for ( ; shifts > 0; shifts--) { + buffer |= mask; + if ((mask >>= 1) == 0) { + if (op >= dstend) { + *dstlen = written; + return -1; + } + *(op++) = buffer; + buffer = 0; + mask = 128; + written++; + *srclen = read; + } + } + } else if (low >= Q2) { + buffer |= mask; + if ((mask >>= 1) == 0) { + if (op >= dstend) { + *dstlen = written; + return -1; + } + *(op++) = buffer; + buffer = 0; + mask = 128; + written++; + *srclen = read; + } + for ( ; shifts > 0; shifts--) { + if ((mask >>= 1) == 0) { + if (op >= dstend) { + *dstlen = written; + return -1; + } + *(op++) = buffer; + buffer = 0; + mask = 128; + written++; + *srclen = read; + } + } + low -= Q2; + high -= Q2; + } else if (low >= Q1 && high <= Q3) { + shifts++; + low -= Q1; + high -= Q1; + } else break; + low += low; high += high; + } + UpdateModel(sym); + } else { + sym = char_to_sym[255 - THRESHOLD + match_length]; + range = high - low; + high = low + (range * sym_cum[sym - 1]) / sym_cum[0]; + low += (range * sym_cum[sym ]) / sym_cum[0]; + for ( ; ; ) { + if (high <= Q2) { + if ((mask >>= 1) == 0) { + if (op >= dstend) { + *dstlen = written; + return -1; + } + *(op++) = buffer; + buffer = 0; + mask = 128; + written++; + *srclen = read; + } + for ( ; shifts > 0; shifts--) { + buffer |= mask; + if ((mask >>= 1) == 0) { + if (op >= dstend) { + *dstlen = written; + return -1; + } + *(op++) = buffer; + buffer = 0; + mask = 128; + written++; + *srclen = read; + } + } + } else if (low >= Q2) { + buffer |= mask; + if ((mask >>= 1) == 0) { + if (op >= dstend) { + *dstlen = written; + return -1; + } + *(op++) = buffer; + buffer = 0; + mask = 128; + written++; + *srclen = read; + } + for ( ; shifts > 0; shifts--) { + if ((mask >>= 1) == 0) { + if (op >= dstend) { + *dstlen = written; + return -1; + } + *(op++) = buffer; + buffer = 0; + mask = 128; + written++; + *srclen = read; + } + } + low -= Q2; + high -= Q2; + } else if (low >= Q1 && high <= Q3) { + shifts++; + low -= Q1; + high -= Q1; + } else break; + low += low; high += high; + } + UpdateModel(sym); + range = high - low; + high = low + (range * position_cum[match_position - 1]) / position_cum[0]; + low += (range * position_cum[match_position ]) / position_cum[0]; + for ( ; ; ) { + if (high <= Q2) { + if ((mask >>= 1) == 0) { + if (op >= dstend) { + *dstlen = written; + return -1; + } + *(op++) = buffer; + buffer = 0; + mask = 128; + written++; + *srclen = read; + } + for ( ; shifts > 0; shifts--) { + buffer |= mask; + if ((mask >>= 1) == 0) { + if (op >= dstend) { + *dstlen = written; + return -1; + } + *(op++) = buffer; + buffer = 0; + mask = 128; + written++; + *srclen = read; + } + } + } else { + if (low >= Q2) { + buffer |= mask; + if ((mask >>= 1) == 0) { + if (op >= dstend) { + *dstlen = written; + return -1; + } + *(op++) = buffer; + buffer = 0; + mask = 128; + written++; + *srclen = read; + } + for ( ; shifts > 0; shifts--) { + if ((mask >>= 1) == 0) { + if (op >= dstend) { + *dstlen = written; + return -1; + } + *(op++) = buffer; + buffer = 0; + mask = 128; + written++; + *srclen = read; + } + } + low -= Q2; + high -= Q2; + } else { + if ((low >= Q1) && (high <= Q3)) { + shifts++; + low -= Q1; + high -= Q1; + } else { + break; + } + } + } + low += low; + high += high; + } + } + last_match_length = match_length; + for (i = 0; (i < last_match_length) && (ip < srcend); i++) { + c = *(ip++); + DeleteNode(s); + text_buf[s] = c; + if (s < F - 1) + text_buf[s + N] = c; + s = (s + 1) & (N - 1); + r = (r + 1) & (N - 1); + InsertNode(r); + } + read += i; + while (i++ < last_match_length) { + DeleteNode(s); + s = (s + 1) & (N - 1); + r = (r + 1) & (N - 1); + if (--len) InsertNode(r); + } + } while (len > 0); + shifts++; + if (low < Q1) { + if ((mask >>= 1) == 0) { + if (op >= dstend) { + *dstlen = written; + return -1; + } + *(op++) = buffer; + buffer = 0; + mask = 128; + written++; + *srclen = read; + } + for ( ; shifts > 0; shifts--) { + buffer |= mask; + if ((mask >>= 1) == 0) { + if (op >= dstend) { + *dstlen = written; + return -1; + } + *(op++) = buffer; + buffer = 0; + mask = 128; + written++; + *srclen = read; + } + } + } else { + buffer |= mask; + if ((mask >>= 1) == 0) { + if (op >= dstend) { + *dstlen = written; + return -1; + } + *(op++) = buffer; + buffer = 0; + mask = 128; + written++; + *srclen = read; + } + for ( ; shifts > 0; shifts--) { + if ((mask >>= 1) == 0) { + if (op >= dstend) { + *dstlen = written; + return -1; + } + *(op++) = buffer; + buffer = 0; + mask = 128; + written++; + *srclen = read; + } + } + } + for (i = 0; i < 7; i++) { + if ((mask >>= 1) == 0) { + if (op >= dstend) { + *dstlen = written; + return -1; + } + *(op++) = buffer; + buffer = 0; + mask = 128; + written++; + *srclen = read; + } + } + *dstlen = written; + return 0; +} + +static int Decode(unsigned char *srcbuf, unsigned char *dstbuf, unsigned long srclen, + unsigned long dstlen) /* Just the reverse of Encode(). */ +{ + unsigned long i, r, j, k, c, range, sym; + unsigned char *ip, *op; + unsigned char *srcend = srcbuf + srclen; + unsigned char *dstend = dstbuf + dstlen; + unsigned char buffer = 0; + unsigned char mask = 0; + unsigned long low = 0; + unsigned long high = Q4; + unsigned long value = 0; + ip = srcbuf; + op = dstbuf; + for (i = 0; i < M + 2; i++) { + value *= 2; + if ((mask >>= 1) == 0) { + buffer = (ip >= srcend) ? 0 : *(ip++); + mask = 128; + } + value += ((buffer & mask) != 0); + } + StartModel(); + for (i = 0; i < N - F; i++) text_buf[i] = ' '; + r = N - F; + while (op < dstend) { + range = high - low; + sym = BinarySearchSym((unsigned long) + (((value - low + 1) * sym_cum[0] - 1) / range)); + high = low + (range * sym_cum[sym - 1]) / sym_cum[0]; + low += (range * sym_cum[sym ]) / sym_cum[0]; + for ( ; ; ) { + if (low >= Q2) { + value -= Q2; low -= Q2; high -= Q2; + } else if (low >= Q1 && high <= Q3) { + value -= Q1; low -= Q1; high -= Q1; + } else if (high > Q2) break; + low += low; high += high; + value *= 2; + if ((mask >>= 1) == 0) { + buffer = (ip >= srcend) ? 0 : *(ip++); + mask = 128; + } + value += ((buffer & mask) != 0); + } + c = sym_to_char[sym]; + UpdateModel(sym); + if (c < 256) { + if (op >= dstend) return -1; + *(op++) = c; + text_buf[r++] = c; + r &= (N - 1); + } else { + j = c - 255 + THRESHOLD; + range = high - low; + i = BinarySearchPos((unsigned long) + (((value - low + 1) * position_cum[0] - 1) / range)); + high = low + (range * position_cum[i ]) / position_cum[0]; + low += (range * position_cum[i + 1]) / position_cum[0]; + for ( ; ; ) { + if (low >= Q2) { + value -= Q2; low -= Q2; high -= Q2; + } else if (low >= Q1 && high <= Q3) { + value -= Q1; low -= Q1; high -= Q1; + } else if (high > Q2) break; + low += low; high += high; + value *= 2; + if ((mask >>= 1) == 0) { + buffer = (ip >= srcend) ? 0 : *(ip++); + mask = 128; + } + value += ((buffer & mask) != 0); + } + i = (r - i - 1) & (N - 1); + for (k = 0; k < j; k++) { + c = text_buf[(i + k) & (N - 1)]; + if (op >= dstend) return -1; + *(op++) = c; + text_buf[r++] = c; + r &= (N - 1); + } + } + } + return 0; +} + +/* interface to jffs2 follows */ + +#include "compr.h" +#include + +int jffs2_lzari_compress (unsigned char *input, + unsigned char *output, uint32_t *sourcelen, + uint32_t *dstlen, void *model); + +int jffs2_lzari_decompress (unsigned char *input, + unsigned char *output, uint32_t sourcelen, + uint32_t dstlen, void *model); + +struct jffs2_compressor jffs2_lzari_comp = { + .priority = JFFS2_LZARI_PRIORITY, + .name = "lzari", + .compr = JFFS2_COMPR_LZARI, + .compress = &jffs2_lzari_compress, + .decompress = &jffs2_lzari_decompress, +#ifdef JFFS2_LZARI_DISABLED + .disabled = 1, +#else + .disabled = 0, +#endif +}; + +int jffs2_lzari_compress (unsigned char *input, + unsigned char *output, uint32_t *sourcelen, + uint32_t *dstlen, void *model) +{ + return Encode(input, output, (unsigned long *)sourcelen, (unsigned long *)dstlen); +} + +int jffs2_lzari_decompress (unsigned char *input, + unsigned char *output, uint32_t sourcelen, + uint32_t dstlen, void *model) +{ + return Decode(input, output, sourcelen, dstlen); +} + +int jffs2_lzari_init (void) +{ + return jffs2_register_compressor(&jffs2_lzari_comp); +} + +void jffs2_lzari_exit (void) +{ + jffs2_unregister_compressor (&jffs2_lzari_comp); +} diff -NaurbB linux-2.6.9-pre1-bk18/fs/jffs2/compr_lzo.c linux-2.6.9_nbp/fs/jffs2/compr_lzo.c --- linux-2.6.9-pre1-bk18/fs/jffs2/compr_lzo.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/fs/jffs2/compr_lzo.c 2004-09-13 13:41:20.000000000 +0200 @@ -0,0 +1,2329 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2004 Patrik Kluba, + * University of Szeged, Hungary + * + * For licensing information, see the file 'LICENCE' in the + * jffs2 directory. + * + * $Id: compr_lzo.c,v 1.3 2004/06/23 16:34:39 havasi Exp $ + * + */ + +/* + LZO1X-1 (and -999) compression module for jffs2 + based on the original LZO sources +*/ + +/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */ + +/* + Original copyright notice follows: + + lzo1x_9x.c -- implementation of the LZO1X-999 compression algorithm + lzo_ptr.h -- low-level pointer constructs + lzo_swd.ch -- sliding window dictionary + lzoconf.h -- configuration for the LZO real-time data compression library + lzo_mchw.ch -- matching functions using a window + minilzo.c -- mini subset of the LZO real-time data compression library + config1x.h -- configuration for the LZO1X algorithm + lzo1x.h -- public interface of the LZO1X compression algorithm + + These files are part of the LZO real-time data compression library. + + Copyright (C) 1996-2002 Markus Franz Xaver Johannes Oberhumer + All Rights Reserved. + + The LZO library 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. + + The LZO library 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 the LZO library; see the file COPYING. + If not, write to the Free Software Foundation, Inc., + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + + Markus F.X.J. Oberhumer + +*/ + +/* + + 2004-02-16 pajko + Initial release + -removed all 16 bit code + -all sensitive data will be on 4 byte boundary + -removed check parts for library use + -removed all but LZO1X-* compression + +*/ + +#ifndef __KERNEL__ + #include + #include + #include + #include +#else + #include + #include + #include + #include + #define USHRT_MAX 65535 + /* #define UINT_MAX 4294967295U */ +#endif + +/* data type definitions */ +#define U32 unsigned long +#define S32 signed long +#define I32 long +#define U16 unsigned short +#define S16 signed short +#define I16 short +#define U8 unsigned char +#define S8 signed char +#define I8 char + +/*************************************/ + +/* lzo_swd.ch */ + +#define SWD_N N +#define SWD_F F +#define SWD_THRESHOLD THRESHOLD + +/* shortest unsigned int that 2 * SWD_F + SWD_N (currently 53248) fits in */ +typedef unsigned short swd_uint; +/* upper limit of that data type */ +#define SWD_UINT_MAX USHRT_MAX + +/* minilzo.c */ + +#define LZO_VERSION_DATE "Jul 12 2002" +#define LZO_VERSION_STRING "1.08" +#define LZO_VERSION 0x1080 + +/* lzo_ptr.h */ + +/* Integral types that have *exactly* the same number of bits as a lzo_voidp */ +typedef unsigned long lzo_ptr_t; +typedef long lzo_sptr_t; + + +/*************************************/ + +/* config1x.h */ + +#define M1_MAX_OFFSET 0x0400 +#define M2_MAX_OFFSET 0x0800 +#define M3_MAX_OFFSET 0x4000 +#define M4_MAX_OFFSET 0xbfff + +#define MX_MAX_OFFSET (M1_MAX_OFFSET + M2_MAX_OFFSET) + +#define M1_MIN_LEN 2 +#define M1_MAX_LEN 2 +#define M2_MIN_LEN 3 +#define M2_MAX_LEN 8 +#define M3_MIN_LEN 3 +#define M3_MAX_LEN 33 +#define M4_MIN_LEN 3 +#define M4_MAX_LEN 9 + +#define M1_MARKER 0 +#define M2_MARKER 64 +#define M3_MARKER 32 +#define M4_MARKER 16 + +#define MIN_LOOKAHEAD (M2_MAX_LEN + 1) + +/* minilzo.c */ + +#define LZO_BYTE(x) ((unsigned char) ((x) & 0xff)) + +#define LZO_MAX(a,b) ((a) >= (b) ? (a) : (b)) +#define LZO_MIN(a,b) ((a) <= (b) ? (a) : (b)) +#define LZO_MAX3(a,b,c) ((a) >= (b) ? LZO_MAX(a,c) : LZO_MAX(b,c)) +#define LZO_MIN3(a,b,c) ((a) <= (b) ? LZO_MIN(a,c) : LZO_MIN(b,c)) + +#define lzo_sizeof(type) ((lzo_uint) (sizeof(type))) + +#define LZO_HIGH(array) ((lzo_uint) (sizeof(array)/sizeof(*(array)))) + +#define LZO_SIZE(bits) (1u << (bits)) +#define LZO_MASK(bits) (LZO_SIZE(bits) - 1) + +#define LZO_LSIZE(bits) (1ul << (bits)) +#define LZO_LMASK(bits) (LZO_LSIZE(bits) - 1) + +#define LZO_USIZE(bits) ((lzo_uint) 1 << (bits)) +#define LZO_UMASK(bits) (LZO_USIZE(bits) - 1) + +#define LZO_STYPE_MAX(b) (((1l << (8*(b)-2)) - 1l) + (1l << (8*(b)-2))) +#define LZO_UTYPE_MAX(b) (((1ul << (8*(b)-1)) - 1ul) + (1ul << (8*(b)-1))) + +#define _LZO_STRINGIZE(x) #x +#define _LZO_MEXPAND(x) _LZO_STRINGIZE(x) + +#define _LZO_CONCAT2(a,b) a ## b +#define _LZO_CONCAT3(a,b,c) a ## b ## c +#define _LZO_CONCAT4(a,b,c,d) a ## b ## c ## d +#define _LZO_CONCAT5(a,b,c,d,e) a ## b ## c ## d ## e + +#define _LZO_ECONCAT2(a,b) _LZO_CONCAT2(a,b) +#define _LZO_ECONCAT3(a,b,c) _LZO_CONCAT3(a,b,c) +#define _LZO_ECONCAT4(a,b,c,d) _LZO_CONCAT4(a,b,c,d) +#define _LZO_ECONCAT5(a,b,c,d,e) _LZO_CONCAT5(a,b,c,d,e) + +#define lzo_dict_t const lzo_bytep +#define lzo_dict_p lzo_dict_t * +#define lzo_moff_t lzo_uint + +#define MEMCPY8_DS(dest,src,len) \ + memcpy(dest,src,len); \ + dest += len; \ + src += len + +#define MEMCPY_DS(dest,src,len) \ + do *dest++ = *src++; \ + while (--len > 0) + +#define MEMMOVE_DS(dest,src,len) \ + do *dest++ = *src++; \ + while (--len > 0) + +#define BZERO8_PTR(s,l,n) memset((s),0,(lzo_uint)(l)*(n)) + +#define LZO_BASE 65521u +#define LZO_NMAX 5552 + +#define LZO_DO1(buf,i) {s1 += buf[i]; s2 += s1;} +#define LZO_DO2(buf,i) LZO_DO1(buf,i); LZO_DO1(buf,i+1); +#define LZO_DO4(buf,i) LZO_DO2(buf,i); LZO_DO2(buf,i+2); +#define LZO_DO8(buf,i) LZO_DO4(buf,i); LZO_DO4(buf,i+4); +#define LZO_DO16(buf,i) LZO_DO8(buf,i); LZO_DO8(buf,i+8); + +#define IS_SIGNED(type) (((type) (-1)) < ((type) 0)) +#define IS_UNSIGNED(type) (((type) (-1)) > ((type) 0)) + +#define IS_POWER_OF_2(x) (((x) & ((x) - 1)) == 0) + +#define D_BITS 14 +#define D_INDEX1(d,p) d = DM((0x21*DX3(p,5,5,6)) >> 5) +#define D_INDEX2(d,p) d = (d & (D_MASK & 0x7ff)) ^ (D_HIGH | 0x1f) + +#define LZO_HASH LZO_HASH_LZO_INCREMENTAL_B + +#define DL_MIN_LEN M2_MIN_LEN + +#define D_SIZE LZO_SIZE(D_BITS) +#define D_MASK LZO_MASK(D_BITS) + +#define D_HIGH ((D_MASK >> 1) + 1) + +#define DINDEX1 D_INDEX1 +#define DINDEX2 D_INDEX2 + +#define DX2(p,s1,s2) \ + (((((lzo_uint32)((p)[2]) << (s2)) ^ (p)[1]) << (s1)) ^ (p)[0]) + +#define DX3(p,s1,s2,s3) ((DX2((p)+1,s2,s3) << (s1)) ^ (p)[0]) +#define DMS(v,s) ((lzo_uint) (((v) & (D_MASK >> (s))) << (s))) +#define DM(v) DMS(v,0) + +#define DENTRY(p,in) (p) +#define GINDEX(m_pos,m_off,dict,dindex,in) m_pos = dict[dindex] + +#define LZO_CHECK_MPOS_DET(m_pos,m_off,in,ip,max_offset) \ + (m_pos == NULL || (m_off = (lzo_moff_t) (ip - m_pos)) > max_offset) + +#define LZO_CHECK_MPOS_NON_DET(m_pos,m_off,in,ip,max_offset) \ + (BOUNDS_CHECKING_OFF_IN_EXPR( \ + (PTR_LT(m_pos,in) || \ + (m_off = (lzo_moff_t) PTR_DIFF(ip,m_pos)) <= 0 || \ + m_off > max_offset) )) + +#define BOUNDS_CHECKING_OFF_IN_EXPR(expr) (expr) + +#define DD_BITS 0 +#define DD_SIZE LZO_SIZE(DD_BITS) +#define DD_MASK LZO_MASK(DD_BITS) + +#define DL_BITS (D_BITS - DD_BITS) +#define DL_SIZE LZO_SIZE(DL_BITS) +#define DL_MASK LZO_MASK(DL_BITS) + +#define UPDATE_D(dict,drun,dv,p,in) dict[ DINDEX(dv,p) ] = DENTRY(p,in) +#define UPDATE_I(dict,drun,index,p,in) dict[index] = DENTRY(p,in) +#define UPDATE_P(ptr,drun,p,in) (ptr)[0] = DENTRY(p,in) + +#define __COPY4(dst,src) * (lzo_uint32p)(dst) = * (const lzo_uint32p)(src) +#define COPY4(dst,src) __COPY4((lzo_ptr_t)(dst),(lzo_ptr_t)(src)) + +#define TEST_IP (ip < ip_end) +#define TEST_OP (op <= op_end) + +#define NEED_IP(x) \ + if ((lzo_uint)(ip_end - ip) < (lzo_uint)(x)) goto input_overrun +#define NEED_OP(x) \ + if ((lzo_uint)(op_end - op) < (lzo_uint)(x)) goto output_overrun +#define TEST_LOOKBEHIND(m_pos,out) if (m_pos < out) goto lookbehind_overrun + +/* lzo1x_9x.c */ + +#define LZO_UINT_MAX UINT_MAX +#define N M4_MAX_OFFSET +#define THRESHOLD 1 +#define F 2048 + +#define SWD_BEST_OFF (LZO_MAX3( M2_MAX_LEN, M3_MAX_LEN, M4_MAX_LEN ) + 1) + +/* ../include/lzoconf.h */ + +typedef U32 lzo_uint32; +typedef I32 lzo_int32; +typedef U32 lzo_uint; +typedef I32 lzo_int; +typedef int lzo_bool; + +#define lzo_byte U8 +#define lzo_bytep U8 * +#define lzo_charp char * +#define lzo_voidp void * +#define lzo_shortp short * +#define lzo_ushortp unsigned short * +#define lzo_uint32p lzo_uint32 * +#define lzo_int32p lzo_int32 * +#define lzo_uintp lzo_uint * +#define lzo_intp lzo_int * +#define lzo_voidpp lzo_voidp * +#define lzo_bytepp lzo_bytep * +#define lzo_sizeof_dict_t sizeof(lzo_bytep) + +#define LZO_E_OK 0 +#define LZO_E_ERROR (-1) +#define LZO_E_OUT_OF_MEMORY (-2) /* not used right now */ +#define LZO_E_NOT_COMPRESSIBLE (-3) /* not used right now */ +#define LZO_E_INPUT_OVERRUN (-4) +#define LZO_E_OUTPUT_OVERRUN (-5) +#define LZO_E_LOOKBEHIND_OVERRUN (-6) +#define LZO_E_EOF_NOT_FOUND (-7) +#define LZO_E_INPUT_NOT_CONSUMED (-8) + +#define LZO_PTR_ALIGN_UP(_ptr,_size) \ + ((_ptr) + (lzo_uint) __lzo_align_gap((const lzo_voidp)(_ptr),(lzo_uint)(_size))) +#define LZO_ALIGN(_ptr,_size) LZO_PTR_ALIGN_UP(_ptr,_size) + +typedef int + (*lzo_compress_t) (const lzo_byte * src, lzo_uint src_len, + lzo_byte * dst, lzo_uintp dst_len, + lzo_voidp wrkmem); + +typedef int + (*lzo_decompress_t) (const lzo_byte * src, lzo_uint src_len, + lzo_byte * dst, lzo_uintp dst_len, + lzo_voidp wrkmem); + +typedef int + (*lzo_optimize_t) (lzo_byte * src, lzo_uint src_len, + lzo_byte * dst, lzo_uintp dst_len, + lzo_voidp wrkmem); + +typedef int + (*lzo_compress_dict_t) (const lzo_byte * src, lzo_uint src_len, + lzo_byte * dst, lzo_uintp dst_len, + lzo_voidp wrkmem, + const lzo_byte * dict, lzo_uint dict_len); + +typedef int + (*lzo_decompress_dict_t) (const lzo_byte * src, lzo_uint src_len, + lzo_byte * dst, lzo_uintp dst_len, + lzo_voidp wrkmem, + const lzo_byte * dict, lzo_uint dict_len); + +typedef int + (*lzo_compress_asm_t) (const lzo_byte * src, lzo_uint src_len, + lzo_byte * dst, lzo_uintp dst_len, + lzo_voidp wrkmem); + +typedef int + (*lzo_decompress_asm_t) (const lzo_byte * src, lzo_uint src_len, + lzo_byte * dst, lzo_uintp dst_len, + lzo_voidp wrkmem); + +typedef void (*lzo_progress_callback_t) (lzo_uint, lzo_uint); + +typedef union +{ + lzo_bytep p; + lzo_uint u; +} __lzo_pu_u; +typedef union +{ + lzo_bytep p; + lzo_uint32 u32; +} __lzo_pu32_u; +typedef union +{ + void *vp; + lzo_bytep bp; + lzo_uint32 u32; + long l; +} lzo_align_t; + +/* lzo1x.h */ + +#define LZO1X_1_MEM_COMPRESS ((lzo_uint32) (16384L * lzo_sizeof_dict_t)) +#define LZO1X_999_MEM_COMPRESS ((lzo_uint32) (14 * 16384L * sizeof(short))) + +/* lzo_ptr.h */ + +#define PTR(a) ((lzo_ptr_t) (a)) +#define PTR_LINEAR(a) PTR(a) +#define PTR_ALIGNED_4(a) ((PTR_LINEAR(a) & 3) == 0) +#define PTR_ALIGNED_8(a) ((PTR_LINEAR(a) & 7) == 0) +#define PTR_ALIGNED2_4(a,b) (((PTR_LINEAR(a) | PTR_LINEAR(b)) & 3) == 0) +#define PTR_ALIGNED2_8(a,b) (((PTR_LINEAR(a) | PTR_LINEAR(b)) & 7) == 0) +#define PTR_LT(a,b) (PTR(a) < PTR(b)) +#define PTR_GE(a,b) (PTR(a) >= PTR(b)) +#define PTR_DIFF(a,b) ((lzo_ptrdiff_t) (PTR(a) - PTR(b))) +#define pd(a,b) ((lzo_uint) ((a)-(b))) + +typedef ptrdiff_t lzo_ptrdiff_t; + +typedef union +{ + char a_char; + unsigned char a_uchar; + short a_short; + unsigned short a_ushort; + int a_int; + unsigned int a_uint; + long a_long; + unsigned long a_ulong; + lzo_int a_lzo_int; + lzo_uint a_lzo_uint; + lzo_int32 a_lzo_int32; + lzo_uint32 a_lzo_uint32; + ptrdiff_t a_ptrdiff_t; + lzo_ptrdiff_t a_lzo_ptrdiff_t; + lzo_ptr_t a_lzo_ptr_t; + lzo_voidp a_lzo_voidp; + void *a_void_p; + lzo_bytep a_lzo_bytep; + lzo_bytepp a_lzo_bytepp; + lzo_uintp a_lzo_uintp; + lzo_uint *a_lzo_uint_p; + lzo_uint32p a_lzo_uint32p; + lzo_uint32 *a_lzo_uint32_p; + unsigned char *a_uchar_p; + char *a_char_p; +} +lzo_full_align_t; + +/* lzo_mchw.ch */ + +typedef struct +{ + int init; + + lzo_uint look; + + lzo_uint m_len; + lzo_uint m_off; + + lzo_uint last_m_len; + lzo_uint last_m_off; + + const lzo_byte *bp; + const lzo_byte *ip; + const lzo_byte *in; + const lzo_byte *in_end; + lzo_byte *out; + + lzo_progress_callback_t cb; + + lzo_uint textsize; + lzo_uint codesize; + lzo_uint printcount; + + unsigned long lit_bytes; + unsigned long match_bytes; + unsigned long rep_bytes; + unsigned long lazy; + + lzo_uint r1_lit; + lzo_uint r1_m_len; + + unsigned long m1a_m, m1b_m, m2_m, m3_m, m4_m; + unsigned long lit1_r, lit2_r, lit3_r; +} +lzo1x_999_t; + +#define getbyte(c) ((c).ip < (c).in_end ? *((c).ip)++ : (-1)) + +/* lzo_swd.ch */ + +#define SWD_UINT(x) ((swd_uint)(x)) +#define SWD_HSIZE 16384 +#define SWD_MAX_CHAIN 2048 +#define HEAD3(b,p) \ + (((0x9f5f*(((((lzo_uint32)b[p]<<5)^b[p+1])<<5)^b[p+2]))>>5) & (SWD_HSIZE-1)) +#define HEAD2(b,p) (b[p] ^ ((unsigned)b[p+1]<<8)) +#define NIL2 SWD_UINT_MAX + +typedef struct +{ + lzo_uint n; + lzo_uint f; + lzo_uint threshold; + + lzo_uint max_chain; + lzo_uint nice_length; + lzo_bool use_best_off; + lzo_uint lazy_insert; + + lzo_uint m_len; + lzo_uint m_off; + lzo_uint look; + int b_char; + + lzo_uint best_off[SWD_BEST_OFF]; + + lzo1x_999_t *c; + lzo_uint m_pos; + + lzo_uint best_pos[SWD_BEST_OFF]; + + const lzo_byte *dict; + const lzo_byte *dict_end; + lzo_uint dict_len; + + lzo_uint ip; + lzo_uint bp; + lzo_uint rp; + lzo_uint b_size; + + unsigned char *b_wrap; + + lzo_uint node_count; + lzo_uint first_rp; + + unsigned char b[SWD_N + SWD_F + SWD_F]; + swd_uint head3[SWD_HSIZE]; + swd_uint succ3[SWD_N + SWD_F]; + swd_uint best3[SWD_N + SWD_F]; + swd_uint llen3[SWD_HSIZE]; + + swd_uint head2[65536L]; +} +lzo1x_999_swd_t; + +#define s_head3(s,key) s->head3[key] +#define swd_pos2off(s,pos) \ + (s->bp > (pos) ? s->bp - (pos) : s->b_size - ((pos) - s->bp)) + +static __inline__ void +swd_getbyte (lzo1x_999_swd_t * s) +{ + int c; + + if ((c = getbyte (*(s->c))) < 0) + { + if (s->look > 0) + --s->look; + } + else + { + s->b[s->ip] = LZO_BYTE (c); + if (s->ip < s->f) + s->b_wrap[s->ip] = LZO_BYTE (c); + } + if (++s->ip == s->b_size) + s->ip = 0; + if (++s->bp == s->b_size) + s->bp = 0; + if (++s->rp == s->b_size) + s->rp = 0; +} + +static void +swd_initdict (lzo1x_999_swd_t * s, const lzo_byte * dict, lzo_uint dict_len) +{ + s->dict = s->dict_end = NULL; + s->dict_len = 0; + + if (!dict || dict_len <= 0) + return; + if (dict_len > s->n) + { + dict += dict_len - s->n; + dict_len = s->n; + } + + s->dict = dict; + s->dict_len = dict_len; + s->dict_end = dict + dict_len; + memcpy (s->b, dict, dict_len); + s->ip = dict_len; +} + +static void +swd_insertdict (lzo1x_999_swd_t * s, lzo_uint node, lzo_uint len) +{ + lzo_uint key; + + s->node_count = s->n - len; + s->first_rp = node; + + while (len-- > 0) + { + key = HEAD3 (s->b, node); + s->succ3[node] = s_head3 (s, key); + s->head3[key] = SWD_UINT (node); + s->best3[node] = SWD_UINT (s->f + 1); + s->llen3[key]++; + + key = HEAD2 (s->b, node); + s->head2[key] = SWD_UINT (node); + + node++; + } +} + +static int +swd_init (lzo1x_999_swd_t * s, const lzo_byte * dict, lzo_uint dict_len) +{ + + s->n = SWD_N; + s->f = SWD_F; + s->threshold = SWD_THRESHOLD; + + + + s->max_chain = SWD_MAX_CHAIN; + s->nice_length = SWD_F; + s->use_best_off = 0; + s->lazy_insert = 0; + + s->b_size = s->n + s->f; + if (2 * s->f >= s->n || s->b_size + s->f >= NIL2) + return LZO_E_ERROR; + s->b_wrap = s->b + s->b_size; + s->node_count = s->n; + + memset (s->llen3, 0, sizeof (s->llen3[0]) * SWD_HSIZE); + memset (s->head2, 0xff, sizeof (s->head2[0]) * 65536L); + + s->ip = 0; + swd_initdict (s, dict, dict_len); + s->bp = s->ip; + s->first_rp = s->ip; + + s->look = (lzo_uint) (s->c->in_end - s->c->ip); + if (s->look > 0) + { + if (s->look > s->f) + s->look = s->f; + memcpy (&s->b[s->ip], s->c->ip, s->look); + s->c->ip += s->look; + s->ip += s->look; + } + + if (s->ip == s->b_size) + s->ip = 0; + + if (s->look >= 2 && s->dict_len > 0) + swd_insertdict (s, 0, s->dict_len); + + s->rp = s->first_rp; + if (s->rp >= s->node_count) + s->rp -= s->node_count; + else + s->rp += s->b_size - s->node_count; + + return LZO_E_OK; +} + +static __inline__ void +swd_remove_node (lzo1x_999_swd_t * s, lzo_uint node) +{ + if (s->node_count == 0) + { + lzo_uint key; + + key = HEAD3 (s->b, node); + + --s->llen3[key]; + + key = HEAD2 (s->b, node); + + if ((lzo_uint) s->head2[key] == node) + s->head2[key] = NIL2; + } + else + --s->node_count; +} + +static void +swd_accept (lzo1x_999_swd_t * s, lzo_uint n) +{ + + while (n--) + { + lzo_uint key; + + swd_remove_node (s, s->rp); + + key = HEAD3 (s->b, s->bp); + s->succ3[s->bp] = s_head3 (s, key); + s->head3[key] = SWD_UINT (s->bp); + s->best3[s->bp] = SWD_UINT (s->f + 1); + s->llen3[key]++; + + key = HEAD2 (s->b, s->bp); + s->head2[key] = SWD_UINT (s->bp);; + + swd_getbyte (s); + } +} + +static void +swd_search (lzo1x_999_swd_t * s, lzo_uint node, lzo_uint cnt) +{ + const unsigned char *p1; + const unsigned char *p2; + const unsigned char *px; + + lzo_uint m_len = s->m_len; + const unsigned char *b = s->b; + const unsigned char *bp = s->b + s->bp; + const unsigned char *bx = s->b + s->bp + s->look; + unsigned char scan_end1; + + scan_end1 = bp[m_len - 1]; + for (; cnt-- > 0; node = s->succ3[node]) + { + p1 = bp; + p2 = b + node; + px = bx; + + if (p2[m_len - 1] == scan_end1 && + p2[m_len] == p1[m_len] && + p2[0] == p1[0] && p2[1] == p1[1]) + { + lzo_uint i; + + p1 += 2; + p2 += 2; + do + { + } + while (++p1 < px && *p1 == *++p2); + + i = p1 - bp; + + if (i < SWD_BEST_OFF) + { + if (s->best_pos[i] == 0) + s->best_pos[i] = node + 1; + } + + if (i > m_len) + { + s->m_len = m_len = i; + s->m_pos = node; + if (m_len == s->look) + return; + if (m_len >= s->nice_length) + return; + if (m_len > (lzo_uint) s->best3[node]) + return; + scan_end1 = bp[m_len - 1]; + } + } + } +} + +static lzo_bool +swd_search2 (lzo1x_999_swd_t * s) +{ + lzo_uint key; + + key = s->head2[HEAD2 (s->b, s->bp)]; + if (key == NIL2) + return 0; + + if (s->best_pos[2] == 0) + s->best_pos[2] = key + 1; + + if (s->m_len < 2) + { + s->m_len = 2; + s->m_pos = key; + } + return 1; +} + +static void +swd_findbest (lzo1x_999_swd_t * s) +{ + lzo_uint key; + lzo_uint cnt, node; + lzo_uint len; + + key = HEAD3 (s->b, s->bp); + node = s->succ3[s->bp] = s_head3 (s, key); + cnt = s->llen3[key]++; + + if (cnt > s->max_chain && s->max_chain > 0) + cnt = s->max_chain; + s->head3[key] = SWD_UINT (s->bp); + + s->b_char = s->b[s->bp]; + len = s->m_len; + if (s->m_len >= s->look) + { + if (s->look == 0) + s->b_char = -1; + s->m_off = 0; + s->best3[s->bp] = SWD_UINT (s->f + 1); + } + else + { + + if (swd_search2 (s)) + + if (s->look >= 3) + swd_search (s, node, cnt); + if (s->m_len > len) + s->m_off = swd_pos2off (s, s->m_pos); + s->best3[s->bp] = SWD_UINT (s->m_len); + + if (s->use_best_off) + { + int i; + for (i = 2; i < SWD_BEST_OFF; i++) + if (s->best_pos[i] > 0) + s->best_off[i] = + swd_pos2off (s, + s->best_pos[i] - + 1); + else + s->best_off[i] = 0; + } + + } + + swd_remove_node (s, s->rp); + + key = HEAD2 (s->b, s->bp); + s->head2[key] = SWD_UINT (s->bp); + +} + +/* lzo_mchw.ch */ + +static int +init_match (lzo1x_999_t * c, lzo1x_999_swd_t * s, + const lzo_byte * dict, lzo_uint dict_len, lzo_uint32 flags) +{ + int r; + + c->init = 1; + + s->c = c; + + c->last_m_len = c->last_m_off = 0; + + c->textsize = c->codesize = c->printcount = 0; + c->lit_bytes = c->match_bytes = c->rep_bytes = 0; + c->lazy = 0; + + r = swd_init (s, dict, dict_len); + if (r != 0) + return r; + + s->use_best_off = (flags & 1) ? 1 : 0; + return r; +} + +static int +find_match (lzo1x_999_t * c, lzo1x_999_swd_t * s, + lzo_uint this_len, lzo_uint skip) +{ + if (skip > 0) + { + swd_accept (s, this_len - skip); + c->textsize += this_len - skip + 1; + } + else + { + c->textsize += this_len - skip; + } + + s->m_len = 1; + s->m_len = 1; + + if (s->use_best_off) + memset (s->best_pos, 0, sizeof (s->best_pos)); + + swd_findbest (s); + c->m_len = s->m_len; + c->m_off = s->m_off; + + swd_getbyte (s); + + if (s->b_char < 0) + { + c->look = 0; + c->m_len = 0; + } + else + { + c->look = s->look + 1; + } + c->bp = c->ip - c->look; + + if (c->cb && c->textsize > c->printcount) + { + (*c->cb) (c->textsize, c->codesize); + c->printcount += 1024; + } + + return LZO_E_OK; +} + +/* lzo1x_9x.c */ + +static lzo_byte * +code_match (lzo1x_999_t * c, lzo_byte * op, lzo_uint m_len, lzo_uint m_off) +{ + lzo_uint x_len = m_len; + lzo_uint x_off = m_off; + + c->match_bytes += m_len; + + if (m_len == 2) + { + m_off -= 1; + + *op++ = LZO_BYTE (M1_MARKER | ((m_off & 3) << 2)); + *op++ = LZO_BYTE (m_off >> 2); + + c->m1a_m++; + } + + else if (m_len <= M2_MAX_LEN && m_off <= M2_MAX_OFFSET) + + { + + m_off -= 1; + *op++ = LZO_BYTE (((m_len - 1) << 5) | ((m_off & 7) << 2)); + *op++ = LZO_BYTE (m_off >> 3); + c->m2_m++; + } + else if (m_len == M2_MIN_LEN && m_off <= MX_MAX_OFFSET + && c->r1_lit >= 4) + { + m_off -= 1 + M2_MAX_OFFSET; + + *op++ = LZO_BYTE (M1_MARKER | ((m_off & 3) << 2)); + *op++ = LZO_BYTE (m_off >> 2); + + c->m1b_m++; + } + else if (m_off <= M3_MAX_OFFSET) + { + m_off -= 1; + if (m_len <= M3_MAX_LEN) + *op++ = LZO_BYTE (M3_MARKER | (m_len - 2)); + else + { + m_len -= M3_MAX_LEN; + *op++ = M3_MARKER | 0; + while (m_len > 255) + { + m_len -= 255; + *op++ = 0; + } + *op++ = LZO_BYTE (m_len); + } + + *op++ = LZO_BYTE (m_off << 2); + *op++ = LZO_BYTE (m_off >> 6); + + c->m3_m++; + } + else + { + lzo_uint k; + + m_off -= 0x4000; + k = (m_off & 0x4000) >> 11; + if (m_len <= M4_MAX_LEN) + *op++ = LZO_BYTE (M4_MARKER | k | (m_len - 2)); + else + { + m_len -= M4_MAX_LEN; + *op++ = LZO_BYTE (M4_MARKER | k | 0); + while (m_len > 255) + { + m_len -= 255; + *op++ = 0; + } + *op++ = LZO_BYTE (m_len); + } + + *op++ = LZO_BYTE (m_off << 2); + *op++ = LZO_BYTE (m_off >> 6); + + c->m4_m++; + } + + c->last_m_len = x_len; + c->last_m_off = x_off; + return op; +} + +static lzo_byte * +STORE_RUN (lzo1x_999_t * c, lzo_byte * op, const lzo_byte * ii, lzo_uint t) +{ + c->lit_bytes += t; + + if (op == c->out && t <= 238) + { + *op++ = LZO_BYTE (17 + t); + } + else if (t <= 3) + { + op[-2] |= LZO_BYTE (t); + + c->lit1_r++; + } + else if (t <= 18) + { + *op++ = LZO_BYTE (t - 3); + c->lit2_r++; + } + else + { + lzo_uint tt = t - 18; + + *op++ = 0; + while (tt > 255) + { + tt -= 255; + *op++ = 0; + } + *op++ = LZO_BYTE (tt); + c->lit3_r++; + } + do + *op++ = *ii++; + while (--t > 0); + + return op; +} + +static lzo_byte * +code_run (lzo1x_999_t * c, lzo_byte * op, const lzo_byte * ii, + lzo_uint lit, lzo_uint m_len) +{ + if (lit > 0) + { + op = STORE_RUN (c, op, ii, lit); + c->r1_m_len = m_len; + c->r1_lit = lit; + } + else + { + c->r1_m_len = 0; + c->r1_lit = 0; + } + + return op; +} + +static int +len_of_coded_match (lzo_uint m_len, lzo_uint m_off, lzo_uint lit) +{ + int n = 4; + + if (m_len < 2) + return -1; + if (m_len == 2) + return (m_off <= M1_MAX_OFFSET && lit > 0 + && lit < 4) ? 2 : -1; + if (m_len <= M2_MAX_LEN && m_off <= M2_MAX_OFFSET) + return 2; + if (m_len == M2_MIN_LEN && m_off <= MX_MAX_OFFSET && lit >= 4) + return 2; + if (m_off <= M3_MAX_OFFSET) + { + if (m_len <= M3_MAX_LEN) + return 3; + m_len -= M3_MAX_LEN; + while (m_len > 255) + { + m_len -= 255; + n++; + } + return n; + } + if (m_off <= M4_MAX_OFFSET) + { + if (m_len <= M4_MAX_LEN) + return 3; + m_len -= M4_MAX_LEN; + while (m_len > 255) + { + m_len -= 255; + n++; + } + return n; + } + return -1; +} + +static lzo_int +min_gain (lzo_uint ahead, lzo_uint lit1, lzo_uint lit2, int l1, int l2, + int l3) +{ + lzo_int lazy_match_min_gain = 0; + + lazy_match_min_gain += ahead; + + if (lit1 <= 3) + lazy_match_min_gain += (lit2 <= 3) ? 0 : 2; + else if (lit1 <= 18) + lazy_match_min_gain += (lit2 <= 18) ? 0 : 1; + + lazy_match_min_gain += (l2 - l1) * 2; + if (l3 > 0) + lazy_match_min_gain -= (ahead - l3) * 2; + + if (lazy_match_min_gain < 0) + lazy_match_min_gain = 0; + + return lazy_match_min_gain; +} + +static void +better_match (const lzo1x_999_swd_t * swd, lzo_uint * m_len, lzo_uint * m_off) +{ + if (*m_len <= M2_MIN_LEN) + return; + + if (*m_off <= M2_MAX_OFFSET) + return; + + if (*m_off > M2_MAX_OFFSET && + *m_len >= M2_MIN_LEN + 1 && *m_len <= M2_MAX_LEN + 1 && + swd->best_off[*m_len - 1] + && swd->best_off[*m_len - 1] <= M2_MAX_OFFSET) + { + *m_len = *m_len - 1; + *m_off = swd->best_off[*m_len]; + return; + } + + if (*m_off > M3_MAX_OFFSET && + *m_len >= M4_MAX_LEN + 1 && *m_len <= M2_MAX_LEN + 2 && + swd->best_off[*m_len - 2] + && swd->best_off[*m_len - 2] <= M2_MAX_OFFSET) + { + *m_len = *m_len - 2; + *m_off = swd->best_off[*m_len]; + return; + } + + if (*m_off > M3_MAX_OFFSET && + *m_len >= M4_MAX_LEN + 1 && *m_len <= M3_MAX_LEN + 1 && + swd->best_off[*m_len - 1] + && swd->best_off[*m_len - 1] <= M3_MAX_OFFSET) + { + *m_len = *m_len - 1; + *m_off = swd->best_off[*m_len]; + } + +} + +/* minilzo.c */ + +static lzo_bool +lzo_assert (int expr) +{ + return (expr) ? 1 : 0; +} + +/* lzo1x_9x.c */ + +static int +lzo1x_999_compress_internal (const lzo_byte * in, lzo_uint in_len, + lzo_byte * out, lzo_uintp out_len, + lzo_voidp wrkmem, + const lzo_byte * dict, lzo_uint dict_len, + lzo_progress_callback_t cb, + int try_lazy, + lzo_uint good_length, + lzo_uint max_lazy, + lzo_uint nice_length, + lzo_uint max_chain, lzo_uint32 flags) +{ + lzo_byte *op; + const lzo_byte *ii; + lzo_uint lit; + lzo_uint m_len, m_off; + lzo1x_999_t cc; + lzo1x_999_t *const c = &cc; + lzo1x_999_swd_t *const swd = (lzo1x_999_swd_t *) wrkmem; + int r; + + if (!lzo_assert + (LZO1X_999_MEM_COMPRESS >= lzo_sizeof (lzo1x_999_swd_t))) + return LZO_E_ERROR; + + if (try_lazy < 0) + try_lazy = 1; + + if (good_length <= 0) + good_length = 32; + + if (max_lazy <= 0) + max_lazy = 32; + + if (nice_length <= 0) + nice_length = 0; + + if (max_chain <= 0) + max_chain = SWD_MAX_CHAIN; + + c->init = 0; + c->ip = c->in = in; + c->in_end = in + in_len; + c->out = out; + c->cb = cb; + c->m1a_m = c->m1b_m = c->m2_m = c->m3_m = c->m4_m = 0; + c->lit1_r = c->lit2_r = c->lit3_r = 0; + + op = out; + ii = c->ip; + lit = 0; + c->r1_lit = c->r1_m_len = 0; + + r = init_match (c, swd, dict, dict_len, flags); + if (r != 0) + return r; + if (max_chain > 0) + swd->max_chain = max_chain; + if (nice_length > 0) + swd->nice_length = nice_length; + + r = find_match (c, swd, 0, 0); + if (r != 0) + return r; + while (c->look > 0) + { + lzo_uint ahead; + lzo_uint max_ahead; + int l1, l2, l3; + + c->codesize = op - out; + + m_len = c->m_len; + m_off = c->m_off; + + if (lit == 0) + ii = c->bp; + + if (m_len < 2 || + (m_len == 2 + && (m_off > M1_MAX_OFFSET || lit == 0 || lit >= 4)) + || (m_len == 2 && op == out) || (op == out && lit == 0)) + { + + m_len = 0; + } + else if (m_len == M2_MIN_LEN) + { + + if (m_off > MX_MAX_OFFSET && lit >= 4) + m_len = 0; + } + + if (m_len == 0) + { + + lit++; + swd->max_chain = max_chain; + r = find_match (c, swd, 1, 0); + continue; + } + + if (swd->use_best_off) + better_match (swd, &m_len, &m_off); + + ahead = 0; + if (try_lazy <= 0 || m_len >= max_lazy) + { + + l1 = 0; + max_ahead = 0; + } + else + { + + l1 = len_of_coded_match (m_len, m_off, lit); + + max_ahead = LZO_MIN (try_lazy, l1 - 1); + + } + + while (ahead < max_ahead && c->look > m_len) + { + lzo_int lazy_match_min_gain; + + if (m_len >= good_length) + swd->max_chain = max_chain >> 2; + else + swd->max_chain = max_chain; + r = find_match (c, swd, 1, 0); + ahead++; + + if (c->m_len < m_len) + continue; + + if (c->m_len == m_len && c->m_off >= m_off) + continue; + + if (swd->use_best_off) + better_match (swd, &c->m_len, &c->m_off); + + l2 = len_of_coded_match (c->m_len, c->m_off, + lit + ahead); + if (l2 < 0) + continue; + + l3 = (op == out) ? -1 : len_of_coded_match (ahead, + m_off, + lit); + + lazy_match_min_gain = + min_gain (ahead, lit, lit + ahead, l1, l2, + l3); + if (c->m_len >= m_len + lazy_match_min_gain) + { + c->lazy++; + + if (l3 > 0) + { + + op = code_run (c, op, ii, lit, ahead); + lit = 0; + + op = code_match (c, op, ahead, m_off); + } + else + { + lit += ahead; + } + goto lazy_match_done; + } + } + + op = code_run (c, op, ii, lit, m_len); + lit = 0; + + op = code_match (c, op, m_len, m_off); + swd->max_chain = max_chain; + r = find_match (c, swd, m_len, 1 + ahead); + + lazy_match_done:; + } + + if (lit > 0) + op = STORE_RUN (c, op, ii, lit); + + *op++ = M4_MARKER | 1; + *op++ = 0; + *op++ = 0; + + c->codesize = op - out; + + *out_len = op - out; + + if (c->cb) + (*c->cb) (c->textsize, c->codesize); + + return LZO_E_OK; +} + +static int +lzo1x_999_compress_level (const lzo_byte * in, lzo_uint in_len, + lzo_byte * out, lzo_uintp out_len, + lzo_voidp wrkmem, + const lzo_byte * dict, lzo_uint dict_len, + lzo_progress_callback_t cb, int compression_level) +{ + static const struct + { + int try_lazy; + lzo_uint good_length; + lzo_uint max_lazy; + lzo_uint nice_length; + lzo_uint max_chain; + lzo_uint32 flags; + } c[9] = + { + { + 0, 0, 0, 8, 4, 0}, + { + 0, 0, 0, 16, 8, 0}, + { + 0, 0, 0, 32, 16, 0}, + { + 1, 4, 4, 16, 16, 0}, + { + 1, 8, 16, 32, 32, 0}, + { + 1, 8, 16, 128, 128, 0}, + { + 2, 8, 32, 128, 256, 0}, + { + 2, 32, 128, F, 2048, 1}, + { + 2, F, F, F, 4096, 1} + }; + + if (compression_level < 1 || compression_level > 9) + return LZO_E_ERROR; + + compression_level -= 1; + return lzo1x_999_compress_internal (in, in_len, out, out_len, wrkmem, + dict, dict_len, cb, + c[compression_level].try_lazy, + c[compression_level].good_length, + c[compression_level].max_lazy, + 0, + c[compression_level].max_chain, + c[compression_level].flags); +} + +static int +lzo1x_999_compress (const lzo_byte * in, lzo_uint in_len, + lzo_byte * out, lzo_uintp out_len, lzo_voidp wrkmem) +{ + return lzo1x_999_compress_level (in, in_len, out, out_len, wrkmem, + NULL, 0, 0, 8); +} + +/* minilzo.c */ + +#ifdef JFFS2_LZO_1 +static const lzo_byte __lzo_copyright[] = LZO_VERSION_STRING; + +static lzo_uint +_lzo1x_1_do_compress (const lzo_byte * in, lzo_uint in_len, + lzo_byte * out, lzo_uintp out_len, lzo_voidp wrkmem) +{ + + register const lzo_byte *ip; + + lzo_byte *op; + const lzo_byte *const in_end = in + in_len; + const lzo_byte *const ip_end = in + in_len - 8 - 5; + const lzo_byte *ii; + lzo_dict_p const dict = (lzo_dict_p) wrkmem; + + op = out; + ip = in; + ii = ip; + + ip += 4; + for (;;) + { + register const lzo_byte *m_pos; + + lzo_uint m_off; + lzo_uint m_len; + lzo_uint dindex; + + DINDEX1 (dindex, ip); + GINDEX (m_pos, m_off, dict, dindex, in); + if (LZO_CHECK_MPOS_NON_DET + (m_pos, m_off, in, ip, M4_MAX_OFFSET)) + goto literal; + + if (m_off <= M2_MAX_OFFSET || m_pos[3] == ip[3]) + goto try_match; + DINDEX2 (dindex, ip); + GINDEX (m_pos, m_off, dict, dindex, in); + + if (LZO_CHECK_MPOS_NON_DET + (m_pos, m_off, in, ip, M4_MAX_OFFSET)) + goto literal; + if (m_off <= M2_MAX_OFFSET || m_pos[3] == ip[3]) + goto try_match; + goto literal; + + try_match: + if (m_pos[0] != ip[0] || m_pos[1] != ip[1]) + { + } + else + { + if (m_pos[2] == ip[2]) + { + goto match; + } + else + { + } + } + + literal: + UPDATE_I (dict, 0, dindex, ip, in); + ++ip; + if (ip >= ip_end) + break; + continue; + + match: + UPDATE_I (dict, 0, dindex, ip, in); + + if (pd (ip, ii) > 0) + { + register lzo_uint t = pd (ip, ii); + + if (t <= 3) + { + op[-2] |= LZO_BYTE (t); + } + else if (t <= 18) + *op++ = LZO_BYTE (t - 3); + else + { + register lzo_uint tt = t - 18; + + *op++ = 0; + while (tt > 255) + { + tt -= 255; + *op++ = 0; + } + *op++ = LZO_BYTE (tt);; + } + do + *op++ = *ii++; + while (--t > 0); + } + + ip += 3; + if (m_pos[3] != *ip++ || m_pos[4] != *ip++ + || m_pos[5] != *ip++ || m_pos[6] != *ip++ + || m_pos[7] != *ip++ || m_pos[8] != *ip++) + { + --ip; + m_len = ip - ii; + + if (m_off <= M2_MAX_OFFSET) + { + m_off -= 1; + + *op++ = LZO_BYTE (((m_len - + 1) << 5) | ((m_off & 7) << + 2)); + *op++ = LZO_BYTE (m_off >> 3); + } + else if (m_off <= M3_MAX_OFFSET) + { + m_off -= 1; + *op++ = LZO_BYTE (M3_MARKER | (m_len - 2)); + goto m3_m4_offset; + } + else + + { + m_off -= 0x4000; + + *op++ = LZO_BYTE (M4_MARKER | + ((m_off & 0x4000) >> 11) | + (m_len - 2)); + goto m3_m4_offset; + } + } + else + { + { + const lzo_byte *end = in_end; + const lzo_byte *m = m_pos + M2_MAX_LEN + 1; + while (ip < end && *m == *ip) + m++, ip++; + m_len = (ip - ii); + } + + + if (m_off <= M3_MAX_OFFSET) + { + m_off -= 1; + if (m_len <= 33) + *op++ = LZO_BYTE (M3_MARKER | + (m_len - 2)); + else + { + m_len -= 33; + *op++ = M3_MARKER | 0; + goto m3_m4_len; + } + } + else + { + m_off -= 0x4000; + + if (m_len <= M4_MAX_LEN) + *op++ = LZO_BYTE (M4_MARKER | + ((m_off & 0x4000) >> + 11) | (m_len - 2)); + + else + { + m_len -= M4_MAX_LEN; + *op++ = LZO_BYTE (M4_MARKER | + ((m_off & 0x4000) >> + 11)); + m3_m4_len: + while (m_len > 255) + { + m_len -= 255; + *op++ = 0; + } + + *op++ = LZO_BYTE (m_len); + } + } + + m3_m4_offset: + *op++ = LZO_BYTE ((m_off & 63) << 2); + *op++ = LZO_BYTE (m_off >> 6); + } + ii = ip; + if (ip >= ip_end) + break; + } + + *out_len = op - out; + return pd (in_end, ii); +} +#endif + +#ifdef JFFS2_LZO_1 +static int +lzo1x_1_compress (const lzo_byte * in, lzo_uint in_len, + lzo_byte * out, lzo_uintp out_len, lzo_voidp wrkmem) +{ + lzo_byte *op = out; + lzo_uint t; + + if (in_len <= M2_MAX_LEN + 5) + t = in_len; + else + { + t = _lzo1x_1_do_compress (in, in_len, op, out_len, wrkmem); + op += *out_len; + } + + if (t > 0) + { + const lzo_byte *ii = in + in_len - t; + + if (op == out && t <= 238) + *op++ = LZO_BYTE (17 + t); + else if (t <= 3) + op[-2] |= LZO_BYTE (t); + else if (t <= 18) + *op++ = LZO_BYTE (t - 3); + else + { + lzo_uint tt = t - 18; + + *op++ = 0; + while (tt > 255) + { + tt -= 255; + *op++ = 0; + } + + *op++ = LZO_BYTE (tt); + } + do + *op++ = *ii++; + while (--t > 0); + } + + *op++ = M4_MARKER | 1; + *op++ = 0; + *op++ = 0; + + *out_len = op - out; + return 0; +} +#endif + +static int +lzo1x_decompress (const lzo_byte * in, lzo_uint in_len, + lzo_byte * out, lzo_uintp out_len, lzo_voidp wrkmem) +{ + register lzo_byte *op; + register const lzo_byte *ip; + register lzo_uint t; + + register const lzo_byte *m_pos; + + const lzo_byte *const ip_end = in + in_len; + lzo_byte *const op_end = out + *out_len; + + *out_len = 0; + + op = out; + ip = in; + + if (*ip > 17) + { + t = *ip++ - 17; + if (t < 4) + goto match_next; + NEED_OP (t); + NEED_IP (t + 1); + do + *op++ = *ip++; + while (--t > 0); + goto first_literal_run; + } + + while (TEST_IP && TEST_OP) + { + t = *ip++; + if (t >= 16) + goto match; + if (t == 0) + { + NEED_IP (1); + while (*ip == 0) + { + t += 255; + ip++; + NEED_IP (1); + } + t += 15 + *ip++; + } + NEED_OP (t + 3); + NEED_IP (t + 4); + if (PTR_ALIGNED2_4 (op, ip)) + { + COPY4 (op, ip); + + op += 4; + ip += 4; + if (--t > 0) + { + if (t >= 4) + { + do + { + COPY4 (op, ip); + op += 4; + ip += 4; + t -= 4; + } + while (t >= 4); + if (t > 0) + do + *op++ = *ip++; + while (--t > 0); + } + else + do + *op++ = *ip++; + while (--t > 0); + } + } + else + { + *op++ = *ip++; + *op++ = *ip++; + *op++ = *ip++; + do + *op++ = *ip++; + while (--t > 0); + } + first_literal_run: + + t = *ip++; + if (t >= 16) + goto match; + + m_pos = op - (1 + M2_MAX_OFFSET); + m_pos -= t >> 2; + m_pos -= *ip++ << 2; + TEST_LOOKBEHIND (m_pos, out); + NEED_OP (3); + *op++ = *m_pos++; + *op++ = *m_pos++; + *op++ = *m_pos; + + goto match_done; + + while (TEST_IP && TEST_OP) + { + match: + if (t >= 64) + { + m_pos = op - 1; + m_pos -= (t >> 2) & 7; + m_pos -= *ip++ << 3; + t = (t >> 5) - 1; + TEST_LOOKBEHIND (m_pos, out); + NEED_OP (t + 3 - 1); + goto copy_match; + + } + else if (t >= 32) + { + t &= 31; + if (t == 0) + { + NEED_IP (1); + while (*ip == 0) + { + t += 255; + ip++; + NEED_IP (1); + } + t += 31 + *ip++; + } + + m_pos = op - 1; + m_pos -= (ip[0] >> 2) + (ip[1] << 6); + + ip += 2; + } + else if (t >= 16) + { + m_pos = op; + m_pos -= (t & 8) << 11; + + t &= 7; + if (t == 0) + { + NEED_IP (1); + while (*ip == 0) + { + t += 255; + ip++; + NEED_IP (1); + } + t += 7 + *ip++; + } + + m_pos -= (ip[0] >> 2) + (ip[1] << 6); + + ip += 2; + if (m_pos == op) + goto eof_found; + m_pos -= 0x4000; + } + else + { + + m_pos = op - 1; + m_pos -= t >> 2; + m_pos -= *ip++ << 2; + TEST_LOOKBEHIND (m_pos, out); + NEED_OP (2); + *op++ = *m_pos++; + *op++ = *m_pos; + + goto match_done; + } + + TEST_LOOKBEHIND (m_pos, out); + NEED_OP (t + 3 - 1); + if (t >= 2 * 4 - (3 - 1) + && PTR_ALIGNED2_4 (op, m_pos)) + { + COPY4 (op, m_pos); + op += 4; + m_pos += 4; + t -= 4 - (3 - 1); + do + { + COPY4 (op, m_pos); + op += 4; + m_pos += 4; + t -= 4; + } + while (t >= 4); + if (t > 0) + do + *op++ = *m_pos++; + while (--t > 0); + } + else + + { + copy_match: + *op++ = *m_pos++; + *op++ = *m_pos++; + do + *op++ = *m_pos++; + while (--t > 0); + } + + match_done: + t = ip[-2] & 3; + + if (t == 0) + break; + + match_next: + NEED_OP (t); + NEED_IP (t + 1); + do + *op++ = *ip++; + while (--t > 0); + t = *ip++; + } + } + *out_len = op - out; + return LZO_E_EOF_NOT_FOUND; + + eof_found: + *out_len = op - out; + return (ip == ip_end ? LZO_E_OK : + (ip < + ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN)); + + input_overrun: + *out_len = op - out; + return LZO_E_INPUT_OVERRUN; + + output_overrun: + *out_len = op - out; + return LZO_E_OUTPUT_OVERRUN; + + lookbehind_overrun: + *out_len = op - out; + return LZO_E_LOOKBEHIND_OVERRUN; +} + +/* lzo1x_oo.ch */ + +#define NO_LIT LZO_UINT_MAX + +static void +copy2 (lzo_byte * ip, const lzo_byte * m_pos, lzo_ptrdiff_t off) +{ + ip[0] = m_pos[0]; + if (off == 1) + ip[1] = m_pos[0]; + else + ip[1] = m_pos[1]; +} + +static void +copy3 (lzo_byte * ip, const lzo_byte * m_pos, lzo_ptrdiff_t off) +{ + ip[0] = m_pos[0]; + if (off == 1) + { + ip[2] = ip[1] = m_pos[0]; + } + else if (off == 2) + { + ip[1] = m_pos[1]; + ip[2] = m_pos[0]; + } + else + { + ip[1] = m_pos[1]; + ip[2] = m_pos[2]; + } +} + +static int +lzo1x_optimize (lzo_byte * in, lzo_uint in_len, + lzo_byte * out, lzo_uintp out_len, lzo_voidp wrkmem) +{ + register lzo_byte *op; + register lzo_byte *ip; + register lzo_uint t; + register lzo_byte *m_pos; + lzo_uint nl; + const lzo_byte *const ip_end = in + in_len; + const lzo_byte *const op_end = out + *out_len; + lzo_byte *litp = NULL; + lzo_uint lit = 0; + lzo_uint next_lit = NO_LIT; + long o_m1_a = 0, o_m1_b = 0, o_m2 = 0, o_m3_a = 0, o_m3_b = 0; + + *out_len = 0; + + op = out; + ip = in; + + if (*ip > 17) + { + t = *ip++ - 17; + if (t < 4) + goto match_next; + goto first_literal_run; + } + + while (TEST_IP && TEST_OP) + { + t = *ip++; + if (t >= 16) + goto match; + litp = ip - 1; + if (t == 0) + { + t = 15; + while (*ip == 0) + t += 255, ip++; + t += *ip++; + } + lit = t + 3; + copy_literal_run: + *op++ = *ip++; + *op++ = *ip++; + *op++ = *ip++; + first_literal_run: + do + *op++ = *ip++; + while (--t > 0); + + t = *ip++; + + if (t >= 16) + goto match; + m_pos = op - 1 - 0x800; + m_pos -= t >> 2; + m_pos -= *ip++ << 2; + *op++ = *m_pos++; + *op++ = *m_pos++; + *op++ = *m_pos++; + lit = 0; + goto match_done; + + while (TEST_IP && TEST_OP) + { + if (t < 16) + { + m_pos = op - 1; + m_pos -= t >> 2; + m_pos -= *ip++ << 2; + + if (litp == NULL) + goto copy_m1; + + nl = ip[-2] & 3; + if (nl == 0 && lit == 1 && ip[0] >= 16) + { + next_lit = nl; + lit += 2; + *litp = LZO_BYTE ((*litp & ~3) | lit); + copy2 (ip - 2, m_pos, op - m_pos); + o_m1_a++; + } + else if (nl == 0 && ip[0] < 16 && ip[0] != 0 + && (lit + 2 + ip[0] < 16)) + { + t = *ip++; + *litp &= ~3; + copy2 (ip - 3 + 1, m_pos, op - m_pos); + litp += 2; + if (lit > 0) + memmove (litp + 1, litp, lit); + lit += 2 + t + 3; + *litp = LZO_BYTE (lit - 3); + + o_m1_b++; + *op++ = *m_pos++; + *op++ = *m_pos++; + goto copy_literal_run; + } + copy_m1: + *op++ = *m_pos++; + *op++ = *m_pos++; + } + else + { + match: + if (t >= 64) + { + m_pos = op - 1; + m_pos -= (t >> 2) & 7; + m_pos -= *ip++ << 3; + t = (t >> 5) - 1; + if (litp == NULL) + goto copy_m; + + nl = ip[-2] & 3; + if (t == 1 && lit > 3 && nl == 0 && + ip[0] < 16 && ip[0] != 0 + && (lit + 3 + ip[0] < 16)) + { + t = *ip++; + copy3 (ip - 1 - 2, m_pos, + op - m_pos); + lit += 3 + t + 3; + *litp = LZO_BYTE (lit - 3); + o_m2++; + *op++ = *m_pos++; + *op++ = *m_pos++; + *op++ = *m_pos++; + goto copy_literal_run; + } + } + else + { + if (t >= 32) + { + t &= 31; + if (t == 0) + { + t = 31; + while (*ip == 0) + t += 255, + ip++; + t += *ip++; + } + m_pos = op - 1; + m_pos -= *ip++ >> 2; + m_pos -= *ip++ << 6; + } + else + { + m_pos = op; + m_pos -= (t & 8) << 11; + t &= 7; + if (t == 0) + { + t = 7; + while (*ip == 0) + t += 255, + ip++; + t += *ip++; + } + m_pos -= *ip++ >> 2; + m_pos -= *ip++ << 6; + if (m_pos == op) + goto eof_found; + m_pos -= 0x4000; + } + if (litp == NULL) + goto copy_m; + + nl = ip[-2] & 3; + if (t == 1 && lit == 0 && nl == 0 + && ip[0] >= 16) + { + next_lit = nl; + lit += 3; + *litp = LZO_BYTE ((*litp & ~3) + | lit); + copy3 (ip - 3, m_pos, + op - m_pos); + o_m3_a++; + } + else if (t == 1 && lit <= 3 && nl == 0 + && ip[0] < 16 && ip[0] != 0 + && (lit + 3 + ip[0] < 16)) + { + t = *ip++; + *litp &= ~3; + copy3 (ip - 4 + 1, m_pos, + op - m_pos); + litp += 2; + if (lit > 0) + memmove (litp + 1, + litp, lit); + lit += 3 + t + 3; + *litp = LZO_BYTE (lit - 3); + + o_m3_b++; + *op++ = *m_pos++; + *op++ = *m_pos++; + *op++ = *m_pos++; + goto copy_literal_run; + } + } + copy_m: + *op++ = *m_pos++; + *op++ = *m_pos++; + do + *op++ = *m_pos++; + while (--t > 0); + } + + match_done: + if (next_lit == NO_LIT) + { + t = ip[-2] & 3; + lit = t; + litp = ip - 2; + } + else + t = next_lit; + next_lit = NO_LIT; + if (t == 0) + break; + match_next: + do + *op++ = *ip++; + while (--t > 0); + t = *ip++; + } + } + + *out_len = op - out; + return LZO_E_EOF_NOT_FOUND; + + eof_found: + *out_len = op - out; + return (ip == ip_end ? LZO_E_OK : + (ip < + ip_end ? LZO_E_INPUT_NOT_CONSUMED : LZO_E_INPUT_OVERRUN)); +} + +/* interface to jffs2 follows */ + +#include "compr.h" +#include + +/*#define BLOCKSIZE JFFS2_PAGE_SIZE +#define OUTBLOCKSIZE (BLOCKSIZE + BLOCKSIZE / 64 + 16 + 3)*/ + +int jffs2_lzo_compress (unsigned char *input, + unsigned char *output, uint32_t *sourcelen, + uint32_t *dstlen, void *model); + +int jffs2_lzo_decompress (unsigned char *input, + unsigned char *output, uint32_t sourcelen, + uint32_t dstlen, void *model); + +static struct jffs2_compressor jffs2_lzo_comp = { + .priority = JFFS2_LZO_PRIORITY, + .name = "lzo", + .compr = JFFS2_COMPR_LZO, + .compress = &jffs2_lzo_compress, + .decompress = &jffs2_lzo_decompress, +#ifdef JFFS2_LZO_DISABLED + .disabled = 1, +#else + .disabled = 0, +#endif +}; + +#ifdef JFFS2_LZO_1 +static int +no_lzo1x_optimize (lzo_byte * src, lzo_uint src_len, + lzo_byte * dst, lzo_uintp dst_len, lzo_voidp wrkmem) +{ + return 0; +} +#endif + +static lzo_compress_t lzo1x_compressor = lzo1x_999_compress; +static lzo_optimize_t lzo1x_optimizer = lzo1x_optimize; +#ifdef JFFS2_LZO_1 +static int lzo1x_compressor_type = 999; +static int lzo1x_optimize_type = 1; +#endif +static unsigned long lzo1x_compressor_memsize = LZO1X_999_MEM_COMPRESS; + +static lzo_bytep wrkmem = NULL; /* temporary buffer for compression, used by lzo */ +static lzo_bytep cmprssmem = NULL; /* temporary buffer for compression, used by interface */ +static int cmprssmem_size = 0; + +static int prepare_out_buf(uint32_t ssize, uint32_t dsize) +{ + uint32_t msize,req; + + msize = (ssize>dsize)? ssize : dsize; + req = (msize<<1) + 20; + if ((!cmprssmem)||(cmprssmem_size + +extern const uint32_t crc32_table[256]; + +/* Return a 32-bit CRC of the contents of the buffer. */ + +static inline uint32_t +crc32(uint32_t val, const void *ss, int len) +{ + const unsigned char *s = ss; + while (--len >= 0) + val = crc32_table[(val ^ *s++) & 0xff] ^ (val >> 8); + return val; +} + +#endif diff -NaurbB linux-2.6.9-pre1-bk18/fs/jffs2/erase.c linux-2.6.9_nbp/fs/jffs2/erase.c --- linux-2.6.9-pre1-bk18/fs/jffs2/erase.c 2004-08-14 07:36:16.000000000 +0200 +++ linux-2.6.9_nbp/fs/jffs2/erase.c 2004-09-13 12:43:28.000000000 +0200 @@ -402,7 +402,7 @@ goto bad2; } if (retlen != sizeof(marker)) { - printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %zd, got %zd\n", + printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %d, got %zd\n", jeb->offset, sizeof(marker), retlen); goto bad2; } diff -NaurbB linux-2.6.9-pre1-bk18/fs/jffs2/fs.c linux-2.6.9_nbp/fs/jffs2/fs.c --- linux-2.6.9-pre1-bk18/fs/jffs2/fs.c 2004-08-14 07:36:56.000000000 +0200 +++ linux-2.6.9_nbp/fs/jffs2/fs.c 2004-09-28 15:52:52.000000000 +0200 @@ -463,10 +463,12 @@ */ c->sector_size = c->mtd->erasesize; blocks = c->flash_size / c->sector_size; +#ifndef CONFIG_JFFS2_LARGEMEM while ((blocks * sizeof (struct jffs2_eraseblock)) > (128 * 1024)) { blocks >>= 1; c->sector_size <<= 1; } +#endif /* * Size alignment check @@ -533,7 +535,11 @@ out_nodes: jffs2_free_ino_caches(c); jffs2_free_raw_node_refs(c); +#ifdef CONFIG_JFFS2_LARGEMEM + vfree(c->blocks); +#else kfree(c->blocks); +#endif out_inohash: kfree(c->inocache_list); out_wbuf: diff -NaurbB linux-2.6.9-pre1-bk18/fs/jffs2/gc.c linux-2.6.9_nbp/fs/jffs2/gc.c --- linux-2.6.9-pre1-bk18/fs/jffs2/gc.c 2004-08-14 07:36:32.000000000 +0200 +++ linux-2.6.9_nbp/fs/jffs2/gc.c 2004-09-13 12:43:28.000000000 +0200 @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: gc.c,v 1.136 2004/05/27 19:06:09 gleixner Exp $ + * $Id: gc.c,v 1.137 2004/07/20 13:44:55 dwmw2 Exp $ * */ @@ -359,10 +359,14 @@ spin_unlock(&c->inocache_lock); f = jffs2_gc_fetch_inode(c, inum, nlink); - if (IS_ERR(f)) - return PTR_ERR(f); - if (!f) - return 0; + if (IS_ERR(f)) { + ret = PTR_ERR(f); + goto release_sem; + } + if (!f) { + ret = 0; + goto release_sem; + } ret = jffs2_garbage_collect_live(c, jeb, raw, f); @@ -824,7 +828,7 @@ continue; } if (retlen != rawlen) { - printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %u) reading header from obsolete node at %08x\n", + printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %zd) reading header from obsolete node at %08x\n", retlen, rawlen, ref_offset(raw)); continue; } diff -NaurbB linux-2.6.9-pre1-bk18/fs/jffs2/Makefile linux-2.6.9_nbp/fs/jffs2/Makefile --- linux-2.6.9-pre1-bk18/fs/jffs2/Makefile 2004-08-14 07:36:32.000000000 +0200 +++ linux-2.6.9_nbp/fs/jffs2/Makefile 2004-09-27 04:19:38.000000000 +0200 @@ -15,3 +15,5 @@ jffs2-$(CONFIG_JFFS2_RUBIN) += compr_rubin.o jffs2-$(CONFIG_JFFS2_RTIME) += compr_rtime.o jffs2-$(CONFIG_JFFS2_ZLIB) += compr_zlib.o +jffs2-$(CONFIG_JFFS2_LZO) += compr_lzo.o +jffs2-$(CONFIG_JFFS2_LZARI) += compr_lzari.o diff -NaurbB linux-2.6.9-pre1-bk18/fs/jffs2/rbtree.c linux-2.6.9_nbp/fs/jffs2/rbtree.c --- linux-2.6.9-pre1-bk18/fs/jffs2/rbtree.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/fs/jffs2/rbtree.c 2004-09-13 13:41:20.000000000 +0200 @@ -0,0 +1,363 @@ +/* + Red Black Trees + (C) 1999 Andrea Arcangeli + (C) 2002 David Woodhouse + + 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 + + $Id: rbtree.c,v 1.3 2003/10/16 08:02:19 dwmw2 Exp $ +*/ + +#ifdef __ECOS /* This file is _not_ under the eCos licence; it is pure GPL. */ +#error "Licence problem. eCos has its own rbtree code." +#endif + +#include +#include + +/* This wasn't present till 2.4.11, wasn't exported till 2.4.19 */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,11) || \ + (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,19) && defined(MODULE)) +static void __rb_rotate_left(struct rb_node * node, struct rb_root * root) +{ + struct rb_node * right = node->rb_right; + + if ((node->rb_right = right->rb_left)) + right->rb_left->rb_parent = node; + right->rb_left = node; + + if ((right->rb_parent = node->rb_parent)) + { + if (node == node->rb_parent->rb_left) + node->rb_parent->rb_left = right; + else + node->rb_parent->rb_right = right; + } + else + root->rb_node = right; + node->rb_parent = right; +} + +static void __rb_rotate_right(struct rb_node * node, struct rb_root * root) +{ + struct rb_node * left = node->rb_left; + + if ((node->rb_left = left->rb_right)) + left->rb_right->rb_parent = node; + left->rb_right = node; + + if ((left->rb_parent = node->rb_parent)) + { + if (node == node->rb_parent->rb_right) + node->rb_parent->rb_right = left; + else + node->rb_parent->rb_left = left; + } + else + root->rb_node = left; + node->rb_parent = left; +} + +void rb_insert_color(struct rb_node * node, struct rb_root * root) +{ + struct rb_node * parent, * gparent; + + while ((parent = node->rb_parent) && parent->rb_color == RB_RED) + { + gparent = parent->rb_parent; + + if (parent == gparent->rb_left) + { + { + register struct rb_node * uncle = gparent->rb_right; + if (uncle && uncle->rb_color == RB_RED) + { + uncle->rb_color = RB_BLACK; + parent->rb_color = RB_BLACK; + gparent->rb_color = RB_RED; + node = gparent; + continue; + } + } + + if (parent->rb_right == node) + { + register struct rb_node * tmp; + __rb_rotate_left(parent, root); + tmp = parent; + parent = node; + node = tmp; + } + + parent->rb_color = RB_BLACK; + gparent->rb_color = RB_RED; + __rb_rotate_right(gparent, root); + } else { + { + register struct rb_node * uncle = gparent->rb_left; + if (uncle && uncle->rb_color == RB_RED) + { + uncle->rb_color = RB_BLACK; + parent->rb_color = RB_BLACK; + gparent->rb_color = RB_RED; + node = gparent; + continue; + } + } + + if (parent->rb_left == node) + { + register struct rb_node * tmp; + __rb_rotate_right(parent, root); + tmp = parent; + parent = node; + node = tmp; + } + + parent->rb_color = RB_BLACK; + gparent->rb_color = RB_RED; + __rb_rotate_left(gparent, root); + } + } + + root->rb_node->rb_color = RB_BLACK; +} + +static void __rb_erase_color(struct rb_node * node, struct rb_node * parent, + struct rb_root * root) +{ + struct rb_node * other; + + while ((!node || node->rb_color == RB_BLACK) && node != root->rb_node) + { + if (parent->rb_left == node) + { + other = parent->rb_right; + if (other->rb_color == RB_RED) + { + other->rb_color = RB_BLACK; + parent->rb_color = RB_RED; + __rb_rotate_left(parent, root); + other = parent->rb_right; + } + if ((!other->rb_left || + other->rb_left->rb_color == RB_BLACK) + && (!other->rb_right || + other->rb_right->rb_color == RB_BLACK)) + { + other->rb_color = RB_RED; + node = parent; + parent = node->rb_parent; + } + else + { + if (!other->rb_right || + other->rb_right->rb_color == RB_BLACK) + { + register struct rb_node * o_left; + if ((o_left = other->rb_left)) + o_left->rb_color = RB_BLACK; + other->rb_color = RB_RED; + __rb_rotate_right(other, root); + other = parent->rb_right; + } + other->rb_color = parent->rb_color; + parent->rb_color = RB_BLACK; + if (other->rb_right) + other->rb_right->rb_color = RB_BLACK; + __rb_rotate_left(parent, root); + node = root->rb_node; + break; + } + } + else + { + other = parent->rb_left; + if (other->rb_color == RB_RED) + { + other->rb_color = RB_BLACK; + parent->rb_color = RB_RED; + __rb_rotate_right(parent, root); + other = parent->rb_left; + } + if ((!other->rb_left || + other->rb_left->rb_color == RB_BLACK) + && (!other->rb_right || + other->rb_right->rb_color == RB_BLACK)) + { + other->rb_color = RB_RED; + node = parent; + parent = node->rb_parent; + } + else + { + if (!other->rb_left || + other->rb_left->rb_color == RB_BLACK) + { + register struct rb_node * o_right; + if ((o_right = other->rb_right)) + o_right->rb_color = RB_BLACK; + other->rb_color = RB_RED; + __rb_rotate_left(other, root); + other = parent->rb_left; + } + other->rb_color = parent->rb_color; + parent->rb_color = RB_BLACK; + if (other->rb_left) + other->rb_left->rb_color = RB_BLACK; + __rb_rotate_right(parent, root); + node = root->rb_node; + break; + } + } + } + if (node) + node->rb_color = RB_BLACK; +} + +void rb_erase(struct rb_node * node, struct rb_root * root) +{ + struct rb_node * child, * parent; + int color; + + if (!node->rb_left) + child = node->rb_right; + else if (!node->rb_right) + child = node->rb_left; + else + { + struct rb_node * old = node, * left; + + node = node->rb_right; + while ((left = node->rb_left)) + node = left; + child = node->rb_right; + parent = node->rb_parent; + color = node->rb_color; + + if (child) + child->rb_parent = parent; + if (parent) + { + if (parent->rb_left == node) + parent->rb_left = child; + else + parent->rb_right = child; + } + else + root->rb_node = child; + + if (node->rb_parent == old) + parent = node; + node->rb_parent = old->rb_parent; + node->rb_color = old->rb_color; + node->rb_right = old->rb_right; + node->rb_left = old->rb_left; + + if (old->rb_parent) + { + if (old->rb_parent->rb_left == old) + old->rb_parent->rb_left = node; + else + old->rb_parent->rb_right = node; + } else + root->rb_node = node; + + old->rb_left->rb_parent = node; + if (old->rb_right) + old->rb_right->rb_parent = node; + goto color; + } + + parent = node->rb_parent; + color = node->rb_color; + + if (child) + child->rb_parent = parent; + if (parent) + { + if (parent->rb_left == node) + parent->rb_left = child; + else + parent->rb_right = child; + } + else + root->rb_node = child; + + color: + if (color == RB_BLACK) + __rb_erase_color(child, parent, root); +} +#endif /* Before 2.4.11 */ + + /* These routines haven't made it into 2.4 (yet) */ +struct rb_node *rb_next(struct rb_node *node) +{ + /* If we have a right-hand child, go down and then left as far + as we can. */ + if (node->rb_right) { + node = node->rb_right; + while (node->rb_left) + node=node->rb_left; + return node; + } + + /* No right-hand children. Everything down and left is + smaller than us, so any 'next' node must be in the general + direction of our parent. Go up the tree; any time the + ancestor is a right-hand child of its parent, keep going + up. First time it's a left-hand child of its parent, said + parent is our 'next' node. */ + while (node->rb_parent && node == node->rb_parent->rb_right) + node = node->rb_parent; + + return node->rb_parent; +} + +struct rb_node *rb_prev(struct rb_node *node) +{ + if (node->rb_left) { + node = node->rb_left; + while (node->rb_right) + node=node->rb_right; + return node; + } + while (node->rb_parent && node == node->rb_parent->rb_left) + node = node->rb_parent; + + return node->rb_parent; +} + +void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root) +{ + struct rb_node *parent = victim->rb_parent; + + /* Set the surrounding nodes to point to the replacement */ + if (parent) { + if (victim == parent->rb_left) + parent->rb_left = new; + else + parent->rb_right = new; + } else { + root->rb_node = new; + } + if (victim->rb_left) + victim->rb_left->rb_parent = new; + if (victim->rb_right) + victim->rb_right->rb_parent = new; + + /* Copy the pointers/colour from the victim to the replacement */ + *new = *victim; +} diff -NaurbB linux-2.6.9-pre1-bk18/fs/jffs2/scan.c linux-2.6.9_nbp/fs/jffs2/scan.c --- linux-2.6.9-pre1-bk18/fs/jffs2/scan.c 2004-08-14 07:37:56.000000000 +0200 +++ linux-2.6.9_nbp/fs/jffs2/scan.c 2004-09-13 12:43:28.000000000 +0200 @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: scan.c,v 1.110 2004/06/17 17:15:31 gleixner Exp $ + * $Id: scan.c,v 1.112 2004/09/12 09:56:13 gleixner Exp $ * */ #include @@ -104,6 +104,10 @@ else buf_size = PAGE_SIZE; + /* Respect kmalloc limitations */ + if (buf_size > 128*1024) + buf_size = 128*1024; + D1(printk(KERN_DEBUG "Allocating readbuf of %d bytes\n", buf_size)); flashbuf = kmalloc(buf_size, GFP_KERNEL); if (!flashbuf) @@ -237,7 +241,7 @@ } #endif if (c->nr_erasing_blocks) { - if ( !c->used_size && ((empty_blocks+bad_blocks)!= c->nr_blocks || bad_blocks == c->nr_blocks) ) { + if ( !c->used_size && ((c->nr_free_blocks+empty_blocks+bad_blocks)!= c->nr_blocks || bad_blocks == c->nr_blocks) ) { printk(KERN_NOTICE "Cowardly refusing to erase blocks on filesystem with no valid JFFS2 nodes\n"); printk(KERN_NOTICE "empty_blocks %d, bad_blocks %d, c->nr_blocks %d\n",empty_blocks,bad_blocks,c->nr_blocks); ret = -EIO; diff -NaurbB linux-2.6.9-pre1-bk18/fs/jffs2/super.c linux-2.6.9_nbp/fs/jffs2/super.c --- linux-2.6.9-pre1-bk18/fs/jffs2/super.c 2005-08-15 01:14:42.000000000 +0200 +++ linux-2.6.9_nbp/fs/jffs2/super.c 2004-09-28 15:52:52.000000000 +0200 @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: super.c,v 1.97 2004/07/16 15:17:57 dwmw2 Exp $ + * $Id: super.c,v 1.99 2004/08/24 07:59:57 dwmw2 Exp $ * */ @@ -130,7 +130,7 @@ mtd->index, mtd->name)); sb->s_op = &jffs2_super_operations; - sb->s_flags |= MS_NOATIME; + sb->s_flags = flags | MS_NOATIME; ret = jffs2_do_fill_super(sb, data, (flags&MS_VERBOSE)?1:0); @@ -266,7 +266,11 @@ up(&c->alloc_sem); jffs2_free_ino_caches(c); jffs2_free_raw_node_refs(c); +#ifdef CONFIG_JFFS2_LARGEMEM + vfree(c->blocks); +#else kfree(c->blocks); +#endif jffs2_flash_cleanup(c); kfree(c->inocache_list); if (c->mtd->sync) @@ -302,7 +306,7 @@ jffs2_inode_cachep = kmem_cache_create("jffs2_i", sizeof(struct jffs2_inode_info), - 0, SLAB_RECLAIM_ACCOUNT, + 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, jffs2_i_init_once, NULL); if (!jffs2_inode_cachep) { printk(KERN_ERR "JFFS2 error: Failed to initialise inode cache\n"); @@ -330,6 +334,7 @@ out_compressors: jffs2_compressors_exit(); out: + kmem_cache_destroy(jffs2_inode_cachep); return ret; } diff -NaurbB linux-2.6.9-pre1-bk18/fs/jffs2/super-v24.c linux-2.6.9_nbp/fs/jffs2/super-v24.c --- linux-2.6.9-pre1-bk18/fs/jffs2/super-v24.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/fs/jffs2/super-v24.c 2004-09-13 13:41:20.000000000 +0200 @@ -0,0 +1,167 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001-2003 Red Hat, Inc. + * + * Created by David Woodhouse + * + * For licensing information, see the file 'LICENCE' in this directory. + * + * $Id: super-v24.c,v 1.80 2004/07/16 15:17:57 dwmw2 Exp $ + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "compr.h" +#include "nodelist.h" + +#ifndef MTD_BLOCK_MAJOR +#define MTD_BLOCK_MAJOR 31 +#endif + +static void jffs2_put_super (struct super_block *); + +static struct super_operations jffs2_super_operations = +{ + .read_inode = jffs2_read_inode, + .put_super = jffs2_put_super, + .write_super = jffs2_write_super, + .statfs = jffs2_statfs, + .remount_fs = jffs2_remount_fs, + .clear_inode = jffs2_clear_inode, + .dirty_inode = jffs2_dirty_inode, +}; + + +static struct super_block *jffs2_read_super(struct super_block *sb, void *data, int silent) +{ + struct jffs2_sb_info *c; + int ret; + + D1(printk(KERN_DEBUG "jffs2: read_super for device %s\n", kdevname(sb->s_dev))); + + if (major(sb->s_dev) != MTD_BLOCK_MAJOR) { + if (!silent) + printk(KERN_DEBUG "jffs2: attempt to mount non-MTD device %s\n", kdevname(sb->s_dev)); + return NULL; + } + + c = JFFS2_SB_INFO(sb); + memset(c, 0, sizeof(*c)); + + sb->s_op = &jffs2_super_operations; + + c->mtd = get_mtd_device(NULL, minor(sb->s_dev)); + if (!c->mtd) { + D1(printk(KERN_DEBUG "jffs2: MTD device #%u doesn't appear to exist\n", minor(sb->s_dev))); + return NULL; + } + + ret = jffs2_do_fill_super(sb, data, silent); + if (ret) { + put_mtd_device(c->mtd); + return NULL; + } + + return sb; +} + +static void jffs2_put_super (struct super_block *sb) +{ + struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); + + D2(printk(KERN_DEBUG "jffs2: jffs2_put_super()\n")); + + + if (!(sb->s_flags & MS_RDONLY)) + jffs2_stop_garbage_collect_thread(c); + down(&c->alloc_sem); + jffs2_flush_wbuf_pad(c); + up(&c->alloc_sem); + jffs2_free_ino_caches(c); + jffs2_free_raw_node_refs(c); + kfree(c->blocks); + jffs2_flash_cleanup(c); + kfree(c->inocache_list); + if (c->mtd->sync) + c->mtd->sync(c->mtd); + put_mtd_device(c->mtd); + + D1(printk(KERN_DEBUG "jffs2_put_super returning\n")); +} + +static DECLARE_FSTYPE_DEV(jffs2_fs_type, "jffs2", jffs2_read_super); + +static int __init init_jffs2_fs(void) +{ + int ret; + + printk(KERN_INFO "JFFS2 version 2.2." +#ifdef CONFIG_JFFS2_FS_NAND + " (NAND)" +#endif + " (C) 2001-2003 Red Hat, Inc.\n"); + +#ifdef JFFS2_OUT_OF_KERNEL + /* sanity checks. Could we do these at compile time? */ + if (sizeof(struct jffs2_sb_info) > sizeof (((struct super_block *)NULL)->u)) { + printk(KERN_ERR "JFFS2 error: struct jffs2_sb_info (%d bytes) doesn't fit in the super_block union (%d bytes)\n", + sizeof(struct jffs2_sb_info), sizeof (((struct super_block *)NULL)->u)); + return -EIO; + } + + if (sizeof(struct jffs2_inode_info) > sizeof (((struct inode *)NULL)->u)) { + printk(KERN_ERR "JFFS2 error: struct jffs2_inode_info (%d bytes) doesn't fit in the inode union (%d bytes)\n", + sizeof(struct jffs2_inode_info), sizeof (((struct inode *)NULL)->u)); + return -EIO; + } +#endif + ret = jffs2_compressors_init(); + if (ret) { + printk(KERN_ERR "JFFS2 error: Failed to initialise compressors\n"); + goto out; + } + ret = jffs2_create_slab_caches(); + if (ret) { + printk(KERN_ERR "JFFS2 error: Failed to initialise slab caches\n"); + goto out_compressors; + } + ret = register_filesystem(&jffs2_fs_type); + if (ret) { + printk(KERN_ERR "JFFS2 error: Failed to register filesystem\n"); + goto out_slab; + } + return 0; + + out_slab: + jffs2_destroy_slab_caches(); + out_compressors: + jffs2_compressors_exit(); + out: + return ret; +} + +static void __exit exit_jffs2_fs(void) +{ + jffs2_destroy_slab_caches(); + jffs2_compressors_exit(); + unregister_filesystem(&jffs2_fs_type); +} + +module_init(init_jffs2_fs); +module_exit(exit_jffs2_fs); + +MODULE_DESCRIPTION("The Journalling Flash File System, v2"); +MODULE_AUTHOR("Red Hat, Inc."); +MODULE_LICENSE("GPL"); // Actually dual-licensed, but it doesn't matter for + // the sake of this tag. It's Free Software. diff -NaurbB linux-2.6.9-pre1-bk18/fs/jffs2/symlink-v24.c linux-2.6.9_nbp/fs/jffs2/symlink-v24.c --- linux-2.6.9-pre1-bk18/fs/jffs2/symlink-v24.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/fs/jffs2/symlink-v24.c 2004-09-13 13:41:20.000000000 +0200 @@ -0,0 +1,57 @@ +/* + * JFFS2 -- Journalling Flash File System, Version 2. + * + * Copyright (C) 2001, 2002 Red Hat, Inc. + * + * Created by David Woodhouse + * + * For licensing information, see the file 'LICENCE' in this directory. + * + * $Id: symlink-v24.c,v 1.14 2004/08/06 18:09:29 dmarlin Exp $ + * + */ + + +#include +#include +#include +#include "nodelist.h" + +int jffs2_readlink(struct dentry *dentry, char *buffer, int buflen); +int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd); + +struct inode_operations jffs2_symlink_inode_operations = +{ + .readlink = jffs2_readlink, + .follow_link = jffs2_follow_link, + .setattr = jffs2_setattr +}; + +int jffs2_readlink(struct dentry *dentry, char *buffer, int buflen) +{ + unsigned char *kbuf; + int ret; + + kbuf = jffs2_getlink(JFFS2_SB_INFO(dentry->d_inode->i_sb), JFFS2_INODE_INFO(dentry->d_inode)); + if (IS_ERR(kbuf)) + return PTR_ERR(kbuf); + + ret = vfs_readlink(dentry, buffer, buflen, kbuf); + kfree(kbuf); + return ret; +} + +int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd) +{ + unsigned char *buf; + int ret; + + buf = jffs2_getlink(JFFS2_SB_INFO(dentry->d_inode->i_sb), JFFS2_INODE_INFO(dentry->d_inode)); + + if (IS_ERR(buf)) + return PTR_ERR(buf); + + ret = vfs_follow_link(nd, buf); + kfree(buf); + return ret; +} diff -NaurbB linux-2.6.9-pre1-bk18/fs/jffs2/wbuf.c linux-2.6.9_nbp/fs/jffs2/wbuf.c --- linux-2.6.9-pre1-bk18/fs/jffs2/wbuf.c 2004-08-14 07:36:32.000000000 +0200 +++ linux-2.6.9_nbp/fs/jffs2/wbuf.c 2004-09-13 12:43:28.000000000 +0200 @@ -9,7 +9,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: wbuf.c,v 1.70 2004/07/13 08:58:25 dwmw2 Exp $ + * $Id: wbuf.c,v 1.72 2004/09/11 19:22:43 gleixner Exp $ * */ @@ -225,7 +225,7 @@ /* Do the read... */ ret = c->mtd->read_ecc(c->mtd, start, c->wbuf_ofs - start, &retlen, buf, NULL, c->oobinfo); - if (ret == -EIO && retlen == c->wbuf_ofs - start) { + if (ret == -EBADMSG && retlen == c->wbuf_ofs - start) { /* ECC recovered */ ret = 0; } @@ -791,7 +791,7 @@ if (!jffs2_can_mark_obsolete(c)) { ret = c->mtd->read_ecc(c->mtd, ofs, len, retlen, buf, NULL, c->oobinfo); - if ( (ret == -EIO) && (*retlen == len) ) { + if ( (ret == -EBADMSG) && (*retlen == len) ) { printk(KERN_WARNING "mtd->read(0x%zx bytes from 0x%llx) returned ECC error\n", len, ofs); /* @@ -1047,7 +1047,7 @@ if (oinfo && oinfo->useecc == MTD_NANDECC_AUTOPLACE) { D1(printk(KERN_DEBUG "JFFS2 using autoplace on NAND\n")); /* Get the position of the free bytes */ - if (!oinfo->oobfree[0][0]) { + if (!oinfo->oobfree[0][1]) { printk (KERN_WARNING "jffs2_nand_set_oobinfo(): Eeep. Autoplacement selected and no empty space in oob\n"); return -ENOSPC; } diff -NaurbB linux-2.6.9-pre1-bk18/fs/Kconfig linux-2.6.9_nbp/fs/Kconfig --- linux-2.6.9-pre1-bk18/fs/Kconfig 2005-08-15 01:14:42.000000000 +0200 +++ linux-2.6.9_nbp/fs/Kconfig 2004-09-28 15:53:55.000000000 +0200 @@ -1136,20 +1136,33 @@ messages at debug level 1 while the misbehaviour was occurring. config JFFS2_FS_NAND - bool "JFFS2 support for NAND flash" - depends on JFFS2_FS + bool "JFFS2 support for NAND flash (EXPERIMENTAL)" + depends on JFFS2_FS && EXPERIMENTAL default n help - This enables the support for NAND flash in JFFS2. NAND is a newer - type of flash chip design than the traditional NOR flash, with - higher density but a handful of characteristics which make it more - interesting for the file system to use. + This enables the experimental support for NAND flash in JFFS2. NAND + is a newer type of flash chip design than the traditional NOR flash, + with higher density but a handful of characteristics which make it + more interesting for the file system to use. Support for NAND flash + is not yet complete and may corrupt data. For further information, + including a link to the mailing list where details of the remaining + work to be completed for NAND flash support can be found, see the + JFFS2 web site at . + + Say 'N' unless you have NAND flash and you are willing to test and + develop JFFS2 support for it. - Say 'N' unless you have NAND flash. +config JFFS2_LARGEMEM + bool "JFFS2 large memory allocation" + depends on JFFS2_FS + default n + help + This enables the mounting of large flash chips with small + erase block sizes by allowing the allocation of more than 128KiB + of block info config JFFS2_COMPRESSION_OPTIONS bool "Advanced compression options for JFFS2" - depends on JFFS2_FS default n help Enabling this option allows you to explicitly choose which @@ -1160,6 +1173,25 @@ If unsure, you should _definitely_ say 'N'. +config JFFS2_LZARI + bool "JFFS2 LZARI compression support" if JFFS2_COMPRESSION_OPTIONS + depends on JFFS2_FS + default y + help + LZARI compression module + + Say 'Y' if unsure. + +config JFFS2_LZO + bool "JFFS2 LZO compression support" if JFFS2_COMPRESSION_OPTIONS + depends on JFFS2_FS + default y + help + LZO compression module + + Say 'Y' if unsure. + + config JFFS2_ZLIB bool "JFFS2 ZLIB compression support" if JFFS2_COMPRESSION_OPTIONS select ZLIB_INFLATE diff -NaurbB linux-2.6.9-pre1-bk18/include/asm-arm/apm.h linux-2.6.9_nbp/include/asm-arm/apm.h --- linux-2.6.9-pre1-bk18/include/asm-arm/apm.h 2005-08-15 01:14:42.000000000 +0200 +++ linux-2.6.9_nbp/include/asm-arm/apm.h 2004-09-19 13:17:49.000000000 +0200 @@ -14,43 +14,52 @@ #define ARM_ASM_SA1100_APM_H #include +#include -#if defined(CONFIG_APM) || defined(CONFIG_APM_MODULE) - - +/* + * This structure gets filled in by the machine specific 'get_power_status' + * implementation. Any fields which are not set default to a safe value. + */ +struct apm_power_info { + unsigned char ac_line_status; #define APM_AC_OFFLINE 0 #define APM_AC_ONLINE 1 #define APM_AC_BACKUP 2 -#define APM_AC_UNKNOWN 0xFF +#define APM_AC_UNKNOWN 0xff + unsigned char battery_status; #define APM_BATTERY_STATUS_HIGH 0 #define APM_BATTERY_STATUS_LOW 1 #define APM_BATTERY_STATUS_CRITICAL 2 #define APM_BATTERY_STATUS_CHARGING 3 -#define APM_BATTERY_STATUS_UNKNOWN 0xFF +#define APM_BATTERY_STATUS_NOT_PRESENT 4 +#define APM_BATTERY_STATUS_UNKNOWN 0xff -#define APM_BATTERY_LIFE_UNKNOWN 0xFFFF -#define APM_BATTERY_LIFE_MINUTES 0x8000 -#define APM_BATTERY_LIFE_VALUE_MASK 0x7FFF - -/* - * This structure gets filled in by the machine specific 'get_power_status' - * implementation. Any fields which are not set default to a safe value. - */ -struct apm_power_info { - unsigned char ac_line_status; - unsigned char battery_status; unsigned char battery_flag; - unsigned char battery_life; +#define APM_BATTERY_FLAG_HIGH (1 << 0) +#define APM_BATTERY_FLAG_LOW (1 << 1) +#define APM_BATTERY_FLAG_CRITICAL (1 << 2) +#define APM_BATTERY_FLAG_CHARGING (1 << 3) +#define APM_BATTERY_FLAG_NOT_PRESENT (1 << 7) +#define APM_BATTERY_FLAG_UNKNOWN 0xff + + int battery_life; int time; int units; +#define APM_UNITS_MINS 0 +#define APM_UNITS_SECS 1 +#define APM_UNITS_UNKNOWN -1 + }; /* * This allows machines to provide their own "apm get power status" function. */ extern void (*apm_get_power_status)(struct apm_power_info *); -#endif +/* + * Queue an event (APM_SYS_SUSPEND or APM_CRITICAL_SUSPEND) + */ +void apm_queue_event(apm_event_t event); #endif diff -NaurbB linux-2.6.9-pre1-bk18/include/asm-arm/arch-pxa/i2c.h linux-2.6.9_nbp/include/asm-arm/arch-pxa/i2c.h --- linux-2.6.9-pre1-bk18/include/asm-arm/arch-pxa/i2c.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/include/asm-arm/arch-pxa/i2c.h 2004-09-25 21:51:04.000000000 +0200 @@ -0,0 +1,70 @@ +/* + * i2c_pxa.h + * + * Copyright (C) 2002 Intrinsyc Software Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#ifndef _I2C_PXA_H_ +#define _I2C_PXA_H_ + +#if 0 +#define DEF_TIMEOUT 3 +#else +/* need a longer timeout if we're dealing with the fact we may well be + * looking at a multi-master environment +*/ +#define DEF_TIMEOUT 32 +#endif + +#define SLAVE_STARTED (-EDEADLK) +#define BUS_ERROR (-EREMOTEIO) +#define XFER_NAKED (-ECONNREFUSED) + +#define ACK_DELAY 0 /* time to delay before checking bus error */ +#define MAX_MESSAGES 65536 /* maximum number of messages to send */ + +#define I2C_SLEEP_TIMEOUT 2 /* time to sleep for on i2c transactions */ +#define I2C_RETRY (-2000) /* an error has occurred retry transmit */ +#define I2C_TRANSMIT 1 +#define I2C_RECEIVE 0 +#define I2C_PXA_SLAVE_ADDR 0x1 /* slave pxa unit address */ +#define I2C_ICR_INIT (ICR_BEIE | ICR_IRFIE | ICR_ITEIE | ICR_GCD | ICR_SCLE) /* ICR initialization value */ +/* ICR initialize bit values +* +* 15. FM 0 (100 Khz operation) +* 14. UR 0 (No unit reset) +* 13. SADIE 0 (Disables the unit from interrupting on slave addresses +* matching its slave address) +* 12. ALDIE 0 (Disables the unit from interrupt when it loses arbitration +* in master mode) +* 11. SSDIE 0 (Disables interrupts from a slave stop detected, in slave mode) +* 10. BEIE 1 (Enable interrupts from detected bus errors, no ACK sent) +* 9. IRFIE 1 (Enable interrupts from full buffer received) +* 8. ITEIE 1 (Enables the I2C unit to interrupt when transmit buffer empty) +* 7. GCD 1 (Disables i2c unit response to general call messages as a slave) +* 6. IUE 0 (Disable unit until we change settings) +* 5. SCLE 1 (Enables the i2c clock output for master mode (drives SCL) +* 4. MA 0 (Only send stop with the ICR stop bit) +* 3. TB 0 (We are not transmitting a byte initially) +* 2. ACKNAK 0 (Send an ACK after the unit receives a byte) +* 1. STOP 0 (Do not send a STOP) +* 0. START 0 (Do not send a START) +* +*/ + +#define I2C_ISR_INIT 0x7FF /* status register init */ +/* I2C status register init values + * + * 10. BED 1 (Clear bus error detected) + * 9. SAD 1 (Clear slave address detected) + * 7. IRF 1 (Clear IDBR Receive Full) + * 6. ITE 1 (Clear IDBR Transmit Empty) + * 5. ALD 1 (Clear Arbitration Loss Detected) + * 4. SSD 1 (Clear Slave Stop Detected) + */ + +#endif diff -NaurbB linux-2.6.9-pre1-bk18/include/asm-arm/arch-pxa/mmc.h linux-2.6.9_nbp/include/asm-arm/arch-pxa/mmc.h --- linux-2.6.9-pre1-bk18/include/asm-arm/arch-pxa/mmc.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/include/asm-arm/arch-pxa/mmc.h 2004-09-21 01:20:35.000000000 +0200 @@ -0,0 +1,19 @@ +#ifndef ASMARM_ARCH_MMC_H +#define ASMARM_ARCH_MMC_H + +#include +#include + +struct device; +struct mmc_host; + +struct pxamci_platform_data { + unsigned int ocr_mask; /* available voltages */ + int (*init)(struct device *, irqreturn_t (*)(int, void *, struct pt_regs *), void *); + void (*setpower)(struct device *, unsigned int); + void (*exit)(struct device *, void *); +}; + +extern void pxa_set_mci_info(struct pxamci_platform_data *info); + +#endif diff -NaurbB linux-2.6.9-pre1-bk18/include/asm-arm/arch-pxa/netbookpro.h linux-2.6.9_nbp/include/asm-arm/arch-pxa/netbookpro.h --- linux-2.6.9-pre1-bk18/include/asm-arm/arch-pxa/netbookpro.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/include/asm-arm/arch-pxa/netbookpro.h 2004-09-19 13:17:49.000000000 +0200 @@ -0,0 +1,55 @@ + +/* NandFlash is in CS1 */ +/* + * SL18HS, CS4, A0=AB16 + * SID13806, CS5, +*/ + +#define NETBOOKPRO_PA_NANDFLASH (0x04000000) +#define NETBOOKPRO_PA_SL81HS (0x10000000) + +#define EEPROM_NETBOOK2_ERASED_VALUE ( 0xff ) + +#define EEPROM_NETBOOK2_SIZE_IN_BYTES ( 128 ) + +#define EEPROM_NETBOOK2_MAX_ADDR ( EEPROM_NETBOOK2_SIZE_IN_BYTES - 1 ) + +// Address 0 - 9 +#define EEPROM_NETBOOK2_TERMINAL_SERIAL_NUMBER_SIZE ( 10 ) +#define EEPROM_NETBOOK2_TERMINAL_SERIAL_NUMBER_ADDRESS ( 0 ) + +// Address 10 - 19 +#define EEPROM_NETBOOK2_HARDWARE_SERIAL_NUMBER_SIZE ( 10 ) +#define EEPROM_NETBOOK2_HARDWARE_SERIAL_NUMBER_ADDRESS ( EEPROM_NETBOOK2_TERMINAL_SERIAL_NUMBER_ADDRESS + EEPROM_NETBOOK2_TERMINAL_SERIAL_NUMBER_SIZE ) + +// Address 20 +#define EEPROM_NETBOOK2_HARDWARE_REVISION_SIZE ( 1 ) +#define EEPROM_NETBOOK2_HARDWARE_REVISION_ADDRESS ( EEPROM_NETBOOK2_HARDWARE_SERIAL_NUMBER_ADDRESS + EEPROM_NETBOOK2_HARDWARE_SERIAL_NUMBER_SIZE ) + +// Address 21 - 40 +#define EEPROM_NETBOOK2_TOUCH_CALIBRATION_DATA_SIZE ( 20 ) +#define EEPROM_NETBOOK2_TOUCH_CALIBRATION_DATA_ADDRESS ( EEPROM_NETBOOK2_HARDWARE_REVISION_ADDRESS + EEPROM_NETBOOK2_HARDWARE_REVISION_SIZE ) + +// Address 41 +#define EEPROM_NETBOOK2_KEYBOARD_TYPE_SIZE ( 1 ) +#define EEPROM_NETBOOK2_KEYBOARD_TYPE_ADDRESS ( EEPROM_NETBOOK2_TOUCH_CALIBRATION_DATA_ADDRESS + EEPROM_NETBOOK2_TOUCH_CALIBRATION_DATA_SIZE ) + +// Address 42 - 43 +#define EEPROM_NETBOOK2_BATTERY_CAPACITY_SIZE ( 2 ) +#define EEPROM_NETBOOK2_BATTERY_CAPACITY_ADDRESS ( EEPROM_NETBOOK2_KEYBOARD_TYPE_ADDRESS + EEPROM_NETBOOK2_KEYBOARD_TYPE_SIZE ) + +// Address 44 +#define EEPROM_NETBOOK2_BOOST_SECURITY_SIZE ( 1 ) +#define EEPROM_NETBOOK2_BOOST_SECURITY_ADDRESS ( EEPROM_NETBOOK2_BATTERY_CAPACITY_ADDRESS + EEPROM_NETBOOK2_BATTERY_CAPACITY_SIZE ) + #define EEPROM_NETBOOK2_BOOST_SECURITY_DISABLED ( 0 ) + #define EEPROM_NETBOOK2_BOOST_SECURITY_ENABLED ( EEPROM_NETBOOK2_ERASED_VALUE ) + #define EEPROM_NETBOOK2_BOOST_SECURITY_LOCKDOWN ( 2 ) + + +extern int netbookpro_pm_pre_sleep(unsigned long resume_fn); +extern void netbookpro_pm_post_sleep(void); + +extern int nbp_pwr_waitchange(void); + +/* removed all the pcon stuff to seperate include file */ + diff -NaurbB linux-2.6.9-pre1-bk18/include/asm-arm/arch-pxa/netbookpro-ioctl.h linux-2.6.9_nbp/include/asm-arm/arch-pxa/netbookpro-ioctl.h --- linux-2.6.9-pre1-bk18/include/asm-arm/arch-pxa/netbookpro-ioctl.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/include/asm-arm/arch-pxa/netbookpro-ioctl.h 2004-09-19 13:17:49.000000000 +0200 @@ -0,0 +1,2 @@ + +#define NVRAM_UNLOCK_TOUCH_CALIBRATION _IO('p', 0x42) diff -NaurbB linux-2.6.9-pre1-bk18/include/asm-arm/arch-pxa/netbookpro-pcon.h linux-2.6.9_nbp/include/asm-arm/arch-pxa/netbookpro-pcon.h --- linux-2.6.9-pre1-bk18/include/asm-arm/arch-pxa/netbookpro-pcon.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/include/asm-arm/arch-pxa/netbookpro-pcon.h 2004-11-19 22:05:34.000000000 +0100 @@ -0,0 +1,138 @@ + +/* old ioctl() style commands */ + +#define I2C_SETREDLED _IOW('i', 1, int) +#define I2C_SETUSBHOSTPOWER _IOW('i', 2, int) +#define I2C_SETMMCPOWER _IOW('i', 3, int) +#define I2C_SETVMODEMPOWER _IOW('i', 4, int) +#define I2C_SETSERIALPOWER _IOW('i', 5, int) +#define I2C_SETPCMCIAPOWER _IOW('i', 6, int) +#define I2C_SETCFPOWER _IOW('i', 7, int) + +#define I2C_GETUSBHOSTPOWER _IOR('i', 8, int) +#define I2C_GETMMCPOWER _IOR('i', 9, int) +#define I2C_GETVMODEMPOWER _IOR('i', 10, int) +#define I2C_GETSERIALPOWER _IOR('i', 11, int) +#define I2C_GETPCMCIAPOWER _IOR('i', 12, int) +#define I2C_GETCFPOWER _IOR('i', 13, int) + +/* internal command definitions */ + +#define PCON_CMD(x) ((x)) + +#define PCON_MONITORBATTERY PCON_CMD(0x01) +#define PCON_KEYBOARD_CMD PCON_CMD(0x02) +#define PCON_POWER_STATE PCON_CMD(0x03) + +#define PCON_FLG_ASYNC (1) + +struct pcon_cmd_eedata { + unsigned int addr; + unsigned int length; + unsigned char *data; +}; + +/* values for system */ + +typedef enum { + PCON_CMD_Ready = 0, + PCON_CMD_ADCRead = 9, + PCON_CMD_GetVersion = 10, + PCON_CMD_SetMMCPower = 11, + PCON_CMD_SetModemPower = 12, + PCON_CMD_SetUSBPower = 13, + PCON_CMD_SetRedLED = 14, + PCON_CMD_SetSerialPower = 15, + PCON_CMD_GetSerialCfg = 17, + PCON_CMD_BatteryMonitor = 18, + PCON_CMD_GetMMCPower = 19, + PCON_CMD_GetVModemPower = 20, + PCON_CMD_GetUSBPower = 21, + PCON_CMD_SetWakeupAlarm = 22, +} pcon_cmd_t; + + +typedef enum pcon_pwrstate_e { + PCON_PWRSTATE_HWReset = 0, + PCON_PWRSTATE_SWReset = 1, + PCON_PWRSTATE_Off = 2, + PCON_PWRSTATE_Suspend = 3, + PCON_PWRSTATE_Sleep = 4, + PCON_PWRSTATE_Booting = 5, + PCON_PWRSTATE_LoadingOS = 6, + PCON_PWRSTATE_Idle = 7, + PCON_PWRSTATE_Run = 8, + PCON_PWRSTATE_Comatose = 9, + PCON_PWRSTATE_PowerFail = 10 +} pcon_pwrstate_t; + + +#define NETBOOKPRO_PCON_KEYBOARD_CMD_ADDR (0x00) +#define NETBOOKPRO_PCON_KEYBOARD_CMD_VAL_READY (0) +#define NETBOOKPRO_PCON_KEYBOARD_CMD_VAL_BUSY (1) + +#define NETBOOKPRO_PCON_POWER_PCMCIA (3) +#define NETBOOKPRO_PCON_POWER_CF (2) + +#define NETBOOKPRO_PCON_POWERCONFIG (4) +#define NETBOOKPRO_PCON_POWER (5) + +#define NETBOOKPRO_PCON_COMMAND_ADDR (12) + + +#define NETBOOKPRO_I2C_EEPEMU_KEYBOARD_START (0x00) +#define NETBOOKPRO_I2C_EEPEMU_KEYBOARD_SIZE (1) + +#define NETBOOKPRO_I2C_EEPEMU_SERIAL_START (0x02) +#define NETBOOKPRO_I2C_EEPEMU_SERIAL_SIZE (1) + +#define NETBOOKPRO_I2C_EEPEMU_POWER_START (0x05) +#define NETBOOKPRO_I2C_EEPEMU_POWER_SIZE (1) + +#define NETBOOKPRO_I2C_EEPEMU_CMDRESP_START (13) +#define NETBOOKPRO_I2C_EEPEMU_CMDRESP_SIZE (4) + + +/* exported functions */ + +extern int nbp_pcon_write2(unsigned int, unsigned int); +extern int nbp_pcon_command(unsigned long cmd, unsigned int arg, + unsigned int flags, int *result); + +extern int nbp_pcon_ready(void); + +struct rtc_time; +extern int nbp_pcon_rtc_wakeup(struct rtc_time *tm); + +struct pcon_device; + +struct pcon_interface { + struct list_head node; + int (*add)(struct pcon_device *); + void (*remove)(struct pcon_device *); + int (*suspend)(struct pcon_device *, int); + int (*resume)(struct pcon_device *); +}; + +/* + * New interface. pcon "drivers" should use this. + */ +int pcon_drv_add(struct pcon_interface *intf); +void pcon_drv_remove(struct pcon_interface *intf); + +struct pcon_device { + struct list_head node; + struct pcon_interface *intf; + struct device *dev; + void *data; +}; + +#define clsdev_to_pcon_dev(cdev) container_of(cdev, struct pcon_device, cdev) + +/* + * Use this to register the pcon "device". + */ +struct pcon_device *pcon_dev_add(const char *name, struct device *dev, void *data); +void pcon_dev_remove(struct pcon_device *cdev); +int pcon_dev_resume(struct pcon_device *pcon); +int pcon_dev_suspend(struct pcon_device *pcon, int state); diff -NaurbB linux-2.6.9-pre1-bk18/include/asm-arm/arch-pxa/pxa-regs.h linux-2.6.9_nbp/include/asm-arm/arch-pxa/pxa-regs.h --- linux-2.6.9-pre1-bk18/include/asm-arm/arch-pxa/pxa-regs.h 2005-08-15 01:14:42.000000000 +0200 +++ linux-2.6.9_nbp/include/asm-arm/arch-pxa/pxa-regs.h 2004-10-06 00:58:13.000000000 +0200 @@ -180,12 +180,16 @@ #define DRCMRTXPCDR DRCMR12 #define DRCMRRXSSDR DRCMR13 #define DRCMRTXSSDR DRCMR14 +#define DRCMRRXSS2DR DRCMR15 +#define DRCMRTXSS2DR DRCMR16 #define DRCMRRXICDR DRCMR17 #define DRCMRTXICDR DRCMR18 #define DRCMRRXSTRBR DRCMR19 #define DRCMRTXSTTHR DRCMR20 #define DRCMRRXMMC DRCMR21 #define DRCMRTXMMC DRCMR22 +#define DRCMRRXSS3DR DRCMR66 +#define DRCMRTXSS3DR DRCMR67 #define DRCMRUDC(x) DRCMR((x) + 24) #define DRCMR_MAPVLD (1 << 7) /* Map Valid (read / write) */ @@ -466,6 +470,43 @@ #define SADR __REG(0x40400080) /* Serial Audio Data Register (TX and RX FIFO access Register). */ #endif +#define SACR0_RFTH(x) (x << 12) /* Rx FIFO Interrupt or DMA Trigger Threshold */ +#define SACR0_TFTH(x) (x << 8) /* Tx FIFO Interrupt or DMA Trigger Threshold */ +#define SACR0_STRF (1 << 5) /* FIFO Select for EFWR Special Function */ +#define SACR0_EFWR (1 << 4) /* Enable EFWR Function */ +#define SACR0_RST (1 << 3) /* FIFO, i2s Register Reset */ +#define SACR0_BCKD (1 << 2) /* Bit Clock Direction */ +#define SACR0_ENB (1 << 0) /* Enable I2S Link */ + +#define SACR1_ENLBF (1 << 5) /* Enable Loopback */ +#define SACR1_DRPL (1 << 4) /* Disable Replaying Function */ +#define SACR1_DREC (1 << 3) /* Disable Recording Function */ +#define SACR1_AMSL (1 << 1) /* Specify Alternate Mode */ + +#define SASR0_I2SOFF (1 << 7) /* Controller Status */ +#define SASR0_ROR (1 << 6) /* Rx FIFO Overrun */ +#define SASR0_TUR (1 << 5) /* Tx FIFO Underrun */ +#define SASR0_RFS (1 << 4) /* Rx FIFO Service Request */ +#define SASR0_TFS (1 << 3) /* Tx FIFO Service Request */ +#define SASR0_BSY (1 << 2) /* I2S Busy */ +#define SASR0_RNE (1 << 1) /* Rx FIFO Not Empty */ +#define SASR0_TNF (1 << 0) /* Tx FIFO Not Empty */ + +#define SADIV_3_058M 0x0c /* Serial Clock Divider 3.058MHz */ +#define SADIV_2_836M 0x0d /* 2.836 MHz */ +#define SADIV_1_405M 0x1a /* 1.405 MHz */ +#define SADIV_1_026M 0x24 /* 1.026 MHz */ +#define SADIV_702K 0x34 /* 702 kHz */ +#define SADIV_513K 0x48 /* 513 kHz */ + +#define SAICR_ROR (1 << 6) /* Clear Rx FIFO Overrun Interrupt */ +#define SAICR_TUR (1 << 5) /* Clear Tx FIFO Underrun Interrupt */ + +#define SAIMR_ROR (1 << 6) /* Enable Rx FIFO Overrun Condition Interrupt */ +#define SAIMR_TUR (1 << 5) /* Enable Tx FIFO Underrun Condition Interrupt */ +#define SAIMR_RFS (1 << 4) /* Enable Rx FIFO Service Interrupt */ +#define SAIMR_TFS (1 << 3) /* Enable Tx FIFO Service Interrupt */ + /* * AC97 Controller registers @@ -1200,6 +1241,7 @@ #define GPIO43_BTTXD 43 /* BTUART transmit data */ #define GPIO44_BTCTS 44 /* BTUART clear to send */ #define GPIO45_BTRTS 45 /* BTUART request to send */ +#define GPIO45_AC97_SYSCLK 45 /* AC97 System Clock */ #define GPIO46_ICPRXD 46 /* ICP receive data */ #define GPIO46_STRXD 46 /* STD_UART receive data */ #define GPIO47_ICPTXD 47 /* ICP transmit data */ @@ -1307,6 +1349,7 @@ #define GPIO43_BTTXD_MD (43 | GPIO_ALT_FN_2_OUT) #define GPIO44_BTCTS_MD (44 | GPIO_ALT_FN_1_IN) #define GPIO45_BTRTS_MD (45 | GPIO_ALT_FN_2_OUT) +#define GPIO45_SYSCLK_AC97_MD (45 | GPIO_ALT_FN_1_OUT) #define GPIO46_ICPRXD_MD (46 | GPIO_ALT_FN_1_IN) #define GPIO46_STRXD_MD (46 | GPIO_ALT_FN_2_IN) #define GPIO47_ICPTXD_MD (47 | GPIO_ALT_FN_2_OUT) @@ -1354,7 +1397,8 @@ #define GPIO78_nCS_2_MD (78 | GPIO_ALT_FN_2_OUT) #define GPIO79_nCS_3_MD (79 | GPIO_ALT_FN_2_OUT) #define GPIO80_nCS_4_MD (80 | GPIO_ALT_FN_2_OUT) - +#define GPIO117_I2CSCL_MD (117 | GPIO_ALT_FN_1_OUT) +#define GPIO118_I2CSDA_MD (118 | GPIO_ALT_FN_1_IN) /* * Power Manager @@ -1451,14 +1495,11 @@ /* * SSP Serial Port Registers + * PXA250, PXA255, PXA26x and PXA27x SSP controllers are all slightly different. + * PXA255, PXA26x and PXA27x have extra ports, registers and bits. */ -#define SSCR0 __REG(0x41000000) /* SSP Control Register 0 */ -#define SSCR1 __REG(0x41000004) /* SSP Control Register 1 */ -#define SSSR __REG(0x41000008) /* SSP Status Register */ -#define SSITR __REG(0x4100000C) /* SSP Interrupt Test Register */ -#define SSDR __REG(0x41000010) /* (Write / Read) SSP Data Write Register/SSP Data Read Register */ - + /* Common PXA2xx bits first */ #define SSCR0_DSS (0x0000000f) /* Data Size Select (mask) */ #define SSCR0_DataSize(x) ((x) - 1) /* Data Size Select [4..16] */ #define SSCR0_FRF (0x00000030) /* FRame Format (mask) */ @@ -1488,6 +1529,103 @@ #define SSSR_RFS (1 << 6) /* Receive FIFO Service Request */ #define SSSR_ROR (1 << 7) /* Receive FIFO Overrun */ +#define SSCR0_TIM (1 << 23) /* Transmit FIFO Under Run Interrupt Mask */ +#define SSCR0_RIM (1 << 22) /* Receive FIFO Over Run interrupt Mask */ +#define SSCR0_NCS (1 << 21) /* Network Clock Select */ +#define SSCR0_EDSS (1 << 20) /* Extended Data Size Select */ + +/* extra bits in PXA255, PXA26x and PXA27x SSP ports */ +#define SSCR1_TTELP (1 << 31) /* TXD Tristate Enable Last Phase */ +#define SSCR1_TTE (1 << 30) /* TXD Tristate Enable */ +#define SSCR1_EBCEI (1 << 29) /* Enable Bit Count Error interrupt */ +#define SSCR1_SCFR (1 << 28) /* Slave Clock free Running */ +#define SSCR1_ECRA (1 << 27) /* Enable Clock Request A */ +#define SSCR1_ECRB (1 << 26) /* Enable Clock request B */ +#define SSCR1_SCLKDIR (1 << 25) /* Serial Bit Rate Clock Direction */ +#define SSCR1_SFRMDIR (1 << 24) /* Frame Direction */ +#define SSCR1_RWOT (1 << 23) /* Receive Without Transmit */ +#define SSCR1_TRAIL (1 << 22) /* Trailing Byte */ +#define SSCR1_TSRE (1 << 21) /* Transmit Service Request Enable */ +#define SSCR1_RSRE (1 << 20) /* Receive Service Request Enable */ +#define SSCR1_TINTE (1 << 19) /* Receiver Time-out Interrupt enable */ +#define SSCR1_PINTE (1 << 18) /* Peripheral Trailing Byte Interupt Enable */ +#define SSCR1_STRF (1 << 15) /* Select FIFO or EFWR */ +#define SSCR1_EFWR (1 << 14) /* Enable FIFO Write/Read */ + +#define SSSR_BCE (1 << 23) /* Bit Count Error */ +#define SSSR_CSS (1 << 22) /* Clock Synchronisation Status */ +#define SSSR_TUR (1 << 21) /* Transmit FIFO Under Run */ +#define SSSR_EOC (1 << 20) /* End Of Chain */ +#define SSSR_TINT (1 << 19) /* Receiver Time-out Interrupt */ +#define SSSR_PINT (1 << 18) /* Peripheral Trailing Byte Interrupt */ + +#define SSPSP_DMYSTOP(x) (x << 23) /* Dummy Stop */ +#define SSPSP_SFRMWDTH(x) (x << 16) /* Serial Frame Width */ +#define SSPSP_SFRMDLY(x) (x << 9) /* Serial Frame Delay */ +#define SSPSP_DMYSTRT(x) (x << 7) /* Dummy Start */ +#define SSPSP_STRTDLY(x) (x << 4) /* Start Delay */ +#define SSPSP_ETDS (1 << 3) /* End of Transfer data State */ +#define SSPSP_SFRMP (1 << 2) /* Serial Frame Polarity */ +#define SSPSP_SCMODE(x) (x << 0) /* Serial Bit Rate Clock Mode */ + + +#define SSCR0_P1 __REG(0x41000000) /* SSP Port 1 Control Register 0 */ +#define SSCR1_P1 __REG(0x41000004) /* SSP Port 1 Control Register 1 */ +#define SSSR_P1 __REG(0x41000008) /* SSP Port 1 Status Register */ +#define SSITR_P1 __REG(0x4100000C) /* SSP Port 1 Interrupt Test Register */ +#define SSDR_P1 __REG(0x41000010) /* (Write / Read) SSP Port 1 Data Write Register/SSP Data Read Register */ + +/* Support existing PXA25x drivers */ +#define SSCR0 SSCR0_P1 /* SSP Control Register 0 */ +#define SSCR1 SSCR1_P1 /* SSP Control Register 1 */ +#define SSSR SSSR_P1 /* SSP Status Register */ +#define SSITR SSITR_P1 /* SSP Interrupt Test Register */ +#define SSDR SSDR_P1 /* (Write / Read) SSP Data Write Register/SSP Data Read Register */ + +/* PXA27x ports */ +#if defined (CONFIG_PXA27x) +#define SSTO_P1 __REG(0x41000028) /* SSP Port 1 Time Out Register */ +#define SSPSP_P1 __REG(0x4100002C) /* SSP Port 1 Programmable Serial Protocol */ +#define SSCR0_P2 __REG(0x41700000) /* SSP Port 2 Control Register 0 */ +#define SSCR1_P2 __REG(0x41700004) /* SSP Port 2 Control Register 1 */ +#define SSSR_P2 __REG(0x41700008) /* SSP Port 2 Status Register */ +#define SSITR_P2 __REG(0x4170000C) /* SSP Port 2 Interrupt Test Register */ +#define SSDR_P2 __REG(0x41700010) /* (Write / Read) SSP Port 2 Data Write Register/SSP Data Read Register */ +#define SSTO_P2 __REG(0x41700028) /* SSP Port 2 Time Out Register */ +#define SSPSP_P2 __REG(0x4170002C) /* SSP Port 2 Programmable Serial Protocol */ +#define SSCR0_P3 __REG(0x41900000) /* SSP Port 3 Control Register 0 */ +#define SSCR1_P3 __REG(0x41900004) /* SSP Port 3 Control Register 1 */ +#define SSSR_P3 __REG(0x41900008) /* SSP Port 3 Status Register */ +#define SSITR_P3 __REG(0x4190000C) /* SSP Port 3 Interrupt Test Register */ +#define SSDR_P3 __REG(0x41900010) /* (Write / Read) SSP Port 3 Data Write Register/SSP Data Read Register */ +#define SSTO_P3 __REG(0x41900028) /* SSP Port 3 Time Out Register */ +#define SSPSP_P3 __REG(0x4190002C) /* SSP Port 3 Programmable Serial Protocol */ +#else /* PXA255 (only port 2) and PXA26x ports*/ +#define SSTO_P1 __REG(0x41000028) /* SSP Port 1 Time Out Register */ +#define SSPSP_P1 __REG(0x4100002C) /* SSP Port 1 Programmable Serial Protocol */ +#define SSCR0_P2 __REG(0x41400000) /* SSP Port 2 Control Register 0 */ +#define SSCR1_P2 __REG(0x41400004) /* SSP Port 2 Control Register 1 */ +#define SSSR_P2 __REG(0x41400008) /* SSP Port 2 Status Register */ +#define SSITR_P2 __REG(0x4140000C) /* SSP Port 2 Interrupt Test Register */ +#define SSDR_P2 __REG(0x41400010) /* (Write / Read) SSP Port 2 Data Write Register/SSP Data Read Register */ +#define SSTO_P2 __REG(0x41400028) /* SSP Port 2 Time Out Register */ +#define SSPSP_P2 __REG(0x4140002C) /* SSP Port 2 Programmable Serial Protocol */ +#define SSCR0_P3 __REG(0x41500000) /* SSP Port 3 Control Register 0 */ +#define SSCR1_P3 __REG(0x41500004) /* SSP Port 3 Control Register 1 */ +#define SSSR_P3 __REG(0x41500008) /* SSP Port 3 Status Register */ +#define SSITR_P3 __REG(0x4150000C) /* SSP Port 3 Interrupt Test Register */ +#define SSDR_P3 __REG(0x41500010) /* (Write / Read) SSP Port 3 Data Write Register/SSP Data Read Register */ +#define SSTO_P3 __REG(0x41500028) /* SSP Port 3 Time Out Register */ +#define SSPSP_P3 __REG(0x4150002C) /* SSP Port 3 Programmable Serial Protocol */ +#endif + +#define SSCR0_P(x) (*(((x) == 1) ? &SSCR0_P1 : ((x) == 2) ? &SSCR0_P2 : ((x) == 3) ? &SSCR0_P3 : NULL)) +#define SSCR1_P(x) (*(((x) == 1) ? &SSCR1_P1 : ((x) == 2) ? &SSCR1_P2 : ((x) == 3) ? &SSCR1_P3 : NULL)) +#define SSSR_P(x) (*(((x) == 1) ? &SSSR_P1 : ((x) == 2) ? &SSSR_P2 : ((x) == 3) ? &SSSR_P3 : NULL)) +#define SSITR_P(x) (*(((x) == 1) ? &SSITR_P1 : ((x) == 2) ? &SSITR_P2 : ((x) == 3) ? &SSITR_P3 : NULL)) +#define SSDR_P(x) (*(((x) == 1) ? &SSDR_P1 : ((x) == 2) ? &SSDR_P2 : ((x) == 3) ? &SSDR_P3 : NULL)) +#define SSTO_P(x) (*(((x) == 1) ? &SSTO_P1 : ((x) == 2) ? &SSTO_P2 : ((x) == 3) ? &SSTO_P3 : NULL)) +#define SSPSP_P(x) (*(((x) == 1) ? &SSPSP_P1 : ((x) == 2) ? &SSPSP_P2 : ((x) == 3) ? &SSPSP_P3 : NULL)) /* * MultiMediaCard (MMC) controller @@ -1540,8 +1678,10 @@ #define CKEN13_FICP (1 << 13) /* FICP Unit Clock Enable */ #define CKEN12_MMC (1 << 12) /* MMC Unit Clock Enable */ #define CKEN11_USB (1 << 11) /* USB Unit Clock Enable */ +#define CKEN10_ASSP (1 << 10) /* ASSP (SSP3) Clock Enable */ #define CKEN10_USBHOST (1 << 10) /* USB Host Unit Clock Enable */ #define CKEN9_OSTIMER (1 << 9) /* OS Timer Unit Clock Enable */ +#define CKEN9_NSSP (1 << 9) /* NSSP (SSP2) Clock Enable */ #define CKEN8_I2S (1 << 8) /* I2S Unit Clock Enable */ #define CKEN7_BTUART (1 << 7) /* BTUART Unit Clock Enable */ #define CKEN6_FFUART (1 << 6) /* FFUART Unit Clock Enable */ diff -NaurbB linux-2.6.9-pre1-bk18/include/asm-arm/arch-pxa/serial.h linux-2.6.9_nbp/include/asm-arm/arch-pxa/serial.h --- linux-2.6.9-pre1-bk18/include/asm-arm/arch-pxa/serial.h 2004-08-14 07:36:58.000000000 +0200 +++ linux-2.6.9_nbp/include/asm-arm/arch-pxa/serial.h 2004-09-26 14:16:57.000000000 +0200 @@ -15,6 +15,7 @@ /* Standard COM flags */ #define STD_COM_FLAGS (ASYNC_SKIP_TEST) +#if !defined(CONFIG_SERIAL_PXA_16550) && !defined(CONFIG_SERIAL_PXA_ALTMAJOR) #define STD_SERIAL_PORT_DEFNS \ { \ type: PORT_PXA, \ @@ -44,6 +45,10 @@ irq: IRQ_BTUART, \ flags: STD_COM_FLAGS, \ } +#else +#define STD_SERIAL_PORT_DEFNS +#define BASE_BAUD (1843200 / 16) +#endif #define EXTRA_SERIAL_PORT_DEFNS diff -NaurbB linux-2.6.9-pre1-bk18/include/asm-arm/mach/time.h linux-2.6.9_nbp/include/asm-arm/mach/time.h --- linux-2.6.9-pre1-bk18/include/asm-arm/mach/time.h 2004-08-14 07:36:57.000000000 +0200 +++ linux-2.6.9_nbp/include/asm-arm/mach/time.h 2004-09-25 21:51:09.000000000 +0200 @@ -13,8 +13,11 @@ extern void (*init_arch_time)(void); extern int (*set_rtc)(void); -extern unsigned long(*gettimeoffset)(void); +extern unsigned long (*gettimeoffset)(void); -void timer_tick(struct pt_regs *); +extern void timer_tick(struct pt_regs *); + +extern long save_time_delta(unsigned long rtc_secs); +extern void restore_time_delta(unsigned long rtc_secs, long delta); #endif diff -NaurbB linux-2.6.9-pre1-bk18/include/linux/ac97_codec.h linux-2.6.9_nbp/include/linux/ac97_codec.h --- linux-2.6.9-pre1-bk18/include/linux/ac97_codec.h 2004-08-14 07:36:13.000000000 +0200 +++ linux-2.6.9_nbp/include/linux/ac97_codec.h 2004-09-21 01:20:35.000000000 +0200 @@ -299,6 +299,8 @@ extern unsigned int ac97_set_dac_rate(struct ac97_codec *codec, unsigned int rate); extern int ac97_save_state(struct ac97_codec *codec); extern int ac97_restore_state(struct ac97_codec *codec); +extern int ac97_suspend(struct ac97_codec *codec, int state); +extern void ac97_resume(struct ac97_codec *codec); extern struct ac97_codec *ac97_alloc_codec(void); extern void ac97_release_codec(struct ac97_codec *codec); @@ -310,6 +312,8 @@ u32 codec_mask; int (*probe) (struct ac97_codec *codec, struct ac97_driver *driver); void (*remove) (struct ac97_codec *codec, struct ac97_driver *driver); + int (*suspend) (struct ac97_codec *codec, int state); + void (*resume) (struct ac97_codec *codec); }; extern int ac97_register_driver(struct ac97_driver *driver); diff -NaurbB linux-2.6.9-pre1-bk18/include/linux/dma-mapping.h linux-2.6.9_nbp/include/linux/dma-mapping.h --- linux-2.6.9-pre1-bk18/include/linux/dma-mapping.h 2005-08-15 01:14:42.000000000 +0200 +++ linux-2.6.9_nbp/include/linux/dma-mapping.h 2004-11-22 17:13:18.000000000 +0100 @@ -2,6 +2,7 @@ #define _ASM_LINUX_DMA_MAPPING_H #include +#include /* These definitions mirror those in pci.h, so they can be used * interchangeably with their PCI_ counterparts */ diff -NaurbB linux-2.6.9-pre1-bk18/include/linux/i2c-algo-pxa.h linux-2.6.9_nbp/include/linux/i2c-algo-pxa.h --- linux-2.6.9-pre1-bk18/include/linux/i2c-algo-pxa.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/include/linux/i2c-algo-pxa.h 2004-09-25 21:50:58.000000000 +0200 @@ -0,0 +1,47 @@ +struct i2c_eeprom_emu; +struct i2c_eeprom_emu_byte; + +struct i2c_eeprom_emu_watcher { + int (*read)(struct i2c_eeprom_emu *, int addr); + int (*write)(struct i2c_eeprom_emu *, int addr, int newval); +}; + +struct i2c_eeprom_emu_byte { + unsigned long last_modified; + struct i2c_eeprom_emu_watcher *watcher; + unsigned char val; +}; + +#define I2C_EEPROM_EMU_SIZE (256) + +struct i2c_eeprom_emu { + int size; + int ptr; + int seen_start; + + struct i2c_eeprom_emu_byte bytes[I2C_EEPROM_EMU_SIZE]; +}; + +typedef enum i2c_slave_event_e { + I2C_SLAVE_EVENT_NONE, + I2C_SLAVE_EVENT_START, /* start */ + I2C_SLAVE_EVENT_STOP +} i2c_slave_event_t; + +typedef enum i2c_slave_mode_e { + I2C_SLAVE_START_READ, + I2C_SLAVE_START_WRITE +} i2c_slave_mode_t; + +struct i2c_slave_client { + int (*event)(void *pw, i2c_slave_event_t event, int value); + int (*read) (void *pw); + int (*write)(void *pw, int val); +}; + +extern int i2c_eeprom_emu_addwatcher(struct i2c_eeprom_emu *, + int addr, int size, + struct i2c_eeprom_emu_watcher *); + + +extern struct i2c_eeprom_emu *i2c_pxa_get_eeprom(void); diff -NaurbB linux-2.6.9-pre1-bk18/include/linux/i2c.h linux-2.6.9_nbp/include/linux/i2c.h --- linux-2.6.9-pre1-bk18/include/linux/i2c.h 2005-08-15 01:14:33.000000000 +0200 +++ linux-2.6.9_nbp/include/linux/i2c.h 2004-09-25 21:50:53.000000000 +0200 @@ -137,6 +137,9 @@ */ int (*command)(struct i2c_client *client,unsigned int cmd, void *arg); + int (*suspend)(struct i2c_client *client, u32 state); + int (*resume)(struct i2c_client *client); + struct device_driver driver; struct list_head list; }; diff -NaurbB linux-2.6.9-pre1-bk18/include/linux/i2c-id.h linux-2.6.9_nbp/include/linux/i2c-id.h --- linux-2.6.9-pre1-bk18/include/linux/i2c-id.h 2005-08-15 01:14:33.000000000 +0200 +++ linux-2.6.9_nbp/include/linux/i2c-id.h 2004-09-14 12:25:07.000000000 +0200 @@ -110,6 +110,8 @@ #define I2C_DRIVERID_TDA7313 62 /* TDA7313 audio processor */ #define I2C_DRIVERID_MAX6900 63 /* MAX6900 real-time clock */ +#define I2C_DRIVERID_24LC64 56 /* EEprom 24LC64 */ +#define I2C_DRIVERID_FM24CLB4 57 /* EEprom FM24CLB4 */ #define I2C_DRIVERID_EXP0 0xF0 /* experimental use id's */ #define I2C_DRIVERID_EXP1 0xF1 @@ -196,6 +198,8 @@ #define I2C_ALGO_OCP_IOP3XX 0x140000 /* XSCALE IOP3XX On-chip I2C alg */ #define I2C_ALGO_PCA 0x150000 /* PCA 9564 style adapters */ +#define I2C_ALGO_PXA 0x400000 /* Intel PXA I2C algorithm */ + #define I2C_ALGO_EXP 0x800000 /* experimental */ #define I2C_ALGO_MASK 0xff0000 /* Mask for algorithms */ diff -NaurbB linux-2.6.9-pre1-bk18/include/linux/mmc/card.h linux-2.6.9_nbp/include/linux/mmc/card.h --- linux-2.6.9-pre1-bk18/include/linux/mmc/card.h 2005-08-15 01:14:33.000000000 +0200 +++ linux-2.6.9_nbp/include/linux/mmc/card.h 2004-09-21 01:20:35.000000000 +0200 @@ -14,16 +14,17 @@ struct mmc_cid { unsigned int manfid; - unsigned int serial; char prod_name[8]; + unsigned int serial; + unsigned short oemid; + unsigned short year; unsigned char hwrev; unsigned char fwrev; unsigned char month; - unsigned char year; }; struct mmc_csd { - unsigned char mmc_prot; + unsigned char mmca_vsn; unsigned short cmdclass; unsigned short tacc_clks; unsigned int tacc_ns; @@ -43,14 +44,22 @@ struct device dev; /* the device */ unsigned int rca; /* relative card address of device */ unsigned int state; /* (our) card state */ -#define MMC_STATE_PRESENT (1<<0) -#define MMC_STATE_DEAD (1<<1) +#define MMC_STATE_PRESENT (1<<0) /* present in sysfs */ +#define MMC_STATE_DEAD (1<<1) /* device no longer in stack */ +#define MMC_STATE_BAD (1<<2) /* unrecognised device */ + u32 raw_cid[4]; /* raw card CID */ + u32 raw_csd[4]; /* raw card CSD */ struct mmc_cid cid; /* card identification */ struct mmc_csd csd; /* card specific */ }; -#define mmc_card_dead(c) ((c)->state & MMC_STATE_DEAD) #define mmc_card_present(c) ((c)->state & MMC_STATE_PRESENT) +#define mmc_card_dead(c) ((c)->state & MMC_STATE_DEAD) +#define mmc_card_bad(c) ((c)->state & MMC_STATE_BAD) + +#define mmc_card_set_present(c) ((c)->state |= MMC_STATE_PRESENT) +#define mmc_card_set_dead(c) ((c)->state |= MMC_STATE_DEAD) +#define mmc_card_set_bad(c) ((c)->state |= MMC_STATE_BAD) #define mmc_card_name(c) ((c)->cid.prod_name) #define mmc_card_id(c) ((c)->dev.bus_id) diff -NaurbB linux-2.6.9-pre1-bk18/include/linux/mtd/cfi.h linux-2.6.9_nbp/include/linux/mtd/cfi.h --- linux-2.6.9-pre1-bk18/include/linux/mtd/cfi.h 2004-08-14 07:36:56.000000000 +0200 +++ linux-2.6.9_nbp/include/linux/mtd/cfi.h 2004-09-13 12:43:28.000000000 +0200 @@ -1,7 +1,7 @@ /* Common Flash Interface structures * See http://support.intel.com/design/flash/technote/index.htm - * $Id: cfi.h,v 1.45 2004/07/20 02:44:27 dwmw2 Exp $ + * $Id: cfi.h,v 1.46 2004/08/12 07:49:04 eric Exp $ */ #ifndef __MTD_CFI_H__ @@ -354,13 +353,23 @@ struct cfi_fixup { uint16_t mfr; uint16_t id; - void (*fixup)(struct map_info *map, void* param); + void (*fixup)(struct mtd_info *mtd, void* param); void* param; }; #define CFI_MFR_ANY 0xffff #define CFI_ID_ANY 0xffff -void cfi_fixup(struct map_info *map, struct cfi_fixup* fixups); +#define CFI_MFR_AMD 0x0001 +#define CFI_MFR_ST 0x0020 /* STMicroelectronics */ + +void cfi_fixup(struct mtd_info *mtd, struct cfi_fixup* fixups); + +typedef int (*varsize_frob_t)(struct map_info *map, struct flchip *chip, + unsigned long adr, int len, void *thunk); + +int cfi_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob, + loff_t ofs, size_t len, void *thunk); + #endif /* __MTD_CFI_H__ */ diff -NaurbB linux-2.6.9-pre1-bk18/include/linux/mtd/compatmac.h linux-2.6.9_nbp/include/linux/mtd/compatmac.h --- linux-2.6.9-pre1-bk18/include/linux/mtd/compatmac.h 2004-08-14 07:37:39.000000000 +0200 +++ linux-2.6.9_nbp/include/linux/mtd/compatmac.h 2004-09-13 12:43:28.000000000 +0200 @@ -1,10 +1,208 @@ +/* + * $Id: compatmac.h,v 1.66 2004/09/02 01:45:46 eric Exp $ + * + * Extensions and omissions from the normal 'linux/compatmac.h' + * files. hopefully this will end up empty as the 'real' one + * becomes fully-featured. + */ #ifndef __LINUX_MTD_COMPATMAC_H__ #define __LINUX_MTD_COMPATMAC_H__ -/* Nothing to see here. We write 2.5-compatible code and this - file makes it all OK in older kernels, but it's empty in _current_ - kernels. Include guard just to make GCC ignore it in future inclusions - anyway... */ +#include + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10) +#error "This kernel is too old: not supported by this file" +#endif + + /* O(1) scheduler stuff. */ + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,5) && !defined(__rh_config_h__) +#include +static inline void __recalc_sigpending(void) +{ + recalc_sigpending(current); +} +#undef recalc_sigpending +#define recalc_sigpending() __recalc_sigpending () + +#define set_user_nice(tsk, n) do { (tsk)->nice = n; } while(0) + +#endif + + + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,20) + +#ifndef yield +#define yield() do { set_current_state(TASK_RUNNING); schedule(); } while(0) +#endif + +#ifndef minor +#define major(d) (MAJOR(to_kdev_t(d))) +#define minor(d) (MINOR(to_kdev_t(d))) +#endif + +#ifndef mk_kdev +#define mk_kdev(ma,mi) MKDEV(ma,mi) +#define kdev_t_to_nr(x) (x) +#endif + +#define need_resched() (current->need_resched) +#define cond_resched() do { if need_resched() { yield(); } } while(0) + +#endif /* < 2.4.20 */ +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,73) +#define iminor(i) minor((i)->i_rdev) +#define imajor(i) major((i)->i_rdev) +#define old_encode_dev(d) ( (major(d)<<8) | minor(d) ) +#define old_decode_dev(rdev) (kdev_t_to_nr(mk_kdev((rdev)>>8, (rdev)&0xff))) +#define old_valid_dev(d) (1) +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,61) + +#include + +#ifdef __rh_config_h__ +#define sigmask_lock sighand->siglock +#define sig sighand +#endif + +static inline void __daemonize_modvers(void) +{ + daemonize(); + + spin_lock_irq(¤t->sigmask_lock); + sigfillset(¤t->blocked); + recalc_sigpending(); + spin_unlock_irq(¤t->sigmask_lock); +} +#undef daemonize +#define daemonize(fmt, ...) do { \ + snprintf(current->comm, sizeof(current->comm), fmt ,##__VA_ARGS__); \ + __daemonize_modvers(); \ + } while(0) + +static inline int dequeue_signal_lock(struct task_struct *tsk, sigset_t *mask, siginfo_t *info) +{ + unsigned long flags; + unsigned long ret; + + spin_lock_irqsave(¤t->sigmask_lock, flags); + ret = dequeue_signal(mask, info); + spin_unlock_irqrestore(¤t->sigmask_lock, flags); + + return ret; +} + +static inline int allow_signal(int sig) +{ + if (sig < 1 || sig > _NSIG) + return -EINVAL; + + spin_lock_irq(¤t->sigmask_lock); + sigdelset(¤t->blocked, sig); + recalc_sigpending(); + /* Make sure the kernel neither eats it now converts to SIGKILL */ + current->sig->action[sig-1].sa.sa_handler = (void *)2; + spin_unlock_irq(¤t->sigmask_lock); + return 0; +} +static inline int disallow_signal(int sig) +{ + if (sig < 1 || sig > _NSIG) + return -EINVAL; + + spin_lock_irq(¤t->sigmask_lock); + sigaddset(¤t->blocked, sig); + recalc_sigpending(); + + current->sig->action[sig-1].sa.sa_handler = SIG_DFL; + spin_unlock_irq(¤t->sigmask_lock); + return 0; +} + +#define PF_FREEZE 0 +#define refrigerator(x) do { ; } while(0) +#endif + + /* Module bits */ + + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,60) +#define try_module_get(m) try_inc_mod_count(m) +#define __module_get(m) do { if (!try_inc_mod_count(m)) BUG(); } while(0) +#define module_put(m) do { if (m) __MOD_DEC_USE_COUNT((struct module *)(m)); } while(0) +#define set_module_owner(x) do { x->owner = THIS_MODULE; } while(0) +#endif + + + /* Random filesystem stuff, only for JFFS2 really */ + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,5) +#define parent_ino(d) ((d)->d_parent->d_inode->i_ino) +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,12) +#define PageUptodate(x) Page_Uptodate(x) +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,48) +#define get_seconds() CURRENT_TIME +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,53) +#define generic_file_readonly_mmap generic_file_mmap +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,70) + +#include +#include + +static inline char *strlcpy(char *dest, const char *src, int len) +{ + dest[len-1] = 0; + return strncpy(dest, src, len-1); +} + +static inline int do_old_request_module(const char *mod) +{ + return request_module(mod); +} +#undef request_module +#define request_module(fmt, ...) \ + ({ char modname[32]; snprintf(modname, 31, fmt ,##__VA_ARGS__); do_old_request_module(modname); }) + +#endif /* 2.5.70 */ + +#ifndef container_of +#define container_of(ptr, type, member) ({ \ + const typeof( ((type *)0)->member ) *__mptr = (ptr); \ + (type *)( (char *)__mptr - offsetof(type,member) );}) +#endif + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) +#define kvec iovec +#define __user +#endif + + +#ifndef list_for_each_entry_safe +/** + * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry + * @pos: the type * to use as a loop counter. + * @n: another type * to use as temporary storage + * @head: the head for your list. + * @member: the name of the list_struct within the struct. + */ +#define list_for_each_entry_safe(pos, n, head, member) \ + for (pos = list_entry((head)->next, typeof(*pos), member), \ + n = list_entry(pos->member.next, typeof(*pos), member); \ + &pos->member != (head); \ + pos = n, n = list_entry(n->member.next, typeof(*n), member)) + +#endif #endif /* __LINUX_MTD_COMPATMAC_H__ */ diff -NaurbB linux-2.6.9-pre1-bk18/include/linux/pm.h linux-2.6.9_nbp/include/linux/pm.h --- linux-2.6.9-pre1-bk18/include/linux/pm.h 2004-08-14 07:37:15.000000000 +0200 +++ linux-2.6.9_nbp/include/linux/pm.h 2004-10-15 22:11:14.000000000 +0200 @@ -192,6 +192,7 @@ */ extern void (*pm_idle)(void); extern void (*pm_power_off)(void); +extern void (*pm_restart)(void); enum { PM_SUSPEND_ON, diff -NaurbB linux-2.6.9-pre1-bk18/include/linux/serial_core.h linux-2.6.9_nbp/include/linux/serial_core.h --- linux-2.6.9-pre1-bk18/include/linux/serial_core.h 2004-08-14 07:36:16.000000000 +0200 +++ linux-2.6.9_nbp/include/linux/serial_core.h 2004-10-25 18:10:13.000000000 +0200 @@ -265,6 +265,7 @@ #define UIF_CHECK_CD (1 << 25) #define UIF_CTS_FLOW (1 << 26) #define UIF_NORMAL_ACTIVE (1 << 29) +#define UIF_SUSPENDED (1 << 30) #define UIF_INITIALIZED (1 << 31) unsigned char *tmpbuf; diff -NaurbB linux-2.6.9-pre1-bk18/include/linux/usb_sl811.h linux-2.6.9_nbp/include/linux/usb_sl811.h --- linux-2.6.9-pre1-bk18/include/linux/usb_sl811.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/include/linux/usb_sl811.h 2004-11-21 23:46:50.000000000 +0100 @@ -0,0 +1,26 @@ + +/* + * board initialization should put one of these into dev->platform_data + * and place the sl811hs onto platform_bus. + */ + +struct sl811_platform_data { + unsigned can_wakeup:1; + + /* given port_power, msec/2 after power on till power good */ + u8 potpg; + + /* mA/2 power supplied on this port (max = default = 250) */ + u8 power; + + /* sl811 relies on an external source of VBUS current */ + /* this must be safe to call in_irq() */ + void (*port_power)(struct device *dev, int is_on); + + /* pulse sl811 nRST (probably with a GPIO) */ + void (*reset)(struct device *dev); + + // FIXME KARO boards need something like these: + // bool = check_overcurrent() +}; + diff -NaurbB linux-2.6.9-pre1-bk18/include/linux/wait.h linux-2.6.9_nbp/include/linux/wait.h --- linux-2.6.9-pre1-bk18/include/linux/wait.h 2005-08-15 01:14:42.000000000 +0200 +++ linux-2.6.9_nbp/include/linux/wait.h 2004-09-25 21:50:46.000000000 +0200 @@ -156,6 +156,29 @@ __wait_event(wq, condition); \ } while (0) +#define __wait_event_timeout(wq, condition, ret) \ +do { \ + DEFINE_WAIT(__wait); \ + \ + for (;;) { \ + prepare_to_wait(&wq, &__wait, TASK_UNINTERRUPTIBLE); \ + if (condition) \ + break; \ + ret = schedule_timeout(ret); \ + if (!ret) \ + break; \ + } \ + finish_wait(&wq, &__wait); \ +} while (0) + +#define wait_event_timeout(wq, condition, timeout) \ +({ \ + long __ret = timeout; \ + if (!(condition)) \ + __wait_event_timeout(wq, condition, __ret); \ + __ret; \ +}) + #define __wait_event_interruptible(wq, condition, ret) \ do { \ DEFINE_WAIT(__wait); \ diff -NaurbB linux-2.6.9-pre1-bk18/include/linux/wm97xx.h linux-2.6.9_nbp/include/linux/wm97xx.h --- linux-2.6.9-pre1-bk18/include/linux/wm97xx.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/include/linux/wm97xx.h 2004-09-13 17:18:21.000000000 +0200 @@ -0,0 +1,96 @@ + +/* + * Register bits for Wolfson WM97xx series of codecs + */ + +#ifndef _WM97XX_H_ +#define _WM97XX_H_ + +#include /* AC97 control layer */ + +/* + * WM97xx AC97 Touchscreen registers + */ +#define AC97_WM97XX_DIGITISER1 0x76 +#define AC97_WM97XX_DIGITISER2 0x78 +#define AC97_WM97XX_DIGITISER_RD 0x7a + +/* + * WM97xx register bits + */ +#define WM97XX_POLL 0x8000 /* initiate a polling measurement */ +#define WM97XX_ADCSEL_X 0x1000 /* x coord measurement */ +#define WM97XX_ADCSEL_Y 0x2000 /* y coord measurement */ +#define WM97XX_ADCSEL_PRES 0x3000 /* pressure measurement */ +#define WM97XX_COO 0x0800 /* enable coordinate mode */ +#define WM97XX_CTC 0x0400 /* enable continuous mode */ +#define WM97XX_CM_RATE_93 0x0000 /* 93.75Hz continuous rate */ +#define WM97XX_CM_RATE_187 0x0100 /* 187.5Hz continuous rate */ +#define WM97XX_CM_RATE_375 0x0200 /* 375Hz continuous rate */ +#define WM97XX_CM_RATE_750 0x0300 /* 750Hz continuous rate */ +#define WM97XX_CM_RATE_8K 0x00f0 /* 8kHz continuous rate */ +#define WM97XX_CM_RATE_12K 0x01f0 /* 12kHz continuous rate */ +#define WM97XX_CM_RATE_24K 0x02f0 /* 24kHz continuous rate */ +#define WM97XX_CM_RATE_48K 0x03f0 /* 48kHz continuous rate */ +#define WM97XX_DELAY(i) ((i << 4) & 0x00f0) /* sample delay times */ +#define WM97XX_SLEN 0x0008 /* slot read back enable */ +#define WM97XX_SLT(i) ((i - 5) & 0x7) /* touchpanel slot selection (5-11) */ +#define WM97XX_PRP_DETW 0x4000 /* pen detect on, digitiser off, wake up */ +#define WM97XX_PRP_DET 0x8000 /* pen detect on, digitiser off, no wake up */ +#define WM97XX_PRP_DET_DIG 0xc000 /* pen detect on, digitiser on */ +#define WM97XX_RPR 0x2000 /* wake up on pen down */ +#define WM97XX_PEN_DOWN 0x8000 /* pen is down */ + +/* WM9712 Bits */ +#define WM9712_45W 0x1000 /* set for 5-wire touchscreen */ +#define WM9712_PDEN 0x0800 /* measure only when pen down */ +#define WM9712_WAIT 0x0200 /* wait until adc is read before next sample */ +#define WM9712_PIL 0x0100 /* current used for pressure measurement. set 400uA else 200uA */ +#define WM9712_MASK_HI 0x0040 /* hi on mask pin (47) stops conversions */ +#define WM9712_MASK_EDGE 0x0080 /* rising/falling edge on pin delays sample */ +#define WM9712_MASK_SYNC 0x00c0 /* rising/falling edge on mask initiates sample */ +#define WM9712_RPU(i) (i&0x3f) /* internal pull up on pen detect (64k / rpu) */ +#define WM9712_ADCSEL_COMP1 0x4000 /* COMP1/AUX1 measurement (pin29) */ +#define WM9712_ADCSEL_COMP2 0x5000 /* COMP2/AUX2 measurement (pin30) */ +#define WM9712_ADCSEL_BMON 0x6000 /* BMON/AUX3 measurement (pin31) */ +#define WM9712_ADCSEL_WIPER 0x7000 /* WIPER/AUX4 measurement (pin12) */ +#define WM9712_PD(i) (0x1 << i) /* power management */ + +/* WM9712 Registers */ +#define AC97_WM9712_POWER 0x24 +#define AC97_WM9712_REV 0x58 + +/* WM9705 Bits */ +#define WM9705_PDEN 0x1000 /* measure only when pen is down */ +#define WM9705_PINV 0x0800 /* inverts sense of pen down output */ +#define WM9705_BSEN 0x0400 /* BUSY flag enable, pin47 is 1 when busy */ +#define WM9705_BINV 0x0200 /* invert BUSY (pin47) output */ +#define WM9705_WAIT 0x0100 /* wait until adc is read before next sample */ +#define WM9705_PIL 0x0080 /* current used for pressure measurement. set 400uA else 200uA */ +#define WM9705_PHIZ 0x0040 /* set PHONE and PCBEEP inputs to high impedance */ +#define WM9705_MASK_HI 0x0010 /* hi on mask stops conversions */ +#define WM9705_MASK_EDGE 0x0020 /* rising/falling edge on pin delays sample */ +#define WM9705_MASK_SYNC 0x0030 /* rising/falling edge on mask initiates sample */ +#define WM9705_PDD(i) (i & 0x000f) /* pen detect comparator threshold */ +#define WM9705_ADCSEL_BMON 0x4000 /* BMON measurement */ +#define WM9705_ADCSEL_AUX 0x5000 /* AUX measurement */ +#define WM9705_ADCSEL_PHONE 0x6000 /* PHONE measurement */ +#define WM9705_ADCSEL_PCBEEP 0x7000 /* PCBEEP measurement */ + +/* AUX ADC ID's */ +#define TS_COMP1 0x0 +#define TS_COMP2 0x1 +#define TS_BMON 0x2 +#define TS_WIPER 0x3 + +/* ID numbers */ +#define WM97XX_ID1 0x574d +#define WM9712_ID2 0x4c12 +#define WM9705_ID2 0x4c05 + +#define AC97_LINK_FRAME 21 /* time in uS for AC97 link frame */ + +void register_touchscreen_codec(struct ac97_codec *codec); +void unregister_touchscreen_codec(struct ac97_codec *codec); + +#endif diff -NaurbB linux-2.6.9-pre1-bk18/include/mtd/mtd-abi.h linux-2.6.9_nbp/include/mtd/mtd-abi.h --- linux-2.6.9-pre1-bk18/include/mtd/mtd-abi.h 2004-08-14 07:37:14.000000000 +0200 +++ linux-2.6.9_nbp/include/mtd/mtd-abi.h 2004-09-13 12:43:28.000000000 +0200 @@ -1,5 +1,5 @@ /* - * $Id: mtd-abi.h,v 1.5 2004/06/22 09:29:35 gleixner Exp $ + * $Id: mtd-abi.h,v 1.6 2004/08/09 13:38:30 dwmw2 Exp $ * * Portions of MTD ABI definition which are shared by kernel and user space */ @@ -7,6 +7,11 @@ #ifndef __MTD_ABI_H__ #define __MTD_ABI_H__ +#ifndef __KERNEL__ /* Urgh. The whole point of splitting this out into + separate files was to avoid #ifdef __KERNEL__ */ +#define __user +#endif + struct erase_info_user { uint32_t start; uint32_t length; diff -NaurbB linux-2.6.9-pre1-bk18/include/pcmcia/ss.h linux-2.6.9_nbp/include/pcmcia/ss.h --- linux-2.6.9-pre1-bk18/include/pcmcia/ss.h 2005-08-15 01:14:42.000000000 +0200 +++ linux-2.6.9_nbp/include/pcmcia/ss.h 2004-10-22 11:48:09.000000000 +0200 @@ -58,6 +58,7 @@ #define SS_CAP_VIRTUAL_BUS 0x0002 #define SS_CAP_MEM_ALIGN 0x0004 #define SS_CAP_STATIC_MAP 0x0008 +#define SS_CAP_NOPOWER 0x0010 /* no power detection and control */ #define SS_CAP_PCCARD 0x4000 #define SS_CAP_CARDBUS 0x8000 diff -NaurbB linux-2.6.9-pre1-bk18/kernel/printk.c linux-2.6.9_nbp/kernel/printk.c --- linux-2.6.9-pre1-bk18/kernel/printk.c 2005-08-15 01:14:34.000000000 +0200 +++ linux-2.6.9_nbp/kernel/printk.c 2004-09-14 12:25:07.000000000 +0200 @@ -534,6 +534,21 @@ /* Emit the output into the temporary buffer */ printed_len = vscnprintf(printk_buf, sizeof(printk_buf), fmt, args); +#if 0 + if (1 || (strncmp(printk_buf, KERN_DEBUG, 3) == 0)) { +#else + if (0) { +#endif + extern void printascii(const char *buf); + //printascii("{"); + printascii(printk_buf); + //printascii("}"); + + if ((strncmp(printk_buf, KERN_DEBUG, 3) == 0) && 0) { + printk_buf[0] = '\0'; + } + } + /* * Copy the output into log_buf. If the caller didn't provide * appropriate log level tags, we insert them here diff -NaurbB linux-2.6.9-pre1-bk18/Makefile linux-2.6.9_nbp/Makefile --- linux-2.6.9-pre1-bk18/Makefile 2005-08-15 01:14:40.000000000 +0200 +++ linux-2.6.9_nbp/Makefile 2004-09-14 12:25:05.000000000 +0200 @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 6 SUBLEVEL = 9 -EXTRAVERSION = -rc1-bk18 +EXTRAVERSION = -rc1-bk18-nb1 NAME=Zonked Quokka # *DOCUMENTATION* diff -NaurbB linux-2.6.9-pre1-bk18/sound/oss/ac97_codec.c linux-2.6.9_nbp/sound/oss/ac97_codec.c --- linux-2.6.9-pre1-bk18/sound/oss/ac97_codec.c 2004-08-14 07:36:17.000000000 +0200 +++ linux-2.6.9_nbp/sound/oss/ac97_codec.c 2004-09-21 01:20:35.000000000 +0200 @@ -149,6 +149,7 @@ {0x43525931, "Cirrus Logic CS4299 rev A", &crystal_digital_ops}, {0x43525933, "Cirrus Logic CS4299 rev C", &crystal_digital_ops}, {0x43525934, "Cirrus Logic CS4299 rev D", &crystal_digital_ops}, + {0x4352594d, "Cirrus Logic CS4201", &null_ops}, {0x43585442, "CXT66", &default_ops, AC97_DELUDED_MODEM }, {0x44543031, "Diamond Technology DT0893", &default_ops}, {0x45838308, "ESS Allegro ES1988", &null_ops}, @@ -156,6 +157,7 @@ {0x4e534331, "National Semiconductor LM4549", &null_ops}, {0x53494c22, "Silicon Laboratory Si3036", &null_ops}, {0x53494c23, "Silicon Laboratory Si3038", &null_ops}, + {0x50534304, "Philips UCB1400", &default_ops}, {0x545200FF, "TriTech TR?????", &tritech_m_ops}, {0x54524102, "TriTech TR28022", &null_ops}, {0x54524103, "TriTech TR28023", &null_ops}, @@ -1069,8 +1071,20 @@ /* WM9705, WM9710 */ static int wolfson_init05(struct ac97_codec * codec) { + unsigned long tmp; + /* set front mixer volume */ codec->codec_write(codec, AC97_WM97XX_FMIXER_VOL, 0x0808); + + tmp = codec->codec_read(codec, 0x2A); + printk("wolfson_init05: read %08lx\n", tmp); + + if ((tmp & 1) == 0) { + printk("ac97: enabling WM9705 Variable-Rate sampling\n"); + tmp |= 1; + codec->codec_write(codec, 0x2A, tmp); + } + return 0; } @@ -1305,6 +1319,8 @@ if(rate != codec->codec_read(codec, AC97_PCM_FRONT_DAC_RATE)) { + printk(KERN_DEBUG "ac97_set_dac_rate: changing rate to %ld\n", + rate); /* Mute several registers */ mast_vol = codec->codec_read(codec, AC97_MASTER_VOL_STEREO); mono_vol = codec->codec_read(codec, AC97_MASTER_VOL_MONO); @@ -1321,6 +1337,9 @@ /* Load the rate and read the effective rate */ codec->codec_write(codec, AC97_PCM_FRONT_DAC_RATE, rate); new_rate=codec->codec_read(codec, AC97_PCM_FRONT_DAC_RATE); + + printk(KERN_DEBUG "new_rate=%ld\n", new_rate); + /* Power it back up */ codec->codec_write(codec, AC97_POWER_CONTROL, dacp); @@ -1330,6 +1349,9 @@ codec->codec_write(codec, AC97_HEADPHONE_VOL, phone_vol); codec->codec_write(codec, AC97_PCMOUT_VOL, pcm_vol); } + + printk(KERN_DEBUG "ac97_set_dac_rate: returning %ld\n", + new_rate); return new_rate; } @@ -1391,6 +1413,33 @@ EXPORT_SYMBOL(ac97_restore_state); +int ac97_suspend(struct ac97_codec *codec, int state) +{ + struct ac97_driver *driver; + int ret = 0; + + down(&codec_sem); + driver = codec->driver; + if (driver != NULL && driver->suspend) + ret = driver->suspend(codec, state); + up(&codec_sem); + + return ret; +} +EXPORT_SYMBOL(ac97_suspend); + +void ac97_resume(struct ac97_codec *codec) +{ + struct ac97_driver *driver; + + down(&codec_sem); + driver = codec->driver; + if (driver != NULL && driver->resume) + driver->resume(codec); + up(&codec_sem); +} +EXPORT_SYMBOL(ac97_resume); + /** * ac97_register_driver - register a codec helper * @driver: Driver handler diff -NaurbB linux-2.6.9-pre1-bk18/sound/oss/ac97_plugin_wm97xx.c linux-2.6.9_nbp/sound/oss/ac97_plugin_wm97xx.c --- linux-2.6.9-pre1-bk18/sound/oss/ac97_plugin_wm97xx.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/sound/oss/ac97_plugin_wm97xx.c 2004-11-19 23:38:21.000000000 +0100 @@ -0,0 +1,1791 @@ +/* + * ac97_plugin_wm97xx.c -- Touch screen driver for Wolfson WM9705 and WM9712 + * AC97 Codecs. + * + * Copyright 2003, 2004 Wolfson Microelectronics PLC. + * Author: Liam Girdwood + * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * Parts Copyright : Ian Molton + * Andrew Zabolotny + * Russell King + * + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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. + * + * Notes: + * + * Features: + * - supports WM9705, WM9712 + * - polling mode + * - coordinate polling + * - continuous mode (arch-dependent) + * - adjustable rpu/dpp settings + * - adjustable pressure current + * - adjustable sample settle delay + * - 4 and 5 wire touchscreens (5 wire is WM9712 only) + * - pen down detection + * - battery monitor + * - sample AUX adc's + * - power management + * + * TODO: + * - adjustable sample rate + * - AUX adc in coordinate / continous modes + * - Codec GPIO + * + * Revision history + * 7th May 2003 Initial version. + * 6th June 2003 Added non module support and AC97 registration. + * 18th June 2003 Added AUX adc sampling. + * 23rd June 2003 Did some minimal reformatting, fixed a couple of + * locking bugs and noted a race to fix. + * 24th June 2003 Added power management and fixed race condition. + * 10th July 2003 Changed to a misc device. + * 31st July 2003 Moved TS_EVENT and TS_CAL to wm97xx.h + * 8th Aug 2003 Added option for read() calling wm97xx_sample_touch() + * because some ac97_read/ac_97_write call schedule() + * 7th Nov 2003 Added Input touch event interface, stanley.cai@intel.com + * 13th Nov 2003 Removed h3600 touch interface, added interrupt based + * pen down notification and implemented continous mode + * on XScale arch. + * 16th Nov 2003 Ian Molton + * Modified so that it suits the new 2.6 driver model. + * 25th Jan 2004 Andrew Zabolotny + * Implemented IRQ-driven pen down detection, implemented + * the private API meant to be exposed to platform-specific + * drivers, reorganized the driver so that it supports + * an arbitrary number of devices. + * 1st Feb 2004 Moved continuous mode handling to a separate + * architecture-dependent file. For now only PXA + * built-in AC97 controller is supported (pxa-ac97-wm97xx.c). + * 11th Feb 2004 Reduced CPU usage by keeping a cached copy of both + * digitizer registers instead of reading them every time. + * A reorganization of the whole code for better + * error handling. + * 17th Apr 2004 Added BMON support. + * 17th Nov 2004 Added codec GPIO, codec event handling (real and virtual + * GPIOs) and 2.6 power management. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm97xx.h" + +#define TS_NAME "wm97xx" +#define WM_TS_VERSION "0.23" +#define DEFAULT_PRESSURE 0xb0c0 + +/* + * Debug + */ +#if 0 +#define dbg(format, arg...) printk(KERN_DEBUG TS_NAME ": " format "\n" , ## arg) +#else +#define dbg(format, arg...) +#endif +#define err(format, arg...) printk(KERN_ERR TS_NAME ": " format "\n" , ## arg) +#define info(format, arg...) printk(KERN_INFO TS_NAME ": " format "\n" , ## arg) +#define warn(format, arg...) printk(KERN_WARNING TS_NAME ": " format "\n" , ## arg) + +#if defined(CONFIG_PROC_FS) && !defined(CONFIG_EMBEDDED) +# define PROC_FS_SUPPORT +#endif + +#define PROC_FS_SUPPORT + +/* + * Module parameters + */ + +static int useless; + +/* + * Set the codec sample mode. + * + * The WM9712 can sample touchscreen data in 3 different operating + * modes. i.e. polling, coordinate and continous. + * + * Polling: The driver polls the codec and issues 3 seperate commands + * over the AC97 link to read X,Y and pressure. + * + * Coordinate: The driver polls the codec and only issues 1 command over + * the AC97 link to read X,Y and pressure. This mode has + * strict timing requirements and may drop samples if + * interrupted. However, it is less demanding on the AC97 + * link. Note: this mode requires a larger delay than polling + * mode. + * + * Continuous: The codec automatically samples X,Y and pressure and then + * sends the data over the AC97 link in slots. This is the + * same method used by the codec when recording audio. + * This mode is not selectable by this parameter because + * it needs separate architecture-dependent support. The + * following drivers will add continuouse mode support: + * + * pxa-wm97xx-touch.c - for Intel PXA built-in AC97 controller. + */ +static int mode; +module_param (mode, int, 0); +MODULE_PARM_DESC(mode, "WM97XX operation mode (0:polling 1:coordinate)"); + +/* + * WM9712 - Set internal pull up for pen detect. + * + * Pull up is in the range 1.02k (least sensitive) to 64k (most sensitive) + * i.e. pull up resistance = 64k Ohms / rpu. + * + * Adjust this value if you are having problems with pen detect not + * detecting any down events. + */ +static int rpu; +module_param(rpu, int, 0); +MODULE_PARM_DESC(rpu, "Set internal pull up resitor for pen detect."); + +/* + * WM9705 - Pen detect comparator threshold. + * + * 0 to Vmid in 15 steps, 0 = use zero power comparator with Vmid threshold + * i.e. 1 = Vmid/15 threshold + * 15 = Vmid/1 threshold + * + * Adjust this value if you are having problems with pen detect not + * detecting any down events. + */ +static int pdd = 8; +module_param(pdd, int, 0); +MODULE_PARM_DESC(pdd, "Set pen detect comparator threshold"); + +/* + * Set current used for pressure measurement. + * + * Set pil = 2 to use 400uA + * pil = 1 to use 200uA and + * pil = 0 to disable pressure measurement. + * + * This is used to increase the range of values returned by the adc + * when measureing touchpanel pressure. + */ +static int pil = 0; +module_param(pil, int, 0); +MODULE_PARM_DESC(pil, "Set current used for pressure measurement."); + +/* + * Set threshold for pressure measurement. + * + * Pen down pressure below threshold is ignored. + */ +static int pressure = DEFAULT_PRESSURE & 0xfff; +module_param(pressure, int, 0); +MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement."); + +/* + * WM9712 - Set five_wire = 1 to use a 5 wire touchscreen. + * + * NOTE: Five wire mode does not allow for readback of pressure. + */ +static int five_wire; +module_param(five_wire, int, 0); +MODULE_PARM_DESC(five_wire, "Set to '1' to use 5-wire touchscreen."); + +/* + * Set adc sample delay. + * + * For accurate touchpanel measurements, some settling time may be + * required between the switch matrix applying a voltage across the + * touchpanel plate and the ADC sampling the signal. + * + * This delay can be set by setting delay = n, where n is the array + * position of the delay in the array delay_table below. + * Long delays > 1ms are supported for completeness, but are not + * recommended. + */ +static int delay = 4; +module_param(delay, int, 0); +MODULE_PARM_DESC(delay, "Set adc sample delay."); + +/* + * Coordinate inversion control. + * + * On some hardware the coordinate system of the touchscreen and the + * coordinate system of the graphics controller are different. A well-written + * calibration tool won't depend on that; however for compatibility with + * some old apps sometimes it is required to bring both coordinate + * systems together. That's what this parameter is for. + * + * Bit 0: if 1, the X coordinate is inverted + * Bit 1: if 1, the Y coordinate is inverted + */ +static int invert = 0; +module_param(invert, int, 0); +MODULE_PARM_DESC(invert, "Set bit 0 to invert X coordinate, set bit 1 to invert Y coordinate"); + +/* + * Touchscreen absolute values + * + * These parameters are used to help the input layer discard out of + * range readings and reduce jitter etc. + * + * o min, max:- indicate the min and max values your touch screen returns + * o fuzz:- use a higher number to reduce jitter + * + * The default values correspond to Mainstone II in QVGA mode + * + * Please read + * Documentation/input/input-programming.txt for more details. + */ + +static int abs_x[3] = {350,3900,5}; +module_param_array(abs_x, int, useless, 0); +MODULE_PARM_DESC(abs_x, "Touchscreen absolute X min, max, fuzz"); + +static int abs_y[3] = {320,3750,40}; +module_param_array(abs_y, int, useless, 0); +MODULE_PARM_DESC(abs_y, "Touchscreen absolute Y min, max, fuzz"); + +static int abs_p[3] = {0,150,4}; +module_param_array(abs_p, int, useless, 0); +MODULE_PARM_DESC(abs_p, "Touchscreen absolute Pressure min, max, fuzz"); + +static u16 wm97xx_adcsel [2][4] = +{ + /* WM9705 */ + { WM9705_ADCSEL_BMON, WM9705_ADCSEL_AUX, WM9705_ADCSEL_PHONE, WM9705_ADCSEL_PCBEEP }, + /* WM9712 */ + { WM9712_ADCSEL_COMP1, WM9712_ADCSEL_COMP2, WM9712_ADCSEL_BMON, WM9712_ADCSEL_WIPER }, +}; + +typedef struct { + unsigned adcsel; /* ADC select mask; !=0 -> enabled */ + unsigned next_jiffies; /* Next time mark to collect data at */ + unsigned period; /* Interval in jiffies to collect data */ + wm97xx_ac_prepare func; /* Before/after conversion callback */ + int value; /* Last value sampled (-1 = no data yet) */ +} wm97xx_auxconv_data_t; + +typedef struct { + u16 dig1, dig2; /* Cached digitizer registers */ + spinlock_t lock; /* Hardware lock */ + struct ac97_codec *codec; /* AC97 codec */ + struct semaphore sem; + struct completion ts_init; + struct completion ts_exit; + volatile struct task_struct *ts_task; + codec_read_t cont_read_sample; /* continuous mode reader */ + wm97xx_auxconv_data_t aux_conv [4]; /* AUX data */ + struct completion aux_exit; + struct completion aux_init; + volatile struct task_struct *aux_task; + int aux_task_ref; + wait_queue_head_t aux_wait; /* AUX pen-up wait queue */ + unsigned int pen_irq; /* Pen IRQ number in use */ + volatile int pen_irq_count; + unsigned int codec_irq; /* Codec IRQ number in use */ + volatile int codec_irq_count; + wait_queue_head_t pen_irq_wait; /* Pen IRQ wait queue */ + int codec_irq_ref_count; /* Number of gpio irq users */ + int adc_errs; /* Sample read back errors */ + unsigned int pen_is_down; /* Pen down time */ + unsigned int use_count; + struct workqueue_struct *pen_irq_workq; + struct work_struct pen_event_work; + struct workqueue_struct *codec_event_workq; + struct work_struct codec_event_work; + codec_event_t codec_event[WM97XX_MAX_GPIO]; /* codec event handlers */ +#if defined(PROC_FS_SUPPORT) + struct proc_dir_entry *wm97xx_ts_ps; +#endif + /* Bitfields grouped here to get some space gain */ + unsigned is_wm9712:1; /* are we a WM9705/12 */ + unsigned pen_probably_down:1; /* used in polling and coordinate modes */ + unsigned pen_is_up:1; /* pen is up */ + unsigned invert_x:1; /* invert X coords */ + unsigned invert_y:1; /* invert Y coords */ + unsigned aux_waiting:1; /* aux measurement waiting */ + unsigned ac97_link:1; /* interrupt over AC link */ + unsigned probe:1; /* probe complete */ +#if defined(CONFIG_PM) + unsigned phone_pga:5; /* PGA settings for PM */ + unsigned line_pga:16; + unsigned mic_pga:16; +#endif +} wm97xx_ts_t; + +/* Shortcut since we have a lot of such references */ +#define CODEC ts.codec + +/* + * We only support a single codec, however we may need to provide + * similtanious access to the device e.g. X and detect-stylus (GPE) + */ +static wm97xx_ts_t ts; +static struct input_dev wm97xx_input; +static LIST_HEAD(wm97xx_drivers); + +/* + * Codec ID registers + */ +u32 wm97xx_id; +EXPORT_SYMBOL_GPL (wm97xx_id); + +/* + * ADC sample delay times in uS + */ +static const int delay_table[16] = { + 21, // 1 AC97 Link frames + 42, // 2 + 84, // 4 + 167, // 8 + 333, // 16 + 667, // 32 + 1000, // 48 + 1333, // 64 + 2000, // 96 + 2667, // 128 + 3333, // 160 + 4000, // 192 + 4667, // 224 + 5333, // 256 + 6000, // 288 + 0 // No delay, switch matrix always on +}; + +/* + * Delay after issuing a POLL command. + * + * The delay is 3 AC97 link frames + the touchpanel settling delay + */ +static inline void poll_delay(int d) +{ + udelay (3 * AC97_LINK_FRAME + delay_table [d]); +} + +static inline int is_pden (void) +{ + u16 pden_mask; + + if (ts.is_wm9712) + pden_mask = WM9712_PDEN; + else + pden_mask = WM9705_PDEN; + + return ts.dig2 & pden_mask; +} + +/* + * Read a sample from the adc in polling mode. + */ +static int wm97xx_poll_sample (int adcsel, int *sample) +{ + int timeout = 5 * delay; + + if (!ts.pen_probably_down) { + u16 data = CODEC->codec_read(CODEC, AC97_WM97XX_DIGITISER_RD); + if (!(data & WM97XX_PEN_DOWN)) + return RC_PENUP; + ts.pen_probably_down = 1; + } + + /* set up digitiser */ + CODEC->codec_write(CODEC, AC97_WM97XX_DIGITISER1, adcsel | WM97XX_POLL | WM97XX_DELAY(delay)); + + /* wait 3 AC97 time slots + delay for conversion */ + poll_delay (delay); + + /* wait for POLL to go low */ + while ((CODEC->codec_read(CODEC, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) && timeout) { + udelay(AC97_LINK_FRAME); + timeout--; + } + + if (timeout <= 0) { + /* If PDEN is set, we can get a timeout when pen goes up */ + if (is_pden()) + ts.pen_probably_down = 0; + else { + ts.adc_errs++; + dbg ("adc sample timeout"); + } + return 0; + } + + *sample = CODEC->codec_read(CODEC, AC97_WM97XX_DIGITISER_RD); + + /* check we have correct sample */ + if ((*sample & WM97XX_ADCSEL_MASK) != adcsel) { + ts.adc_errs++; + dbg ("adc wrong sample, read %x got %x", adcsel, + *sample & WM97XX_ADCSEL_MASK); + return 0; + } + + if (!(*sample & WM97XX_PEN_DOWN)) { + ts.pen_probably_down = 0; + return RC_PENUP; + } + + return RC_VALID; +} + +/* + * Read sample data (x,y,[p]) from the adc in coordinate mode. + */ +static int wm97xx_poll_coord_touch(wm97xx_data_t *data) +{ + u16 dig1; + int timeout = 5 * delay; + + if (!ts.pen_probably_down) { + u16 data = CODEC->codec_read(CODEC, AC97_WM97XX_DIGITISER_RD); + if (!(data & WM97XX_PEN_DOWN)) + return RC_PENUP; + ts.pen_probably_down = 1; + } + + /* set up digitiser */ + dig1 = (ts.dig1 & ~WM97XX_ADCSEL_MASK) | WM97XX_ADCSEL_PRES; + CODEC->codec_write(CODEC, AC97_WM97XX_DIGITISER1, + (ts.dig1 = dig1) | WM97XX_POLL); + + /* wait 3 AC97 time slots + delay for conversion */ + poll_delay(delay); + + /* read X then wait for 1 AC97 link frame + settling delay */ + data->x = CODEC->codec_read(CODEC, AC97_WM97XX_DIGITISER_RD); + udelay (AC97_LINK_FRAME + delay_table[delay]); + + /* read Y */ + data->y = CODEC->codec_read(CODEC, AC97_WM97XX_DIGITISER_RD); + + /* wait for POLL to go low and then read pressure */ + while ((CODEC->codec_read(CODEC, AC97_WM97XX_DIGITISER1) & WM97XX_POLL)&& timeout) { + udelay(AC97_LINK_FRAME); + timeout--; + } + + if (timeout <= 0) { + if (is_pden()) + ts.pen_probably_down = 0; + else { + ts.adc_errs++; + dbg ("adc sample timeout"); + } + return 0; + } + + /* read pressure */ + data->p = CODEC->codec_read(CODEC, AC97_WM97XX_DIGITISER_RD); + + /* check we have correct samples */ + if (((data->x & WM97XX_ADCSRC_MASK) != 0x1000) || + ((data->y & WM97XX_ADCSRC_MASK) != 0x2000) || + ((data->p & WM97XX_ADCSRC_MASK) != 0x3000)) { + ts.adc_errs++; + dbg ("wrong sample order"); + return 0; + } + + if (!(data->x & WM97XX_PEN_DOWN)) { + ts.pen_probably_down = 0; + return RC_PENUP; + } + + return RC_VALID; +} + +/* + * Sample the touchscreen in polling mode + */ +static int wm97xx_poll_touch(wm97xx_data_t *data) +{ + int rc; + + if ((rc = wm97xx_poll_sample(WM97XX_ADCSEL_X, &data->x)) != RC_VALID) + return rc; + if ((rc = wm97xx_poll_sample(WM97XX_ADCSEL_Y, &data->y)) != RC_VALID) + return rc; + if (pil && !five_wire) { + if ((rc = wm97xx_poll_sample(WM97XX_ADCSEL_PRES, &data->p)) != RC_VALID) + return rc; + } else + data->p = DEFAULT_PRESSURE; + + return RC_VALID; +} + +/* + * Read AUX adc + */ +static int wm97xx_check_aux (void) +{ + int i, restore_state = 0, min_delta = 0x7fffffff; + u16 adcsel; + unsigned jiff = jiffies; + + spin_lock(&ts.lock); + if (ts.pen_is_down) { + /* If pen is down, delay auxiliary sampling. The touchscreen + * thread will wake us up when the pen will be released. + */ + ts.aux_waiting = 1; + spin_unlock(&ts.lock); + return 10*HZ; + } + + ts.aux_waiting = 0; + for (i = 0; i < ARRAY_SIZE (ts.aux_conv); i++) + if ((adcsel = ts.aux_conv [i].adcsel)) { + int delta = ts.aux_conv [i].next_jiffies - jiff; + + /* Loose the check here to syncronize events that + * have close periods. This allows less timer + * interrupts to be generated, and better performance + * because of less codec writes. + */ + if (delta < (int)(ts.aux_conv [i].period >> 5)) { + int auxval = 0; + + if (!restore_state) { + /* This is the first AUX sample we + * read, prepare the chip. + */ + restore_state = 1; + ts.dig1 = CODEC->codec_read(CODEC, AC97_WM97XX_DIGITISER1); + ts.dig2 = CODEC->codec_read(CODEC, AC97_WM97XX_DIGITISER2); + CODEC->codec_write (CODEC, AC97_WM97XX_DIGITISER1, 0); + CODEC->codec_write (CODEC, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG); + } + + /* Fool wm97xx_poll_read_adc() */ + ts.pen_probably_down = 1; + + if (ts.aux_conv [i].func) + ts.aux_conv [i].func (i, 0); + wm97xx_poll_sample (adcsel, &auxval); + if (ts.aux_conv [i].func) + ts.aux_conv [i].func (i, 1); + if ((auxval & WM97XX_ADCSRC_MASK) == adcsel) { + ts.aux_conv [i].value = auxval & 0xfff; + ts.aux_conv [i].next_jiffies = jiff + ts.aux_conv [i].period; + } + } + + delta = ts.aux_conv [i].next_jiffies - jiff; + if (delta < min_delta) + min_delta = delta; + } + + if (restore_state) { + /* We know for sure pen is not down */ + ts.pen_probably_down = 0; + /* Restore previous state of digitizer registers */ + CODEC->codec_write (CODEC, AC97_WM97XX_DIGITISER1, ts.dig1); + CODEC->codec_write (CODEC, AC97_WM97XX_DIGITISER2, ts.dig2); + } + spin_unlock(&ts.lock); + + if (min_delta < 1) + min_delta = 1; + + return min_delta; +} + +/* + * Auxiliary ADC reading thread. + */ +static int wm97xx_aux_thread (void *data) +{ + /* set up thread context */ + ts.aux_task = current; + daemonize("kwm97xx_aux"); + set_user_nice (current, 5); + + if(ts.codec == NULL) { + ts.aux_task = NULL; + printk(KERN_ERR "codec is NULL, bailing\n"); + } + complete (&ts.aux_init); + + /* touch reader loop */ + while (ts.aux_task) { + int sleep_time = wm97xx_check_aux (); + + if (ts.aux_waiting) { + wait_event_interruptible_timeout (ts.aux_wait, + ts.aux_waiting == 0, + sleep_time); + } else { + set_task_state (current, TASK_INTERRUPTIBLE); + schedule_timeout (sleep_time); + } + } + complete_and_exit (&ts.aux_exit, 0); +} + +/*---/ Public interface exposed to custom architecture-dependent drivers /---*/ + +int wm97xx_request_auxconv (struct wm97xx_device *dev, wm97xx_aux_conv_t ac, + int period, wm97xx_ac_prepare func) +{ + int ret = 0; + + spin_lock(&ts.lock); + if (ts.aux_conv[ac].func) { + spin_unlock(&ts.lock); + return -EBUSY; + } + + ts.aux_conv [ac].value = -1; + ts.aux_conv [ac].period = period; + ts.aux_conv [ac].next_jiffies = jiffies + period; + ts.aux_conv [ac].func = func; + ts.aux_conv [ac].adcsel = ts.is_wm9712 ? wm97xx_adcsel[1][ac] : wm97xx_adcsel[0][ac]; + ts.aux_task_ref++; + + /* Start the aux thread, if not started already */ + if (ts.aux_task) { + spin_unlock(&ts.lock); + return 0; + } + spin_unlock(&ts.lock); + init_completion (&ts.aux_init); + init_completion (&ts.aux_exit); + ret = kernel_thread (wm97xx_aux_thread, &ts, CLONE_KERNEL); + if (ret >= 0) { + wait_for_completion (&ts.aux_init); + if(ts.aux_task == NULL) + ret = -EINVAL; + } + return ret; +} +EXPORT_SYMBOL_GPL(wm97xx_request_auxconv); + +void wm97xx_free_auxconv (struct wm97xx_device *dev, wm97xx_aux_conv_t ac) +{ + spin_lock(&ts.lock); + if (ts.aux_conv == NULL) + goto out; + + ts.aux_conv [ac].value = -1; + ts.aux_conv [ac].func = NULL; + ts.aux_task_ref--; + + if (!ts.aux_task_ref && ts.aux_task) { + spin_unlock(&ts.lock); + /* kill thread */ + ts.aux_task = NULL; + ts.aux_waiting = 0; + wait_for_completion (&ts.aux_exit); + return; + } +out: + spin_unlock(&ts.lock); +} +EXPORT_SYMBOL_GPL(wm97xx_free_auxconv); + +int wm97xx_get_auxconv (struct wm97xx_device *dev, wm97xx_aux_conv_t ac) +{ + return ts.aux_conv [ac].adcsel ? ts.aux_conv [ac].value : -1; +} +EXPORT_SYMBOL_GPL(wm97xx_get_auxconv); + +/* + * Arch specific wm97xx components can register here. This means they are + * probed/removed and can have a PM suspend/resume. + */ +int wm97xx_driver_register (struct wm97xx_device *drv) +{ + if (!drv->probe || !drv->remove) + return -ENODEV; + + INIT_LIST_HEAD(&drv->devices); + spin_lock(&ts.lock); + list_add(&drv->devices, &wm97xx_drivers); + if(ts.probe) { + spin_unlock(&ts.lock); + return drv->probe(); + } + spin_unlock(&ts.lock); + return 0; +} +EXPORT_SYMBOL_GPL(wm97xx_driver_register); + +/* + * Arch specific unregister. + */ +void wm97xx_driver_unregister (struct wm97xx_device *drv) +{ + spin_lock(&ts.lock); + list_del_init(&drv->devices); + if (ts.probe){ + spin_unlock(&ts.lock); + drv->remove(); + } else + spin_unlock(&ts.lock); +} +EXPORT_SYMBOL_GPL(wm97xx_driver_unregister); + +/* + * Get the status of a codec GPIO pin + */ +wm97xx_gpio_status_t wm97xx_get_codec_gpio (struct wm97xx_device *dev, u32 gpio) +{ + u16 status; + + status = CODEC->codec_read(CODEC, AC97_GPIO_STATUS); + + if (status & gpio) + return WM97XX_GPIO_HIGH; + else + return WM97XX_GPIO_LOW; +} +EXPORT_SYMBOL_GPL(wm97xx_get_codec_gpio); + +/* + * Set the status of a codec GPIO pin + */ +void wm97xx_set_codec_gpio (struct wm97xx_device *dev, u32 gpio, wm97xx_gpio_status_t status) +{ + u16 reg; + + spin_lock(&ts.lock); + reg = CODEC->codec_read(CODEC, AC97_GPIO_STATUS); + if (status & WM97XX_GPIO_HIGH) + reg |= gpio; + else + reg &= ~gpio; + + if (ts.is_wm9712) { + /* small hack for wm9712 */ + CODEC->id = 1; + CODEC->codec_write(CODEC, AC97_GPIO_STATUS, reg << 1); + CODEC->id = 0; + } else + CODEC->codec_write(CODEC, AC97_GPIO_STATUS, reg); + + spin_unlock(&ts.lock); +} +EXPORT_SYMBOL_GPL(wm97xx_set_codec_gpio); + +/* + * Codec GPIO pin configuration, this set's pin direction, polarity, + * stickyness and wake up. + */ +void wm97xx_config_codec_gpio (struct wm97xx_device *dev, u32 gpio, + wm97xx_gpio_dir_t dir, wm97xx_gpio_pol_t pol, + wm97xx_gpio_sticky_t sticky, wm97xx_gpio_wake_t wake) +{ + u16 reg; + spin_lock(&ts.lock); + + reg = CODEC->codec_read(CODEC, AC97_GPIO_POLARITY); + if (pol == WM97XX_GPIO_POL_HIGH) + reg |= gpio; + else + reg &= ~gpio; + CODEC->codec_write(CODEC, AC97_GPIO_POLARITY, reg); + + reg = CODEC->codec_read(CODEC, AC97_GPIO_STICKY); + if (sticky == WM97XX_GPIO_STICKY) + reg |= gpio; + else + reg &= ~gpio; + CODEC->codec_write(CODEC, AC97_GPIO_STICKY, reg); + + reg = CODEC->codec_read(CODEC, AC97_GPIO_WAKE_UP); + if (wake == WM97XX_GPIO_WAKE) + reg |= gpio; + else + reg &= ~gpio; + CODEC->codec_write(CODEC, AC97_GPIO_WAKE_UP, reg); + + reg = CODEC->codec_read(CODEC, AC97_GPIO_CONFIG); + if (dir == WM97XX_GPIO_IN) + reg |= gpio; + else + reg &= ~gpio; + CODEC->codec_write(CODEC, AC97_GPIO_CONFIG, reg); + spin_unlock(&ts.lock); +} +EXPORT_SYMBOL_GPL(wm97xx_config_codec_gpio); + +/* + * Simple and safe codec register IO for external drivers + */ +u16 wm97xx_reg_read (struct wm97xx_device *dev, u16 reg) +{ + return CODEC->codec_read(CODEC, reg); +} +EXPORT_SYMBOL_GPL(wm97xx_reg_read); + +void wm97xx_reg_write (struct wm97xx_device *dev, u16 reg, u16 val) +{ + CODEC->codec_write(CODEC, reg, val); +} +EXPORT_SYMBOL_GPL(wm97xx_reg_write); + +/* + * Allow arch specific driver to use the codec PENDOWN gpio signal + * as a pen down interrupt source. The PENDOWN signal is usually + * routed via a cpu irq/gpio. + * + * NOTE: The wm9712 also allows the pen down signal to be sent over + * the AC97 link or over the codec irq pin. Use wm97xx_enable_codec_irq + * in this case. + */ +int wm97xx_set_pen_irq (struct wm97xx_device *dev, int irq) +{ + spin_lock(&ts.lock); + if (!ts.pen_irq) + ts.pen_irq = irq; + + spin_unlock(&ts.lock); + return 0; +} +EXPORT_SYMBOL_GPL(wm97xx_set_pen_irq); + +/* + * Allow arch specific drivers to use the codec AC97 interrupt. This + * can either be over the AC97 link or using the codec irq pin. + * This interrupt can be used to notify arch specific drivers of + * numerous events i.e. battery level, ADC data, pen down, etc + */ +int wm97xx_set_codec_irq (struct wm97xx_device *dev, int irq, int ac97_link) +{ + spin_lock(&ts.lock); + ts.codec_irq_ref_count++; + if (!ts.codec_irq) { + ts.codec_irq = irq; + ts.ac97_link = ac97_link; + } + spin_unlock(&ts.lock); + return 0; +} +EXPORT_SYMBOL_GPL(wm97xx_set_codec_irq); + +void wm97xx_clear_pen_irq (struct wm97xx_device *dev) +{ + spin_lock(&ts.lock); + if (ts.pen_irq) + ts.pen_irq = 0; + + spin_unlock(&ts.lock); +} +EXPORT_SYMBOL_GPL(wm97xx_clear_pen_irq); + +int wm97xx_clear_codec_irq (struct wm97xx_device *dev) +{ + spin_lock(&ts.lock); + if (ts.codec_irq_ref_count == 0) + goto out; + + /* last one ? */ + if (--ts.codec_irq_ref_count == 0) { + ts.codec_irq = 0; + } + +out: + spin_unlock(&ts.lock); + return ts.codec_irq_ref_count; +} +EXPORT_SYMBOL_GPL(wm97xx_clear_codec_irq); + +/* + * Register arch specific codec event handler. One handler per event. + */ +int wm97xx_request_codec_event (struct wm97xx_device *dev, int gpio, codec_event_t work) +{ + u16 reg; + int irq = generic_ffs(gpio) - 1; + + if (irq < 0 || irq > WM97XX_MAX_GPIO) + return -ENODEV; + + spin_lock(&ts.lock); + if (!ts.is_wm9712) + goto err; + + /* cant use GPIO2 for irq and gpio at the same time */ + if (!ts.ac97_link && gpio == WM97XX_GPIO_2) + goto err; + + if (ts.codec_event[irq]) + goto err; + + ts.codec_event[irq] = work; + + /* handle pen down internally */ + if (gpio == WM97XX_GPIO_13) + ts.pen_irq = ts.codec_irq; + + /* set pin to GPIO function */ + reg= CODEC->codec_read(CODEC, AC97_MISC_MODEM_STAT); + CODEC->codec_write(CODEC, AC97_MISC_MODEM_STAT, reg | gpio); + spin_unlock(&ts.lock); + wm97xx_config_codec_gpio (dev, gpio, WM97XX_GPIO_IN, WM97XX_GPIO_POL_HIGH, + WM97XX_GPIO_STICKY, WM97XX_GPIO_WAKE); + + return 0; + +err: + spin_unlock(&ts.lock); + return -ENODEV; +} +EXPORT_SYMBOL_GPL(wm97xx_request_codec_event); + +/* + * Free codec event handler + */ +void wm97xx_free_codec_event (struct wm97xx_device *dev, int gpio) +{ + int irq = generic_ffs(gpio) - 1; + + spin_lock(&ts.lock); + if (!ts.is_wm9712) + goto out; + + if (irq >= 0 && irq < WM97XX_MAX_GPIO) + ts.codec_event[irq] = NULL; + + /* handle pen down internally */ + if (gpio == WM97XX_GPIO_13) + ts.pen_irq = 0; + +out: + spin_unlock(&ts.lock); +} +EXPORT_SYMBOL_GPL(wm97xx_free_codec_event); + +/* + * Enable continuous mode, i.e. touch data is streamed across an AC97 slot + */ +void wm97xx_set_continuous_mode (struct wm97xx_device *dev, int enable, int slot, int rate) +{ + u16 dig1, dig2; + + spin_lock(&ts.lock); + + dig1 = ts.dig1; + dig2 = ts.dig2; + + if (enable) { + /* continous mode */ + dbg("Enabling continous mode"); + ts.cont_read_sample = dev->cont_read; + dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK | + WM97XX_DELAY_MASK | WM97XX_SLT_MASK); + dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN | + WM97XX_DELAY (delay) | + WM97XX_SLT (slot) | + WM97XX_RATE (rate); + if (pil) + dig1 |= WM97XX_ADCSEL_PRES; + if (ts.is_wm9712) + dig2 |= WM9712_PDEN; + else + dig2 |= WM9705_PDEN; + } else { + ts.cont_read_sample = NULL; + dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN); + if (mode == 1) + /* coordinate mode */ + dig1 |= WM97XX_COO; + + if (ts.is_wm9712) + dig2 &= ~WM9712_PDEN; + else + dig2 &= ~WM9705_PDEN; + } + + CODEC->codec_write(CODEC, AC97_WM97XX_DIGITISER1, ts.dig1 = dig1); + CODEC->codec_write(CODEC, AC97_WM97XX_DIGITISER2, ts.dig2 = dig2); + + spin_unlock(&ts.lock); +} +EXPORT_SYMBOL_GPL(wm97xx_set_continuous_mode); +/*---------------------------------------------------------------------------*/ + +#if defined(CONFIG_PM) +/* save state of wm9712 PGA's */ +static void wm9712_pga_save(void) +{ + ts.phone_pga = CODEC->codec_read(CODEC, AC97_PHONE_VOL) & 0x001f; + ts.line_pga = CODEC->codec_read(CODEC, AC97_LINEIN_VOL) & 0x1f1f; + ts.mic_pga = CODEC->codec_read(CODEC, AC97_MIC_VOL) & 0x1f1f; +} + +/* restore state of wm9712 PGA's */ +static void wm9712_pga_restore(void) +{ + u16 reg; + + reg = CODEC->codec_read(CODEC, AC97_PHONE_VOL); + CODEC->codec_write(CODEC, AC97_PHONE_VOL, (reg & ~0x1f) | ts.phone_pga); + reg = CODEC->codec_read(CODEC, AC97_LINEIN_VOL); + CODEC->codec_write(CODEC, AC97_LINEIN_VOL, (reg & ~0x1f1f) | ts.line_pga); + reg = CODEC->codec_read(CODEC, AC97_MIC_VOL); + CODEC->codec_write(CODEC, AC97_MIC_VOL, (reg & ~0x1f1f) | ts.mic_pga); +} + +/* + * Power down the codec + */ +static int wm97xx_suspend(struct ac97_codec *codec, int state) +{ + u16 reg; + struct list_head *l; + + /* save digitiser settings */ + ts.dig1 = CODEC->codec_read(CODEC, AC97_WM97XX_DIGITISER1); + ts.dig2 = CODEC->codec_read(CODEC, AC97_WM97XX_DIGITISER2); + + /* suspend any arch specific drivers */ + list_for_each(l, &wm97xx_drivers) { + struct wm97xx_device *c = list_entry(l, struct wm97xx_device, devices); + if (c->suspend) + c->suspend(state, 0); + } + + /* wm9705 does not have extra PM */ + if (!ts.is_wm9712) + return 0; + + /* save and mute the PGA's */ + wm9712_pga_save(); + + reg = CODEC->codec_read(CODEC, AC97_PHONE_VOL); + CODEC->codec_write(CODEC, AC97_PHONE_VOL, reg | 0x001f); + reg = CODEC->codec_read(CODEC, AC97_MIC_VOL); + CODEC->codec_write(CODEC, AC97_MIC_VOL, reg | 0x1f1f); + reg = CODEC->codec_read(CODEC, AC97_LINEIN_VOL); + CODEC->codec_write(CODEC, AC97_LINEIN_VOL, reg | 0x1f1f); + + /* power down, dont disable the AC link */ + CODEC->codec_write(CODEC, AC97_WM9712_POWER, WM9712_PD(14) | + WM9712_PD(13) | WM9712_PD(12) | WM9712_PD(11) | + WM9712_PD(10) | WM9712_PD(9) | WM9712_PD(8) | + WM9712_PD(7) | WM9712_PD(6) | WM9712_PD(5) | + WM9712_PD(4) | WM9712_PD(3) | WM9712_PD(2) | + WM9712_PD(1) | WM9712_PD(0)); + + return 0; +} + +/* + * Power up the Codec + */ +static void wm97xx_resume(struct ac97_codec* codec) +{ + struct list_head *l; + + /* resume any arch specific drivers */ + list_for_each(l, &wm97xx_drivers) { + struct wm97xx_device *c = list_entry(l, struct wm97xx_device, devices); + if (c->resume) + c->resume(0); + } + + /* restore digitiser settings */ + CODEC->codec_write(CODEC, AC97_WM97XX_DIGITISER1, ts.dig1); + CODEC->codec_write(CODEC, AC97_WM97XX_DIGITISER2, ts.dig2); + + /* wm9705 does not have extra PM other than muting AUX and VID */ + if (!ts.is_wm9712) { + CODEC->codec_write(CODEC, AC97_AUX_VOL, 0x8000); + CODEC->codec_write(CODEC, AC97_VIDEO_VOL, 0x8000); + return; + } + + /* power up */ + CODEC->codec_write(CODEC, AC97_WM9712_POWER, 0x0); + + /* restore PGA state */ + wm9712_pga_restore(); +} + +#else +#define wm97xx_suspend NULL +#define wm97xx_resume NULL +#endif + +/* + * Handle a pen down interrupt. + */ +static void wm97xx_pen_irq_worker (void *ptr) +{ + u32 status = 0; + + /* do we need to enable the touch panel reader */ + if (!ts.is_wm9712) { + ts.pen_is_down++; + wake_up_interruptible (&ts.pen_irq_wait); + } else { + status = CODEC->codec_read(CODEC, AC97_GPIO_STATUS); + if (status & WM97XX_GPIO_13) { + ts.pen_is_down++; + wake_up_interruptible (&ts.pen_irq_wait); + } + } +} + +/* + * Handle a codec event interrupt. + * Can also include pen down depending upon configuration. + */ +static void wm97xx_codec_irq_worker (void *ptr) +{ + int i = WM97XX_MAX_GPIO; + u16 status, sticky = 0; + + status = CODEC->codec_read(CODEC, AC97_GPIO_STATUS); + + /* check for pen down as we handle it internally + * MASK pen down the unmask at pen up */ + if (status & WM97XX_GPIO_13) + wm97xx_pen_irq_worker(ptr); + + /* reset codec slot 12 sticky bits and disable active interrupt + * sources by clearing the sticky status. + */ + spin_lock(&ts.lock); + if (ts.is_wm9712) { + sticky = CODEC->codec_read(CODEC, AC97_GPIO_STICKY); + CODEC->id = 1; + CODEC->codec_write(CODEC, AC97_GPIO_STATUS, status << 1); + CODEC->id = 0; + CODEC->codec_write(CODEC, AC97_GPIO_STICKY, status ^ sticky); + } + + /* Iterate through archictecture specific codec workers (if they exist) in + * priority order. + */ + while(--i >= 0) { + if (ts.codec_event[i] && status & 1 << i) + ts.codec_event[i](status); + } + + /* turn sticky bits back on, except for pen down */ + if (ts.is_wm9712) + CODEC->codec_write(CODEC, AC97_GPIO_STICKY, sticky & ~WM97XX_GPIO_13); + + spin_unlock(&ts.lock); +} + +/* + * Codec PENDOWN signal irq handler + */ +static irqreturn_t wm97xx_pen_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + ts.pen_irq_count++; + queue_work(ts.pen_irq_workq, &ts.pen_event_work); + + return IRQ_HANDLED; +} + +/* + * Codec PENDOWN/Power/GPIO/status signal irq handler + */ +static irqreturn_t wm97xx_codec_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ + ts.codec_irq_count++; + queue_work(ts.codec_event_workq, &ts.codec_event_work); + + return IRQ_HANDLED; +} + +/* + * initialise codec IRQ handler and workqueue + */ +static int wm97xx_init_codec_irq (void) +{ + u16 reg; + + INIT_WORK(&ts.codec_event_work, wm97xx_codec_irq_worker, &ts); + if ((ts.codec_event_workq = create_singlethread_workqueue("kwm97ev")) == NULL) { + err("could not create codec event work queue"); + ts.codec_irq = 0; + return -1; + } + + ts.codec_irq_count = 0; + if (request_irq(ts.codec_irq, wm97xx_codec_interrupt, SA_SHIRQ, "wm97xx-ac97", &ts)) { + err("could not register codec interrupt"); + destroy_workqueue(ts.codec_event_workq); + ts.codec_irq = 0; + return -1; + } + + /* wm9712 can interrupt using it's IRQ pin or over the AC97 link */ + if (ts.is_wm9712) { + if (ts.ac97_link) { /* use link */ + reg = CODEC->codec_read(CODEC, AC97_RESERVED_58); + CODEC->codec_write(CODEC, AC97_RESERVED_58, reg |= 0x2); + } else { /* use pin */ + reg = CODEC->codec_read(CODEC, AC97_MISC_MODEM_STAT); + CODEC->codec_write(CODEC, AC97_MISC_MODEM_STAT, reg &= 0xfffb); + reg = CODEC->codec_read(CODEC, AC97_GPIO_CONFIG); + CODEC->codec_write(CODEC, AC97_GPIO_CONFIG, reg &= 0xfffb); + } + } + + return 0; +} + +/* + * initialise pen IRQ handler and workqueue + */ +static int wm97xx_init_pen_irq (void) +{ + u16 reg; + + INIT_WORK(&ts.pen_event_work, wm97xx_pen_irq_worker, &ts); + if ((ts.pen_irq_workq = create_singlethread_workqueue("kwm97pen")) == NULL) { + err("could not create pen irq work queue"); + ts.pen_irq = 0; + return -1; + } + + ts.pen_irq_count = 0; + if (request_irq(ts.pen_irq, wm97xx_pen_interrupt, SA_SHIRQ, "wm97xx-pen", &ts)) { + err("could not register codec pen down interrupt, will poll for pen down"); + destroy_workqueue(ts.pen_irq_workq); + ts.pen_irq = 0; + return -1; + } + + /* enable PEN down on wm9712 */ + if (ts.is_wm9712) { + reg = CODEC->codec_read(CODEC, AC97_MISC_MODEM_STAT); + CODEC->codec_write(CODEC, AC97_MISC_MODEM_STAT, reg & 0xfff7); + } + + return 0; +} + +/* + * set up the physical settings of the device + */ +static void init_wm97xx_phy(void) +{ + ts.dig1 = 0; + + if (ts.is_wm9712) + ts.dig2 = WM97XX_RPR | WM9712_RPU(1); + else { + ts.dig2 = WM97XX_RPR; + + /* + * mute VIDEO and AUX as they share X and Y touchscreen + * inputs on the WM9705 + */ + CODEC->codec_write(CODEC, AC97_AUX_VOL, 0x8000); + CODEC->codec_write(CODEC, AC97_VIDEO_VOL, 0x8000); + } + + /* WM9712 rpu */ + if (ts.is_wm9712 && rpu) { + ts.dig2 &= 0xffc0; + ts.dig2 |= WM9712_RPU(rpu); + dbg("setting pen detect pull-up to %d Ohms",64000 / rpu); + } + + /* touchpanel pressure current*/ + if (pil == 2) { + if (ts.is_wm9712) + ts.dig2 |= WM9712_PIL; + else + ts.dig2 |= WM9705_PIL; + dbg("setting pressure measurement current to 400uA."); + } else if (pil) + dbg("setting pressure measurement current to 200uA."); + if(!pil) + pressure = 0; + + /* WM9712 five wire */ + if (ts.is_wm9712 && five_wire) { + ts.dig2 |= WM9712_45W; + dbg("setting 5-wire touchscreen mode."); + } + + /* polling mode sample settling delay */ + if (delay!=4) { + if (delay < 0 || delay > 15) { + dbg("supplied delay out of range."); + delay = 4; + } + } + ts.dig1 &= 0xff0f; + ts.dig1 |= WM97XX_DELAY(delay); + dbg("setting adc sample delay to %d u Secs.", delay_table[delay]); + + /* coordinate mode */ + if (mode == 1) { + ts.dig1 |= WM97XX_COO; + dbg("using coordinate mode"); + } + + /* WM9705 pdd */ + if (pdd && !ts.is_wm9712) { + ts.dig2 |= (pdd & 0x000f); + dbg("setting pdd to Vmid/%d", 1 - (pdd & 0x000f)); + } + + CODEC->codec_write(CODEC, AC97_WM97XX_DIGITISER1, ts.dig1); + CODEC->codec_write(CODEC, AC97_WM97XX_DIGITISER2, ts.dig2); +} + +/* Private struct for communication between wm97xx_ts_thread and wm97xx_read_samples */ +struct ts_state { + int sleep_time; + int min_sleep_time; +}; + +/* + * When we use an irq to detect a pen down event, we must manually check + * for a pen up by checking the PDEN bit. If the pen goes up, we have to + * re-enable the codec pen down detection. + */ +static int wm97xx_irq_pen_check(int rc) +{ + u16 pen_status, reg; + + pen_status = CODEC->codec_read(CODEC, AC97_WM97XX_DIGITISER_RD); + if (!(pen_status & WM97XX_PEN_DOWN)) { + rc = RC_PENUP; + + /* Pen is now going to the up position */ + if (ts.pen_is_down && (ts.pen_irq == ts.codec_irq)) { + reg = CODEC->codec_read(CODEC, AC97_GPIO_STICKY); + CODEC->codec_write(CODEC, AC97_GPIO_STICKY, reg | WM97XX_GPIO_13); + } + } + return rc; +} + +static int wm97xx_read_samples(struct ts_state *state) +{ + wm97xx_data_t data; + int rc = RC_PENUP; + + spin_lock(&ts.lock); + + if (ts.cont_read_sample) { + rc = ts.cont_read_sample (&wm97xx_input, ts.pen_is_down, pressure); + rc = wm97xx_irq_pen_check (rc); + } else { + switch (mode) { + case 0: + rc = wm97xx_poll_touch (&data); + break; + case 1: + rc = wm97xx_poll_coord_touch (&data); + break; + } + } + + if (rc & RC_PENUP) { + if (ts.pen_is_down) { + ts.pen_is_down = 0; + dbg("pen up"); + input_report_abs(&wm97xx_input, ABS_PRESSURE, 0); + input_sync(&wm97xx_input); + if (ts.aux_waiting) { + ts.aux_waiting = 0; + wake_up_interruptible (&ts.aux_wait); + } + } else if (!(rc & RC_AGAIN)) { + /* We need high frequency updates only while pen is down, + * the user never will be able to touch screen faster than + * a few times per second... On the other hand, when the + * user is actively working with the touchscreen we don't + * want to lose the quick response. So we will slowly + * increase sleep time after the pen is up and quicky + * restore it to ~one task switch when pen is down again. + */ + if (state->sleep_time < HZ/10) + state->sleep_time++; + } + } else if (rc & RC_VALID) { + dbg("pen down: x=%x:%d, y=%x:%d, pressure=%x:%d\n", + data.x >> 12, data.x & 0xfff, + data.y >> 12, data.y & 0xfff, + data.p >> 12, data.p & 0xfff); + if (ts.invert_x) + data.x ^= 0xfff; + if (ts.invert_y) + data.y ^= 0xfff; + input_report_abs (&wm97xx_input, ABS_X, data.x & 0xfff); + input_report_abs (&wm97xx_input, ABS_Y, data.y & 0xfff); + input_report_abs (&wm97xx_input, ABS_PRESSURE, data.p & 0xfff); + input_sync (&wm97xx_input); + ts.pen_is_down++; + state->sleep_time = state->min_sleep_time; + } else if (rc & RC_PENDOWN) { + dbg("pen down"); + ts.pen_is_down++; + state->sleep_time = state->min_sleep_time; + } + + spin_unlock(&ts.lock); + return rc; +} + +/* + * The touchscreen sample reader thread. + * Also once in 30 seconds we'll update the battery status. + */ +static int wm97xx_ts_thread(void *data) +{ + int rc; + struct ts_state state; + + /* set up thread context */ + ts.ts_task = current; + daemonize("wm97xx_ts"); + + if(ts.codec == NULL) { + ts.ts_task = NULL; + printk(KERN_ERR "codec is NULL, bailing\n"); + } + complete(&ts.ts_init); + + ts.pen_is_down = 0; + state.min_sleep_time = HZ >= 100 ? HZ/100 : 1; + if (state.min_sleep_time < 1) + state.min_sleep_time = 1; + state.sleep_time = state.min_sleep_time; + + /* touch reader loop */ + while (ts.ts_task) { + do { + rc = wm97xx_read_samples (&state); + } while (rc & RC_AGAIN); + if (!ts.pen_is_down && ts.pen_irq) { + /* Nice, we don't have to poll for pen down event */ + wait_event_interruptible (ts.pen_irq_wait, ts.pen_is_down); + } else { + set_task_state(current, TASK_INTERRUPTIBLE); + schedule_timeout(state.sleep_time); + } + } + complete_and_exit(&ts.ts_exit, 0); +} + +/* + * Start the touchscreen thread and + * the touch digitiser. + */ +static int wm97xx_ts_input_open(struct input_dev *idev) +{ + int ret = 0; + u16 dig2; + + if (ts.codec == NULL) { + printk("error: ts.codec == NULL\n"); + return -EINVAL; + } + + if (ts.codec->codec_read == NULL) { + printk("error: codec_read == NULL\n"); + return -EINVAL; + } + + if (ts.codec->codec_write == NULL) { + printk("error: codec_write == NULL\n"); + return -EINVAL; + } + + down(&ts.sem); + /* first time opened ? */ + if (ts.use_count++ == 0) { + /* start touchscreen thread */ + init_completion(&ts.ts_init); + init_completion(&ts.ts_exit); + + ret = kernel_thread(wm97xx_ts_thread, &ts, CLONE_KERNEL); + if (ret >= 0) { + wait_for_completion(&ts.ts_init); + if (ts.ts_task == NULL) + ret = -EINVAL; + } + if (ret < 0) + goto out; + + /* start digitiser */ + spin_lock(&ts.lock); + + dig2 = ts.dig2 | WM97XX_PRP_DET_DIG; + CODEC->codec_write(CODEC, AC97_WM97XX_DIGITISER2, ts.dig2 = dig2); + CODEC->codec_read(CODEC, AC97_WM97XX_DIGITISER_RD); /* dummy read */ + + /* init pen down/up irq handling */ + if (ts.codec_irq || ts.pen_irq) { + if (ts.codec_irq == ts.pen_irq) { + u16 reg = CODEC->codec_read(CODEC, AC97_GPIO_STICKY); + CODEC->codec_write(CODEC, AC97_GPIO_STICKY, reg | WM97XX_GPIO_13); + wm97xx_init_codec_irq(); + } else { + wm97xx_init_pen_irq(); + } + } + spin_unlock(&ts.lock); + } + +out: + up(&ts.sem); + return ret; +} + +/* + * Kill the touchscreen thread and stop + * the touch digitiser. + */ +static void wm97xx_ts_input_close(struct input_dev *idev) +{ + int val; + + down(&ts.sem); + if (--ts.use_count == 0) { + /* destroy workqueue and free irq's */ + if (ts.codec_irq || ts.pen_irq) { + if (ts.pen_irq == ts.codec_irq) { + free_irq(ts.codec_irq, &ts); + destroy_workqueue(ts.codec_event_workq); + } else { + free_irq(ts.pen_irq, &ts); + destroy_workqueue(ts.pen_irq_workq); + } + } + + /* kill thread */ + ts.ts_task = NULL; + ts.pen_is_down = 1; + wake_up_interruptible (&ts.pen_irq_wait); + wait_for_completion(&ts.ts_exit); + + /* stop digitiser */ + val = ts.codec->codec_read(ts.codec, AC97_WM97XX_DIGITISER2); + ts.codec->codec_write(ts.codec, AC97_WM97XX_DIGITISER2, + val & ~WM97XX_PRP_DET_DIG); + + } + up(&ts.sem); +} + +/* + * Called by the audio codec initialisation to register + * the touchscreen driver. + */ +static int wm97xx_probe(struct ac97_codec *codec, struct ac97_driver *driver) +{ + u16 id1, id2; + struct list_head *l; + + spin_lock(&ts.lock); + /* stop multiple probes */ + if (ts.probe) { + spin_unlock(&ts.lock); + return 0; + } + + /* set up AC97 codec interface */ + CODEC = codec; + codec->driver_private = &ts; + + /* + * We can only use a WM9705 or WM9712 that has been *first* initialised + * by the AC97 audio driver. This is because we have to use the audio + * drivers codec read() and write() functions to sample the touchscreen + * + * If an initialsed WM97xx is found then get the codec read and write + * functions. + */ + + /* test for a WM9712 or a WM9705 */ + id1 = codec->codec_read(codec, AC97_VENDOR_ID1); + id2 = codec->codec_read(codec, AC97_VENDOR_ID2); + if (id1 == WM97XX_ID1 && id2 == WM9712_ID2) + ts.is_wm9712 = 1; + else if (id1 == WM97XX_ID1 && id2 == WM9705_ID2) + ts.is_wm9712 = 0; + else { + err("could not find a WM9705/12 codec. Found a 0x%4x:0x%4x instead", + id1, id2); + spin_unlock(&ts.lock); + return -ENODEV; + } + info("Detected a WM97%s codec connected to AC97 controller %d", ts.is_wm9712 ? "12" : "05", codec->id); + wm97xx_id = (id1 << 16) | id2; + + /* tell input system what we events we accept and register */ + memset (&wm97xx_input, 0, sizeof (struct input_dev)); + wm97xx_input.name = ts.is_wm9712 ? "wm9712 touchscreen" : "wm9705 touchscreen"; + wm97xx_input.open = wm97xx_ts_input_open; + wm97xx_input.close = wm97xx_ts_input_close; + set_bit(EV_ABS, wm97xx_input.evbit); + set_bit(ABS_X, wm97xx_input.absbit); + set_bit(ABS_Y, wm97xx_input.absbit); + set_bit(ABS_PRESSURE, wm97xx_input.absbit); + wm97xx_input.absmax[ABS_X] = abs_x[1]; + wm97xx_input.absmax[ABS_Y] = abs_y[1]; + wm97xx_input.absmax[ABS_PRESSURE] = abs_p[1]; + wm97xx_input.absmin[ABS_X] = abs_x[0]; + wm97xx_input.absmin[ABS_Y] = abs_y[0]; + wm97xx_input.absmin[ABS_PRESSURE] = abs_p[0]; + wm97xx_input.absfuzz[ABS_X] = abs_x[2]; + wm97xx_input.absfuzz[ABS_Y] = abs_y[2]; + wm97xx_input.absfuzz[ABS_PRESSURE] = abs_p[2]; + wm97xx_input.private = &ts; + input_register_device(&wm97xx_input); + + /* set up physical characteristics */ + init_wm97xx_phy(); + + /* probe any arch specific drivers */ + list_for_each(l, &wm97xx_drivers){ + struct wm97xx_device *c = list_entry(l, struct wm97xx_device, devices); + c->probe(); + } + + ts.probe = 1; + spin_unlock(&ts.lock); + + return 0; +} + +/* + * Called by ac97_codec when it is unloaded. + */ +static void wm97xx_remove(struct ac97_codec *codec, struct ac97_driver *driver) +{ + u16 dig1, dig2; + struct list_head *l; + + dbg("Unloading AC97 codec %d", codec->id); + + input_unregister_device(&wm97xx_input); + + /* remove any arch specific drivers */ + list_for_each(l, &wm97xx_drivers) { + struct wm97xx_device *c = list_entry(l, struct wm97xx_device, devices); + c->remove(); + } + + /* Stop touch read thread */ + if (ts.ts_task) { + ts.ts_task = NULL; + ts.pen_is_down = 1; + wake_up_interruptible (&ts.pen_irq_wait); + wait_for_completion(&ts.ts_exit); + } + + /* Stop AUX sampling thread */ + if (ts.aux_task) { + ts.aux_task = NULL; + ts.aux_waiting = 0; + wake_up_interruptible (&ts.aux_wait); + wait_for_completion (&ts.aux_exit); + } + + spin_lock(&ts.lock); + codec->driver_private = NULL; + + /* restore default digitiser values */ + dig1 = WM97XX_DELAY(4) | WM97XX_SLT(6); + if (ts.is_wm9712) + dig2 = WM9712_RPU(1); + else + dig2 = 0x0; + + codec->codec_write(codec, AC97_WM97XX_DIGITISER1, dig1); + codec->codec_write(codec, AC97_WM97XX_DIGITISER2, dig2); + ts.probe = 0; + + spin_unlock(&ts.lock); +} + +/* AC97 registration info */ +static struct ac97_driver wm9705_driver = { + .codec_id = (WM97XX_ID1 << 16) | WM9705_ID2, + .codec_mask = 0xFFFFFFFF, + .name = "Wolfson WM9705 Touchscreen/Battery Monitor", + .probe = wm97xx_probe, + .remove = __devexit_p(wm97xx_remove), + .suspend = wm97xx_suspend, + .resume = wm97xx_resume, +}; + +static struct ac97_driver wm9712_driver = { + .codec_id = (WM97XX_ID1 << 16) | WM9712_ID2, + .codec_mask = 0xFFFFFFFF, + .name = "Wolfson WM9712 Touchscreen/Battery Monitor", + .probe = wm97xx_probe, + .remove = __devexit_p(wm97xx_remove), + .suspend = wm97xx_suspend, + .resume = wm97xx_resume, +}; + +static int __init wm97xx_plugin_init(void) +{ + info("Wolfson WM9705/WM9712 Touchscreen Controller driver"); + info("Version %s liam.girdwood@wolfsonmicro.com", WM_TS_VERSION); + + memset(&ts, 0, sizeof(ts)); + init_MUTEX(&ts.sem); + spin_lock_init (&ts.lock); + init_waitqueue_head (&ts.pen_irq_wait); + init_waitqueue_head (&ts.aux_wait); + + /* register with the AC97 layer */ + ac97_register_driver(&wm9705_driver); + ac97_register_driver(&wm9712_driver); + + return 0; +} + +static void __exit wm97xx_plugin_exit(void) +{ + /* Unregister from the AC97 layer */ + ac97_unregister_driver(&wm9712_driver); + ac97_unregister_driver(&wm9705_driver); +} + +/* Module information */ +MODULE_AUTHOR("Liam Girdwood, liam.girdwood@wolfsonmicro.com, www.wolfsonmicro.com"); +MODULE_DESCRIPTION("WM9705/WM9712 Touch Screen / BMON Driver"); +MODULE_LICENSE("GPL"); + +module_init(wm97xx_plugin_init); +module_exit(wm97xx_plugin_exit); diff -NaurbB linux-2.6.9-pre1-bk18/sound/oss/Kconfig linux-2.6.9_nbp/sound/oss/Kconfig --- linux-2.6.9-pre1-bk18/sound/oss/Kconfig 2005-08-15 01:14:42.000000000 +0200 +++ linux-2.6.9_nbp/sound/oss/Kconfig 2004-09-13 17:33:19.000000000 +0200 @@ -153,6 +153,14 @@ Say Y or M if you have a sound system driven by ESS's Maestro 3 PCI sound chip. +config SOUND_PXA_AC97 + tristate "PXA AC97 support" + depends on SOUND_PRIME!=n && ARCH_PXA && SOUND + +config SOUND_PXA_AUDIO + tristate "PXA audio support" + depends on SOUND_PXA_AC97 + config SOUND_ICH tristate "Intel ICH (i8xx) audio support" depends on SOUND_PRIME!=n && PCI @@ -1088,6 +1096,9 @@ tristate "AD1980 front/back switch plugin" depends on SOUND_PRIME!=n +config SOUND_WM97XX + tristate "WM97XX sound/touchscreen codec" + config SOUND_SH_DAC_AUDIO tristate "SuperH DAC audio support" depends on SOUND_PRIME!=n && SOUND && CPU_SH3 diff -NaurbB linux-2.6.9-pre1-bk18/sound/oss/Makefile linux-2.6.9_nbp/sound/oss/Makefile --- linux-2.6.9-pre1-bk18/sound/oss/Makefile 2005-08-15 01:14:34.000000000 +0200 +++ linux-2.6.9_nbp/sound/oss/Makefile 2004-11-20 00:24:52.000000000 +0100 @@ -44,6 +44,8 @@ ifeq ($(CONFIG_MIDI_VIA82CXXX),y) obj-$(CONFIG_SOUND_VIA82CXXX) += sound.o uart401.o endif +obj-$(CONFIG_SOUND_PXA_AC97) += pxa-ac97.o ac97_codec.o +obj-$(CONFIG_SOUND_PXA_AUDIO) += pxa-audio.o obj-$(CONFIG_SOUND_YMFPCI) += ymfpci.o ac97_codec.o ifeq ($(CONFIG_SOUND_YMFPCI_LEGACY),y) obj-$(CONFIG_SOUND_YMFPCI) += opl3.o uart401.o @@ -78,7 +80,7 @@ obj-$(CONFIG_SOUND_FORTE) += forte.o ac97_codec.o obj-$(CONFIG_SOUND_AD1980) += ac97_plugin_ad1980.o -obj-$(CONFIG_SOUND_WM97XX) += ac97_plugin_wm97xx.o +obj-$(CONFIG_SOUND_WM97XX) += ac97_plugin_wm97xx.o pxa-wm97xx-touch.o ifeq ($(CONFIG_MIDI_EMU10K1),y) obj-$(CONFIG_SOUND_EMU10K1) += sound.o diff -NaurbB linux-2.6.9-pre1-bk18/sound/oss/pxa-ac97.c linux-2.6.9_nbp/sound/oss/pxa-ac97.c --- linux-2.6.9-pre1-bk18/sound/oss/pxa-ac97.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/sound/oss/pxa-ac97.c 2004-11-10 18:10:48.000000000 +0100 @@ -0,0 +1,801 @@ +/* + * linux/drivers/sound/pxa-ac97.c -- AC97 interface for the Cotula chip + * + * Author: Nicolas Pitre + * Created: Aug 15, 2001 + * Copyright: MontaVista Software Inc. + * + * Forward ported to 2.6 by Ian Molton 15/09/2003 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Sep 1st 2004: Liam Girdwood + * Changed codec initialisation sequence to perform a cold reset + * followed by a warm reset. This allows a greater range of + * codecs to be used on the XScale. + * Added codec init failure error detection, fast codec GPIO + * for PXA25x and PXA27x, new PXA27x AC97 read/write and + * shared the AC97 interrupt for use with codec specifig GPIO's. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "pxa-audio.h" + +#if defined(CONFIG_PREEMPT) && defined(CONFIG_MACH_MAINSTONE) +#error this driver is buggy with preempt enabled +#endif + +/* We need a dummy irq tag to request/free our shared irq handler */ +#define AC97_IRQ_TAG (void*)0xac97c0de + +static DECLARE_MUTEX(CAR_mutex); + +/* + * PXA27x has a different AC97 IO flow than PXA25x. + */ +#ifdef CONFIG_PXA27x + +static int pxa27x_read_wait(void) +{ + unsigned long timeout = jiffies + 2; + int ret = 1; + u32 gsr; + + do { + if (GSR & GSR_SDONE) + break; + + msleep(1); + } while (time_before(jiffies, timeout)); + + gsr = GSR; + GSR = gsr | GSR_SDONE | GSR_CDONE; + + if (!(gsr & GSR_SDONE)) { + printk(KERN_CRIT "%s: read codec register timeout.\n", __FUNCTION__); + ret = 0; + } + + return ret; +} + +static int pxa27x_write_wait(void) +{ + unsigned long timeout = jiffies + 2; + int ret = 1; + u32 gsr; + + do { + if (GSR & GSR_CDONE) + break; + + msleep(1); + } while (time_before(jiffies, timeout)); + + gsr = GSR; + GSR = gsr | GSR_CDONE; + + if (!(gsr & GSR_CDONE)) { + printk(KERN_CRIT "%s: write codec register timeout.\n", __FUNCTION__); + ret = 0; + } + + return ret; +} + +static u16 pxa_ac97_read(struct ac97_codec *codec, u8 reg) +{ + u16 val =-1; + volatile u32 *reg_addr; + + down(&CAR_mutex); + + /* set up primary or secondary space */ + if (codec->id & 0x1) + reg_addr = (u32 *)&SAC_REG_BASE + (reg >> 1); + else + reg_addr = (u32 *)&PAC_REG_BASE + (reg >> 1); + + /* No fast codec GPIO reads on PXA27x */ + (void)*reg_addr; // dummy read + if (pxa27x_read_wait()) { + val = *reg_addr; + if (!pxa27x_read_wait()) + val = -1; + } + + up(&CAR_mutex); + return val; +} + +static void pxa_ac97_write(struct ac97_codec *codec, u8 reg, u16 val) +{ + volatile u32 *reg_addr; + down(&CAR_mutex); + + /* Status reg (0x54) writes are fast, others need to wait */ + if (reg == AC97_GPIO_STATUS) { + + /* set up primary or secondary modem space */ + if (codec->id & 0x1) + reg_addr = (u32 *)&SMC_REG_BASE + (reg >> 1); + else + reg_addr = (u32 *)&PMC_REG_BASE + (reg >> 1); + + *reg_addr = val; + udelay(50); + } else { + /* set up primary or secondary codec space */ + if (codec->id & 0x1) + reg_addr = (u32 *)&SAC_REG_BASE + (reg >> 1); + else + reg_addr = (u32 *)&PAC_REG_BASE + (reg >> 1); + + *reg_addr = val; + pxa27x_write_wait(); + } + up(&CAR_mutex); +} + +#else +static struct completion CAR_completion; +static int waitingForMask; + +static u16 pxa_ac97_read(struct ac97_codec *codec, u8 reg) +{ + u16 val = -1; + + down(&CAR_mutex); + if (!(CAR & CAR_CAIP)) { + volatile u32 *reg_addr; + + /* Status reg (0x54) reads are fast, others need to wait */ + if (reg == AC97_GPIO_STATUS) { + /* set up primary or secondary modem space */ + if (codec->id & 0x1) + reg_addr = (u32 *)&SMC_REG_BASE + (reg >> 1); + else + reg_addr = (u32 *)&PMC_REG_BASE + (reg >> 1); + + val = *reg_addr; + up(&CAR_mutex); + return val; + } + + /* set up primary or secondary codec space */ + if (codec->id & 0x1) + reg_addr = (u32 *)&SAC_REG_BASE + (reg >> 1); + else + reg_addr = (u32 *)&PAC_REG_BASE + (reg >> 1); + + waitingForMask=GSR_SDONE; + init_completion(&CAR_completion); + (void)*reg_addr; //start read access across the ac97 link + wait_for_completion(&CAR_completion); + + if (GSR & GSR_RDCS) { + GSR |= GSR_RDCS; //write a 1 to clear + printk(KERN_CRIT "%s: read codec register timeout.\n", __FUNCTION__); + } + + init_completion(&CAR_completion); + val = *reg_addr; //valid data now but we've just started another cycle... + wait_for_completion(&CAR_completion); + + } else { + printk(KERN_CRIT"%s: CAR_CAIP already set\n", __FUNCTION__); + } + up(&CAR_mutex); + + return val; +} + +static void pxa_ac97_write(struct ac97_codec *codec, u8 reg, u16 val) +{ + down(&CAR_mutex); + if (!(CAR & CAR_CAIP)) { + volatile u32 *reg_addr; + + /* Status reg (0x54) writes are fast, others need to wait */ + if (reg == AC97_GPIO_STATUS) { + /* set up primary or secondary modem space */ + if (codec->id & 0x1) + reg_addr = (u32 *)&SMC_REG_BASE + (reg >> 1); + else + reg_addr = (u32 *)&PMC_REG_BASE + (reg >> 1); + + *reg_addr = val; + up(&CAR_mutex); + return; + } + + reg_addr = (u32 *)&PAC_REG_BASE + (reg >> 1); + + /* set up primary or secondary codec space */ + if (codec->id & 0x1) + reg_addr = (u32 *)&SAC_REG_BASE + (reg >> 1); + else + reg_addr = (u32 *)&PAC_REG_BASE + (reg >> 1); + + waitingForMask=GSR_CDONE; + init_completion(&CAR_completion); + *reg_addr = val; + wait_for_completion(&CAR_completion); + } else { + printk(KERN_CRIT "%s: CAR_CAIP already set\n", __FUNCTION__); + } + up(&CAR_mutex); + + if (0) + printk("%s(0x%02x, 0x%04x)\n", __FUNCTION__, reg, val); +} + +static irqreturn_t pxa_ac97_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + int gsr = GSR; + + /* we only handle CDONE and SDONE */ + if (gsr & (GSR_SDONE|GSR_CDONE)) { + GSR = gsr & (GSR_SDONE|GSR_CDONE); //write a 1 to clear + if (gsr & waitingForMask) + complete(&CAR_completion); + return IRQ_HANDLED; + } else + return IRQ_NONE; +} +#endif + +static struct ac97_codec pxa_ac97_codec = { + codec_read: pxa_ac97_read, + codec_write: pxa_ac97_write, +}; + +static int pxa_ac97_cold_reset(void) +{ + int timeout = 5; + + if (machine_is_netbookpro()) { + /* remove codec reset */ + GPSR(63) = GPIO_bit(63); + } + GCR = 0; + udelay(10); +#ifdef CONFIG_PXA27x + GCR = GCR_COLD_RST; +#else + GCR = GCR_COLD_RST|GCR_CDONE_IE|GCR_SDONE_IE; +#endif + + /* link should be up after 10 uS */ + udelay(10); + while (!(GSR & GSR_PCR) && timeout--) { + msleep(1); + } + + return timeout; +} + +static int pxa_ac97_warm_reset(void) +{ + int timeout = 5; + +#ifdef CONFIG_PXA27x + /* warm reset broken on Bulverde, so manually keep AC97 reset high */ + GAFR3_U &= ~0xC; + GPSR3 = 0x20000 | GPLR3; +#endif + + GCR &= ~GCR_ACLINK_OFF; +#ifdef CONFIG_PXA27x + GCR |= GCR_WARM_RST; +#else + GCR |= GCR_WARM_RST|GCR_CDONE_IE|GCR_SDONE_IE; +#endif + udelay(500); + + /* link should be up after 500 uS */ + while (!(GSR & GSR_PCR) && timeout--) { + msleep(1); + } + + return timeout; +} + +static DECLARE_MUTEX(pxa_ac97_mutex); +static int pxa_ac97_refcount; + +int pxa_ac97_get(struct ac97_codec **codec) +{ + int ret; + + *codec = NULL; + down(&pxa_ac97_mutex); + + if (!pxa_ac97_refcount) { +#ifndef CONFIG_PXA27x + ret = request_irq(IRQ_AC97, pxa_ac97_irq, SA_SHIRQ, "AC97", AC97_IRQ_TAG); + if (ret) + return ret; +#endif + pxa_set_cken(CKEN2_AC97, 1); + + /* + * On Mainstone, we enable the on board audio amp and + * route AC97_SYSCLK via GPIO45 to the audio daughter card + */ + if(machine_is_mainstone()) { + MST_MSCWR2 &= 0xffff000b; + pxa_gpio_mode(GPIO45_SYSCLK_AC97_MD); + } + + pxa_gpio_mode(GPIO31_SYNC_AC97_MD); + pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD); + pxa_gpio_mode(GPIO28_BITCLK_AC97_MD); + pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD); + + /* + * Use GPIO 113 as AC97 Reset on Bulverde + */ +#ifdef CONFIG_PXA27x + pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT); +#endif + /* + * We try a cold reset followed by a warm reset. If the codec doesn't + * respond after this then something is wrong. + */ + if (pxa_ac97_cold_reset() < 0) + if (pxa_ac97_warm_reset() < 0) + goto ac97_fail; + + ret = ac97_probe_codec(&pxa_ac97_codec); + if (ret != 1) + goto ac97_fail; + } + + pxa_ac97_refcount++; + up(&pxa_ac97_mutex); + *codec = &pxa_ac97_codec; + return 0; + +ac97_fail: + if(machine_is_mainstone()) { + MST_MSCWR2 &= ~0xc0; + pxa_gpio_mode(GPIO45_BTRTS_MD); + } + free_irq(IRQ_AC97, AC97_IRQ_TAG); + GCR = GCR_ACLINK_OFF; + pxa_set_cken(CKEN2_AC97, 0); + printk(KERN_ERR "could not initialise AC97 codec."); + return -ENODEV; +} + +void pxa_ac97_put(void) +{ + down(&pxa_ac97_mutex); + pxa_ac97_refcount--; + if (!pxa_ac97_refcount) { + if(machine_is_mainstone()) { + MST_MSCWR2 &= ~0xc0; + pxa_gpio_mode(GPIO45_BTRTS_MD); + } + GCR = GCR_ACLINK_OFF; + pxa_set_cken(CKEN2_AC97, 0); +#ifndef CONFIG_PXA27x + free_irq(IRQ_AC97, AC97_IRQ_TAG); +#endif + } + up(&pxa_ac97_mutex); +} + +EXPORT_SYMBOL(pxa_ac97_get); +EXPORT_SYMBOL(pxa_ac97_put); + + +/* + * Audio Mixer stuff + */ + +static audio_state_t ac97_audio_state; +static audio_stream_t ac97_audio_in; + +static int mixer_ioctl( struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int ret; + + /* we need to disable/intercept some ioctls that interfere with the nbp setup */ + if (machine_is_netbookpro()) { + if (cmd == MIXER_WRITE(SOUND_MIXER_LINE1)|| + cmd == MIXER_WRITE(SOUND_MIXER_VIDEO)|| + cmd == MIXER_WRITE(SOUND_MIXER_CD) || + cmd == MIXER_WRITE(SOUND_MIXER_ALTPCM) || + cmd == MIXER_WRITE(SOUND_MIXER_PHONEOUT)) { + return 0; + } + if (cmd == MIXER_READ(SOUND_MIXER_ALTPCM) || + cmd == MIXER_READ(SOUND_MIXER_PHONEOUT)) + cmd = MIXER_READ(SOUND_MIXER_VOLUME); + } + + ret = pxa_ac97_codec.mixer_ioctl(&pxa_ac97_codec, cmd, arg); + if (ret) + return ret; + + /* We must snoop for some commands to provide our own extra processing */ + switch (cmd) { + case SOUND_MIXER_WRITE_RECSRC: + /* + * According to the PXA250 spec, mic-in should use different + * DRCMR and different AC97 FIFO. + * Unfortunately current UCB1400 versions (up to ver 2A) don't + * produce slot 6 for the audio input frame, therefore the PXA + * AC97 mic-in FIFO is always starved. + */ +#if 0 + ret = get_user(val, (int *)arg); + if (ret) + return ret; + pxa_audio_clear_buf(&ac97_audio_in); + *ac97_audio_in.drcmr = 0; + if (val & (1 << SOUND_MIXER_MIC)) { + ac97_audio_in.dcmd = DCMD_RXMCDR; + ac97_audio_in.drcmr = &DRCMRRXMCDR; + ac97_audio_in.dev_addr = __PREG(MCDR); + } else { + ac97_audio_in.dcmd = DCMD_RXPCDR; + ac97_audio_in.drcmr = &DRCMRRXPCDR; + ac97_audio_in.dev_addr = __PREG(PCDR); + } + if (ac97_audio_state.rd_ref) + *ac97_audio_in.drcmr = + ac97_audio_in.dma_ch | DRCMR_MAPVLD; +#endif + break; + if (machine_is_netbookpro()) { + u16 vol; + case SOUND_MIXER_WRITE_VOLUME: + /* + * nbp does not use line out, so we must also change mono + * and hp volumes. + */ + vol = pxa_ac97_read(&pxa_ac97_codec, AC97_MASTER_VOL_STEREO); + pxa_ac97_write(&pxa_ac97_codec, AC97_HEADPHONE_VOL, vol); + pxa_ac97_write(&pxa_ac97_codec, AC97_MASTER_VOL_MONO, vol); + break; + } + + } + return 0; +} + +static struct file_operations mixer_fops = { + ioctl: mixer_ioctl, + llseek: no_llseek, + owner: THIS_MODULE +}; + +/* + * AC97 codec ioctls + */ + +static int codec_adc_rate = 48000; +static int codec_dac_rate = 48000; + +static int ac97_ioctl(struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + int ret; + long val = 0; + + switch(cmd) { + case SNDCTL_DSP_STEREO: /* Only stereo is supported */ + return put_user(1, (int *) arg); + + case SNDCTL_DSP_CHANNELS: + case SOUND_PCM_READ_CHANNELS: /* Only stereo is supported */ + return put_user(2, (long *) arg); + + case SNDCTL_DSP_SPEED: + ret = get_user(val, (long *) arg); + if (ret) + return ret; + if (file->f_mode & FMODE_READ) + codec_adc_rate = ac97_set_adc_rate(&pxa_ac97_codec, val); + if (file->f_mode & FMODE_WRITE) { + codec_dac_rate = ac97_set_dac_rate(&pxa_ac97_codec, val); + printk("SNDCTL_DSP_SPEED: codec_dac_rate=%d\n", + codec_dac_rate); + } + + /* fall through */ + case SOUND_PCM_READ_RATE: + if (file->f_mode & FMODE_READ) + val = codec_adc_rate; + if (file->f_mode & FMODE_WRITE) + val = codec_dac_rate; + return put_user(val, (long *) arg); + + case SNDCTL_DSP_SETFMT: + case SNDCTL_DSP_GETFMTS: /* Only one format is supported */ + return put_user(AFMT_S16_LE, (long *) arg); + + default: + /* Maybe this is meant for the mixer (As per OSS Docs) */ + return mixer_ioctl(inode, file, cmd, arg); + } + return 0; +} + + +/* + * Audio stuff + */ + +static audio_stream_t ac97_audio_out = { + name: "AC97 audio out", + dcmd: DCMD_TXPCDR, + drcmr: &DRCMRTXPCDR, + dev_addr: __PREG(PCDR), +}; + +static audio_stream_t ac97_audio_in = { + name: "AC97 audio in", + dcmd: DCMD_RXPCDR, + drcmr: &DRCMRRXPCDR, + dev_addr: __PREG(PCDR), +}; + +static audio_state_t ac97_audio_state = { + output_stream: &ac97_audio_out, + input_stream: &ac97_audio_in, + client_ioctl: ac97_ioctl, + sem: __MUTEX_INITIALIZER(ac97_audio_state.sem), +}; + +static int ac97_audio_open(struct inode *inode, struct file *file) +{ + return pxa_audio_attach(inode, file, &ac97_audio_state); +} + +static int pxa_ac97_suspend(struct device * dev, u32 state, u32 level) +{ + u16 reg, pwrmode; + int ret; + + if ( level != SUSPEND_POWER_DOWN ) + return 0; + + + switch (state) { + case 1: pwrmode = AC97_PWR_D1; break; + case 2: pwrmode = AC97_PWR_D2; break; + case 3: pwrmode = AC97_PWR_D3; break; + default: return 1; + } + + ret = ac97_suspend(&pxa_ac97_codec, state); + if (ret) + return ret; + + reg = pxa_ac97_read(&pxa_ac97_codec, AC97_POWER_CONTROL); + reg |= (pwrmode & ~(AC97_PWR_PR6|AC97_PWR_PR5|AC97_PWR_PR4)); + pxa_ac97_write(&pxa_ac97_codec, AC97_POWER_CONTROL, reg); + udelay(500); + + reg = pxa_ac97_read(&pxa_ac97_codec, AC97_POWER_CONTROL); + reg |= pwrmode; + pxa_ac97_write(&pxa_ac97_codec, AC97_POWER_CONTROL, reg); + + GCR |= GCR_ACLINK_OFF; + pxa_set_cken(CKEN2_AC97, 0); + + return 0; +} + +static int pxa_ac97_resume(struct device * dev, u32 level) +{ + if ( level != RESUME_POWER_ON ) + return 0; + + pxa_set_cken(CKEN2_AC97, 1); + + if (machine_is_netbookpro()) { + /* it seems this may well be the only way to + * get the ac97 setup on the netbookpro to play + * ball nicely... + */ + pxa_ac97_cold_reset(); + + ac97_probe_codec(&pxa_ac97_codec); + + } else { + pxa_ac97_warm_reset(); + } + + pxa_ac97_write(&pxa_ac97_codec, AC97_POWER_CONTROL, AC97_PWR_D0); + + ac97_resume(&pxa_ac97_codec); + + return 0; +} + +/* + * Missing fields of this structure will be patched with the call + * to pxa_audio_attach(). + */ + +static struct file_operations ac97_audio_fops = { + open: ac97_audio_open, + owner: THIS_MODULE +}; + +#ifdef CONFIG_MACH_NETBOOKPRO +static void nbp_hp_remove(void) +{ + u16 reg; + + /* power down and mute headphones, unmute spk */ + reg = pxa_ac97_read(&pxa_ac97_codec, 0x5c); + pxa_ac97_write(&pxa_ac97_codec, 0x5c, reg & ~0x0200); + reg = pxa_ac97_read(&pxa_ac97_codec, AC97_HEADPHONE_VOL); + pxa_ac97_write(&pxa_ac97_codec, AC97_HEADPHONE_VOL, reg | AC97_MUTE); + reg = pxa_ac97_read(&pxa_ac97_codec, AC97_POWER_CONTROL); + pxa_ac97_write(&pxa_ac97_codec, AC97_POWER_CONTROL, reg | AC97_PWR_PR6); + reg = pxa_ac97_read(&pxa_ac97_codec, AC97_MASTER_VOL_MONO); + pxa_ac97_write(&pxa_ac97_codec, AC97_MASTER_VOL_MONO, reg & ~AC97_MUTE); + GPCR(65) = GPIO_bit(65); +} + +static void nbp_hp_insert(void) +{ + u16 reg; + + /* power up headphones and mute spk, unmute hp */ + GPSR(65) = GPIO_bit(65); + reg = pxa_ac97_read(&pxa_ac97_codec, AC97_POWER_CONTROL); + pxa_ac97_write(&pxa_ac97_codec, AC97_POWER_CONTROL, reg & ~AC97_PWR_PR6); + reg = pxa_ac97_read(&pxa_ac97_codec, AC97_HEADPHONE_VOL); + pxa_ac97_write(&pxa_ac97_codec, AC97_HEADPHONE_VOL, reg & ~AC97_MUTE); + reg = pxa_ac97_read(&pxa_ac97_codec, AC97_MASTER_VOL_MONO); + pxa_ac97_write(&pxa_ac97_codec, AC97_MASTER_VOL_MONO, reg | AC97_MUTE); + +} + +static void nbp_hp_status_detect (void *ptr) +{ + /* do we have a headphone/MIC insertion or removal event */ + if (GPLR0 & 0x8) { + nbp_hp_remove(); + } else { + nbp_hp_insert(); + } +} + +static struct workqueue_struct *nbp_hp_workq; +DECLARE_WORK(nbp_hp_status_work, nbp_hp_status_detect, NULL); + +static void nbp_hp_debounce(unsigned long data) +{ + /* debounce should now be complete */ + queue_work(nbp_hp_workq, &nbp_hp_status_work); +} + +static struct timer_list nbp_hp_timer = + TIMER_INITIALIZER(nbp_hp_debounce, 0, 0); + +static irqreturn_t nbp_hp_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + /* debounce the jack insertion */ + mod_timer(&nbp_hp_timer, jiffies + HZ); + + return IRQ_HANDLED; +} + +#endif + +static int __devinit pxa_ac97_probe(struct device *dev) +{ + int ret; + struct ac97_codec *dummy; + + ret = pxa_ac97_get(&dummy); + if (ret) + return ret; + + if (machine_is_netbookpro()) { + if ((nbp_hp_workq = create_singlethread_workqueue("nbp-hp")) == NULL) { + pxa_ac97_put(); + return -ENODEV; + } + + if ((ret = request_irq(IRQ_GPIO(3), nbp_hp_irq, 0, "Headphones/MIC", NULL))) { + pxa_ac97_put(); + destroy_workqueue(nbp_hp_workq); + return -ENODEV; + } + set_irq_type(IRQ_GPIO(3), IRQT_BOTHEDGE); + + if (GPLR0 & 0x8) + nbp_hp_remove(); + else + nbp_hp_insert(); + + /* not used on nbp */ + pxa_ac97_write(&pxa_ac97_codec, AC97_MASTER_VOL_STEREO, AC97_MUTE); + pxa_ac97_write(&pxa_ac97_codec, AC97_CD_VOL, AC97_MUTE); + + /* keep startup quite */ + pxa_ac97_write(&pxa_ac97_codec, AC97_GENERAL_PURPOSE, 0x0); + pxa_ac97_write(&pxa_ac97_codec, AC97_LINEIN_VOL, AC97_MUTE); + pxa_ac97_write(&pxa_ac97_codec, AC97_MIC_VOL, AC97_MUTE); + } + + ac97_audio_state.dev_dsp = register_sound_dsp(&ac97_audio_fops, -1); + pxa_ac97_codec.dev_mixer = register_sound_mixer(&mixer_fops, -1); + ac97_audio_state.output_stream->dev = dev; + ac97_audio_state.input_stream->dev = dev; + + return 0; +} + +static int __devexit pxa_ac97_remove(struct device *dev) +{ + unregister_sound_dsp(ac97_audio_state.dev_dsp); + unregister_sound_mixer(pxa_ac97_codec.dev_mixer); + if (machine_is_netbookpro()) { + free_irq(IRQ_GPIO(3), NULL); + destroy_workqueue(nbp_hp_workq); + } + pxa_ac97_put(); + ac97_audio_state.output_stream->dev = NULL; + ac97_audio_state.input_stream->dev = NULL; + return 0; +} + +static struct device_driver pxa_ac97_driver = { + .name = "pxa-audio", + .bus = &platform_bus_type, + .probe = pxa_ac97_probe, + .remove = pxa_ac97_remove, + .suspend = pxa_ac97_suspend, + .resume = pxa_ac97_resume, +}; + +static int __init pxa_ac97_init(void) +{ + return driver_register(&pxa_ac97_driver); +} + +static void __exit pxa_ac97_exit(void) +{ + driver_unregister(&pxa_ac97_driver); +} + +module_init(pxa_ac97_init); +module_exit(pxa_ac97_exit); + +MODULE_AUTHOR("Nicolas Pitre"); +MODULE_DESCRIPTION("AC97 interface for the Cotula chip"); +MODULE_LICENSE("GPL"); diff -NaurbB linux-2.6.9-pre1-bk18/sound/oss/pxa-audio.c linux-2.6.9_nbp/sound/oss/pxa-audio.c --- linux-2.6.9-pre1-bk18/sound/oss/pxa-audio.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/sound/oss/pxa-audio.c 2004-10-06 01:23:02.000000000 +0200 @@ -0,0 +1,867 @@ +/* + * linux/drivers/sound/pxa-audio.c -- audio interface for the Cotula chip + * + * Author: Nicolas Pitre + * Created: Aug 15, 2001 + * Copyright: MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "pxa-audio.h" + + +#define AUDIO_NBFRAGS_DEFAULT 8 +#define AUDIO_FRAGSIZE_DEFAULT 8192 + +#define MAX_DMA_SIZE 4096 +#define DMA_DESC_SIZE sizeof(pxa_dma_desc) + + +/* + * This function frees all buffers + */ +#define audio_clear_buf pxa_audio_clear_buf + +void pxa_audio_clear_buf(audio_stream_t * s) +{ + DECLARE_WAITQUEUE(wait, current); + int frag; + + if (!s->buffers) + return; + + /* Ensure DMA isn't running */ + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&s->stop_wq, &wait); + DCSR(s->dma_ch) = DCSR_STOPIRQEN; + schedule(); + remove_wait_queue(&s->stop_wq, &wait); + + /* free DMA buffers */ + for (frag = 0; frag < s->nbfrags; frag++) { + audio_buf_t *b = &s->buffers[frag]; + if (!b->master) + continue; + dma_free_coherent(s->dev, b->master, b->data, b->dma_desc->dsadr); + } + + /* free descriptor ring */ + if (s->buffers->dma_desc) + dma_free_coherent(s->dev, s->nbfrags * s->descs_per_frag * DMA_DESC_SIZE, + s->buffers->dma_desc, s->dma_desc_phys); + + /* free buffer structure array */ + kfree(s->buffers); + s->buffers = NULL; +} + +/* + * This function allocates the DMA descriptor array and buffer data space + * according to the current number of fragments and fragment size. + */ +static int audio_setup_buf(audio_stream_t * s) +{ + pxa_dma_desc *dma_desc; + dma_addr_t dma_desc_phys; + int nb_desc, frag, i, buf_size = 0; + char *dma_buf = NULL; + dma_addr_t dma_buf_phys = 0; + + /* FIXME - superfluous ? */ + if (s->buffers) + return -EBUSY; + + /* Our buffer structure array */ + s->buffers = kmalloc(sizeof(audio_buf_t) * s->nbfrags, GFP_KERNEL); + if (!s->buffers) + goto err; + memzero(s->buffers, sizeof(audio_buf_t) * s->nbfrags); + + /* + * Our DMA descriptor array: + * for Each fragment we have one checkpoint descriptor plus one + * descriptor per MAX_DMA_SIZE byte data blocks. + */ + nb_desc = (1 + (s->fragsize + MAX_DMA_SIZE - 1)/MAX_DMA_SIZE) * s->nbfrags; + dma_desc = dma_alloc_coherent(s->dev, nb_desc * DMA_DESC_SIZE, + &dma_desc_phys, GFP_KERNEL); + if (!dma_desc) + goto err; + s->descs_per_frag = nb_desc / s->nbfrags; + s->buffers->dma_desc = dma_desc; + s->dma_desc_phys = dma_desc_phys; + for (i = 0; i < nb_desc - 1; i++) + dma_desc[i].ddadr = dma_desc_phys + (i + 1) * DMA_DESC_SIZE; + dma_desc[i].ddadr = dma_desc_phys; + + /* Our actual DMA buffers */ + for (frag = 0; frag < s->nbfrags; frag++) { + audio_buf_t *b = &s->buffers[frag]; + + /* + * Let's allocate non-cached memory for DMA buffers. + * We try to allocate all memory at once. + * If this fails (a common reason is memory fragmentation), + * then we'll try allocating smaller buffers. + */ + if (!buf_size) { + buf_size = (s->nbfrags - frag) * s->fragsize; + do { + dma_buf = dma_alloc_coherent(s->dev, buf_size, &dma_buf_phys, GFP_KERNEL); + if (!dma_buf) + buf_size -= s->fragsize; + } while (!dma_buf && buf_size); + if (!dma_buf) + goto err; + b->master = buf_size; + memzero(dma_buf, buf_size); + } + + /* + * Set up our checkpoint descriptor. Since the count + * is always zero, we'll abuse the dsadr and dtadr fields + * just in case this one is picked up by the hardware + * while processing SOUND_DSP_GETPTR. + */ + dma_desc->dsadr = dma_buf_phys; + dma_desc->dtadr = dma_buf_phys; + dma_desc->dcmd = DCMD_ENDIRQEN; + if (s->output && !s->mapped) + dma_desc->ddadr |= DDADR_STOP; + b->dma_desc = dma_desc++; + + /* set up the actual data descriptors */ + for (i = 0; (i * MAX_DMA_SIZE) < s->fragsize; i++) { + dma_desc[i].dsadr = (s->output) ? + (dma_buf_phys + i*MAX_DMA_SIZE) : s->dev_addr; + dma_desc[i].dtadr = (s->output) ? + s->dev_addr : (dma_buf_phys + i*MAX_DMA_SIZE); + dma_desc[i].dcmd = s->dcmd | + ((s->fragsize < MAX_DMA_SIZE) ? + s->fragsize : MAX_DMA_SIZE); + } + dma_desc += i; + + /* handle buffer pointers */ + b->data = dma_buf; + dma_buf += s->fragsize; + dma_buf_phys += s->fragsize; + buf_size -= s->fragsize; + } + + s->usr_frag = s->dma_frag = 0; + s->bytecount = 0; + s->fragcount = 0; + sema_init(&s->sem, (s->output) ? s->nbfrags : 0); + return 0; + +err: + printk("pxa-audio: unable to allocate audio memory\n "); + audio_clear_buf(s); + return -ENOMEM; +} + +/* + * Our DMA interrupt handler + */ +static void audio_dma_irq(int ch, void *dev_id, struct pt_regs *regs) +{ + audio_stream_t *s = dev_id; + u_int dcsr; + + dcsr = DCSR(ch); + DCSR(ch) = dcsr & ~DCSR_STOPIRQEN; + + if (!s->buffers) { + printk("Audio DMA: wow... received IRQ for channel %d but no buffer exists\n", ch); + return; + } + + if (dcsr & DCSR_BUSERR) + printk("Audio DMA: bus error interrupt on channel %d\n", ch); + + if (dcsr & DCSR_ENDINTR) { + u_long cur_dma_desc; + u_int cur_dma_frag; + + /* + * Find out which DMA desc is current. Note that DDADR + * points to the next desc, not the current one. + */ + cur_dma_desc = DDADR(ch) - s->dma_desc_phys - DMA_DESC_SIZE; + + /* + * Let the compiler nicely optimize constant divisors into + * multiplications for the common cases which is much faster. + * Common cases: x = 1 + (1 << y) for y = [0..3] + */ + switch (s->descs_per_frag) { + case 2: cur_dma_frag = cur_dma_desc / (2*DMA_DESC_SIZE); break; + case 3: cur_dma_frag = cur_dma_desc / (3*DMA_DESC_SIZE); break; + case 5: cur_dma_frag = cur_dma_desc / (5*DMA_DESC_SIZE); break; + case 9: cur_dma_frag = cur_dma_desc / (9*DMA_DESC_SIZE); break; + default: cur_dma_frag = + cur_dma_desc / (s->descs_per_frag * DMA_DESC_SIZE); + } + + /* Account for possible wrap back of cur_dma_desc above */ + if (cur_dma_frag >= s->nbfrags) + cur_dma_frag = s->nbfrags - 1; + + while (s->dma_frag != cur_dma_frag) { + if (!s->mapped) { + /* + * This fragment is done - set the checkpoint + * descriptor to STOP until it is gets + * processed by the read or write function. + */ + s->buffers[s->dma_frag].dma_desc->ddadr |= DDADR_STOP; + up(&s->sem); + } + if (++s->dma_frag >= s->nbfrags) + s->dma_frag = 0; + + /* Accounting */ + s->bytecount += s->fragsize; + s->fragcount++; + } + + /* ... and for polling processes */ + wake_up(&s->frag_wq); + } + + if ((dcsr & DCSR_STOPIRQEN) && (dcsr & DCSR_STOPSTATE)) + wake_up(&s->stop_wq); +} + +/* + * Validate and sets up buffer fragments, etc. + */ +static int audio_set_fragments(audio_stream_t *s, int val) +{ + if (s->mapped || DCSR(s->dma_ch) & DCSR_RUN) + return -EBUSY; + if (s->buffers) + audio_clear_buf(s); + s->nbfrags = (val >> 16) & 0x7FFF; + val &= 0xffff; + if (val < 5) + val = 5; + if (val > 15) + val = 15; + s->fragsize = 1 << val; + if (s->nbfrags < 2) + s->nbfrags = 2; + if (s->nbfrags * s->fragsize > 256 * 1024) + s->nbfrags = 256 * 1024 / s->fragsize; + if (audio_setup_buf(s)) + return -ENOMEM; + return val|(s->nbfrags << 16); +} + + +/* + * The fops functions + */ + +static int audio_write(struct file *file, const char *buffer, + size_t count, loff_t * ppos) +{ + const char *buffer0 = buffer; + audio_state_t *state = (audio_state_t *)file->private_data; + audio_stream_t *s = state->output_stream; + int chunksize, ret = 0; + + if (s->mapped) + return -ENXIO; + if (!s->buffers && audio_setup_buf(s)) + return -ENOMEM; + + while (count > 0) { + audio_buf_t *b = &s->buffers[s->usr_frag]; + + /* Grab a fragment */ + if (file->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + if (down_trylock(&s->sem)) + break; + } else { + ret = -ERESTARTSYS; + if (down_interruptible(&s->sem)) + break; + } + + /* Feed the current buffer */ + chunksize = s->fragsize - b->offset; + if (chunksize > count) + chunksize = count; + if (copy_from_user(b->data + b->offset, buffer, chunksize)) { + up(&s->sem); + return -EFAULT; + } + + b->offset += chunksize; + buffer += chunksize; + count -= chunksize; + if (b->offset < s->fragsize) { + //ret = 0; + up(&s->sem); + break; + } + + /* + * Activate DMA on current buffer. + * We unlock this fragment's checkpoint descriptor and + * kick DMA if it is idle. Using checkpoint descriptors + * allows for control operations without the need for + * stopping the DMA channel if it is already running. + */ + b->offset = 0; + b->dma_desc->ddadr &= ~DDADR_STOP; + if (DCSR(s->dma_ch) & DCSR_STOPSTATE) { + DDADR(s->dma_ch) = b->dma_desc->ddadr; + DCSR(s->dma_ch) = DCSR_RUN; + } + + + /* move the index to the next fragment */ + if (++s->usr_frag >= s->nbfrags) + s->usr_frag = 0; + } + + if ((buffer - buffer0)) + ret = buffer - buffer0; + return ret; +} + + +static int audio_read(struct file *file, char *buffer, + size_t count, loff_t * ppos) +{ + char *buffer0 = buffer; + audio_state_t *state = file->private_data; + audio_stream_t *s = state->input_stream; + int chunksize, ret = 0; + + if (s->mapped) + return -ENXIO; + if (!s->buffers && audio_setup_buf(s)) + return -ENOMEM; + + while (count > 0) { + audio_buf_t *b = &s->buffers[s->usr_frag]; + + /* prime DMA */ + if (DCSR(s->dma_ch) & DCSR_STOPSTATE) { + DDADR(s->dma_ch) = + s->buffers[s->dma_frag].dma_desc->ddadr; + DCSR(s->dma_ch) = DCSR_RUN; + } + + /* Wait for a buffer to become full */ + if (file->f_flags & O_NONBLOCK) { + ret = -EAGAIN; + if (down_trylock(&s->sem)) + break; + } else { + ret = -ERESTARTSYS; + if (down_interruptible(&s->sem)) + break; + } + + /* Grab data from current buffer */ + chunksize = s->fragsize - b->offset; + if (chunksize > count) + chunksize = count; + if (copy_to_user(buffer, b->data + b->offset, chunksize)) { + up(&s->sem); + return -EFAULT; + } + b->offset += chunksize; + buffer += chunksize; + count -= chunksize; + if (b->offset < s->fragsize) { + ret = 0; + up(&s->sem); + break; + } + + /* + * Make this buffer available for DMA again. + * We unlock this fragment's checkpoint descriptor and + * kick DMA if it is idle. Using checkpoint descriptors + * allows for control operations without the need for + * stopping the DMA channel if it is already running. + */ + b->offset = 0; + b->dma_desc->ddadr &= ~DDADR_STOP; + + /* move the index to the next fragment */ + if (++s->usr_frag >= s->nbfrags) + s->usr_frag = 0; + } + + if ((buffer - buffer0)) + ret = buffer - buffer0; + return ret; +} + + +static int audio_sync(struct file *file) +{ + audio_state_t *state = file->private_data; + audio_stream_t *s = state->output_stream; + audio_buf_t *b; + pxa_dma_desc *final_desc; + u_long dcmd_save = 0; + DECLARE_WAITQUEUE(wait, current); + + if (!(file->f_mode & FMODE_WRITE) || !s->buffers || s->mapped) + return 0; + + /* + * Send current buffer if it contains data. Be sure to send + * a full sample count. + */ + final_desc = NULL; + b = &s->buffers[s->usr_frag]; + if (b->offset &= ~3) { + final_desc = &b->dma_desc[1 + b->offset/MAX_DMA_SIZE]; + b->offset &= (MAX_DMA_SIZE-1); + dcmd_save = final_desc->dcmd; + final_desc->dcmd = b->offset | s->dcmd | DCMD_ENDIRQEN; + final_desc->ddadr |= DDADR_STOP; + b->offset = 0; + b->dma_desc->ddadr &= ~DDADR_STOP; + if (DCSR(s->dma_ch) & DCSR_STOPSTATE) { + DDADR(s->dma_ch) = b->dma_desc->ddadr; + DCSR(s->dma_ch) = DCSR_RUN; + } + } + + /* Wait for DMA to complete. */ + set_current_state(TASK_INTERRUPTIBLE); +#if 0 + /* + * The STOPSTATE IRQ never seem to occur if DCSR_STOPIRQEN is set + * along wotj DCSR_RUN. Silicon bug? + */ + add_wait_queue(&s->stop_wq, &wait); + DCSR(s->dma_ch) |= DCSR_STOPIRQEN; + schedule(); +#else + add_wait_queue(&s->frag_wq, &wait); + while ((DCSR(s->dma_ch) & DCSR_RUN) && !signal_pending(current)) { + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + } +#endif + set_current_state(TASK_RUNNING); + remove_wait_queue(&s->frag_wq, &wait); + + /* Restore the descriptor chain. */ + if (final_desc) { + final_desc->dcmd = dcmd_save; + final_desc->ddadr &= ~DDADR_STOP; + b->dma_desc->ddadr |= DDADR_STOP; + } + return 0; +} + + +static unsigned int audio_poll(struct file *file, + struct poll_table_struct *wait) +{ + audio_state_t *state = file->private_data; + audio_stream_t *is = state->input_stream; + audio_stream_t *os = state->output_stream; + unsigned int mask = 0; + + if (file->f_mode & FMODE_READ) { + /* Start audio input if not already active */ + if (!is->buffers && audio_setup_buf(is)) + return -ENOMEM; + if (DCSR(is->dma_ch) & DCSR_STOPSTATE) { + DDADR(is->dma_ch) = + is->buffers[is->dma_frag].dma_desc->ddadr; + DCSR(is->dma_ch) = DCSR_RUN; + } + poll_wait(file, &is->frag_wq, wait); + } + + if (file->f_mode & FMODE_WRITE) { + if (!os->buffers && audio_setup_buf(os)) + return -ENOMEM; + poll_wait(file, &os->frag_wq, wait); + } + + if (file->f_mode & FMODE_READ) + if (( is->mapped && is->bytecount > 0) || + (!is->mapped && atomic_read(&is->sem.count) > 0)) + mask |= POLLIN | POLLRDNORM; + + if (file->f_mode & FMODE_WRITE) + if (( os->mapped && os->bytecount > 0) || + (!os->mapped && atomic_read(&os->sem.count) > 0)) + mask |= POLLOUT | POLLWRNORM; + + return mask; +} + + +static int audio_ioctl( struct inode *inode, struct file *file, + uint cmd, ulong arg) +{ + audio_state_t *state = file->private_data; + audio_stream_t *os = state->output_stream; + audio_stream_t *is = state->input_stream; + long val; + + switch (cmd) { + case OSS_GETVERSION: + return put_user(SOUND_VERSION, (int *)arg); + + case SNDCTL_DSP_GETBLKSIZE: + if (file->f_mode & FMODE_WRITE) + return put_user(os->fragsize, (int *)arg); + else + return put_user(is->fragsize, (int *)arg); + + case SNDCTL_DSP_GETCAPS: + val = DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP; + if (is && os) + val |= DSP_CAP_DUPLEX; + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETFRAGMENT: + if (get_user(val, (long *) arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + int ret = audio_set_fragments(is, val); + if (ret < 0) + return ret; + ret = put_user(ret, (int *)arg); + if (ret) + return ret; + } + if (file->f_mode & FMODE_WRITE) { + int ret = audio_set_fragments(os, val); + if (ret < 0) + return ret; + ret = put_user(ret, (int *)arg); + if (ret) + return ret; + } + return 0; + + case SNDCTL_DSP_SYNC: + return audio_sync(file); + + case SNDCTL_DSP_SETDUPLEX: + return 0; + + case SNDCTL_DSP_POST: + return 0; + + case SNDCTL_DSP_GETTRIGGER: + val = 0; + if (file->f_mode & FMODE_READ && DCSR(is->dma_ch) & DCSR_RUN) + val |= PCM_ENABLE_INPUT; + if (file->f_mode & FMODE_WRITE && DCSR(os->dma_ch) & DCSR_RUN) + val |= PCM_ENABLE_OUTPUT; + return put_user(val, (int *)arg); + + case SNDCTL_DSP_SETTRIGGER: + if (get_user(val, (int *)arg)) + return -EFAULT; + if (file->f_mode & FMODE_READ) { + if (val & PCM_ENABLE_INPUT) { + if (!is->buffers && audio_setup_buf(is)) + return -ENOMEM; + if (!(DCSR(is->dma_ch) & DCSR_RUN)) { + audio_buf_t *b = &is->buffers[is->dma_frag]; + DDADR(is->dma_ch) = b->dma_desc->ddadr; + DCSR(is->dma_ch) = DCSR_RUN; + } + } else { + DCSR(is->dma_ch) = 0; + } + } + if (file->f_mode & FMODE_WRITE) { + if (val & PCM_ENABLE_OUTPUT) { + if (!os->buffers && audio_setup_buf(os)) + return -ENOMEM; + if (!(DCSR(os->dma_ch) & DCSR_RUN)) { + audio_buf_t *b = &os->buffers[os->dma_frag]; + DDADR(os->dma_ch) = b->dma_desc->ddadr; + DCSR(os->dma_ch) = DCSR_RUN; + } + } else { + DCSR(os->dma_ch) = 0; + } + } + return 0; + + case SNDCTL_DSP_GETOSPACE: + case SNDCTL_DSP_GETISPACE: + { + audio_buf_info inf = { 0, }; + audio_stream_t *s = (cmd == SNDCTL_DSP_GETOSPACE) ? os : is; + + if ((s == is && !(file->f_mode & FMODE_READ)) || + (s == os && !(file->f_mode & FMODE_WRITE))) + return -EINVAL; + if (!s->buffers && audio_setup_buf(s)) + return -ENOMEM; + inf.bytes = atomic_read(&s->sem.count) * s->fragsize; + inf.bytes -= s->buffers[s->usr_frag].offset; + inf.fragments = inf.bytes / s->fragsize; + inf.fragsize = s->fragsize; + inf.fragstotal = s->nbfrags; + return copy_to_user((void *)arg, &inf, sizeof(inf)); + } + + case SNDCTL_DSP_GETOPTR: + case SNDCTL_DSP_GETIPTR: + { + count_info inf = { 0, }; + audio_stream_t *s = (cmd == SNDCTL_DSP_GETOPTR) ? os : is; + dma_addr_t ptr; + int bytecount, offset; + unsigned long flags; + + if ((s == is && !(file->f_mode & FMODE_READ)) || + (s == os && !(file->f_mode & FMODE_WRITE))) + return -EINVAL; + local_irq_save(flags); + if (DCSR(s->dma_ch) & DCSR_RUN) { + audio_buf_t *b; + ptr = (s->output) ? DSADR(s->dma_ch) : DTADR(s->dma_ch); + b = &s->buffers[s->dma_frag]; + offset = ptr - b->dma_desc->dsadr; + if (offset >= s->fragsize) + offset = s->fragsize - 4; + } else { + offset = 0; + } + inf.ptr = s->dma_frag * s->fragsize + offset; + bytecount = s->bytecount + offset; + s->bytecount = -offset; + inf.blocks = s->fragcount; + s->fragcount = 0; + local_irq_restore(flags); + if (bytecount < 0) + bytecount = 0; + inf.bytes = bytecount; + return copy_to_user((void *)arg, &inf, sizeof(inf)); + } + + case SNDCTL_DSP_NONBLOCK: + file->f_flags |= O_NONBLOCK; + return 0; + + case SNDCTL_DSP_RESET: + if (file->f_mode & FMODE_WRITE) + audio_clear_buf(os); + if (file->f_mode & FMODE_READ) + audio_clear_buf(is); + return 0; + + default: + return state->client_ioctl ? + state->client_ioctl(inode, file, cmd, arg) : -EINVAL; + } + + return 0; +} + + +static int audio_mmap(struct file *file, struct vm_area_struct *vma) +{ + audio_state_t *state = file->private_data; + audio_stream_t *s; + unsigned long size, vma_addr; + int i, ret; + + if (vma->vm_pgoff != 0) + return -EINVAL; + + if (vma->vm_flags & VM_WRITE) { + if (!state->wr_ref) + return -EINVAL;; + s = state->output_stream; + } else if (vma->vm_flags & VM_READ) { + if (!state->rd_ref) + return -EINVAL; + s = state->input_stream; + } else return -EINVAL; + + if (s->mapped) + return -EINVAL; + size = vma->vm_end - vma->vm_start; + if (size != s->fragsize * s->nbfrags) + return -EINVAL; + if (!s->buffers && audio_setup_buf(s)) + return -ENOMEM; + vma_addr = vma->vm_start; + for (i = 0; i < s->nbfrags; i++) { + audio_buf_t *buf = &s->buffers[i]; + if (!buf->master) + continue; + ret = remap_page_range(vma, vma->vm_start, buf->dma_desc->dsadr, + buf->master, vma->vm_page_prot); + if (ret) + return ret; + vma_addr += buf->master; + } + for (i = 0; i < s->nbfrags; i++) + s->buffers[i].dma_desc->ddadr &= ~DDADR_STOP; + s->mapped = 1; + return 0; +} + + +static int audio_release(struct inode *inode, struct file *file) +{ + audio_state_t *state = file->private_data; + + down(&state->sem); + + if (file->f_mode & FMODE_READ) { + audio_clear_buf(state->input_stream); + *state->input_stream->drcmr = 0; + pxa_free_dma(state->input_stream->dma_ch); + state->rd_ref = 0; + } + + if (file->f_mode & FMODE_WRITE) { + audio_sync(file); + audio_clear_buf(state->output_stream); + *state->output_stream->drcmr = 0; + pxa_free_dma(state->output_stream->dma_ch); + state->wr_ref = 0; + + if (machine_is_netbookpro()) { + printk(KERN_DEBUG "disabling audio amp\n"); + GPSR(65) = GPIO_bit(65); + } + } + + up(&state->sem); + return 0; +} + + +int pxa_audio_attach(struct inode *inode, struct file *file, + audio_state_t *state) +{ + audio_stream_t *is = state->input_stream; + audio_stream_t *os = state->output_stream; + int err; + + down(&state->sem); + + /* set file to be nonseekable */ + nonseekable_open(inode,file); + + /* access control */ + err = -ENODEV; + if ((file->f_mode & FMODE_WRITE) && !os) + goto out; + if ((file->f_mode & FMODE_READ) && !is) + goto out; + err = -EBUSY; + if ((file->f_mode & FMODE_WRITE) && state->wr_ref) + goto out; + if ((file->f_mode & FMODE_READ) && state->rd_ref) + goto out; + + /* request DMA channels */ + if (file->f_mode & FMODE_WRITE) { + err = pxa_request_dma(os->name, DMA_PRIO_LOW, + audio_dma_irq, os); + if (err < 0) + goto out; + os->dma_ch = err; + } + + if (file->f_mode & FMODE_READ) { + err = pxa_request_dma(is->name, DMA_PRIO_LOW, + audio_dma_irq, is); + if (err < 0) { + if (file->f_mode & FMODE_WRITE) { + *os->drcmr = 0; + pxa_free_dma(os->dma_ch); + } + goto out; + } + is->dma_ch = err; + } + + file->private_data = state; + file->f_op->release = audio_release; + file->f_op->write = audio_write; + file->f_op->read = audio_read; + file->f_op->mmap = audio_mmap; + file->f_op->poll = audio_poll; + file->f_op->ioctl = audio_ioctl; + file->f_op->llseek = no_llseek; + + if ((file->f_mode & FMODE_WRITE)) { + state->wr_ref = 1; + os->fragsize = AUDIO_FRAGSIZE_DEFAULT; + os->nbfrags = AUDIO_NBFRAGS_DEFAULT; + os->output = 1; + os->mapped = 0; + init_waitqueue_head(&os->frag_wq); + init_waitqueue_head(&os->stop_wq); + *os->drcmr = os->dma_ch | DRCMR_MAPVLD; + } + if (file->f_mode & FMODE_READ) { + state->rd_ref = 1; + is->fragsize = AUDIO_FRAGSIZE_DEFAULT; + is->nbfrags = AUDIO_NBFRAGS_DEFAULT; + is->output = 0; + is->mapped = 0; + init_waitqueue_head(&is->frag_wq); + init_waitqueue_head(&is->stop_wq); + *is->drcmr = is->dma_ch | DRCMR_MAPVLD; + } + + err = 0; + + if (file->f_mode & FMODE_WRITE && machine_is_netbookpro()) { + printk(KERN_DEBUG "enabling audio amp\n"); + GPCR(65) = GPIO_bit(65); + } + +out: + up(&state->sem); + return err; +} + +EXPORT_SYMBOL(pxa_audio_attach); +EXPORT_SYMBOL(pxa_audio_clear_buf); + +MODULE_AUTHOR("Nicolas Pitre, MontaVista Software Inc."); +MODULE_DESCRIPTION("Audio interface for PXA25x, PXA26x and PXA27x"); +MODULE_LICENSE("GPL"); diff -NaurbB linux-2.6.9-pre1-bk18/sound/oss/pxa-audio.h linux-2.6.9_nbp/sound/oss/pxa-audio.h --- linux-2.6.9-pre1-bk18/sound/oss/pxa-audio.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/sound/oss/pxa-audio.h 2004-09-13 17:33:19.000000000 +0200 @@ -0,0 +1,56 @@ +/* + * linux/drivers/sound/pxa-audio.h -- audio interface for the Cotula chip + * + * Author: Nicolas Pitre + * Created: Aug 15, 2001 + * Copyright: MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +typedef struct { + int offset; /* current buffer position */ + char *data; /* actual buffer */ + pxa_dma_desc *dma_desc; /* pointer to the starting desc */ + int master; /* owner for buffer allocation, contain size whn true */ +} audio_buf_t; + +typedef struct { + char *name; /* stream identifier */ + audio_buf_t *buffers; /* pointer to audio buffer array */ + u_int usr_frag; /* user fragment index */ + u_int dma_frag; /* DMA fragment index */ + u_int fragsize; /* fragment size */ + u_int nbfrags; /* number of fragments */ + struct device *dev; /* device handle for DMA */ + u_int dma_ch; /* DMA channel number */ + dma_addr_t dma_desc_phys; /* phys addr of descriptor ring */ + u_int descs_per_frag; /* nbr descriptors per fragment */ + int bytecount; /* nbr of processed bytes */ + int fragcount; /* nbr of fragment transitions */ + struct semaphore sem; /* account for fragment usage */ + wait_queue_head_t frag_wq; /* for poll(), etc. */ + wait_queue_head_t stop_wq; /* for users of DCSR_STOPIRQEN */ + u_long dcmd; /* DMA descriptor dcmd field */ + volatile u32 *drcmr; /* the DMA request channel to use */ + u_long dev_addr; /* device physical address for DMA */ + int mapped:1; /* mmap()'ed buffers */ + int output:1; /* 0 for input, 1 for output */ +} audio_stream_t; + +typedef struct { + audio_stream_t *output_stream; + audio_stream_t *input_stream; + int dev_dsp; /* audio device handle */ + int rd_ref:1; /* open reference for recording */ + int wr_ref:1; /* open reference for playback */ + int (*client_ioctl)(struct inode *, struct file *, uint, ulong); + struct semaphore sem; /* prevent races in attach/release */ +} audio_state_t; + +extern int pxa_audio_attach(struct inode *inode, struct file *file, + audio_state_t *state); +extern void pxa_audio_clear_buf(audio_stream_t *s); + diff -NaurbB linux-2.6.9-pre1-bk18/sound/oss/pxa-wm97xx-touch.c linux-2.6.9_nbp/sound/oss/pxa-wm97xx-touch.c --- linux-2.6.9-pre1-bk18/sound/oss/pxa-wm97xx-touch.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/sound/oss/pxa-wm97xx-touch.c 2004-11-19 23:38:21.000000000 +0100 @@ -0,0 +1,310 @@ +/* + * pxa-wm97xx-touch.c -- PXA Continuous Touch screen driver for + * Wolfson WM97xx AC97 Codecs. + * + * Copyright 2004 Wolfson Microelectronics PLC. + * Author: Liam Girdwood + * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com + * Parts Copyright : Ian Molton + * Andrew Zabolotny + * + * 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * 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. + * + * Notes: + * This code uses an API exposed by ac97_plugin_wm97xx to capture touch + * data in a continuous manner on the Intel XScale archictecture + * + * Features: + * - codecs supported:- WM9705, WM9712, WM9713 + * - processors supported:- Intel XScale PXA25x, PXA26x, PXA27x + * + * Revision history + * 18th Aug 2004 Initial version. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "wm97xx.h" + +#define TS_NAME "pxa_wm97xx_touch" +#define WM_TS_VERSION "0.4" + +#define WM97XX_PXA_SLOT 5 + +/* + * The WM97xx codecs can signal a pen down event by either sending an irq over + * the AC97 link (not wm9705) or by asserting a pen down GPIO to interrupt the + * OS. Set WM_PEN_DOWN_GPIO to 1 if you are using the dedicated GPIO to signal + * pen down events, otherwise set to 0 if you want to signal over the AC link. + */ +#define WM_PEN_DOWN_GPIO 0 + +/* + * Pen sampling frequency in continuous mode. + * + * This parameter needs an additional architecture-dependent driver + * such as pxa-ac97-wm97xx. + * + * The sampling frequency is given by a number in range 0-3: + * + * 0: 93.75Hz 1: 187.5Hz 2: 375Hz 3: 750Hz + */ +static int cont_rate = 1; +module_param(cont_rate, int, 0); +MODULE_PARM_DESC(cont_rate, "Sampling rate in continuous mode (0-3, default 2)"); + + +/* + * Pen down detection. + * + * This driver can either poll or use an interrupt to indicate a pen down + * event. If the irq request fails then it will fall back to polling mode. + */ +static int pen_int = 1; +module_param(pen_int, int, 0); +MODULE_PARM_DESC(pen_int, "Pen down detection (1 = interrupt, 0 = polling)"); + +/* flush AC97 slot 5 FIFO on pxa machines */ +#define PXA_FLUSH_FIFO \ + do { \ + int count = 0; \ + for (; count < 16; count++) MODR; \ + } while (0); + +static struct wm97xx_device *dev; + +static int pxa_wm97xx_cont_read (struct input_dev* idev, int pen_down_time, int pressure) +{ + u16 x, y, p = 0x100 | WM97XX_ADCSEL_PRES; + int count = 0, find_x = 3; + + /* sometimes there is stale data in the FIFO just after a pen down */ + if (pen_down_time < 6 ) { + PXA_FLUSH_FIFO; + return RC_PENDOWN; + } + + /* find the first X and sync */ + while (find_x--) { + x = MODR; + if ((x & 0x7000) == WM97XX_ADCSEL_X) + break; + } + + /* are we now syncronised X,Y[,P],X,Y[,P],X... ? */ + if (!find_x) + return RC_PENDOWN; + + /* read Y [and pressure] */ + y = MODR & 0xffff; + if (pressure) + p = MODR & 0xffff; + +#ifdef CONFIG_PXA27x + while (MISR & (1 << 2)) { +#else + while (count < 16) { +#endif + /* are samples valid */ + if ((x & 0x7000) != WM97XX_ADCSEL_X || + (y & 0x7000) != WM97XX_ADCSEL_Y || + (p & 0x7000) != WM97XX_ADCSEL_PRES) + goto next; + + /* is pen down pressure great enough ? */ + if (pressure && ((p & 0xfff) > pressure)) + goto next; + + /* coordinate is good */ + //printk("x %x y %x p %x\n", x,y,p); + input_report_abs (idev, ABS_X, x & 0xfff); + input_report_abs (idev, ABS_Y, y & 0xfff); + input_report_abs (idev, ABS_PRESSURE, p & 0xfff); + input_sync (idev); + +next: + x = MODR; + y = MODR; + count += 2; + if (pressure) { + p = MODR; + count++; + } + } + + return RC_PENDOWN; +} + +#ifdef CONFIG_PXA27x +/* + * Sometimes the PXA27x cannot reset sticky codec GPIO's. In this case we + * also need to reset the GPIO in codec space. + */ +static int pxa27x_clear_sticky(int status) +{ + volatile u32 *reg_addr; + u32 gsr; + + reg_addr = (u32 *)&PAC_REG_BASE + (0x54 >> 1); + *reg_addr = (status & ~WM97XX_GPIO_13) << 1; + + udelay(50); + gsr = GSR; + GSR = gsr & GSR_CDONE; + + if (!(gsr & GSR_CDONE)) { + printk(KERN_CRIT "%s: write codec register timeout.\n", __FUNCTION__); + return 0; + } + return 1; +} +#endif + + +/* + * PXA arch pen down handler. + */ +static int pxa_pen_handler(int status) +{ + u32 gsr; +#ifdef CONFIG_PXA27x + pxa27x_clear_sticky(status); +#endif + gsr = GSR; + GSR = gsr & GSR_GSCI; + return 0; +} + + +static int pxa_wm97xx_probe(void) +{ + /* codec specific irq config */ + if (pen_int) { + switch (wm97xx_id & 0xffff) { + case WM9705_ID2: + wm97xx_set_pen_irq(dev, IRQ_GPIO(4)); + set_irq_type(IRQ_GPIO(4), IRQT_BOTHEDGE); + break; + case WM9712_ID2: + case WM9713_ID2: + /* enable pen down interrupt */ +#if WM_PEN_DOWN_GPIO + /* use the PEN_DOWN GPIO */ + wm97xx_set_pen_irq(dev, MAINSTONE_PEN_IRQ); + wm97xx_config_codec_gpio(dev, WM97XX_GPIO_3, WM97XX_GPIO_OUT, + WM97XX_GPIO_POL_HIGH, WM97XX_GPIO_NOTSTICKY, WM97XX_GPIO_NOWAKE); +#else + /* use the virtual PEN_DOWN GPIO to send irq over AC97 */ + wm97xx_set_codec_irq(dev, IRQ_AC97, 1); + wm97xx_request_codec_event(dev, WM97XX_GPIO_13, pxa_pen_handler); + GCR |= GCR_GIE; +#endif + break; + default: + printk("pen down irq not supported on this device\n"); + pen_int = 0; + break; + } + } + + /* Go you big red fire engine */ + wm97xx_set_continuous_mode (dev, 1, WM97XX_PXA_SLOT, cont_rate); + return 0; +} + +static int pxa_wm97xx_remove(void) +{ + /* codec specific deconfig */ + if (pen_int) { + switch (wm97xx_id & 0xffff) { + case WM9705_ID2: + wm97xx_clear_pen_irq(dev); + break; + case WM9712_ID2: + case WM9713_ID2: + /* disable interrupt */ +#if WM_PEN_DOWN_GPIO + wm97xx_clear_pen_irq(dev); +#else + if (wm97xx_clear_codec_irq(dev) == 0) + GCR &= ~GCR_GIE; + wm97xx_free_codec_event(dev, WM97XX_GPIO_13); +#endif + break; + } + } + + wm97xx_set_continuous_mode (dev, 0, 0, 0); + return 0; +} + +#if defined(CONFIG_PM) +static int pxa_wm97xx_suspend(u32 state, u32 level) +{ + return 0; +} + +static int pxa_wm97xx_resume(u32 level) +{ + return 0; +} +#else +#define pxa_wm97xx_suspend NULL +#define pxa_wm97xx_resume NULL +#endif + +static struct wm97xx_device pxa_wm97xx_touch_driver = { + .name = "pxa-wm97xx-touch", + .probe = pxa_wm97xx_probe, + .remove = pxa_wm97xx_remove, + .suspend= pxa_wm97xx_suspend, + .resume = pxa_wm97xx_resume, + .cont_read = pxa_wm97xx_cont_read, +}; + +static int __init pxa_wm97xx_touch_init(void) +{ + dev = &pxa_wm97xx_touch_driver; + return wm97xx_driver_register(dev); +} + +static void __exit pxa_wm97xx_touch_exit(void) +{ + wm97xx_driver_unregister(&pxa_wm97xx_touch_driver); + dev = NULL; +} + +/* Module information */ +MODULE_AUTHOR("Liam Girdwood "); +MODULE_DESCRIPTION("AC97 continuous touch driver for PXA25x,PXA26x and PXA27x"); +MODULE_LICENSE("GPL"); + +module_init(pxa_wm97xx_touch_init); +module_exit(pxa_wm97xx_touch_exit); diff -NaurbB linux-2.6.9-pre1-bk18/sound/oss/wm97xx.h linux-2.6.9_nbp/sound/oss/wm97xx.h --- linux-2.6.9-pre1-bk18/sound/oss/wm97xx.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.9_nbp/sound/oss/wm97xx.h 2004-11-19 23:38:21.000000000 +0100 @@ -0,0 +1,302 @@ +/* + * Register bits for Wolfson WM97xx series of codecs + */ + +#ifndef _WM97XX_H_ +#define _WM97XX_H_ + +#include /* AC97 control layer */ +#include /* Input device layer */ + +/* + * WM97xx AC97 Touchscreen registers + */ +#define AC97_WM97XX_DIGITISER1 0x76 +#define AC97_WM97XX_DIGITISER2 0x78 +#define AC97_WM97XX_DIGITISER_RD 0x7a +#define AC97_WM9713_DIG1 0x74 +#define AC97_WM9713_DIG2 AC97_WM97XX_DIGITISER1 +#define AC97_WM9713_DIG3 AC97_WM97XX_DIGITISER2 + +/* + * WM97xx register bits + */ +#define WM97XX_POLL 0x8000 /* initiate a polling measurement */ +#define WM97XX_ADCSEL_X 0x1000 /* x coord measurement */ +#define WM97XX_ADCSEL_Y 0x2000 /* y coord measurement */ +#define WM97XX_ADCSEL_PRES 0x3000 /* pressure measurement */ +#define WM97XX_ADCSEL_MASK 0x7000 +#define WM97XX_COO 0x0800 /* enable coordinate mode */ +#define WM97XX_CTC 0x0400 /* enable continuous mode */ +#define WM97XX_CM_RATE_93 0x0000 /* 93.75Hz continuous rate */ +#define WM97XX_CM_RATE_187 0x0100 /* 187.5Hz continuous rate */ +#define WM97XX_CM_RATE_375 0x0200 /* 375Hz continuous rate */ +#define WM97XX_CM_RATE_750 0x0300 /* 750Hz continuous rate */ +#define WM97XX_CM_RATE_8K 0x00f0 /* 8kHz continuous rate */ +#define WM97XX_CM_RATE_12K 0x01f0 /* 12kHz continuous rate */ +#define WM97XX_CM_RATE_24K 0x02f0 /* 24kHz continuous rate */ +#define WM97XX_CM_RATE_48K 0x03f0 /* 48kHz continuous rate */ +#define WM97XX_CM_RATE_MASK 0x03f0 +#define WM97XX_RATE(i) (((i & 3) << 8) | ((i & 4) ? 0xf0 : 0)) +#define WM97XX_DELAY(i) ((i << 4) & 0x00f0) /* sample delay times */ +#define WM97XX_DELAY_MASK 0x00f0 +#define WM97XX_SLEN 0x0008 /* slot read back enable */ +#define WM97XX_SLT(i) ((i - 5) & 0x7) /* touchpanel slot selection (5-11) */ +#define WM97XX_SLT_MASK 0x0007 +#define WM97XX_PRP_DETW 0x4000 /* pen detect on, digitiser off, wake up */ +#define WM97XX_PRP_DET 0x8000 /* pen detect on, digitiser off, no wake up */ +#define WM97XX_PRP_DET_DIG 0xc000 /* pen detect on, digitiser on */ +#define WM97XX_RPR 0x2000 /* wake up on pen down */ +#define WM97XX_PEN_DOWN 0x8000 /* pen is down */ +#define WM97XX_ADCSRC_MASK 0x7000 /* ADC source mask */ + +/* WM9712 Bits */ +#define WM9712_45W 0x1000 /* set for 5-wire touchscreen */ +#define WM9712_PDEN 0x0800 /* measure only when pen down */ +#define WM9712_WAIT 0x0200 /* wait until adc is read before next sample */ +#define WM9712_PIL 0x0100 /* current used for pressure measurement. set 400uA else 200uA */ +#define WM9712_MASK_HI 0x0040 /* hi on mask pin (47) stops conversions */ +#define WM9712_MASK_EDGE 0x0080 /* rising/falling edge on pin delays sample */ +#define WM9712_MASK_SYNC 0x00c0 /* rising/falling edge on mask initiates sample */ +#define WM9712_RPU(i) (i&0x3f) /* internal pull up on pen detect (64k / rpu) */ +#define WM9712_ADCSEL_COMP1 0x4000 /* COMP1/AUX1 measurement (pin29) */ +#define WM9712_ADCSEL_COMP2 0x5000 /* COMP2/AUX2 measurement (pin30) */ +#define WM9712_ADCSEL_BMON 0x6000 /* BMON/AUX3 measurement (pin31) */ +#define WM9712_ADCSEL_WIPER 0x7000 /* WIPER/AUX4 measurement (pin12) */ +#define WM9712_PD(i) (0x1 << i) /* power management */ + +/* WM9712 Registers */ +#define AC97_WM9712_POWER 0x24 +#define AC97_WM9712_REV 0x58 + +/* WM9705 Bits */ +#define WM9705_PDEN 0x1000 /* measure only when pen is down */ +#define WM9705_PINV 0x0800 /* inverts sense of pen down output */ +#define WM9705_BSEN 0x0400 /* BUSY flag enable, pin47 is 1 when busy */ +#define WM9705_BINV 0x0200 /* invert BUSY (pin47) output */ +#define WM9705_WAIT 0x0100 /* wait until adc is read before next sample */ +#define WM9705_PIL 0x0080 /* current used for pressure measurement. set 400uA else 200uA */ +#define WM9705_PHIZ 0x0040 /* set PHONE and PCBEEP inputs to high impedance */ +#define WM9705_MASK_HI 0x0010 /* hi on mask stops conversions */ +#define WM9705_MASK_EDGE 0x0020 /* rising/falling edge on pin delays sample */ +#define WM9705_MASK_SYNC 0x0030 /* rising/falling edge on mask initiates sample */ +#define WM9705_PDD(i) (i & 0x000f) /* pen detect comparator threshold */ +#define WM9705_ADCSEL_BMON 0x4000 /* BMON measurement */ +#define WM9705_ADCSEL_AUX 0x5000 /* AUX measurement */ +#define WM9705_ADCSEL_PHONE 0x6000 /* PHONE measurement */ +#define WM9705_ADCSEL_PCBEEP 0x7000 /* PCBEEP measurement */ + +/* WM9713 Bits */ +#define WM9713_PDPOL 0x0400 /* Pen down polarity */ +#define WM9713_POLL 0x0200 /* initiate a polling measurement */ +#define WM9713_CTC 0x0100 /* enable continuous mode */ +#define WM9713_ADCSEL_AUX4 0x0080 /* AUX 4 measurement */ +#define WM9713_ADCSEL_AUX3 0x0040 /* AUX 3 measurement */ +#define WM9713_ADCSEL_AUX2 0x0020 /* AUX 2 measurement */ +#define WM9713_ADCSEL_AUX1 0x0010 /* AUX 1 measurement */ +#define WM9713_ADCSEL_X 0x0002 /* X measurement */ +#define WM9713_ADCSEL_Y 0x0004 /* Y measurement */ +#define WM9713_ADCSEL_PRES 0x0008 /* Pressure measurement */ +#define WM9713_COO 0x0001 /* enable coordinate mode */ +#define WM9713_PDEN 0x0800 /* measure only when pen down */ +#define WM9713_ADCSEL_MASK 0x00fe /* ADC selection mask */ + +/* AUX ADC ID's */ +#define TS_COMP1 0x0 +#define TS_COMP2 0x1 +#define TS_BMON 0x2 +#define TS_WIPER 0x3 + +/* ID numbers */ +#define WM97XX_ID1 0x574d +#define WM9712_ID2 0x4c12 +#define WM9705_ID2 0x4c05 +#define WM9713_ID2 0x4c13 + +/* Codec GPIO's */ +#define WM97XX_MAX_GPIO 16 +#define WM97XX_GPIO_1 (1 << 1) +#define WM97XX_GPIO_2 (1 << 2) +#define WM97XX_GPIO_3 (1 << 3) +#define WM97XX_GPIO_4 (1 << 4) +#define WM97XX_GPIO_5 (1 << 5) +#define WM97XX_GPIO_6 (1 << 6) +#define WM97XX_GPIO_7 (1 << 7) +#define WM97XX_GPIO_8 (1 << 8) +#define WM97XX_GPIO_9 (1 << 9) +#define WM97XX_GPIO_10 (1 << 10) +#define WM97XX_GPIO_11 (1 << 11) +#define WM97XX_GPIO_12 (1 << 12) +#define WM97XX_GPIO_13 (1 << 13) +#define WM97XX_GPIO_14 (1 << 14) +#define WM97XX_GPIO_15 (1 << 15) + + +#define AC97_LINK_FRAME 21 /* time in uS for AC97 link frame */ + + +/*---------------- Return codes from sample reading functions ---------------*/ + +/* More data is available; call the sample gathering function again */ +#define RC_AGAIN 0x00000001 +/* The returned sample is valid */ +#define RC_VALID 0x00000002 +/* The pen is up (the first RC_VALID without RC_PENUP means pen is down) */ +#define RC_PENUP 0x00000004 +/* The pen is down (RC_VALID implies RC_PENDOWN, but sometimes it is helpful + to tell the handler that the pen is down but we don't know yet his coords, + so the handler should not sleep or wait for pendown irq) */ +#define RC_PENDOWN 0x00000008 + +/* The wm97xx driver provides a private API for writing platform-specific + * drivers. + */ + +/* The structure used to return sampled data into */ +typedef struct { + int x; + int y; + int p; +} wm97xx_data_t; + +/* The identifier of an auxiliary ADC conversion. + */ +typedef enum { + WM97XX_AC_BMON, + WM97XX_AC_AUX1, /* WM9705: AUX WM9712: COMP1 */ + WM97XX_AC_AUX2, /* WM9705: PHONE WM9712: COMP2 */ + WM97XX_AC_AUX3 /* WM9705: PCBEEP WM9712: WIPER */ +} wm97xx_aux_conv_t; + +/* Codec GPIO status + */ +typedef enum { + WM97XX_GPIO_HIGH, + WM97XX_GPIO_LOW +} wm97xx_gpio_status_t; + +/* Codec GPIO direction + */ +typedef enum { + WM97XX_GPIO_IN, + WM97XX_GPIO_OUT +} wm97xx_gpio_dir_t; + +/* Codec GPIO polarity + */ +typedef enum { + WM97XX_GPIO_POL_HIGH, + WM97XX_GPIO_POL_LOW +} wm97xx_gpio_pol_t; + +/* Codec GPIO sticky + */ +typedef enum { + WM97XX_GPIO_STICKY, + WM97XX_GPIO_NOTSTICKY +} wm97xx_gpio_sticky_t; + +/* Codec GPIO wake + */ +typedef enum { + WM97XX_GPIO_WAKE, + WM97XX_GPIO_NOWAKE +} wm97xx_gpio_wake_t; + +/* + * codec event callback. + */ +typedef int (*codec_event_t)(int status); + +/* + * continuous mode read callback + */ +typedef int (*codec_read_t)(struct input_dev* idev, int pen_down_time, int pressure); + +/* + * codec id + */ +extern u32 wm97xx_id; + +struct wm97xx_device { + char *name; + struct list_head devices; + int (*probe) (void); + int (*remove) (void); + int (*suspend) (u32 state, u32 level); + int (*resume) (u32 level); + codec_read_t cont_read; +}; + +/* + * WM97xx arch specific drivers must register with the AC97 plugin + */ +extern int wm97xx_driver_register (struct wm97xx_device *dev); +extern void wm97xx_driver_unregister (struct wm97xx_device *dev); + +/* Enable or disable continuous mode. The driver itself doesn't + * support continuous mode since it is highly architecture-specific. + */ +extern void wm97xx_set_continuous_mode (struct wm97xx_device *dev, int enable, int slot, int rate); + + +/* A special function to be called before and after the auxiliary conversion. + * This function can e.g. toggle some external switch via a platform specific + * GPIO to route the required analog signal to the respective WM97xx input. + * The 'stage' parameter is 0 before conversion, 1 after. + */ +typedef void (*wm97xx_ac_prepare) (wm97xx_aux_conv_t ac, int stage); + +/* Start a periodic auxiliary ADC conversion with given period. + * Note that auxiliary conversions are performed only when the + * pen is not down, since they are considered slowly-changing + * events. Period is given in jiffies. Additionally, a function can + * be defined to be called before and after every conversion. This + * can be used e.g. to route some analog signal to the respective + * input via platform-specific GPIOs and such. + * Use a period of 0 to disable sampling. + */ +extern int wm97xx_request_auxconv(struct wm97xx_device *dev, wm97xx_aux_conv_t ac, int period, wm97xx_ac_prepare func); +extern void wm97xx_free_auxconv(struct wm97xx_device *dev, wm97xx_aux_conv_t ac); + +/* Get the result of an auxiliary ADC conversion. + * If an auxiliary ADC reading is not yet available, the function + * returns -1. The platform-specific driver must start + */ +extern int wm97xx_get_auxconv (struct wm97xx_device *dev, wm97xx_aux_conv_t ac); + +/* Codec IRQ access + * The codec can generate IRQ's (over AC97 link or IRQ pin) for + * numerous external or internal events. This is used to + * enable and disable codec IRQ's + */ +extern int wm97xx_set_codec_irq(struct wm97xx_device *dev, int irq, int link); +extern int wm97xx_clear_codec_irq(struct wm97xx_device *dev); + +/* Register interest in external/internal codec events */ +extern int wm97xx_request_codec_event(struct wm97xx_device *dev, int gpio, codec_event_t work); +extern void wm97xx_free_codec_event(struct wm97xx_device *dev, int gpio); + +/* Codec PEN IRQ access + * The codec can additionally generate an IRQ (using a dedicated pin) + * for Pen down events. This is used to enable and disable codec + * Pen down IRQ's + */ +extern int wm97xx_set_pen_irq(struct wm97xx_device *dev, int irq); +extern void wm97xx_clear_pen_irq(struct wm97xx_device *dev); + +/* Codec GPIO access (not supported on WM9705) + * This can be used to set/get codec GPIO and Virtual GPIO status. + */ +extern wm97xx_gpio_status_t wm97xx_get_codec_gpio (struct wm97xx_device *dev, u32 gpio); +extern void wm97xx_set_codec_gpio (struct wm97xx_device *dev, u32 gpio, wm97xx_gpio_status_t status); +extern void wm97xx_config_codec_gpio (struct wm97xx_device *dev, u32 gpio, wm97xx_gpio_dir_t dir, wm97xx_gpio_pol_t pol, + wm97xx_gpio_sticky_t sticky, wm97xx_gpio_wake_t wake); + +/* Codec register access + * This can be used to read and write codec registers. + */ +extern u16 wm97xx_codec_reg_read (struct wm97xx_device *dev, u16 reg); +extern void wm97xx_codec_reg_write (struct wm97xx_device *dev, u16 reg, u16 val); + +#endif diff -NaurbB linux-2.6.9-pre1-bk18/sound/pci/ac97/Makefile linux-2.6.9_nbp/sound/pci/ac97/Makefile --- linux-2.6.9-pre1-bk18/sound/pci/ac97/Makefile 2004-08-14 07:37:15.000000000 +0200 +++ linux-2.6.9_nbp/sound/pci/ac97/Makefile 2004-09-13 17:33:19.000000000 +0200 @@ -9,5 +9,6 @@ # Toplevel Module Dependency obj-$(CONFIG_SND_AC97_CODEC) += snd-ac97-codec.o obj-$(CONFIG_SND_ENS1370) += snd-ak4531-codec.o +obj-$(CONFIG_SND_PXA_AC97) += snd-ac97-codec.o obj-m := $(sort $(obj-m))