diff options
Diffstat (limited to 'recipes/kexecboot/linux-kexecboot-2.6.24')
103 files changed, 72345 insertions, 0 deletions
diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/80-kexec-atags.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/80-kexec-atags.patch new file mode 100644 index 0000000000..bf97f2105b --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/80-kexec-atags.patch @@ -0,0 +1,111 @@ +--- linux-2.6.21.5/arch/arm/kernel/setup.c.orig 2007-06-30 16:39:38.000000000 -0500 ++++ linux-2.6.21.5/arch/arm/kernel/setup.c 2007-06-30 19:02:51.000000000 -0500 +@@ -23,6 +23,7 @@ + #include <linux/cpu.h> + #include <linux/interrupt.h> + #include <linux/smp.h> ++#include <linux/kexec.h> + + #include <asm/cpu.h> + #include <asm/elf.h> +@@ -767,6 +768,23 @@ + } + arch_initcall(customize_machine); + ++#ifdef CONFIG_KEXEC ++ ++/* Physical addr of where the boot params should be for this machine */ ++extern unsigned long kexec_boot_params_address; ++ ++/* Physical addr of the buffer into which the boot params are copied */ ++extern unsigned long kexec_boot_params_copy; ++ ++/* Pointer to the boot params buffer, for manipulation and display */ ++unsigned long kexec_boot_params; ++EXPORT_SYMBOL(kexec_boot_params); ++ ++/* The buffer itself - make sure it is sized correctly */ ++static unsigned long kexec_boot_params_buf[(KEXEC_BOOT_PARAMS_SIZE + 3) / 4]; ++ ++#endif ++ + void __init setup_arch(char **cmdline_p) + { + struct tag *tags = (struct tag *)&init_tags; +@@ -783,6 +801,13 @@ + if (mdesc->boot_params) + tags = phys_to_virt(mdesc->boot_params); + ++#ifdef CONFIG_KEXEC ++ kexec_boot_params_address = mdesc->boot_params; ++ kexec_boot_params_copy = virt_to_phys(kexec_boot_params_buf); ++ kexec_boot_params = (unsigned long)kexec_boot_params_buf; ++ if (mdesc->boot_params) ++ memcpy((void *)kexec_boot_params, tags, KEXEC_BOOT_PARAMS_SIZE); ++#endif + /* + * If we have the old style parameters, convert them to + * a tag list. +--- linux-2.6.21.5/arch/arm/kernel/relocate_kernel.S.orig 2007-06-30 16:39:28.000000000 -0500 ++++ linux-2.6.21.5/arch/arm/kernel/relocate_kernel.S 2007-06-30 19:10:32.000000000 -0500 +@@ -7,6 +7,23 @@ + .globl relocate_new_kernel + relocate_new_kernel: + ++ /* Move boot params back to where the kernel expects them */ ++ ++ ldr r0,kexec_boot_params_address ++ teq r0,#0 ++ beq 8f ++ ++ ldr r1,kexec_boot_params_copy ++ mov r6,#KEXEC_BOOT_PARAMS_SIZE/4 ++7: ++ ldr r5,[r1],#4 ++ str r5,[r0],#4 ++ subs r6,r6,#1 ++ bne 7b ++ ++8: ++ /* Boot params moved, now go on with the kernel */ ++ + ldr r0,kexec_indirection_page + ldr r1,kexec_start_address + +@@ -50,7 +67,7 @@ + mov lr,r1 + mov r0,#0 + ldr r1,kexec_mach_type +- mov r2,#0 ++ ldr r2,kexec_boot_params_address + mov pc,lr + + .globl kexec_start_address +@@ -65,6 +82,16 @@ + kexec_mach_type: + .long 0x0 + ++ /* phy addr where new kernel will expect to find boot params */ ++ .globl kexec_boot_params_address ++kexec_boot_params_address: ++ .long 0x0 ++ ++ /* phy addr where old kernel put a copy of orig boot params */ ++ .globl kexec_boot_params_copy ++kexec_boot_params_copy: ++ .long 0x0 ++ + relocate_new_kernel_end: + + .globl relocate_new_kernel_size +--- linux-2.6.21.5/include/asm-arm/kexec.h.orig 2007-06-30 16:41:17.000000000 -0500 ++++ linux-2.6.21.5/include/asm-arm/kexec.h 2007-06-30 19:11:22.000000000 -0500 +@@ -14,6 +14,8 @@ + + #define KEXEC_ARCH KEXEC_ARCH_ARM + ++#define KEXEC_BOOT_PARAMS_SIZE 1536 ++ + #ifndef __ASSEMBLY__ + + #define MAX_NOTE_BYTES 1024 diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/akita/defconfig b/recipes/kexecboot/linux-kexecboot-2.6.24/akita/defconfig new file mode 100644 index 0000000000..3b35eda5db --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/akita/defconfig @@ -0,0 +1,1239 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.24 +# Sun Feb 1 01:28:12 2009 +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +CONFIG_GENERIC_TIME=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_MMU=y +# CONFIG_NO_IOPORT is not set +CONFIG_GENERIC_HARDIRQS=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +# CONFIG_ARCH_HAS_ILOG2_U32 is not set +# CONFIG_ARCH_HAS_ILOG2_U64 is not set +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_ZONE_DMA=y +CONFIG_ARCH_MTD_XIP=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_LOCK_KERNEL=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_BSD_PROCESS_ACCT_V3=y +# CONFIG_USER_NS is not set +# CONFIG_PID_NS is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_CGROUPS is not set +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_FAIR_USER_SCHED=y +# CONFIG_FAIR_CGROUP_SCHED is not set +CONFIG_SYSFS_DEPRECATED=y +# CONFIG_RELAY is not set +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="initramfs.cpio.gz" +CONFIG_INITRAMFS_ROOT_UID=0 +CONFIG_INITRAMFS_ROOT_GID=0 +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_EMBEDDED=y +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_ANON_INODES=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_SLAB=y +# CONFIG_SLUB is not set +# CONFIG_SLOB is not set +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +# CONFIG_TINY_SHMEM is not set +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_KMOD=y +CONFIG_BLOCK=y +# CONFIG_LBD is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_LSF is not set +# CONFIG_BLK_DEV_BSG is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=m +CONFIG_IOSCHED_CFQ=m +CONFIG_DEFAULT_AS=y +# CONFIG_DEFAULT_DEADLINE is not set +# CONFIG_DEFAULT_CFQ is not set +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="anticipatory" + +# +# System Type +# +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_CLPS7500 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CO285 is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_IMX is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_PNX4008 is not set +CONFIG_ARCH_PXA=y +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP is not set + +# +# Intel PXA2xx/PXA3xx Implementations +# +# CONFIG_ARCH_LUBBOCK is not set +# CONFIG_MACH_LOGICPD_PXA270 is not set +# CONFIG_MACH_MAINSTONE is not set +# CONFIG_ARCH_PXA_IDP is not set +CONFIG_PXA_SHARPSL=y +# CONFIG_MACH_TRIZEPS4 is not set +# CONFIG_MACH_HX2750 is not set +# CONFIG_MACH_EM_X270 is not set +# CONFIG_MACH_ZYLONITE is not set +# CONFIG_MACH_ARMCORE is not set +# CONFIG_PXA_SHARPSL_25x is not set +CONFIG_PXA_SHARPSL_27x=y +# CONFIG_MACH_HTCUNIVERSAL is not set +CONFIG_MACH_AKITA=y +CONFIG_MACH_SPITZ=y +CONFIG_MACH_BORZOI=y +CONFIG_PXA27x=y +CONFIG_PXA_SHARP_Cxx00=y +CONFIG_PXA_SSP=y +# CONFIG_PXA_KEYS is not set + +# +# Boot options +# + +# +# Power management +# + +# +# Processor Type +# +CONFIG_CPU_32=y +CONFIG_CPU_XSCALE=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5T=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_TLB_V4WBI=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_OUTER_CACHE is not set +CONFIG_IWMMXT=y +CONFIG_XSCALE_PMU=y +CONFIG_SHARP_PARAM=y +CONFIG_SHARPSL_PM=y +CONFIG_SHARP_SCOOP=y + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +CONFIG_PCCARD=y +# CONFIG_PCMCIA_DEBUG is not set +CONFIG_PCMCIA=y +CONFIG_PCMCIA_LOAD_CIS=y +CONFIG_PCMCIA_IOCTL=y + +# +# PC-card bridges +# +CONFIG_PCMCIA_PXA2XX=y + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_PREEMPT=y +CONFIG_HZ=100 +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +# CONFIG_SPARSEMEM_STATIC is not set +# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set +CONFIG_SPLIT_PTLOCK_CPUS=4096 +# CONFIG_RESOURCES_64BIT is not set +CONFIG_ZONE_DMA_FLAG=1 +CONFIG_BOUNCE=y +CONFIG_VIRT_TO_BUS=y +CONFIG_ALIGNMENT_TRAP=y + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="console=ttyS0,115200n8 console=tty1 fbcon=rotate:1" +# CONFIG_XIP_KERNEL is not set +CONFIG_KEXEC=y +CONFIG_ATAGS_PROC=y + +# +# CPU Frequency scaling +# +# CONFIG_CPU_FREQ is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_BINFMT_AOUT=m +CONFIG_BINFMT_MISC=m + +# +# Power management options +# +CONFIG_PM=y +# CONFIG_PM_LEGACY is not set +# CONFIG_PM_DEBUG is not set +CONFIG_PM_SLEEP=y +CONFIG_SUSPEND_UP_POSSIBLE=y +CONFIG_SUSPEND=y +CONFIG_APM_EMULATION=y + +# +# Networking +# +# CONFIG_NET is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +# CONFIG_SYS_HYPERVISOR is not set +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_CONCAT is not set +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS 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=y +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +CONFIG_MTD_COMPLEX_MAPPINGS=y +# CONFIG_MTD_PHYSMAP is not set +CONFIG_MTD_SHARP_SL=y +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +CONFIG_MTD_NAND=y +CONFIG_MTD_NAND_VERIFY_WRITE=y +# CONFIG_MTD_NAND_ECC_SMC is not set +# CONFIG_MTD_NAND_MUSEUM_IDS is not set +# CONFIG_MTD_NAND_H1900 is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND_DISKONCHIP is not set +CONFIG_MTD_NAND_SHARPSL=y +# CONFIG_MTD_NAND_NANDSIM is not set +# CONFIG_MTD_NAND_PLATFORM is not set +# CONFIG_MTD_ALAUDA is not set +# CONFIG_MTD_ONENAND is not set + +# +# UBI - Unsorted block images +# +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +# CONFIG_BLK_DEV_UB is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_CDROM_PKTCDVD is not set +CONFIG_MISC_DEVICES=y +# CONFIG_EEPROM_93CX6 is not set +CONFIG_IDE=y +CONFIG_IDE_MAX_HWIFS=4 +CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_IDE_SATA is not set +CONFIG_BLK_DEV_IDEDISK=y +# CONFIG_IDEDISK_MULTI_MODE is not set +CONFIG_BLK_DEV_IDECS=y +# CONFIG_BLK_DEV_IDECD is not set +# 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_PROC_FS=y + +# +# IDE chipset support/bugfixes +# +CONFIG_IDE_GENERIC=y +# CONFIG_BLK_DEV_PLATFORM is not set +# CONFIG_IDE_ARM is not set +# CONFIG_BLK_DEV_IDEDMA is not set +CONFIG_IDE_ARCH_OBSOLETE_INIT=y +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI device support +# +# CONFIG_RAID_ATTRS is not set +CONFIG_SCSI=m +CONFIG_SCSI_DMA=y +# CONFIG_SCSI_TGT is not set +# CONFIG_SCSI_NETLINK is not set +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=m +CONFIG_CHR_DEV_ST=m +CONFIG_CHR_DEV_OSST=m +CONFIG_BLK_DEV_SR=m +# CONFIG_BLK_DEV_SR_VENDOR is not set +CONFIG_CHR_DEV_SG=m +# CONFIG_CHR_DEV_SCH is not set + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +CONFIG_SCSI_MULTI_LUN=y +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set +# CONFIG_SCSI_SCAN_ASYNC is not set +CONFIG_SCSI_WAIT_SCAN=m + +# +# SCSI Transports +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SRP_ATTRS is not set +CONFIG_SCSI_LOWLEVEL=y +# CONFIG_SCSI_DEBUG is not set +# CONFIG_SCSI_LOWLEVEL_PCMCIA is not set +# CONFIG_ATA is not set +CONFIG_MD=y +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_DM=m +# CONFIG_DM_DEBUG is not set +CONFIG_DM_CRYPT=m +CONFIG_DM_SNAPSHOT=m +CONFIG_DM_MIRROR=m +CONFIG_DM_ZERO=m +CONFIG_DM_MULTIPATH=m +CONFIG_DM_MULTIPATH_EMC=m +# CONFIG_DM_MULTIPATH_RDAC is not set +# CONFIG_DM_MULTIPATH_HP is not set +# CONFIG_DM_DELAY is not set +# CONFIG_DM_UEVENT is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=m +# CONFIG_INPUT_MOUSEDEV_PSAUX is not set +CONFIG_INPUT_MOUSEDEV_SCREEN_X=640 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=480 +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +CONFIG_INPUT_POWER=y + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_CORGI is not set +CONFIG_KEYBOARD_SPITZ=y +CONFIG_SHARPSL_RC=y +# CONFIG_KEYBOARD_PXA27x is not set +# CONFIG_KEYBOARD_GPIO is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_CORGI=y +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_UCB1400 is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_ATI_REMOTE is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_YEALINK is not set +CONFIG_INPUT_UINPUT=m + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_VT_HW_CONSOLE_BINDING is not set +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=m +CONFIG_SERIAL_8250_CS=m +CONFIG_SERIAL_8250_NR_UARTS=4 +CONFIG_SERIAL_8250_RUNTIME_UARTS=4 +# CONFIG_SERIAL_8250_EXTENDED is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_PXA=y +CONFIG_SERIAL_PXA_CONSOLE=y +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_UNIX98_PTYS=y +# CONFIG_LEGACY_PTYS is not set +# CONFIG_IPMI_HANDLER is not set +CONFIG_HW_RANDOM=m +# CONFIG_NVRAM is not set +# CONFIG_R3964 is not set + +# +# PCMCIA character devices +# +# CONFIG_SYNCLINK_CS is not set +# CONFIG_CARDMAN_4000 is not set +# CONFIG_CARDMAN_4040 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +# CONFIG_I2C_CHARDEV is not set + +# +# I2C Algorithms +# +CONFIG_I2C_ALGOBIT=y +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set + +# +# I2C Hardware Bus support +# +# CONFIG_I2C_GPIO is not set +CONFIG_I2C_PXA=y +# CONFIG_I2C_PXA_SLAVE is not set +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Miscellaneous I2C Chip support +# +# CONFIG_SENSORS_DS1337 is not set +# CONFIG_SENSORS_DS1374 is not set +# CONFIG_DS1682 is not set +# CONFIG_SENSORS_EEPROM is not set +# CONFIG_SENSORS_PCF8574 is not set +# CONFIG_SENSORS_PCA9539 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_MAX6875 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_DEBUG_CHIP is not set + +# +# SPI support +# +# CONFIG_SPI is not set +# CONFIG_SPI_MASTER is not set +# CONFIG_W1 is not set +# CONFIG_POWER_SUPPLY is not set +# CONFIG_HWMON is not set +# CONFIG_WATCHDOG is not set + +# +# Sonics Silicon Backplane +# +CONFIG_SSB_POSSIBLE=y +# CONFIG_SSB is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_SM501 is not set +# CONFIG_HTC_ASIC3 is not set +# CONFIG_HTC_ASIC3_DS1WM is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set +# CONFIG_DAB is not set + +# +# Graphics support +# +# CONFIG_VGASTATE is not set +CONFIG_VIDEO_OUTPUT_CONTROL=m +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_DEFERRED_IO is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_S1D13XXX is not set +CONFIG_FB_PXA=y +CONFIG_FB_PXA_LCD_QVGA=y +# CONFIG_FB_PXA_LCD_VGA is not set +CONFIG_FB_PXA_OVERLAY=y +# CONFIG_FB_PXA_PARAMETERS is not set +# CONFIG_FB_MBX is not set +# CONFIG_FB_W100 is not set +# CONFIG_FB_VIRTUAL is not set +CONFIG_BACKLIGHT_LCD_SUPPORT=y +# CONFIG_LCD_CLASS_DEVICE is not set +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_CORGI=y + +# +# Display device support +# +# CONFIG_DISPLAY_SUPPORT is not set + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set +CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y +CONFIG_FONTS=y +# CONFIG_FONT_8x8 is not set +CONFIG_FONT_8x16=y +# CONFIG_FONT_6x11 is not set +# CONFIG_FONT_7x14 is not set +# CONFIG_FONT_PEARL_8x8 is not set +# CONFIG_FONT_ACORN_8x8 is not set +# CONFIG_FONT_MINI_4x6 is not set +# CONFIG_FONT_SUN8x16 is not set +# CONFIG_FONT_SUN12x22 is not set +# CONFIG_FONT_10x18 is not set +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 is not set +CONFIG_HID_SUPPORT=y +CONFIG_HID=m +# CONFIG_HID_DEBUG is not set +# CONFIG_HIDRAW is not set + +# +# USB Input Devices +# +CONFIG_USB_HID=m +# CONFIG_USB_HIDINPUT_POWERBOOK is not set +# CONFIG_HID_FF is not set +# CONFIG_USB_HIDDEV is not set + +# +# USB HID Boot Protocol drivers +# +CONFIG_USB_KBD=m +CONFIG_USB_MOUSE=m +CONFIG_USB_SUPPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARCH_HAS_OHCI=y +# CONFIG_USB_ARCH_HAS_EHCI is not set +CONFIG_USB=m +# CONFIG_USB_DEBUG is not set + +# +# Miscellaneous USB options +# +CONFIG_USB_DEVICEFS=y +CONFIG_USB_DEVICE_CLASS=y +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_SUSPEND is not set +# CONFIG_USB_PERSIST is not set +# CONFIG_USB_OTG is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_ISP116X_HCD is not set +CONFIG_USB_OHCI_HCD=m +# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set +# CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set +CONFIG_USB_OHCI_LITTLE_ENDIAN=y +CONFIG_USB_SL811_HCD=m +CONFIG_USB_SL811_CS=m +# CONFIG_USB_R8A66597_HCD is not set + +# +# USB Device Class drivers +# +CONFIG_USB_ACM=m +CONFIG_USB_PRINTER=m + +# +# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' +# + +# +# may also be needed; see USB_STORAGE Help for more information +# +CONFIG_USB_STORAGE=m +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_USBAT is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_ALAUDA is not set +# CONFIG_USB_STORAGE_KARMA is not set +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +CONFIG_USB_MDC800=m +CONFIG_USB_MICROTEK=m +CONFIG_USB_MON=y + +# +# USB port drivers +# + +# +# USB Serial Converter support +# +CONFIG_USB_SERIAL=m +CONFIG_USB_SERIAL_GENERIC=y +# CONFIG_USB_SERIAL_AIRCABLE is not set +# CONFIG_USB_SERIAL_AIRPRIME is not set +# CONFIG_USB_SERIAL_ARK3116 is not set +CONFIG_USB_SERIAL_BELKIN=m +# CONFIG_USB_SERIAL_CH341 is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m +CONFIG_USB_SERIAL_CP2101=m +CONFIG_USB_SERIAL_CYPRESS_M8=m +CONFIG_USB_SERIAL_EMPEG=m +CONFIG_USB_SERIAL_FTDI_SIO=m +# CONFIG_USB_SERIAL_FUNSOFT is not set +CONFIG_USB_SERIAL_VISOR=m +CONFIG_USB_SERIAL_IPAQ=m +CONFIG_USB_SERIAL_IR=m +CONFIG_USB_SERIAL_EDGEPORT=m +CONFIG_USB_SERIAL_EDGEPORT_TI=m +CONFIG_USB_SERIAL_GARMIN=m +CONFIG_USB_SERIAL_IPW=m +CONFIG_USB_SERIAL_KEYSPAN_PDA=m +CONFIG_USB_SERIAL_KEYSPAN=m +# CONFIG_USB_SERIAL_KEYSPAN_MPR is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19QW is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19QI is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49WLC is not set +CONFIG_USB_SERIAL_KLSI=m +CONFIG_USB_SERIAL_KOBIL_SCT=m +CONFIG_USB_SERIAL_MCT_U232=m +# CONFIG_USB_SERIAL_MOS7720 is not set +# CONFIG_USB_SERIAL_MOS7840 is not set +# CONFIG_USB_SERIAL_NAVMAN is not set +CONFIG_USB_SERIAL_PL2303=m +# CONFIG_USB_SERIAL_OTI6858 is not set +# CONFIG_USB_SERIAL_HP4X is not set +CONFIG_USB_SERIAL_SAFE=m +# CONFIG_USB_SERIAL_SAFE_PADDED is not set +# CONFIG_USB_SERIAL_SIERRAWIRELESS is not set +CONFIG_USB_SERIAL_TI=m +CONFIG_USB_SERIAL_CYBERJACK=m +CONFIG_USB_SERIAL_XIRCOM=m +# CONFIG_USB_SERIAL_OPTION is not set +CONFIG_USB_SERIAL_OMNINET=m +# CONFIG_USB_SERIAL_DEBUG is not set +CONFIG_USB_EZUSB=y + +# +# USB Miscellaneous drivers +# +CONFIG_USB_EMI62=m +CONFIG_USB_EMI26=m +# CONFIG_USB_ADUTUX is not set +CONFIG_USB_AUERSWALD=m +CONFIG_USB_RIO500=m +CONFIG_USB_LEGOTOWER=m +CONFIG_USB_LCD=m +# CONFIG_USB_BERRY_CHARGE is not set +CONFIG_USB_LED=m +# CONFIG_USB_CYPRESS_CY7C63 is not set +CONFIG_USB_CYTHERM=m +# CONFIG_USB_PHIDGET is not set +CONFIG_USB_IDMOUSE=m +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set + +# +# USB DSL modem support +# + +# +# USB Gadget Support +# +CONFIG_USB_GADGET=m +# CONFIG_USB_GADGET_DEBUG_FILES is not set +CONFIG_USB_GADGET_SELECTED=y +# CONFIG_USB_GADGET_AMD5536UDC is not set +# CONFIG_USB_GADGET_ATMEL_USBA is not set +# CONFIG_USB_GADGET_FSL_USB2 is not set +# CONFIG_USB_GADGET_NET2280 is not set +# CONFIG_USB_GADGET_PXA2XX is not set +# CONFIG_USB_GADGET_M66592 is not set +CONFIG_USB_GADGET_PXA27X=y +CONFIG_USB_PXA27X=m +# CONFIG_USB_GADGET_GOKU is not set +# CONFIG_USB_GADGET_LH7A40X is not set +# CONFIG_USB_GADGET_OMAP is not set +# CONFIG_USB_GADGET_S3C2410 is not set +# CONFIG_USB_GADGET_AT91 is not set +# CONFIG_USB_GADGET_DUMMY_HCD is not set +# CONFIG_USB_GADGET_DUALSPEED is not set +CONFIG_USB_ZERO=m +# CONFIG_USB_ETH is not set +CONFIG_USB_GADGETFS=m +CONFIG_USB_FILE_STORAGE=m +# CONFIG_USB_FILE_STORAGE_TEST is not set +CONFIG_USB_G_SERIAL=m +# CONFIG_USB_MIDI_GADGET is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_UNSAFE_RESUME=y + +# +# MMC/SD Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_BOUNCE=y +# CONFIG_SDIO_UART is not set + +# +# MMC/SD Host Controller Drivers +# +CONFIG_MMC_PXA=y +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +CONFIG_LEDS_SPITZ=y +# CONFIG_LEDS_TOSA is not set +# CONFIG_LEDS_GPIO is not set + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_IDE_DISK=y +# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set + +# +# SPI RTC drivers +# + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_SA1100=y + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XIP is not set +CONFIG_EXT3_FS=y +# CONFIG_EXT3_FS_XATTR is not set +# CONFIG_EXT4DEV_FS is not set +CONFIG_JBD=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +CONFIG_INOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_QUOTA is not set +CONFIG_DNOTIFY=y +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +# CONFIG_MSDOS_FS is not set +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_WRITEBUFFER=y +# CONFIG_JFFS2_FS_WBUF_VERIFY is not set +CONFIG_JFFS2_SUMMARY=y +# CONFIG_JFFS2_FS_XATTR is not set +# CONFIG_JFFS2_SYSFS is not set +CONFIG_JFFS2_COMPRESSION_OPTIONS=y +CONFIG_JFFS2_ZLIB=y +CONFIG_JFFS2_LZO=y +CONFIG_JFFS2_RTIME=y +CONFIG_JFFS2_RUBIN=y +# CONFIG_JFFS2_CMODE_NONE is not set +CONFIG_JFFS2_CMODE_PRIORITY=y +# CONFIG_JFFS2_CMODE_SIZE is not set +# CONFIG_JFFS2_CMODE_FAVOURLZO is not set +CONFIG_CRAMFS=m +CONFIG_SQUASHFS=m +# CONFIG_SQUASHFS_EMBEDDED is not set +CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 +# 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 + +# +# 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_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_KARMA_PARTITION is not set +# CONFIG_EFI_PARTITION is not set +# CONFIG_SYSV68_PARTITION is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="cp437" +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_CODEPAGE_737=m +CONFIG_NLS_CODEPAGE_775=m +CONFIG_NLS_CODEPAGE_850=m +CONFIG_NLS_CODEPAGE_852=m +CONFIG_NLS_CODEPAGE_855=m +CONFIG_NLS_CODEPAGE_857=m +CONFIG_NLS_CODEPAGE_860=m +CONFIG_NLS_CODEPAGE_861=m +CONFIG_NLS_CODEPAGE_862=m +CONFIG_NLS_CODEPAGE_863=m +CONFIG_NLS_CODEPAGE_864=m +CONFIG_NLS_CODEPAGE_865=m +CONFIG_NLS_CODEPAGE_866=m +CONFIG_NLS_CODEPAGE_869=m +CONFIG_NLS_CODEPAGE_936=m +CONFIG_NLS_CODEPAGE_950=m +CONFIG_NLS_CODEPAGE_932=m +CONFIG_NLS_CODEPAGE_949=m +CONFIG_NLS_CODEPAGE_874=m +CONFIG_NLS_ISO8859_8=m +CONFIG_NLS_CODEPAGE_1250=m +CONFIG_NLS_CODEPAGE_1251=m +CONFIG_NLS_ASCII=m +CONFIG_NLS_ISO8859_1=y +CONFIG_NLS_ISO8859_2=m +CONFIG_NLS_ISO8859_3=m +CONFIG_NLS_ISO8859_4=m +CONFIG_NLS_ISO8859_5=m +CONFIG_NLS_ISO8859_6=m +CONFIG_NLS_ISO8859_7=m +CONFIG_NLS_ISO8859_9=m +CONFIG_NLS_ISO8859_13=m +CONFIG_NLS_ISO8859_14=m +CONFIG_NLS_ISO8859_15=m +CONFIG_NLS_KOI8_R=m +CONFIG_NLS_KOI8_U=m +CONFIG_NLS_UTF8=y +# CONFIG_INSTRUMENTATION is not set + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_KERNEL is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +CONFIG_FRAME_POINTER=y +# CONFIG_SAMPLES is not set +# CONFIG_DEBUG_USER is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITY_FILE_CAPABILITIES is not set +CONFIG_CRYPTO=y +CONFIG_CRYPTO_ALGAPI=m +CONFIG_CRYPTO_BLKCIPHER=m +CONFIG_CRYPTO_HASH=m +CONFIG_CRYPTO_MANAGER=m +CONFIG_CRYPTO_HMAC=m +# CONFIG_CRYPTO_XCBC is not set +CONFIG_CRYPTO_NULL=m +CONFIG_CRYPTO_MD4=m +CONFIG_CRYPTO_MD5=m +CONFIG_CRYPTO_SHA1=m +CONFIG_CRYPTO_SHA256=m +CONFIG_CRYPTO_SHA512=m +CONFIG_CRYPTO_WP512=m +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_GF128MUL is not set +CONFIG_CRYPTO_ECB=m +CONFIG_CRYPTO_CBC=m +CONFIG_CRYPTO_PCBC=m +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_XTS is not set +# CONFIG_CRYPTO_CRYPTD is not set +CONFIG_CRYPTO_DES=m +# CONFIG_CRYPTO_FCRYPT is not set +CONFIG_CRYPTO_BLOWFISH=m +CONFIG_CRYPTO_TWOFISH=m +CONFIG_CRYPTO_TWOFISH_COMMON=m +CONFIG_CRYPTO_SERPENT=m +CONFIG_CRYPTO_AES=m +CONFIG_CRYPTO_CAST5=m +CONFIG_CRYPTO_CAST6=m +CONFIG_CRYPTO_TEA=m +CONFIG_CRYPTO_ARC4=m +CONFIG_CRYPTO_KHAZAD=m +CONFIG_CRYPTO_ANUBIS=m +# CONFIG_CRYPTO_SEED is not set +CONFIG_CRYPTO_DEFLATE=m +CONFIG_CRYPTO_LZO=m +CONFIG_CRYPTO_MICHAEL_MIC=m +CONFIG_CRYPTO_CRC32C=m +CONFIG_CRYPTO_CAMELLIA=m +CONFIG_CRYPTO_TEST=m +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_HW is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_CRC_CCITT=y +# CONFIG_CRC16 is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +CONFIG_LIBCRC32C=m +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_PLIST=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/binutils-buildid-arm.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/binutils-buildid-arm.patch new file mode 100644 index 0000000000..68e35e89e1 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/binutils-buildid-arm.patch @@ -0,0 +1,16 @@ +--- + arch/arm/kernel/vmlinux.lds.S | 1 + + 1 file changed, 1 insertion(+) + +Index: linux-2.6.22/arch/arm/kernel/vmlinux.lds.S +=================================================================== +--- linux-2.6.22.orig/arch/arm/kernel/vmlinux.lds.S 2007-09-11 18:32:29.000000000 +0200 ++++ linux-2.6.22/arch/arm/kernel/vmlinux.lds.S 2007-09-11 18:33:42.000000000 +0200 +@@ -94,6 +94,7 @@ + TEXT_TEXT + SCHED_TEXT + LOCK_TEXT ++ *(.note.*) + #ifdef CONFIG_MMU + *(.fixup) + #endif diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/c7x0/defconfig b/recipes/kexecboot/linux-kexecboot-2.6.24/c7x0/defconfig new file mode 100644 index 0000000000..bc621636fa --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/c7x0/defconfig @@ -0,0 +1,1233 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.24 +# Sun Feb 1 01:19:01 2009 +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +CONFIG_GENERIC_TIME=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_MMU=y +# CONFIG_NO_IOPORT is not set +CONFIG_GENERIC_HARDIRQS=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +# CONFIG_ARCH_HAS_ILOG2_U32 is not set +# CONFIG_ARCH_HAS_ILOG2_U64 is not set +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_ZONE_DMA=y +CONFIG_ARCH_MTD_XIP=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_LOCK_KERNEL=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_BSD_PROCESS_ACCT_V3=y +# CONFIG_USER_NS is not set +# CONFIG_PID_NS is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_CGROUPS is not set +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_FAIR_USER_SCHED=y +# CONFIG_FAIR_CGROUP_SCHED is not set +CONFIG_SYSFS_DEPRECATED=y +# CONFIG_RELAY is not set +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="initramfs.cpio.gz" +CONFIG_INITRAMFS_ROOT_UID=0 +CONFIG_INITRAMFS_ROOT_GID=0 +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_EMBEDDED=y +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_ANON_INODES=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_SLAB=y +# CONFIG_SLUB is not set +# CONFIG_SLOB is not set +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +# CONFIG_TINY_SHMEM is not set +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_KMOD=y +CONFIG_BLOCK=y +# CONFIG_LBD is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_LSF is not set +# CONFIG_BLK_DEV_BSG is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=m +CONFIG_IOSCHED_CFQ=m +CONFIG_DEFAULT_AS=y +# CONFIG_DEFAULT_DEADLINE is not set +# CONFIG_DEFAULT_CFQ is not set +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="anticipatory" + +# +# System Type +# +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_CLPS7500 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CO285 is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_IMX is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_PNX4008 is not set +CONFIG_ARCH_PXA=y +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP is not set + +# +# Intel PXA2xx/PXA3xx Implementations +# +# CONFIG_ARCH_LUBBOCK is not set +# CONFIG_MACH_LOGICPD_PXA270 is not set +# CONFIG_MACH_MAINSTONE is not set +# CONFIG_ARCH_PXA_IDP is not set +CONFIG_PXA_SHARPSL=y +# CONFIG_MACH_TRIZEPS4 is not set +# CONFIG_MACH_HX2750 is not set +# CONFIG_MACH_EM_X270 is not set +# CONFIG_MACH_ZYLONITE is not set +# CONFIG_MACH_ARMCORE is not set +CONFIG_PXA_SHARPSL_25x=y +# CONFIG_PXA_SHARPSL_27x is not set +# CONFIG_MACH_HTCUNIVERSAL is not set +# CONFIG_MACH_POODLE is not set +CONFIG_MACH_CORGI=y +CONFIG_MACH_SHEPHERD=y +CONFIG_MACH_HUSKY=y +# CONFIG_MACH_TOSA is not set +CONFIG_PXA25x=y +CONFIG_PXA_SHARP_C7xx=y +CONFIG_PXA_SSP=y +# CONFIG_PXA_KEYS is not set + +# +# Boot options +# + +# +# Power management +# + +# +# Processor Type +# +CONFIG_CPU_32=y +CONFIG_CPU_XSCALE=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5T=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_TLB_V4WBI=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_OUTER_CACHE is not set +# CONFIG_IWMMXT is not set +CONFIG_XSCALE_PMU=y +CONFIG_SHARP_PARAM=y +CONFIG_SHARPSL_PM=y +CONFIG_SHARP_SCOOP=y + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +CONFIG_PCCARD=y +# CONFIG_PCMCIA_DEBUG is not set +CONFIG_PCMCIA=y +CONFIG_PCMCIA_LOAD_CIS=y +CONFIG_PCMCIA_IOCTL=y + +# +# PC-card bridges +# +CONFIG_PCMCIA_PXA2XX=y + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_PREEMPT=y +CONFIG_HZ=100 +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +# CONFIG_SPARSEMEM_STATIC is not set +# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set +CONFIG_SPLIT_PTLOCK_CPUS=4096 +# CONFIG_RESOURCES_64BIT is not set +CONFIG_ZONE_DMA_FLAG=1 +CONFIG_BOUNCE=y +CONFIG_VIRT_TO_BUS=y +CONFIG_ALIGNMENT_TRAP=y + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="console=ttyS0,115200n8 console=tty1" +# CONFIG_XIP_KERNEL is not set +CONFIG_KEXEC=y +CONFIG_ATAGS_PROC=y + +# +# CPU Frequency scaling +# +# CONFIG_CPU_FREQ is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_BINFMT_AOUT=m +CONFIG_BINFMT_MISC=m + +# +# Power management options +# +CONFIG_PM=y +# CONFIG_PM_LEGACY is not set +# CONFIG_PM_DEBUG is not set +CONFIG_PM_SLEEP=y +CONFIG_SUSPEND_UP_POSSIBLE=y +CONFIG_SUSPEND=y +CONFIG_APM_EMULATION=y + +# +# Networking +# +# CONFIG_NET is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +# CONFIG_SYS_HYPERVISOR is not set +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_CONCAT is not set +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS 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=y +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +CONFIG_MTD_COMPLEX_MAPPINGS=y +# CONFIG_MTD_PHYSMAP is not set +CONFIG_MTD_SHARP_SL=y +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +CONFIG_MTD_NAND=y +CONFIG_MTD_NAND_VERIFY_WRITE=y +# CONFIG_MTD_NAND_ECC_SMC is not set +# CONFIG_MTD_NAND_MUSEUM_IDS is not set +# CONFIG_MTD_NAND_H1900 is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND_DISKONCHIP is not set +CONFIG_MTD_NAND_SHARPSL=y +# CONFIG_MTD_NAND_NANDSIM is not set +# CONFIG_MTD_NAND_PLATFORM is not set +# CONFIG_MTD_ALAUDA is not set +# CONFIG_MTD_ONENAND is not set + +# +# UBI - Unsorted block images +# +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +# CONFIG_BLK_DEV_UB is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_CDROM_PKTCDVD is not set +CONFIG_MISC_DEVICES=y +# CONFIG_EEPROM_93CX6 is not set +CONFIG_IDE=y +CONFIG_IDE_MAX_HWIFS=4 +CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_IDE_SATA is not set +CONFIG_BLK_DEV_IDEDISK=y +# CONFIG_IDEDISK_MULTI_MODE is not set +CONFIG_BLK_DEV_IDECS=y +# CONFIG_BLK_DEV_IDECD is not set +# 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_PROC_FS=y + +# +# IDE chipset support/bugfixes +# +CONFIG_IDE_GENERIC=y +# CONFIG_BLK_DEV_PLATFORM is not set +# CONFIG_IDE_ARM is not set +# CONFIG_BLK_DEV_IDEDMA is not set +CONFIG_IDE_ARCH_OBSOLETE_INIT=y +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI device support +# +# CONFIG_RAID_ATTRS is not set +CONFIG_SCSI=m +CONFIG_SCSI_DMA=y +# CONFIG_SCSI_TGT is not set +# CONFIG_SCSI_NETLINK is not set +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=m +CONFIG_CHR_DEV_ST=m +CONFIG_CHR_DEV_OSST=m +CONFIG_BLK_DEV_SR=m +# CONFIG_BLK_DEV_SR_VENDOR is not set +CONFIG_CHR_DEV_SG=m +# CONFIG_CHR_DEV_SCH is not set + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +CONFIG_SCSI_MULTI_LUN=y +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set +# CONFIG_SCSI_SCAN_ASYNC is not set +CONFIG_SCSI_WAIT_SCAN=m + +# +# SCSI Transports +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SRP_ATTRS is not set +CONFIG_SCSI_LOWLEVEL=y +# CONFIG_SCSI_DEBUG is not set +# CONFIG_SCSI_LOWLEVEL_PCMCIA is not set +# CONFIG_ATA is not set +CONFIG_MD=y +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_DM=m +# CONFIG_DM_DEBUG is not set +CONFIG_DM_CRYPT=m +CONFIG_DM_SNAPSHOT=m +CONFIG_DM_MIRROR=m +CONFIG_DM_ZERO=m +CONFIG_DM_MULTIPATH=m +CONFIG_DM_MULTIPATH_EMC=m +# CONFIG_DM_MULTIPATH_RDAC is not set +# CONFIG_DM_MULTIPATH_HP is not set +# CONFIG_DM_DELAY is not set +# CONFIG_DM_UEVENT is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=m +# CONFIG_INPUT_MOUSEDEV_PSAUX is not set +CONFIG_INPUT_MOUSEDEV_SCREEN_X=640 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=480 +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +CONFIG_INPUT_POWER=y + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +CONFIG_KEYBOARD_CORGI=y +# CONFIG_KEYBOARD_SPITZ is not set +CONFIG_SHARPSL_RC=y +# CONFIG_KEYBOARD_GPIO is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_CORGI=y +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_UCB1400 is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_ATI_REMOTE is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_YEALINK is not set +CONFIG_INPUT_UINPUT=m + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_VT_HW_CONSOLE_BINDING is not set +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=m +CONFIG_SERIAL_8250_CS=m +CONFIG_SERIAL_8250_NR_UARTS=4 +CONFIG_SERIAL_8250_RUNTIME_UARTS=4 +# CONFIG_SERIAL_8250_EXTENDED is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_PXA=y +CONFIG_SERIAL_PXA_CONSOLE=y +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_UNIX98_PTYS=y +# CONFIG_LEGACY_PTYS is not set +# CONFIG_IPMI_HANDLER is not set +CONFIG_HW_RANDOM=m +# CONFIG_NVRAM is not set +# CONFIG_R3964 is not set + +# +# PCMCIA character devices +# +# CONFIG_SYNCLINK_CS is not set +# CONFIG_CARDMAN_4000 is not set +# CONFIG_CARDMAN_4040 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +# CONFIG_I2C_CHARDEV is not set + +# +# I2C Algorithms +# +CONFIG_I2C_ALGOBIT=y +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set + +# +# I2C Hardware Bus support +# +# CONFIG_I2C_GPIO is not set +CONFIG_I2C_PXA=y +# CONFIG_I2C_PXA_SLAVE is not set +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Miscellaneous I2C Chip support +# +# CONFIG_SENSORS_DS1337 is not set +# CONFIG_SENSORS_DS1374 is not set +# CONFIG_DS1682 is not set +# CONFIG_SENSORS_EEPROM is not set +# CONFIG_SENSORS_PCF8574 is not set +# CONFIG_SENSORS_PCA9539 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_MAX6875 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_DEBUG_CHIP is not set + +# +# SPI support +# +# CONFIG_SPI is not set +# CONFIG_SPI_MASTER is not set +# CONFIG_W1 is not set +# CONFIG_POWER_SUPPLY is not set +# CONFIG_HWMON is not set +# CONFIG_WATCHDOG is not set + +# +# Sonics Silicon Backplane +# +CONFIG_SSB_POSSIBLE=y +# CONFIG_SSB is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_SM501 is not set +# CONFIG_HTC_ASIC3 is not set +# CONFIG_HTC_ASIC3_DS1WM is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set +# CONFIG_DAB is not set + +# +# Graphics support +# +# CONFIG_VGASTATE is not set +CONFIG_VIDEO_OUTPUT_CONTROL=m +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_DEFERRED_IO is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_PXA is not set +# CONFIG_FB_MBX is not set +CONFIG_FB_W100=y +# CONFIG_FB_VIRTUAL is not set +CONFIG_BACKLIGHT_LCD_SUPPORT=y +# CONFIG_LCD_CLASS_DEVICE is not set +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_CORGI=y + +# +# Display device support +# +# CONFIG_DISPLAY_SUPPORT is not set + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set +# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set +CONFIG_FONTS=y +# CONFIG_FONT_8x8 is not set +CONFIG_FONT_8x16=y +# CONFIG_FONT_6x11 is not set +# CONFIG_FONT_7x14 is not set +# CONFIG_FONT_PEARL_8x8 is not set +# CONFIG_FONT_ACORN_8x8 is not set +# CONFIG_FONT_MINI_4x6 is not set +# CONFIG_FONT_SUN8x16 is not set +# CONFIG_FONT_SUN12x22 is not set +# CONFIG_FONT_10x18 is not set +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 is not set +CONFIG_HID_SUPPORT=y +CONFIG_HID=m +# CONFIG_HID_DEBUG is not set +# CONFIG_HIDRAW is not set + +# +# USB Input Devices +# +CONFIG_USB_HID=m +# CONFIG_USB_HIDINPUT_POWERBOOK is not set +# CONFIG_HID_FF is not set +# CONFIG_USB_HIDDEV is not set + +# +# USB HID Boot Protocol drivers +# +CONFIG_USB_KBD=m +CONFIG_USB_MOUSE=m +CONFIG_USB_SUPPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +# CONFIG_USB_ARCH_HAS_OHCI is not set +# CONFIG_USB_ARCH_HAS_EHCI is not set +CONFIG_USB=m +# CONFIG_USB_DEBUG is not set + +# +# Miscellaneous USB options +# +CONFIG_USB_DEVICEFS=y +CONFIG_USB_DEVICE_CLASS=y +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_SUSPEND is not set +# CONFIG_USB_PERSIST is not set +# CONFIG_USB_OTG is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_ISP116X_HCD is not set +CONFIG_USB_SL811_HCD=m +CONFIG_USB_SL811_CS=m +# CONFIG_USB_R8A66597_HCD is not set + +# +# USB Device Class drivers +# +CONFIG_USB_ACM=m +CONFIG_USB_PRINTER=m + +# +# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' +# + +# +# may also be needed; see USB_STORAGE Help for more information +# +CONFIG_USB_STORAGE=m +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_USBAT is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_ALAUDA is not set +# CONFIG_USB_STORAGE_KARMA is not set +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +CONFIG_USB_MDC800=m +CONFIG_USB_MICROTEK=m +CONFIG_USB_MON=y + +# +# USB port drivers +# + +# +# USB Serial Converter support +# +CONFIG_USB_SERIAL=m +CONFIG_USB_SERIAL_GENERIC=y +# CONFIG_USB_SERIAL_AIRCABLE is not set +# CONFIG_USB_SERIAL_AIRPRIME is not set +# CONFIG_USB_SERIAL_ARK3116 is not set +CONFIG_USB_SERIAL_BELKIN=m +# CONFIG_USB_SERIAL_CH341 is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m +CONFIG_USB_SERIAL_CP2101=m +CONFIG_USB_SERIAL_CYPRESS_M8=m +CONFIG_USB_SERIAL_EMPEG=m +CONFIG_USB_SERIAL_FTDI_SIO=m +# CONFIG_USB_SERIAL_FUNSOFT is not set +CONFIG_USB_SERIAL_VISOR=m +CONFIG_USB_SERIAL_IPAQ=m +CONFIG_USB_SERIAL_IR=m +CONFIG_USB_SERIAL_EDGEPORT=m +CONFIG_USB_SERIAL_EDGEPORT_TI=m +CONFIG_USB_SERIAL_GARMIN=m +CONFIG_USB_SERIAL_IPW=m +CONFIG_USB_SERIAL_KEYSPAN_PDA=m +CONFIG_USB_SERIAL_KEYSPAN=m +# CONFIG_USB_SERIAL_KEYSPAN_MPR is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19QW is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19QI is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49WLC is not set +CONFIG_USB_SERIAL_KLSI=m +CONFIG_USB_SERIAL_KOBIL_SCT=m +CONFIG_USB_SERIAL_MCT_U232=m +# CONFIG_USB_SERIAL_MOS7720 is not set +# CONFIG_USB_SERIAL_MOS7840 is not set +# CONFIG_USB_SERIAL_NAVMAN is not set +CONFIG_USB_SERIAL_PL2303=m +# CONFIG_USB_SERIAL_OTI6858 is not set +# CONFIG_USB_SERIAL_HP4X is not set +CONFIG_USB_SERIAL_SAFE=m +# CONFIG_USB_SERIAL_SAFE_PADDED is not set +# CONFIG_USB_SERIAL_SIERRAWIRELESS is not set +CONFIG_USB_SERIAL_TI=m +CONFIG_USB_SERIAL_CYBERJACK=m +CONFIG_USB_SERIAL_XIRCOM=m +# CONFIG_USB_SERIAL_OPTION is not set +CONFIG_USB_SERIAL_OMNINET=m +# CONFIG_USB_SERIAL_DEBUG is not set +CONFIG_USB_EZUSB=y + +# +# USB Miscellaneous drivers +# +CONFIG_USB_EMI62=m +CONFIG_USB_EMI26=m +# CONFIG_USB_ADUTUX is not set +CONFIG_USB_AUERSWALD=m +CONFIG_USB_RIO500=m +CONFIG_USB_LEGOTOWER=m +CONFIG_USB_LCD=m +# CONFIG_USB_BERRY_CHARGE is not set +CONFIG_USB_LED=m +# CONFIG_USB_CYPRESS_CY7C63 is not set +CONFIG_USB_CYTHERM=m +# CONFIG_USB_PHIDGET is not set +CONFIG_USB_IDMOUSE=m +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set + +# +# USB DSL modem support +# + +# +# USB Gadget Support +# +CONFIG_USB_GADGET=y +# CONFIG_USB_GADGET_DEBUG_FILES is not set +CONFIG_USB_GADGET_SELECTED=y +# CONFIG_USB_GADGET_AMD5536UDC is not set +# CONFIG_USB_GADGET_ATMEL_USBA is not set +# CONFIG_USB_GADGET_FSL_USB2 is not set +# CONFIG_USB_GADGET_NET2280 is not set +CONFIG_USB_GADGET_PXA2XX=y +CONFIG_USB_PXA2XX=y +CONFIG_USB_PXA2XX_SMALL=y +# CONFIG_USB_GADGET_M66592 is not set +# CONFIG_USB_GADGET_PXA27X is not set +# CONFIG_USB_GADGET_GOKU is not set +# CONFIG_USB_GADGET_LH7A40X is not set +# CONFIG_USB_GADGET_OMAP is not set +# CONFIG_USB_GADGET_S3C2410 is not set +# CONFIG_USB_GADGET_AT91 is not set +# CONFIG_USB_GADGET_DUMMY_HCD is not set +# CONFIG_USB_GADGET_DUALSPEED is not set +CONFIG_USB_ZERO=m +# CONFIG_USB_ETH is not set +CONFIG_USB_GADGETFS=m +CONFIG_USB_FILE_STORAGE=m +# CONFIG_USB_FILE_STORAGE_TEST is not set +CONFIG_USB_G_SERIAL=m +# CONFIG_USB_MIDI_GADGET is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_UNSAFE_RESUME=y + +# +# MMC/SD Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_BOUNCE=y +# CONFIG_SDIO_UART is not set + +# +# MMC/SD Host Controller Drivers +# +CONFIG_MMC_PXA=y +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +CONFIG_LEDS_CORGI=y +# CONFIG_LEDS_TOSA is not set +# CONFIG_LEDS_GPIO is not set + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_IDE_DISK=y +# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set + +# +# SPI RTC drivers +# + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_SA1100=y + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XIP is not set +CONFIG_EXT3_FS=y +# CONFIG_EXT3_FS_XATTR is not set +# CONFIG_EXT4DEV_FS is not set +CONFIG_JBD=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +CONFIG_INOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_QUOTA is not set +CONFIG_DNOTIFY=y +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +# CONFIG_MSDOS_FS is not set +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_WRITEBUFFER=y +# CONFIG_JFFS2_FS_WBUF_VERIFY is not set +CONFIG_JFFS2_SUMMARY=y +# CONFIG_JFFS2_FS_XATTR is not set +# CONFIG_JFFS2_SYSFS is not set +CONFIG_JFFS2_COMPRESSION_OPTIONS=y +CONFIG_JFFS2_ZLIB=y +CONFIG_JFFS2_LZO=y +CONFIG_JFFS2_RTIME=y +CONFIG_JFFS2_RUBIN=y +# CONFIG_JFFS2_CMODE_NONE is not set +CONFIG_JFFS2_CMODE_PRIORITY=y +# CONFIG_JFFS2_CMODE_SIZE is not set +# CONFIG_JFFS2_CMODE_FAVOURLZO is not set +CONFIG_CRAMFS=m +CONFIG_SQUASHFS=m +# CONFIG_SQUASHFS_EMBEDDED is not set +CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 +# 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 + +# +# 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_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_KARMA_PARTITION is not set +# CONFIG_EFI_PARTITION is not set +# CONFIG_SYSV68_PARTITION is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="cp437" +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_CODEPAGE_737=m +CONFIG_NLS_CODEPAGE_775=m +CONFIG_NLS_CODEPAGE_850=m +CONFIG_NLS_CODEPAGE_852=m +CONFIG_NLS_CODEPAGE_855=m +CONFIG_NLS_CODEPAGE_857=m +CONFIG_NLS_CODEPAGE_860=m +CONFIG_NLS_CODEPAGE_861=m +CONFIG_NLS_CODEPAGE_862=m +CONFIG_NLS_CODEPAGE_863=m +CONFIG_NLS_CODEPAGE_864=m +CONFIG_NLS_CODEPAGE_865=m +CONFIG_NLS_CODEPAGE_866=m +CONFIG_NLS_CODEPAGE_869=m +CONFIG_NLS_CODEPAGE_936=m +CONFIG_NLS_CODEPAGE_950=m +CONFIG_NLS_CODEPAGE_932=m +CONFIG_NLS_CODEPAGE_949=m +CONFIG_NLS_CODEPAGE_874=m +CONFIG_NLS_ISO8859_8=m +CONFIG_NLS_CODEPAGE_1250=m +CONFIG_NLS_CODEPAGE_1251=m +CONFIG_NLS_ASCII=m +CONFIG_NLS_ISO8859_1=y +CONFIG_NLS_ISO8859_2=m +CONFIG_NLS_ISO8859_3=m +CONFIG_NLS_ISO8859_4=m +CONFIG_NLS_ISO8859_5=m +CONFIG_NLS_ISO8859_6=m +CONFIG_NLS_ISO8859_7=m +CONFIG_NLS_ISO8859_9=m +CONFIG_NLS_ISO8859_13=m +CONFIG_NLS_ISO8859_14=m +CONFIG_NLS_ISO8859_15=m +CONFIG_NLS_KOI8_R=m +CONFIG_NLS_KOI8_U=m +CONFIG_NLS_UTF8=y +# CONFIG_INSTRUMENTATION is not set + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_KERNEL is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +CONFIG_FRAME_POINTER=y +# CONFIG_SAMPLES is not set +# CONFIG_DEBUG_USER is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITY_FILE_CAPABILITIES is not set +CONFIG_CRYPTO=y +CONFIG_CRYPTO_ALGAPI=m +CONFIG_CRYPTO_BLKCIPHER=m +CONFIG_CRYPTO_HASH=m +CONFIG_CRYPTO_MANAGER=m +CONFIG_CRYPTO_HMAC=m +# CONFIG_CRYPTO_XCBC is not set +CONFIG_CRYPTO_NULL=m +CONFIG_CRYPTO_MD4=m +CONFIG_CRYPTO_MD5=m +CONFIG_CRYPTO_SHA1=m +CONFIG_CRYPTO_SHA256=m +CONFIG_CRYPTO_SHA512=m +CONFIG_CRYPTO_WP512=m +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_GF128MUL is not set +CONFIG_CRYPTO_ECB=m +CONFIG_CRYPTO_CBC=m +CONFIG_CRYPTO_PCBC=m +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_XTS is not set +# CONFIG_CRYPTO_CRYPTD is not set +CONFIG_CRYPTO_DES=m +# CONFIG_CRYPTO_FCRYPT is not set +CONFIG_CRYPTO_BLOWFISH=m +CONFIG_CRYPTO_TWOFISH=m +CONFIG_CRYPTO_TWOFISH_COMMON=m +CONFIG_CRYPTO_SERPENT=m +CONFIG_CRYPTO_AES=m +CONFIG_CRYPTO_CAST5=m +CONFIG_CRYPTO_CAST6=m +CONFIG_CRYPTO_TEA=m +CONFIG_CRYPTO_ARC4=m +CONFIG_CRYPTO_KHAZAD=m +CONFIG_CRYPTO_ANUBIS=m +# CONFIG_CRYPTO_SEED is not set +CONFIG_CRYPTO_DEFLATE=m +CONFIG_CRYPTO_LZO=m +CONFIG_CRYPTO_MICHAEL_MIC=m +CONFIG_CRYPTO_CRC32C=m +CONFIG_CRYPTO_CAMELLIA=m +CONFIG_CRYPTO_TEST=m +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_HW is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_CRC_CCITT=y +# CONFIG_CRC16 is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +CONFIG_LIBCRC32C=m +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_PLIST=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/connectplus-prevent-oops-HACK.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/connectplus-prevent-oops-HACK.patch new file mode 100644 index 0000000000..b5439c62e7 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/connectplus-prevent-oops-HACK.patch @@ -0,0 +1,17 @@ +Index: linux-2.6.21/drivers/net/wireless/hostap/hostap_hw.c +=================================================================== +--- linux-2.6.21.orig/drivers/net/wireless/hostap/hostap_hw.c 2007-07-07 12:45:39.000000000 +0100 ++++ linux-2.6.21/drivers/net/wireless/hostap/hostap_hw.c 2007-07-07 12:47:30.000000000 +0100 +@@ -2666,6 +2666,12 @@ + iface = netdev_priv(dev); + local = iface->local; + ++ if(dev->base_addr == 0) ++ { ++ printk(KERN_DEBUG "%s: IRQ before base_addr set\n", dev->name); ++ return IRQ_HANDLED; ++ } ++ + prism2_io_debug_add(dev, PRISM2_IO_DEBUG_CMD_INTERRUPT, 0, 0); + + if (local->func->card_present && !local->func->card_present(local)) { diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/connectplus-remove-ide-HACK.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/connectplus-remove-ide-HACK.patch new file mode 100644 index 0000000000..4414b21191 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/connectplus-remove-ide-HACK.patch @@ -0,0 +1,12 @@ +Index: linux-2.6.13/drivers/ide/legacy/ide-cs.c +=================================================================== +--- linux-2.6.13.orig/drivers/ide/legacy/ide-cs.c 2005-09-01 22:43:46.000000000 +0100 ++++ linux-2.6.13/drivers/ide/legacy/ide-cs.c 2005-09-01 22:45:46.000000000 +0100 +@@ -488,7 +488,6 @@ + PCMCIA_DEVICE_PROD_ID123("KODAK Picture Card ", "KODAK ", "V100K", 0x94a0d8f3, 0xe4fc3ea0, 0xe5e7eed4), + PCMCIA_DEVICE_PROD_ID1("STI Flash", 0xe4a13209), + PCMCIA_DEVICE_PROD_ID12("STI", "Flash 5.0", 0xbf2df18d, 0x8cb57a0e), +- PCMCIA_MFC_DEVICE_PROD_ID12(1, "SanDisk", "ConnectPlus", 0x7a954bd9, 0x74be00c6), + PCMCIA_DEVICE_NULL, + }; + MODULE_DEVICE_TABLE(pcmcia, ide_ids); diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/hostap-monitor-mode.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/hostap-monitor-mode.patch new file mode 100644 index 0000000000..641fd19e50 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/hostap-monitor-mode.patch @@ -0,0 +1,209 @@ +This is a patch that I've been maintaining for a few years, and I'd +really like to see it added to the mainstream zaurus kernel so I can +finally stop distributing my own. + +This patch only effects the card while in monitor mode, and does not +cause any known stability issues. + +http://patches.aircrack-ng.org/hostap-kernel-2.6.18.patch + +Rick Farina (Zero_Chaos) + +diff -ur linux-2.6.18-gentoo/drivers/net/wireless/hostap/hostap_80211_tx.c linux-2.6.18-gentoo-rawtx/drivers/net/wireless/hostap/hostap_80211_tx.c +--- linux-2.6.18-gentoo/drivers/net/wireless/hostap/hostap_80211_tx.c 2006-09-21 01:26:27.000000000 -0400 ++++ linux-2.6.18-gentoo-rawtx/drivers/net/wireless/hostap/hostap_80211_tx.c 2006-09-21 01:30:18.000000000 -0400 +@@ -69,6 +69,9 @@ + iface = netdev_priv(dev); + local = iface->local; + ++ if (local->iw_mode == IW_MODE_MONITOR) ++ goto xmit; ++ + if (skb->len < ETH_HLEN) { + printk(KERN_DEBUG "%s: hostap_data_start_xmit: short skb " + "(len=%d)\n", dev->name, skb->len); +@@ -234,6 +237,7 @@ + memcpy(skb_put(skb, ETH_ALEN), &hdr.addr4, ETH_ALEN); + } + ++xmit: + iface->stats.tx_packets++; + iface->stats.tx_bytes += skb->len; + +@@ -404,8 +408,6 @@ + } + + if (skb->len < 24) { +- printk(KERN_DEBUG "%s: hostap_master_start_xmit: short skb " +- "(len=%d)\n", dev->name, skb->len); + ret = 0; + iface->stats.tx_dropped++; + goto fail; +Only in linux-2.6.18-gentoo-rawtx/drivers/net/wireless/hostap: hostap_cs.c.orig +Only in linux-2.6.18-gentoo-rawtx/drivers/net/wireless/hostap: hostap_cs.c.rej +diff -ur linux-2.6.18-gentoo/drivers/net/wireless/hostap/hostap_hw.c linux-2.6.18-gentoo-rawtx/drivers/net/wireless/hostap/hostap_hw.c +--- linux-2.6.18-gentoo/drivers/net/wireless/hostap/hostap_hw.c 2006-09-21 01:26:27.000000000 -0400 ++++ linux-2.6.18-gentoo-rawtx/drivers/net/wireless/hostap/hostap_hw.c 2006-09-21 01:30:18.000000000 -0400 +@@ -1005,6 +1005,35 @@ + return fid; + } + ++static int prism2_monitor_enable(struct net_device *dev) ++{ ++ if (hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE, 5)) { ++ printk(KERN_DEBUG "Port type setting for monitor mode " ++ "failed\n"); ++ return -EOPNOTSUPP; ++ } ++ ++ if (hfa384x_cmd(dev, HFA384X_CMDCODE_TEST | (0x0a << 8), ++ 0, NULL, NULL)) { ++ printk(KERN_DEBUG "Could not enter testmode 0x0a\n"); ++ return -EOPNOTSUPP; ++ } ++ ++ if (hostap_set_word(dev, HFA384X_RID_CNFWEPFLAGS, ++ HFA384X_WEPFLAGS_PRIVACYINVOKED | ++ HFA384X_WEPFLAGS_HOSTENCRYPT | ++ HFA384X_WEPFLAGS_HOSTDECRYPT)) { ++ printk(KERN_DEBUG "WEP flags setting failed\n"); ++ return -EOPNOTSUPP; ++ } ++ ++ if (hostap_set_word(dev, HFA384X_RID_PROMISCUOUSMODE, 1)) { ++ printk(KERN_DEBUG "Could not set promiscuous mode\n"); ++ return -EOPNOTSUPP; ++ } ++ ++ return 0; ++} + + static int prism2_reset_port(struct net_device *dev) + { +@@ -1031,6 +1060,10 @@ + "port\n", dev->name); + } + ++ if (local->iw_mode == IW_MODE_MONITOR) ++ /* force mode 0x0a after port 0 reset */ ++ return prism2_monitor_enable(dev); ++ + /* It looks like at least some STA firmware versions reset + * fragmentation threshold back to 2346 after enable command. Restore + * the configured value, if it differs from this default. */ +@@ -1466,6 +1499,10 @@ + return 1; + } + ++ if (local->iw_mode == IW_MODE_MONITOR) ++ /* force mode 0x0a after port 0 reset */ ++ prism2_monitor_enable(dev); ++ + local->hw_ready = 1; + local->hw_reset_tries = 0; + local->hw_resetting = 0; +@@ -3156,6 +3193,7 @@ + local->func->hw_config = prism2_hw_config; + local->func->hw_reset = prism2_hw_reset; + local->func->hw_shutdown = prism2_hw_shutdown; ++ local->func->monitor_enable = prism2_monitor_enable; + local->func->reset_port = prism2_reset_port; + local->func->schedule_reset = prism2_schedule_reset; + #ifdef PRISM2_DOWNLOAD_SUPPORT +Only in linux-2.6.18-gentoo-rawtx/drivers/net/wireless/hostap: hostap_hw.c.orig +diff -ur linux-2.6.18-gentoo/drivers/net/wireless/hostap/hostap_ioctl.c linux-2.6.18-gentoo-rawtx/drivers/net/wireless/hostap/hostap_ioctl.c +--- linux-2.6.18-gentoo/drivers/net/wireless/hostap/hostap_ioctl.c 2006-09-21 01:26:27.000000000 -0400 ++++ linux-2.6.18-gentoo-rawtx/drivers/net/wireless/hostap/hostap_ioctl.c 2006-09-21 01:30:18.000000000 -0400 +@@ -1104,33 +1104,7 @@ + + printk(KERN_DEBUG "Enabling monitor mode\n"); + hostap_monitor_set_type(local); +- +- if (hostap_set_word(dev, HFA384X_RID_CNFPORTTYPE, +- HFA384X_PORTTYPE_PSEUDO_IBSS)) { +- printk(KERN_DEBUG "Port type setting for monitor mode " +- "failed\n"); +- return -EOPNOTSUPP; +- } +- +- /* Host decrypt is needed to get the IV and ICV fields; +- * however, monitor mode seems to remove WEP flag from frame +- * control field */ +- if (hostap_set_word(dev, HFA384X_RID_CNFWEPFLAGS, +- HFA384X_WEPFLAGS_HOSTENCRYPT | +- HFA384X_WEPFLAGS_HOSTDECRYPT)) { +- printk(KERN_DEBUG "WEP flags setting failed\n"); +- return -EOPNOTSUPP; +- } +- +- if (local->func->reset_port(dev) || +- local->func->cmd(dev, HFA384X_CMDCODE_TEST | +- (HFA384X_TEST_MONITOR << 8), +- 0, NULL, NULL)) { +- printk(KERN_DEBUG "Setting monitor mode failed\n"); +- return -EOPNOTSUPP; +- } +- +- return 0; ++ return local->func->reset_port(dev); + } + + +@@ -1199,7 +1173,7 @@ + local->iw_mode = *mode; + + if (local->iw_mode == IW_MODE_MONITOR) +- hostap_monitor_mode_enable(local); ++ return hostap_monitor_mode_enable(local); + else if (local->iw_mode == IW_MODE_MASTER && !local->host_encrypt && + !local->fw_encrypt_ok) { + printk(KERN_DEBUG "%s: defaulting to host-based encryption as " +diff -ur linux-2.6.18-gentoo/drivers/net/wireless/hostap/hostap_main.c linux-2.6.18-gentoo-rawtx/drivers/net/wireless/hostap/hostap_main.c +--- linux-2.6.18-gentoo/drivers/net/wireless/hostap/hostap_main.c 2006-09-21 01:26:27.000000000 -0400 ++++ linux-2.6.18-gentoo-rawtx/drivers/net/wireless/hostap/hostap_main.c 2006-09-21 01:30:18.000000000 -0400 +@@ -331,7 +331,7 @@ + if (local->iw_mode == IW_MODE_REPEAT) + return HFA384X_PORTTYPE_WDS; + if (local->iw_mode == IW_MODE_MONITOR) +- return HFA384X_PORTTYPE_PSEUDO_IBSS; ++ return 5; /*HFA384X_PORTTYPE_PSEUDO_IBSS;*/ + return HFA384X_PORTTYPE_HOSTAP; + } + +Only in linux-2.6.18-gentoo-rawtx/drivers/net/wireless/hostap: hostap_main.c.orig +diff -ur linux-2.6.18-gentoo/drivers/net/wireless/hostap/hostap_pci.c linux-2.6.18-gentoo-rawtx/drivers/net/wireless/hostap/hostap_pci.c +--- linux-2.6.18-gentoo/drivers/net/wireless/hostap/hostap_pci.c 2006-09-21 01:26:27.000000000 -0400 ++++ linux-2.6.18-gentoo-rawtx/drivers/net/wireless/hostap/hostap_pci.c 2006-09-21 01:30:18.000000000 -0400 +@@ -48,6 +48,8 @@ + { 0x1260, 0x3873, PCI_ANY_ID, PCI_ANY_ID }, + /* Samsung MagicLAN SWL-2210P */ + { 0x167d, 0xa000, PCI_ANY_ID, PCI_ANY_ID }, ++ /* NETGEAR MA311 */ ++ { 0x1385, 0x3872, PCI_ANY_ID, PCI_ANY_ID }, + { 0 } + }; + +Only in linux-2.6.18-gentoo-rawtx/drivers/net/wireless/hostap: hostap_pci.c.orig +diff -ur linux-2.6.18-gentoo/drivers/net/wireless/hostap/hostap_plx.c linux-2.6.18-gentoo-rawtx/drivers/net/wireless/hostap/hostap_plx.c +--- linux-2.6.18-gentoo/drivers/net/wireless/hostap/hostap_plx.c 2006-09-21 01:26:27.000000000 -0400 ++++ linux-2.6.18-gentoo-rawtx/drivers/net/wireless/hostap/hostap_plx.c 2006-09-21 01:30:18.000000000 -0400 +@@ -101,6 +101,7 @@ + { 0xc250, 0x0002 } /* EMTAC A2424i */, + { 0xd601, 0x0002 } /* Z-Com XI300 */, + { 0xd601, 0x0005 } /* Zcomax XI-325H 200mW */, ++ { 0xd601, 0x0010 } /* Zcomax XI-325H 100mW */, + { 0, 0} + }; + +Only in linux-2.6.18-gentoo-rawtx/drivers/net/wireless/hostap: hostap_plx.c.orig +diff -ur linux-2.6.18-gentoo/drivers/net/wireless/hostap/hostap_wlan.h linux-2.6.18-gentoo-rawtx/drivers/net/wireless/hostap/hostap_wlan.h +--- linux-2.6.18-gentoo/drivers/net/wireless/hostap/hostap_wlan.h 2006-09-21 01:26:27.000000000 -0400 ++++ linux-2.6.18-gentoo-rawtx/drivers/net/wireless/hostap/hostap_wlan.h 2006-09-21 01:30:18.000000000 -0400 +@@ -575,6 +575,7 @@ + int (*hw_config)(struct net_device *dev, int initial); + void (*hw_reset)(struct net_device *dev); + void (*hw_shutdown)(struct net_device *dev, int no_disable); ++ int (*monitor_enable)(struct net_device *dev); + int (*reset_port)(struct net_device *dev); + void (*schedule_reset)(local_info_t *local); + int (*download)(local_info_t *local, diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/hrw-hostapcard.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/hrw-hostapcard.patch new file mode 100644 index 0000000000..67fc5b7c70 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/hrw-hostapcard.patch @@ -0,0 +1,34 @@ + +From: Marcin Juszkiewicz <openembedded@haerwu.biz> + +Card reported by Ångström user: +http://bugs.openembedded.net/show_bug.cgi?id=3236 + +Socket 1: + product info: "Wireless LAN", "11Mbps PC Card", "Version 01.02", "" + manfid: 0x0156, 0x0002 + function: 6 (network) + +Signed-off-by: Marcin Juszkiewicz <openembedded@haerwu.biz> +Acked-by: Pavel Roskin <proski@gnu.org> + +--- + drivers/net/wireless/hostap/hostap_cs.c | 3 +++ + 1 file changed, 3 insertions(+) + +--- linux-2.6.24.orig/drivers/net/wireless/hostap/hostap_cs.c ++++ linux-2.6.24/drivers/net/wireless/hostap/hostap_cs.c +@@ -892,10 +892,13 @@ static struct pcmcia_device_id hostap_cs + 0xa21501a, 0x59868926, 0xc9049a39), + PCMCIA_DEVICE_PROD_ID1234( + "The Linksys Group, Inc.", "Wireless Network CF Card", "ISL37300P", + "RevA", + 0xa5f472c2, 0x9c05598d, 0xc9049a39, 0x57a66194), ++ PCMCIA_DEVICE_PROD_ID123( ++ "Wireless LAN" , "11Mbps PC Card", "Version 01.02", ++ 0x4b8870ff, 0x70e946d1, 0x4b74baa0), + PCMCIA_DEVICE_NULL + }; + MODULE_DEVICE_TABLE(pcmcia, hostap_cs_ids); + + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/htcuni-acx.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/htcuni-acx.patch new file mode 100644 index 0000000000..1ccebddc8d --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/htcuni-acx.patch @@ -0,0 +1,33527 @@ +--- + drivers/net/wireless/Kconfig | 31 + drivers/net/wireless/Makefile | 2 + drivers/net/wireless/acx/Kconfig | 113 + drivers/net/wireless/acx/Makefile | 21 + drivers/net/wireless/acx/acx.h | 14 + drivers/net/wireless/acx/acx_config.h | 50 + drivers/net/wireless/acx/acx_func.h | 710 ++ + drivers/net/wireless/acx/acx_hw.h | 18 + drivers/net/wireless/acx/acx_struct.h | 2114 ++++++++ + drivers/net/wireless/acx/common.c | 7388 ++++++++++++++++++++++++++++ + drivers/net/wireless/acx/conv.c | 504 + + drivers/net/wireless/acx/cs.c | 5703 +++++++++++++++++++++ + drivers/net/wireless/acx/htcsable_acx.c | 118 + drivers/net/wireless/acx/htcuniversal_acx.c | 108 + drivers/net/wireless/acx/hx4700_acx.c | 108 + drivers/net/wireless/acx/ioctl.c | 2748 ++++++++++ + drivers/net/wireless/acx/mem.c | 5363 ++++++++++++++++++++ + drivers/net/wireless/acx/pci.c | 4234 ++++++++++++++++ + drivers/net/wireless/acx/rx3000_acx.c | 110 + drivers/net/wireless/acx/setrate.c | 213 + drivers/net/wireless/acx/usb.c | 1922 +++++++ + drivers/net/wireless/acx/wlan.c | 424 + + drivers/net/wireless/acx/wlan_compat.h | 260 + drivers/net/wireless/acx/wlan_hdr.h | 497 + + drivers/net/wireless/acx/wlan_mgmt.h | 582 ++ + 25 files changed, 33355 insertions(+) + +Index: linux-2.6.23/drivers/net/wireless/acx/acx_config.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.23/drivers/net/wireless/acx/acx_config.h 2008-01-20 21:13:40.000000000 +0000 +@@ -0,0 +1,50 @@ ++#define ACX_RELEASE "v0.3.36" ++ ++/* ++ * Test out all the channels in reg domain 0x10 ++ */ ++#define ACX_ALLOW_ALLCHANNELS ++ ++/* set to 0 if you don't want any debugging code to be compiled in */ ++/* set to 1 if you want some debugging */ ++/* set to 2 if you want extensive debug log */ ++#define ACX_DEBUG 0 ++ ++/* ++ * Since we'll be changing channels a lot ++#define ACX_DEFAULT_MSG (L_ASSOC|L_INIT) ++*/ ++#define ACX_DEFAULT_MSG (L_ASSOC|L_INIT) ++ ++/* assume 32bit I/O width ++ * (16bit is also compatible with Compact Flash) */ ++#define ACX_IO_WIDTH 32 ++ ++/* Set this to 1 if you want monitor mode to use ++ * phy header. Currently it is not useful anyway since we ++ * don't know what useful info (if any) is in phy header. ++ * If you want faster/smaller code, say 0 here */ ++#define WANT_PHY_HDR 0 ++ ++/* whether to do Tx descriptor cleanup in softirq (i.e. not in IRQ ++ * handler) or not. Note that doing it later does slightly increase ++ * system load, so still do that stuff in the IRQ handler for now, ++ * even if that probably means worse latency */ ++#define TX_CLEANUP_IN_SOFTIRQ 0 ++ ++/* if you want very experimental 802.11 power save mode features */ ++#define POWER_SAVE_80211 0 ++ ++/* if you want very early packet fragmentation bits and pieces */ ++#define ACX_FRAGMENTATION 0 ++ ++/* Locking: */ ++/* very talkative */ ++/* #define PARANOID_LOCKING 1 */ ++/* normal (use when bug-free) */ ++#define DO_LOCKING 1 ++/* else locking is disabled! */ ++ ++/* 0 - normal mode */ ++/* 1 - development/debug: probe for IEs on modprobe */ ++#define CMD_DISCOVERY 0 +Index: linux-2.6.23/drivers/net/wireless/acx/acx_func.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.23/drivers/net/wireless/acx/acx_func.h 2008-01-20 21:13:40.000000000 +0000 +@@ -0,0 +1,710 @@ ++/*********************************************************************** ++** Copyright (C) 2003 ACX100 Open Source Project ++** ++** The contents of this file are subject to the Mozilla Public ++** License Version 1.1 (the "License"); you may not use this file ++** except in compliance with the License. You may obtain a copy of ++** the License at http://www.mozilla.org/MPL/ ++** ++** Software distributed under the License is distributed on an "AS ++** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or ++** implied. See the License for the specific language governing ++** rights and limitations under the License. ++** ++** Alternatively, the contents of this file may be used under the ++** terms of the GNU Public License version 2 (the "GPL"), in which ++** case the provisions of the GPL are applicable instead of the ++** above. If you wish to allow the use of your version of this file ++** only under the terms of the GPL and not to allow others to use ++** your version of this file under the MPL, indicate your decision ++** by deleting the provisions above and replace them with the notice ++** and other provisions required by the GPL. If you do not delete ++** the provisions above, a recipient may use your version of this ++** file under either the MPL or the GPL. ++** --------------------------------------------------------------------- ++** Inquiries regarding the ACX100 Open Source Project can be ++** made directly to: ++** ++** acx100-users@lists.sf.net ++** http://acx100.sf.net ++** --------------------------------------------------------------------- ++*/ ++ ++ ++/*********************************************************************** ++** LOGGING ++** ++** - Avoid SHOUTING needlessly. Avoid excessive verbosity. ++** Gradually remove messages which are old debugging aids. ++** ++** - Use printk() for messages which are to be always logged. ++** Supply either 'acx:' or '<devname>:' prefix so that user ++** can figure out who's speaking among other kernel chatter. ++** acx: is for general issues (e.g. "acx: no firmware image!") ++** while <devname>: is related to a particular device ++** (think about multi-card setup). Double check that message ++** is not confusing to the average user. ++** ++** - use printk KERN_xxx level only if message is not a WARNING ++** but is INFO, ERR etc. ++** ++** - Use printk_ratelimited() for messages which may flood ++** (e.g. "rx DUP pkt!"). ++** ++** - Use log() for messages which may be omitted (and they ++** _will_ be omitted in non-debug builds). Note that ++** message levels may be disabled at compile-time selectively, ++** thus select them wisely. Example: L_DEBUG is the lowest ++** (most likely to be compiled out) -> use for less important stuff. ++** ++** - Do not print important stuff with log(), or else people ++** will never build non-debug driver. ++** ++** Style: ++** hex: capital letters, zero filled (e.g. 0x02AC) ++** str: dont start from capitals, no trailing periods ("tx: queue is stopped") ++*/ ++#if ACX_DEBUG > 1 ++ ++void log_fn_enter(const char *funcname); ++void log_fn_exit(const char *funcname); ++void log_fn_exit_v(const char *funcname, int v); ++ ++#define FN_ENTER \ ++ do { \ ++ if (unlikely(acx_debug & L_FUNC)) { \ ++ log_fn_enter(__func__); \ ++ } \ ++ } while (0) ++ ++#define FN_EXIT1(v) \ ++ do { \ ++ if (unlikely(acx_debug & L_FUNC)) { \ ++ log_fn_exit_v(__func__, v); \ ++ } \ ++ } while (0) ++#define FN_EXIT0 \ ++ do { \ ++ if (unlikely(acx_debug & L_FUNC)) { \ ++ log_fn_exit(__func__); \ ++ } \ ++ } while (0) ++ ++#else ++ ++#define FN_ENTER ++#define FN_EXIT1(v) ++#define FN_EXIT0 ++ ++#endif /* ACX_DEBUG > 1 */ ++ ++ ++#if ACX_DEBUG ++ ++#define log(chan, args...) \ ++ do { \ ++ if (acx_debug & (chan)) \ ++ printk(KERN_DEBUG args); \ ++ } while (0) ++#define printk_ratelimited(args...) printk(args) ++ ++#else /* Non-debug build: */ ++ ++#define log(chan, args...) ++/* Standard way of log flood prevention */ ++#define printk_ratelimited(args...) \ ++do { \ ++ if (printk_ratelimit()) \ ++ printk(args); \ ++} while (0) ++ ++#endif /* ACX_DEBUG */ ++ ++void acx_print_mac(const char *head, const u8 *mac, const char *tail); ++ ++/* Optimized out to nothing in non-debug build */ ++static inline void ++acxlog_mac(int level, const char *head, const u8 *mac, const char *tail) ++{ ++ if (acx_debug & level) { ++ acx_print_mac(head, mac, tail); ++ } ++} ++ ++ ++/*********************************************************************** ++** MAC address helpers ++*/ ++static inline void ++MAC_COPY(u8 *mac, const u8 *src) ++{ ++ *(u32*)mac = *(u32*)src; ++ ((u16*)mac)[2] = ((u16*)src)[2]; ++ /* kernel's memcpy will do the same: memcpy(dst, src, ETH_ALEN); */ ++} ++ ++static inline void ++MAC_FILL(u8 *mac, u8 val) ++{ ++ memset(mac, val, ETH_ALEN); ++} ++ ++static inline void ++MAC_BCAST(u8 *mac) ++{ ++ ((u16*)mac)[2] = *(u32*)mac = -1; ++} ++ ++static inline void ++MAC_ZERO(u8 *mac) ++{ ++ ((u16*)mac)[2] = *(u32*)mac = 0; ++} ++ ++static inline int ++mac_is_equal(const u8 *a, const u8 *b) ++{ ++ /* can't beat this */ ++ return memcmp(a, b, ETH_ALEN) == 0; ++} ++ ++static inline int ++mac_is_bcast(const u8 *mac) ++{ ++ /* AND together 4 first bytes with sign-extended 2 last bytes ++ ** Only bcast address gives 0xffffffff. +1 gives 0 */ ++ return ( *(s32*)mac & ((s16*)mac)[2] ) + 1 == 0; ++} ++ ++static inline int ++mac_is_zero(const u8 *mac) ++{ ++ return ( *(u32*)mac | ((u16*)mac)[2] ) == 0; ++} ++ ++static inline int ++mac_is_directed(const u8 *mac) ++{ ++ return (mac[0] & 1)==0; ++} ++ ++static inline int ++mac_is_mcast(const u8 *mac) ++{ ++ return (mac[0] & 1) && !mac_is_bcast(mac); ++} ++ ++#define MACSTR "%02X:%02X:%02X:%02X:%02X:%02X" ++#define MAC(bytevector) \ ++ ((unsigned char *)bytevector)[0], \ ++ ((unsigned char *)bytevector)[1], \ ++ ((unsigned char *)bytevector)[2], \ ++ ((unsigned char *)bytevector)[3], \ ++ ((unsigned char *)bytevector)[4], \ ++ ((unsigned char *)bytevector)[5] ++ ++ ++/*********************************************************************** ++** Random helpers ++*/ ++#define TO_STRING(x) #x ++#define STRING(x) TO_STRING(x) ++ ++#define CLEAR_BIT(val, mask) ((val) &= ~(mask)) ++#define SET_BIT(val, mask) ((val) |= (mask)) ++ ++/* undefined if v==0 */ ++static inline unsigned int ++lowest_bit(u16 v) ++{ ++ unsigned int n = 0; ++ while (!(v & 0xf)) { v>>=4; n+=4; } ++ while (!(v & 1)) { v>>=1; n++; } ++ return n; ++} ++ ++/* undefined if v==0 */ ++static inline unsigned int ++highest_bit(u16 v) ++{ ++ unsigned int n = 0; ++ while (v>0xf) { v>>=4; n+=4; } ++ while (v>1) { v>>=1; n++; } ++ return n; ++} ++ ++/* undefined if v==0 */ ++static inline int ++has_only_one_bit(u16 v) ++{ ++ return ((v-1) ^ v) >= v; ++} ++ ++ ++static inline int ++is_hidden_essid(char *essid) ++{ ++ return (('\0' == essid[0]) || ++ ((' ' == essid[0]) && ('\0' == essid[1]))); ++} ++ ++/*********************************************************************** ++** LOCKING ++** We have adev->sem and adev->lock. ++** ++** We employ following naming convention in order to get locking right: ++** ++** acx_e_xxxx - external entry points called from process context. ++** It is okay to sleep. adev->sem is to be taken on entry. ++** acx_i_xxxx - external entry points possibly called from atomic context. ++** Sleeping is not allowed (and thus down(sem) is not legal!) ++** acx_s_xxxx - potentially sleeping functions. Do not ever call under lock! ++** acx_l_xxxx - functions which expect lock to be already taken. ++** rest - non-sleeping functions which do not require locking ++** but may be run under lock ++** ++** A small number of local helpers do not have acx_[eisl]_ prefix. ++** They are always close to caller and are to be reviewed locally. ++** ++** Theory of operation: ++** ++** All process-context entry points (_e_ functions) take sem ++** immediately. IRQ handler and other 'atomic-context' entry points ++** (_i_ functions) take lock immediately on entry, but dont take sem ++** because that might sleep. ++** ++** Thus *all* code is either protected by sem or lock, or both. ++** ++** Code which must not run concurrently with IRQ takes lock. ++** Such code is marked with _l_. ++** ++** This results in the following rules of thumb useful in code review: ++** ++** + If a function calls _s_ fn, it must be an _s_ itself. ++** + You can call _l_ fn only (a) from another _l_ fn ++** or (b) from _s_, _e_ or _i_ fn by taking lock, calling _l_, ++** and dropping lock. ++** + All IRQ code runs under lock. ++** + Any _s_ fn is running under sem. ++** + Code under sem can race only with IRQ code. ++** + Code under sem+lock cannot race with anything. ++*/ ++ ++/* These functions *must* be inline or they will break horribly on SPARC, due ++ * to its weird semantics for save/restore flags */ ++ ++#if defined(PARANOID_LOCKING) /* Lock debugging */ ++ ++void acx_lock_debug(acx_device_t *adev, const char* where); ++void acx_unlock_debug(acx_device_t *adev, const char* where); ++void acx_down_debug(acx_device_t *adev, const char* where); ++void acx_up_debug(acx_device_t *adev, const char* where); ++void acx_lock_unhold(void); ++void acx_sem_unhold(void); ++ ++static inline void ++acx_lock_helper(acx_device_t *adev, unsigned long *fp, const char* where) ++{ ++ acx_lock_debug(adev, where); ++ spin_lock_irqsave(&adev->lock, *fp); ++} ++static inline void ++acx_unlock_helper(acx_device_t *adev, unsigned long *fp, const char* where) ++{ ++ acx_unlock_debug(adev, where); ++ spin_unlock_irqrestore(&adev->lock, *fp); ++} ++static inline void ++acx_down_helper(acx_device_t *adev, const char* where) ++{ ++ acx_down_debug(adev, where); ++} ++static inline void ++acx_up_helper(acx_device_t *adev, const char* where) ++{ ++ acx_up_debug(adev, where); ++} ++#define acx_lock(adev, flags) acx_lock_helper(adev, &(flags), __FILE__ ":" STRING(__LINE__)) ++#define acx_unlock(adev, flags) acx_unlock_helper(adev, &(flags), __FILE__ ":" STRING(__LINE__)) ++#define acx_sem_lock(adev) acx_down_helper(adev, __FILE__ ":" STRING(__LINE__)) ++#define acx_sem_unlock(adev) acx_up_helper(adev, __FILE__ ":" STRING(__LINE__)) ++ ++#elif defined(DO_LOCKING) ++ ++#define acx_lock(adev, flags) spin_lock_irqsave(&adev->lock, flags) ++#define acx_unlock(adev, flags) spin_unlock_irqrestore(&adev->lock, flags) ++#define acx_sem_lock(adev) down(&adev->sem) ++#define acx_sem_unlock(adev) up(&adev->sem) ++#define acx_lock_unhold() ((void)0) ++#define acx_sem_unhold() ((void)0) ++ ++#else /* no locking! :( */ ++ ++#define acx_lock(adev, flags) ((void)0) ++#define acx_unlock(adev, flags) ((void)0) ++#define acx_sem_lock(adev) ((void)0) ++#define acx_sem_unlock(adev) ((void)0) ++#define acx_lock_unhold() ((void)0) ++#define acx_sem_unhold() ((void)0) ++ ++#endif ++ ++ ++/*********************************************************************** ++*/ ++ ++/* Can race with rx path (which is not protected by sem): ++** rx -> process_[re]assocresp() -> set_status(ASSOCIATED) -> wake_queue() ++** Can race with tx_complete IRQ: ++** IRQ -> acxpci_l_clean_txdesc -> acx_wake_queue ++** Review carefully all callsites */ ++static inline void ++acx_stop_queue(struct net_device *ndev, const char *msg) ++{ ++ if (netif_queue_stopped(ndev)) ++ return; ++ ++ netif_stop_queue(ndev); ++ if (msg) ++ log(L_BUFT, "tx: stop queue %s\n", msg); ++} ++ ++static inline int ++acx_queue_stopped(struct net_device *ndev) ++{ ++ return netif_queue_stopped(ndev); ++} ++ ++/* ++static inline void ++acx_start_queue(struct net_device *ndev, const char *msg) ++{ ++ netif_start_queue(ndev); ++ if (msg) ++ log(L_BUFT, "tx: start queue %s\n", msg); ++} ++*/ ++ ++static inline void ++acx_wake_queue(struct net_device *ndev, const char *msg) ++{ ++ netif_wake_queue(ndev); ++ if (msg) ++ log(L_BUFT, "tx: wake queue %s\n", msg); ++} ++ ++static inline void ++acx_carrier_off(struct net_device *ndev, const char *msg) ++{ ++ netif_carrier_off(ndev); ++ if (msg) ++ log(L_BUFT, "tx: carrier off %s\n", msg); ++} ++ ++static inline void ++acx_carrier_on(struct net_device *ndev, const char *msg) ++{ ++ netif_carrier_on(ndev); ++ if (msg) ++ log(L_BUFT, "tx: carrier on %s\n", msg); ++} ++ ++/* This function does not need locking UNLESS you call it ++** as acx_set_status(ACX_STATUS_4_ASSOCIATED), bacause this can ++** wake queue. This can race with stop_queue elsewhere. */ ++void acx_set_status(acx_device_t *adev, u16 status); ++ ++ ++/*********************************************************************** ++** Communication with firmware ++*/ ++#define CMD_TIMEOUT_MS(n) (n) ++#define ACX_CMD_TIMEOUT_DEFAULT CMD_TIMEOUT_MS(50) ++ ++#if ACX_DEBUG ++ ++/* We want to log cmd names */ ++int acxpci_s_issue_cmd_timeo_debug(acx_device_t *adev, unsigned cmd, void *param, unsigned len, unsigned timeout, const char* cmdstr); ++int acxmem_s_issue_cmd_timeo_debug(acx_device_t *adev, unsigned cmd, void *param, unsigned len, unsigned timeout, const char* cmdstr); ++int acxusb_s_issue_cmd_timeo_debug(acx_device_t *adev, unsigned cmd, void *param, unsigned len, unsigned timeout, const char* cmdstr); ++static inline int ++acx_s_issue_cmd_timeo_debug(acx_device_t *adev, unsigned cmd, void *param, unsigned len, unsigned timeout, const char* cmdstr) ++{ ++ if (IS_MEM(adev)) ++ return acxmem_s_issue_cmd_timeo_debug(adev, cmd, param, len, timeout, cmdstr); ++ if (IS_PCI(adev)) ++ return acxpci_s_issue_cmd_timeo_debug(adev, cmd, param, len, timeout, cmdstr); ++ return acxusb_s_issue_cmd_timeo_debug(adev, cmd, param, len, timeout, cmdstr); ++} ++#define acx_s_issue_cmd(adev,cmd,param,len) \ ++ acx_s_issue_cmd_timeo_debug(adev,cmd,param,len,ACX_CMD_TIMEOUT_DEFAULT,#cmd) ++#define acx_s_issue_cmd_timeo(adev,cmd,param,len,timeo) \ ++ acx_s_issue_cmd_timeo_debug(adev,cmd,param,len,timeo,#cmd) ++int acx_s_configure_debug(acx_device_t *adev, void *pdr, int type, const char* str); ++#define acx_s_configure(adev,pdr,type) \ ++ acx_s_configure_debug(adev,pdr,type,#type) ++int acx_s_interrogate_debug(acx_device_t *adev, void *pdr, int type, const char* str); ++#define acx_s_interrogate(adev,pdr,type) \ ++ acx_s_interrogate_debug(adev,pdr,type,#type) ++ ++#else ++ ++int acxpci_s_issue_cmd_timeo(acx_device_t *adev, unsigned cmd, void *param, unsigned len, unsigned timeout); ++int acxmem_s_issue_cmd_timeo(acx_device_t *adev, unsigned cmd, void *param, unsigned len, unsigned timeout); ++int acxusb_s_issue_cmd_timeo(acx_device_t *adev, unsigned cmd, void *param, unsigned len, unsigned timeout); ++static inline int ++acx_s_issue_cmd_timeo(acx_device_t *adev, unsigned cmd, void *param, unsigned len, unsigned timeout) ++{ ++ if (IS_MEM(adev)) ++ return acxmem_s_issue_cmd_timeo(adev, cmd, param, len, timeout); ++ if (IS_PCI(adev)) ++ return acxpci_s_issue_cmd_timeo(adev, cmd, param, len, timeout); ++ return acxusb_s_issue_cmd_timeo(adev, cmd, param, len, timeout); ++} ++static inline int ++acx_s_issue_cmd(acx_device_t *adev, unsigned cmd, void *param, unsigned len) ++{ ++ if (IS_MEM(adev)) ++ return acxmem_s_issue_cmd_timeo(adev, cmd, param, len, ACX_CMD_TIMEOUT_DEFAULT); ++ if (IS_PCI(adev)) ++ return acxpci_s_issue_cmd_timeo(adev, cmd, param, len, ACX_CMD_TIMEOUT_DEFAULT); ++ return acxusb_s_issue_cmd_timeo(adev, cmd, param, len, ACX_CMD_TIMEOUT_DEFAULT); ++} ++int acx_s_configure(acx_device_t *adev, void *pdr, int type); ++int acx_s_interrogate(acx_device_t *adev, void *pdr, int type); ++ ++#endif ++ ++void acx_s_cmd_start_scan(acx_device_t *adev); ++ ++ ++/*********************************************************************** ++** Ioctls ++*/ ++int ++acx111pci_ioctl_info( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ struct iw_param *vwrq, ++ char *extra); ++int ++acx100pci_ioctl_set_phy_amp_bias( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ struct iw_param *vwrq, ++ char *extra); ++int ++acx100mem_ioctl_set_phy_amp_bias( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ struct iw_param *vwrq, ++ char *extra); ++ ++ ++/*********************************************************************** ++** /proc ++*/ ++#ifdef CONFIG_PROC_FS ++int acx_proc_register_entries(const struct net_device *ndev); ++int acx_proc_unregister_entries(const struct net_device *ndev); ++#else ++static inline int ++acx_proc_register_entries(const struct net_device *ndev) { return OK; } ++static inline int ++acx_proc_unregister_entries(const struct net_device *ndev) { return OK; } ++#endif ++ ++ ++/*********************************************************************** ++*/ ++firmware_image_t *acx_s_read_fw(struct device *dev, const char *file, u32 *size); ++int acxpci_s_upload_radio(acx_device_t *adev); ++int acxmem_s_upload_radio(acx_device_t *adev); ++ ++ ++/*********************************************************************** ++** Unsorted yet :) ++*/ ++int acxpci_s_read_phy_reg(acx_device_t *adev, u32 reg, u8 *charbuf); ++int acxmem_s_read_phy_reg(acx_device_t *adev, u32 reg, u8 *charbuf); ++int acxusb_s_read_phy_reg(acx_device_t *adev, u32 reg, u8 *charbuf); ++static inline int ++acx_s_read_phy_reg(acx_device_t *adev, u32 reg, u8 *charbuf) ++{ ++ if (IS_MEM(adev)) ++ return acxmem_s_read_phy_reg(adev, reg, charbuf); ++ if (IS_PCI(adev)) ++ return acxpci_s_read_phy_reg(adev, reg, charbuf); ++ return acxusb_s_read_phy_reg(adev, reg, charbuf); ++} ++ ++int acxpci_s_write_phy_reg(acx_device_t *adev, u32 reg, u8 value); ++int acxmem_s_write_phy_reg(acx_device_t *adev, u32 reg, u8 value); ++int acxusb_s_write_phy_reg(acx_device_t *adev, u32 reg, u8 value); ++static inline int ++acx_s_write_phy_reg(acx_device_t *adev, u32 reg, u8 value) ++{ ++ if (IS_MEM(adev)) ++ return acxmem_s_write_phy_reg(adev, reg, value); ++ if (IS_PCI(adev)) ++ return acxpci_s_write_phy_reg(adev, reg, value); ++ return acxusb_s_write_phy_reg(adev, reg, value); ++} ++ ++tx_t* acxpci_l_alloc_tx(acx_device_t *adev); ++tx_t* acxmem_l_alloc_tx(acx_device_t *adev); ++tx_t* acxusb_l_alloc_tx(acx_device_t *adev); ++static inline tx_t* ++acx_l_alloc_tx(acx_device_t *adev) ++{ ++ if (IS_MEM(adev)) ++ return acxmem_l_alloc_tx(adev); ++ if (IS_PCI(adev)) ++ return acxpci_l_alloc_tx(adev); ++ return acxusb_l_alloc_tx(adev); ++} ++ ++void acxusb_l_dealloc_tx(tx_t *tx_opaque); ++void acxmem_l_dealloc_tx(acx_device_t *adev, tx_t *tx_opaque); ++static inline void ++acx_l_dealloc_tx(acx_device_t *adev, tx_t *tx_opaque) ++{ ++#ifdef ACX_MEM ++ acxmem_l_dealloc_tx (adev, tx_opaque); ++#else ++ if (IS_USB(adev)) ++ acxusb_l_dealloc_tx(tx_opaque); ++#endif ++} ++ ++void* acxpci_l_get_txbuf(acx_device_t *adev, tx_t *tx_opaque); ++void* acxmem_l_get_txbuf(acx_device_t *adev, tx_t *tx_opaque); ++void* acxusb_l_get_txbuf(acx_device_t *adev, tx_t *tx_opaque); ++static inline void* ++acx_l_get_txbuf(acx_device_t *adev, tx_t *tx_opaque) ++{ ++#if defined (ACX_MEM) ++ return acxmem_l_get_txbuf(adev, tx_opaque); ++#else ++ if (IS_PCI(adev)) ++ return acxpci_l_get_txbuf(adev, tx_opaque); ++ return acxusb_l_get_txbuf(adev, tx_opaque); ++#endif ++} ++ ++void acxpci_l_tx_data(acx_device_t *adev, tx_t *tx_opaque, int len); ++void acxmem_l_tx_data(acx_device_t *adev, tx_t *tx_opaque, int len); ++void acxusb_l_tx_data(acx_device_t *adev, tx_t *tx_opaque, int len); ++static inline void ++acx_l_tx_data(acx_device_t *adev, tx_t *tx_opaque, int len) ++{ ++#if defined (ACX_MEM) ++ acxmem_l_tx_data(adev, tx_opaque, len); ++#else ++ if (IS_PCI(adev)) ++ acxpci_l_tx_data(adev, tx_opaque, len); ++ else ++ acxusb_l_tx_data(adev, tx_opaque, len); ++#endif ++} ++ ++static inline wlan_hdr_t* ++acx_get_wlan_hdr(acx_device_t *adev, const rxbuffer_t *rxbuf) ++{ ++ return (wlan_hdr_t*)((u8*)&rxbuf->hdr_a3 + adev->phy_header_len); ++} ++ ++void acxpci_l_power_led(acx_device_t *adev, int enable); ++int acxpci_read_eeprom_byte(acx_device_t *adev, u32 addr, u8 *charbuf); ++unsigned int acxpci_l_clean_txdesc(acx_device_t *adev); ++void acxpci_l_clean_txdesc_emergency(acx_device_t *adev); ++int acxpci_s_create_hostdesc_queues(acx_device_t *adev); ++void acxpci_create_desc_queues(acx_device_t *adev, u32 tx_queue_start, u32 rx_queue_start); ++void acxpci_free_desc_queues(acx_device_t *adev); ++char* acxpci_s_proc_diag_output(char *p, acx_device_t *adev); ++int acxpci_proc_eeprom_output(char *p, acx_device_t *adev); ++void acxpci_set_interrupt_mask(acx_device_t *adev); ++int acx100pci_s_set_tx_level(acx_device_t *adev, u8 level_dbm); ++ ++void acxmem_l_power_led(acx_device_t *adev, int enable); ++int acxmem_read_eeprom_byte(acx_device_t *adev, u32 addr, u8 *charbuf); ++unsigned int acxmem_l_clean_txdesc(acx_device_t *adev); ++void acxmem_l_clean_txdesc_emergency(acx_device_t *adev); ++int acxmem_s_create_hostdesc_queues(acx_device_t *adev); ++void acxmem_create_desc_queues(acx_device_t *adev, u32 tx_queue_start, u32 rx_queue_start); ++void acxmem_free_desc_queues(acx_device_t *adev); ++char* acxmem_s_proc_diag_output(char *p, acx_device_t *adev); ++int acxmem_proc_eeprom_output(char *p, acx_device_t *adev); ++void acxmem_set_interrupt_mask(acx_device_t *adev); ++int acx100mem_s_set_tx_level(acx_device_t *adev, u8 level_dbm); ++ ++void acx_s_msleep(int ms); ++int acx_s_init_mac(acx_device_t *adev); ++void acx_set_reg_domain(acx_device_t *adev, unsigned char reg_dom_id); ++void acx_set_timer(acx_device_t *adev, int timeout_us); ++void acx_update_capabilities(acx_device_t *adev); ++void acx_s_start(acx_device_t *adev); ++ ++void acx_s_update_card_settings(acx_device_t *adev); ++void acx_s_parse_configoption(acx_device_t *adev, const acx111_ie_configoption_t *pcfg); ++void acx_l_update_ratevector(acx_device_t *adev); ++ ++void acx_init_task_scheduler(acx_device_t *adev); ++void acx_schedule_task(acx_device_t *adev, unsigned int set_flag); ++ ++int acx_e_ioctl_old(struct net_device *ndev, struct ifreq *ifr, int cmd); ++ ++client_t *acx_l_sta_list_get(acx_device_t *adev, const u8 *address); ++void acx_l_sta_list_del(acx_device_t *adev, client_t *clt); ++ ++int acx_l_transmit_disassoc(acx_device_t *adev, client_t *clt); ++void acx_i_timer(unsigned long a); ++int acx_s_complete_scan(acx_device_t *adev); ++ ++struct sk_buff *acx_rxbuf_to_ether(acx_device_t *adev, rxbuffer_t *rxbuf); ++int acx_ether_to_txbuf(acx_device_t *adev, void *txbuf, const struct sk_buff *skb); ++ ++u8 acx_signal_determine_quality(u8 signal, u8 noise); ++ ++void acx_l_process_rxbuf(acx_device_t *adev, rxbuffer_t *rxbuf); ++void acx_l_handle_txrate_auto(acx_device_t *adev, struct client *txc, ++ u16 intended_rate, u8 rate100, u16 rate111, u8 error, ++ int pkts_to_ignore); ++ ++void acx_dump_bytes(const void *, int); ++void acx_log_bad_eid(wlan_hdr_t* hdr, int len, wlan_ie_t* ie_ptr); ++ ++u8 acx_rate111to100(u16); ++ ++void acx_s_set_defaults(acx_device_t *adev); ++ ++#if !ACX_DEBUG ++static inline const char* acx_get_packet_type_string(u16 fc) { return ""; } ++#else ++const char* acx_get_packet_type_string(u16 fc); ++#endif ++const char* acx_cmd_status_str(unsigned int state); ++ ++int acx_i_start_xmit(struct sk_buff *skb, struct net_device *ndev); ++ ++void great_inquisitor(acx_device_t *adev); ++ ++void acx_s_get_firmware_version(acx_device_t *adev); ++void acx_display_hardware_details(acx_device_t *adev); ++ ++int acx_e_change_mtu(struct net_device *ndev, int mtu); ++struct net_device_stats* acx_e_get_stats(struct net_device *ndev); ++struct iw_statistics* acx_e_get_wireless_stats(struct net_device *ndev); ++ ++#ifdef ACX_MEM ++int __init acxmem_e_init_module(void); ++void __exit acxmem_e_cleanup_module(void); ++void acxmem_e_release(struct device *dev); ++#else ++int __init acxpci_e_init_module(void); ++int __init acxusb_e_init_module(void); ++void __exit acxpci_e_cleanup_module(void); ++void __exit acxusb_e_cleanup_module(void); ++#endif ++int __init acx_cs_init(void); ++void __exit acx_cs_cleanup(void); +Index: linux-2.6.23/drivers/net/wireless/acx/acx.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.23/drivers/net/wireless/acx/acx.h 2008-01-20 21:13:40.000000000 +0000 +@@ -0,0 +1,14 @@ ++#if defined(CONFIG_ACX_MEM) && !defined(ACX_MEM) ++#define ACX_MEM ++#endif ++ ++#if defined(CONFIG_ACX_CS) && !defined(ACX_MEM) ++#define ACX_MEM ++#endif ++ ++#include "acx_config.h" ++#include "wlan_compat.h" ++#include "wlan_hdr.h" ++#include "wlan_mgmt.h" ++#include "acx_struct.h" ++#include "acx_func.h" +Index: linux-2.6.23/drivers/net/wireless/acx/acx_hw.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.23/drivers/net/wireless/acx/acx_hw.h 2008-01-20 21:13:40.000000000 +0000 +@@ -0,0 +1,18 @@ ++/* ++ * Interface for ACX slave memory driver ++ * ++ * Copyright (c) 2006 SDG Systems, LLC ++ * ++ * GPL ++ * ++ */ ++ ++#ifndef _ACX_HW_H ++#define _ACX_HW_H ++ ++struct acx_hardware_data { ++ int (*start_hw)( void ); ++ int (*stop_hw)( void ); ++}; ++ ++#endif /* _ACX_HW_H */ +Index: linux-2.6.23/drivers/net/wireless/acx/acx_struct.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.23/drivers/net/wireless/acx/acx_struct.h 2008-01-20 21:13:40.000000000 +0000 +@@ -0,0 +1,2114 @@ ++/*********************************************************************** ++** Copyright (C) 2003 ACX100 Open Source Project ++** ++** The contents of this file are subject to the Mozilla Public ++** License Version 1.1 (the "License"); you may not use this file ++** except in compliance with the License. You may obtain a copy of ++** the License at http://www.mozilla.org/MPL/ ++** ++** Software distributed under the License is distributed on an "AS ++** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or ++** implied. See the License for the specific language governing ++** rights and limitations under the License. ++** ++** Alternatively, the contents of this file may be used under the ++** terms of the GNU Public License version 2 (the "GPL"), in which ++** case the provisions of the GPL are applicable instead of the ++** above. If you wish to allow the use of your version of this file ++** only under the terms of the GPL and not to allow others to use ++** your version of this file under the MPL, indicate your decision ++** by deleting the provisions above and replace them with the notice ++** and other provisions required by the GPL. If you do not delete ++** the provisions above, a recipient may use your version of this ++** file under either the MPL or the GPL. ++** --------------------------------------------------------------------- ++** Inquiries regarding the ACX100 Open Source Project can be ++** made directly to: ++** ++** acx100-users@lists.sf.net ++** http://acx100.sf.net ++** --------------------------------------------------------------------- ++*/ ++ ++/*********************************************************************** ++** Forward declarations of types ++*/ ++typedef struct tx tx_t; ++typedef struct acx_device acx_device_t; ++typedef struct client client_t; ++typedef struct rxdesc rxdesc_t; ++typedef struct txdesc txdesc_t; ++typedef struct rxhostdesc rxhostdesc_t; ++typedef struct txhostdesc txhostdesc_t; ++ ++ ++/*********************************************************************** ++** Debug / log functionality ++*/ ++enum { ++ L_LOCK = (ACX_DEBUG>1)*0x0001, /* locking debug log */ ++ L_INIT = (ACX_DEBUG>0)*0x0002, /* special card initialization logging */ ++ L_IRQ = (ACX_DEBUG>0)*0x0004, /* interrupt stuff */ ++ L_ASSOC = (ACX_DEBUG>0)*0x0008, /* assocation (network join) and station log */ ++ L_FUNC = (ACX_DEBUG>1)*0x0020, /* logging of function enter / leave */ ++ L_XFER = (ACX_DEBUG>1)*0x0080, /* logging of transfers and mgmt */ ++ L_DATA = (ACX_DEBUG>1)*0x0100, /* logging of transfer data */ ++ L_DEBUG = (ACX_DEBUG>1)*0x0200, /* log of debug info */ ++ L_IOCTL = (ACX_DEBUG>0)*0x0400, /* log ioctl calls */ ++ L_CTL = (ACX_DEBUG>1)*0x0800, /* log of low-level ctl commands */ ++ L_BUFR = (ACX_DEBUG>1)*0x1000, /* debug rx buffer mgmt (ring buffer etc.) */ ++ L_XFER_BEACON = (ACX_DEBUG>1)*0x2000, /* also log beacon packets */ ++ L_BUFT = (ACX_DEBUG>1)*0x4000, /* debug tx buffer mgmt (ring buffer etc.) */ ++ L_USBRXTX = (ACX_DEBUG>0)*0x8000, /* debug USB rx/tx operations */ ++ L_BUF = L_BUFR + L_BUFT, ++ L_ANY = 0xffff ++}; ++ ++#if ACX_DEBUG ++extern unsigned int acx_debug; ++#else ++enum { acx_debug = 0 }; ++#endif ++ ++ ++/*********************************************************************** ++** Random helpers ++*/ ++#define ACX_PACKED __attribute__ ((packed)) ++ ++#define VEC_SIZE(a) (sizeof(a)/sizeof(a[0])) ++ ++/* Use worker_queues for 2.5/2.6 kernels and queue tasks for 2.4 kernels ++ (used for the 'bottom half' of the interrupt routine) */ ++ ++#include <linux/workqueue.h> ++#define USE_WORKER_TASKS ++#define WORK_STRUCT struct work_struct ++#define SCHEDULE_WORK schedule_work ++#define FLUSH_SCHEDULED_WORK flush_scheduled_work ++ ++ ++/*********************************************************************** ++** Constants ++*/ ++#define OK 0 ++#define NOT_OK 1 ++ ++/* The supported chip models */ ++#define CHIPTYPE_ACX100 1 ++#define CHIPTYPE_ACX111 2 ++ ++#define IS_ACX100(adev) ((adev)->chip_type == CHIPTYPE_ACX100) ++#define IS_ACX111(adev) ((adev)->chip_type == CHIPTYPE_ACX111) ++ ++/* Supported interfaces */ ++#define DEVTYPE_PCI 0 ++#define DEVTYPE_USB 1 ++#define DEVTYPE_MEM 2 ++ ++#if !defined(CONFIG_ACX_PCI) && !defined(CONFIG_ACX_USB) && !defined(CONFIG_ACX_MEM) && !defined(CONFIG_ACX_CS) ++#error Driver must include PCI, USB, PCMCIA or memory mapped interface support. You selected none of them. ++#endif ++ ++#if defined(CONFIG_ACX_PCI) ++ #if !defined(CONFIG_ACX_USB) ++ #define IS_PCI(adev) 1 ++ #else ++ #define IS_PCI(adev) ((adev)->dev_type == DEVTYPE_PCI) ++ #endif ++#else ++ #define IS_PCI(adev) 0 ++#endif ++ ++#if defined(CONFIG_ACX_USB) ++ #if !defined(CONFIG_ACX_PCI) ++ #define IS_USB(adev) 1 ++ #else ++ #define IS_USB(adev) ((adev)->dev_type == DEVTYPE_USB) ++ #endif ++#else ++ #define IS_USB(adev) 0 ++#endif ++ ++#if defined(CONFIG_ACX_MEM) || defined(CONFIG_ACX_CS) ++ #define IS_MEM(adev) 1 ++#else ++ #define IS_MEM(adev) 0 ++#endif ++ ++/* Driver defaults */ ++#define DEFAULT_DTIM_INTERVAL 10 ++/* used to be 2048, but FreeBSD driver changed it to 4096 to work properly ++** in noisy wlans */ ++#define DEFAULT_MSDU_LIFETIME 4096 ++#define DEFAULT_RTS_THRESHOLD 2312 /* max. size: disable RTS mechanism */ ++#define DEFAULT_BEACON_INTERVAL 100 ++ ++#define ACX100_BAP_DATALEN_MAX 4096 ++#define ACX100_RID_GUESSING_MAXLEN 2048 /* I'm not really sure */ ++#define ACX100_RIDDATA_MAXLEN ACX100_RID_GUESSING_MAXLEN ++ ++/* Support Constants */ ++/* Radio type names, found in Win98 driver's TIACXLN.INF */ ++#define RADIO_MAXIM_0D 0x0d ++#define RADIO_RFMD_11 0x11 ++#define RADIO_RALINK_15 0x15 ++/* used in ACX111 cards (WG311v2, WL-121, ...): */ ++#define RADIO_RADIA_16 0x16 ++/* most likely *sometimes* used in ACX111 cards: */ ++#define RADIO_UNKNOWN_17 0x17 ++/* FwRad19.bin was found in a Safecom driver; must be an ACX111 radio: */ ++#define RADIO_UNKNOWN_19 0x19 ++#define RADIO_UNKNOWN_1B 0x1b /* radio in SafeCom SWLUT-54125 USB adapter; entirely unknown!! */ ++ ++/* Controller Commands */ ++/* can be found in table cmdTable in firmware "Rev. 1.5.0" (FW150) */ ++#define ACX1xx_CMD_RESET 0x00 ++#define ACX1xx_CMD_INTERROGATE 0x01 ++#define ACX1xx_CMD_CONFIGURE 0x02 ++#define ACX1xx_CMD_ENABLE_RX 0x03 ++#define ACX1xx_CMD_ENABLE_TX 0x04 ++#define ACX1xx_CMD_DISABLE_RX 0x05 ++#define ACX1xx_CMD_DISABLE_TX 0x06 ++#define ACX1xx_CMD_FLUSH_QUEUE 0x07 ++#define ACX1xx_CMD_SCAN 0x08 ++#define ACX1xx_CMD_STOP_SCAN 0x09 ++#define ACX1xx_CMD_CONFIG_TIM 0x0a ++#define ACX1xx_CMD_JOIN 0x0b ++#define ACX1xx_CMD_WEP_MGMT 0x0c ++#ifdef OLD_FIRMWARE_VERSIONS ++#define ACX100_CMD_HALT 0x0e /* mapped to unknownCMD in FW150 */ ++#else ++#define ACX1xx_CMD_MEM_READ 0x0d ++#define ACX1xx_CMD_MEM_WRITE 0x0e ++#endif ++#define ACX1xx_CMD_SLEEP 0x0f ++#define ACX1xx_CMD_WAKE 0x10 ++#define ACX1xx_CMD_UNKNOWN_11 0x11 /* mapped to unknownCMD in FW150 */ ++#define ACX100_CMD_INIT_MEMORY 0x12 ++#define ACX1FF_CMD_DISABLE_RADIO 0x12 /* new firmware? TNETW1450? */ ++#define ACX1xx_CMD_CONFIG_BEACON 0x13 ++#define ACX1xx_CMD_CONFIG_PROBE_RESPONSE 0x14 ++#define ACX1xx_CMD_CONFIG_NULL_DATA 0x15 ++#define ACX1xx_CMD_CONFIG_PROBE_REQUEST 0x16 ++#define ACX1xx_CMD_FCC_TEST 0x17 ++#define ACX1xx_CMD_RADIOINIT 0x18 ++#define ACX111_CMD_RADIOCALIB 0x19 ++#define ACX1FF_CMD_NOISE_HISTOGRAM 0x1c /* new firmware? TNETW1450? */ ++#define ACX1FF_CMD_RX_RESET 0x1d /* new firmware? TNETW1450? */ ++#define ACX1FF_CMD_LNA_CONTROL 0x20 /* new firmware? TNETW1450? */ ++#define ACX1FF_CMD_CONTROL_DBG_TRACE 0x21 /* new firmware? TNETW1450? */ ++ ++/* 'After Interrupt' Commands */ ++#define ACX_AFTER_IRQ_CMD_STOP_SCAN 0x01 ++#define ACX_AFTER_IRQ_CMD_ASSOCIATE 0x02 ++#define ACX_AFTER_IRQ_CMD_RADIO_RECALIB 0x04 ++#define ACX_AFTER_IRQ_UPDATE_CARD_CFG 0x08 ++#define ACX_AFTER_IRQ_TX_CLEANUP 0x10 ++#define ACX_AFTER_IRQ_COMPLETE_SCAN 0x20 ++#define ACX_AFTER_IRQ_RESTART_SCAN 0x40 ++ ++/*********************************************************************** ++** Tx/Rx buffer sizes and watermarks ++** ++** This will alloc and use DMAable buffers of ++** WLAN_A4FR_MAXLEN_WEP_FCS * (RX_CNT + TX_CNT) bytes ++** RX/TX_CNT=32 -> ~150k DMA buffers ++** RX/TX_CNT=16 -> ~75k DMA buffers ++** ++** 2005-10-10: reduced memory usage by lowering both to 16 ++*/ ++#define RX_CNT 16 ++#define TX_CNT 16 ++ ++/* we clean up txdescs when we have N free txdesc: */ ++#define TX_CLEAN_BACKLOG (TX_CNT/4) ++#define TX_START_CLEAN (TX_CNT - TX_CLEAN_BACKLOG) ++#define TX_EMERG_CLEAN 2 ++/* we stop queue if we have < N free txbufs: */ ++#define TX_STOP_QUEUE 3 ++/* we start queue if we have >= N free txbufs: */ ++#define TX_START_QUEUE 5 ++ ++/*********************************************************************** ++** Interrogate/Configure cmd constants ++** ++** NB: length includes JUST the data part of the IE ++** (does not include size of the (type,len) pair) ++** ++** TODO: seems that acx100, acx100usb, acx111 have some differences, ++** fix code with regard to this! ++*/ ++ ++#define DEF_IE(name, val, len) enum { ACX##name=val, ACX##name##_LEN=len } ++ ++/* Information Elements: Network Parameters, Static Configuration Entities */ ++/* these are handled by real_cfgtable in firmware "Rev 1.5.0" (FW150) */ ++DEF_IE(1xx_IE_UNKNOWN_00 ,0x0000, -1); /* mapped to cfgInvalid in FW150 */ ++DEF_IE(100_IE_ACX_TIMER ,0x0001, 0x10); ++DEF_IE(1xx_IE_POWER_MGMT ,0x0002, 0x06); /* TNETW1450: length 0x18!! */ ++DEF_IE(1xx_IE_QUEUE_CONFIG ,0x0003, 0x1c); ++DEF_IE(100_IE_BLOCK_SIZE ,0x0004, 0x02); ++DEF_IE(1FF_IE_SLOT_TIME ,0x0004, 0x08); /* later firmware versions only? */ ++DEF_IE(1xx_IE_MEMORY_CONFIG_OPTIONS ,0x0005, 0x14); ++DEF_IE(1FF_IE_QUEUE_HEAD ,0x0005, 0x14 /* FIXME: length? */); ++DEF_IE(1xx_IE_RATE_FALLBACK ,0x0006, 0x01); /* TNETW1450: length 2 */ ++DEF_IE(100_IE_WEP_OPTIONS ,0x0007, 0x03); ++DEF_IE(111_IE_RADIO_BAND ,0x0007, -1); ++DEF_IE(1FF_IE_TIMING_CFG ,0x0007, -1); /* later firmware versions; TNETW1450 only? */ ++DEF_IE(100_IE_SSID ,0x0008, 0x20); /* huh? */ ++DEF_IE(1xx_IE_MEMORY_MAP ,0x0008, 0x28); /* huh? TNETW1450 has length 0x40!! */ ++DEF_IE(1xx_IE_SCAN_STATUS ,0x0009, 0x04); /* mapped to cfgInvalid in FW150 */ ++DEF_IE(1xx_IE_ASSOC_ID ,0x000a, 0x02); ++DEF_IE(1xx_IE_UNKNOWN_0B ,0x000b, -1); /* mapped to cfgInvalid in FW150 */ ++DEF_IE(1FF_IE_TX_POWER_LEVEL_TABLE ,0x000b, 0x18); /* later firmware versions; TNETW1450 only? */ ++DEF_IE(100_IE_UNKNOWN_0C ,0x000c, -1); /* very small implementation in FW150! */ ++/* ACX100 has an equivalent struct in the cmd mailbox directly after reset. ++ * 0x14c seems extremely large, will trash stack on failure (memset!) ++ * in case of small input struct --> OOPS! */ ++DEF_IE(111_IE_CONFIG_OPTIONS ,0x000c, 0x14c); ++DEF_IE(1xx_IE_FWREV ,0x000d, 0x18); ++DEF_IE(1xx_IE_FCS_ERROR_COUNT ,0x000e, 0x04); ++DEF_IE(1xx_IE_MEDIUM_USAGE ,0x000f, 0x08); ++DEF_IE(1xx_IE_RXCONFIG ,0x0010, 0x04); ++DEF_IE(100_IE_UNKNOWN_11 ,0x0011, -1); /* NONBINARY: large implementation in FW150! link quality readings or so? */ ++DEF_IE(111_IE_QUEUE_THRESH ,0x0011, -1); ++DEF_IE(100_IE_UNKNOWN_12 ,0x0012, -1); /* NONBINARY: VERY large implementation in FW150!! */ ++DEF_IE(111_IE_BSS_POWER_SAVE ,0x0012, /* -1 */ 2); ++DEF_IE(1xx_IE_FIRMWARE_STATISTICS ,0x0013, 0x9c); /* TNETW1450: length 0x134!! */ ++DEF_IE(1FF_IE_RX_INTR_CONFIG ,0x0014, 0x14); /* later firmware versions, TNETW1450 only? */ ++DEF_IE(1xx_IE_FEATURE_CONFIG ,0x0015, 0x08); ++DEF_IE(111_IE_KEY_CHOOSE ,0x0016, 0x04); /* for rekeying. really len=4?? */ ++DEF_IE(1FF_IE_MISC_CONFIG_TABLE ,0x0017, 0x04); /* later firmware versions, TNETW1450 only? */ ++DEF_IE(1FF_IE_WONE_CONFIG ,0x0018, -1); /* later firmware versions, TNETW1450 only? */ ++DEF_IE(1FF_IE_TID_CONFIG ,0x001a, 0x2c); /* later firmware versions, TNETW1450 only? */ ++DEF_IE(1FF_IE_CALIB_ASSESSMENT ,0x001e, 0x04); /* later firmware versions, TNETW1450 only? */ ++DEF_IE(1FF_IE_BEACON_FILTER_OPTIONS ,0x001f, 0x02); /* later firmware versions, TNETW1450 only? */ ++DEF_IE(1FF_IE_LOW_RSSI_THRESH_OPT ,0x0020, 0x04); /* later firmware versions, TNETW1450 only? */ ++DEF_IE(1FF_IE_NOISE_HISTOGRAM_RESULTS ,0x0021, 0x30); /* later firmware versions, TNETW1450 only? */ ++DEF_IE(1FF_IE_PACKET_DETECT_THRESH ,0x0023, 0x04); /* later firmware versions, TNETW1450 only? */ ++DEF_IE(1FF_IE_TX_CONFIG_OPTIONS ,0x0024, 0x04); /* later firmware versions, TNETW1450 only? */ ++DEF_IE(1FF_IE_CCA_THRESHOLD ,0x0025, 0x02); /* later firmware versions, TNETW1450 only? */ ++DEF_IE(1FF_IE_EVENT_MASK ,0x0026, 0x08); /* later firmware versions, TNETW1450 only? */ ++DEF_IE(1FF_IE_DTIM_PERIOD ,0x0027, 0x02); /* later firmware versions, TNETW1450 only? */ ++DEF_IE(1FF_IE_ACI_CONFIG_SET ,0x0029, 0x06); /* later firmware versions; maybe TNETW1450 only? */ ++DEF_IE(1FF_IE_EEPROM_VER ,0x0030, 0x04); /* later firmware versions; maybe TNETW1450 only? */ ++DEF_IE(1xx_IE_DOT11_STATION_ID ,0x1001, 0x06); ++DEF_IE(100_IE_DOT11_UNKNOWN_1002 ,0x1002, -1); /* mapped to cfgInvalid in FW150 */ ++DEF_IE(111_IE_DOT11_FRAG_THRESH ,0x1002, -1); /* mapped to cfgInvalid in FW150; TNETW1450 has length 2!! */ ++DEF_IE(100_IE_DOT11_BEACON_PERIOD ,0x1003, 0x02); /* mapped to cfgInvalid in FW150 */ ++DEF_IE(1xx_IE_DOT11_DTIM_PERIOD ,0x1004, -1); /* mapped to cfgInvalid in FW150 */ ++DEF_IE(1FF_IE_DOT11_MAX_RX_LIFETIME ,0x1004, -1); /* later firmware versions; maybe TNETW1450 only? */ ++DEF_IE(1xx_IE_DOT11_SHORT_RETRY_LIMIT ,0x1005, 0x01); /* TNETW1450: length 2 */ ++DEF_IE(1xx_IE_DOT11_LONG_RETRY_LIMIT ,0x1006, 0x01); /* TNETW1450: length 2 */ ++DEF_IE(100_IE_DOT11_WEP_DEFAULT_KEY_WRITE ,0x1007, 0x20); /* configure default keys; TNETW1450 has length 0x24!! */ ++DEF_IE(1xx_IE_DOT11_MAX_XMIT_MSDU_LIFETIME ,0x1008, 0x04); ++DEF_IE(1xx_IE_DOT11_GROUP_ADDR ,0x1009, -1); ++DEF_IE(1xx_IE_DOT11_CURRENT_REG_DOMAIN ,0x100a, 0x02); ++/* It's harmless to have larger struct. Use USB case always. */ ++DEF_IE(1xx_IE_DOT11_CURRENT_ANTENNA ,0x100b, 0x02); /* in fact len=1 for PCI */ ++DEF_IE(1xx_IE_DOT11_UNKNOWN_100C ,0x100c, -1); /* mapped to cfgInvalid in FW150 */ ++DEF_IE(1xx_IE_DOT11_TX_POWER_LEVEL ,0x100d, 0x01); /* TNETW1450 has length 2!! */ ++DEF_IE(1xx_IE_DOT11_CURRENT_CCA_MODE ,0x100e, 0x02); /* in fact len=1 for PCI */ ++/* USB doesn't return anything - len==0?! */ ++DEF_IE(100_IE_DOT11_ED_THRESHOLD ,0x100f, 0x04); ++DEF_IE(1xx_IE_DOT11_WEP_DEFAULT_KEY_SET ,0x1010, 0x01); /* set default key ID; TNETW1450: length 2 */ ++DEF_IE(100_IE_DOT11_UNKNOWN_1011 ,0x1011, -1); /* mapped to cfgInvalid in FW150 */ ++DEF_IE(1FF_IE_DOT11_CURR_5GHZ_REGDOM ,0x1011, -1); /* later firmware versions; maybe TNETW1450 only? */ ++DEF_IE(100_IE_DOT11_UNKNOWN_1012 ,0x1012, -1); /* mapped to cfgInvalid in FW150 */ ++DEF_IE(100_IE_DOT11_UNKNOWN_1013 ,0x1013, -1); /* mapped to cfgInvalid in FW150 */ ++ ++#if 0 ++/* Experimentally obtained on acx100, fw 1.9.8.b ++** -1 means that fw returned 'invalid IE' ++** 0200 FC00 nnnn... are test read contents: u16 type, u16 len, data ++** (AA are poison bytes marking bytes not written by fw) ++** ++** Looks like acx100 fw does not update len field (thus len=256-4=FC here) ++** A number of IEs seem to trash type,len fields ++** IEs marked 'huge' return gobs of data (no poison bytes remain) ++*/ ++DEF_IE(100_IE_INVAL_00, 0x0000, -1); ++DEF_IE(100_IE_INVAL_01, 0x0001, -1); /* IE_ACX_TIMER, len=16 on older fw */ ++DEF_IE(100_IE_POWER_MGMT, 0x0002, 4); /* 0200FC00 00040000 AAAAAAAA */ ++DEF_IE(100_IE_QUEUE_CONFIG, 0x0003, 28); /* 0300FC00 48060000 9CAD0000 0101AAAA DCB00000 E4B00000 9CAA0000 00AAAAAA */ ++DEF_IE(100_IE_BLOCK_SIZE, 0x0004, 2); /* 0400FC00 0001AAAA AAAAAAAA AAAAAAAA */ ++/* write only: */ ++DEF_IE(100_IE_MEMORY_CONFIG_OPTIONS, 0x0005, 20); ++DEF_IE(100_IE_RATE_FALLBACK, 0x0006, 1); /* 0600FC00 00AAAAAA AAAAAAAA AAAAAAAA */ ++/* write only: */ ++DEF_IE(100_IE_WEP_OPTIONS, 0x0007, 3); ++DEF_IE(100_IE_MEMORY_MAP, 0x0008, 40); /* huge: 0800FC00 30000000 6CA20000 70A20000... */ ++/* gives INVAL on read: */ ++DEF_IE(100_IE_SCAN_STATUS, 0x0009, -1); ++DEF_IE(100_IE_ASSOC_ID, 0x000a, 2); /* huge: 0A00FC00 00000000 01040800 00000000... */ ++DEF_IE(100_IE_INVAL_0B, 0x000b, -1); ++/* 'command rejected': */ ++DEF_IE(100_IE_CONFIG_OPTIONS, 0x000c, -3); ++DEF_IE(100_IE_FWREV, 0x000d, 24); /* 0D00FC00 52657620 312E392E 382E6200 AAAAAAAA AAAAAAAA 05050201 AAAAAAAA */ ++DEF_IE(100_IE_FCS_ERROR_COUNT, 0x000e, 4); ++DEF_IE(100_IE_MEDIUM_USAGE, 0x000f, 8); /* E41F0000 2D780300 FCC91300 AAAAAAAA */ ++DEF_IE(100_IE_RXCONFIG, 0x0010, 4); /* 1000FC00 00280000 AAAAAAAA AAAAAAAA */ ++DEF_IE(100_IE_QUEUE_THRESH, 0x0011, 12); /* 1100FC00 AAAAAAAA 00000000 00000000 */ ++DEF_IE(100_IE_BSS_POWER_SAVE, 0x0012, 1); /* 1200FC00 00AAAAAA AAAAAAAA AAAAAAAA */ ++/* read only, variable len */ ++DEF_IE(100_IE_FIRMWARE_STATISTICS, 0x0013, 256); /* 0000AC00 00000000 ... */ ++DEF_IE(100_IE_INT_CONFIG, 0x0014, 20); /* 00000000 00000000 00000000 00000000 5D74D105 00000000 AAAAAAAA AAAAAAAA */ ++DEF_IE(100_IE_FEATURE_CONFIG, 0x0015, 8); /* 1500FC00 16000000 AAAAAAAA AAAAAAAA */ ++/* returns 'invalid MAC': */ ++DEF_IE(100_IE_KEY_CHOOSE, 0x0016, -4); ++DEF_IE(100_IE_INVAL_17, 0x0017, -1); ++DEF_IE(100_IE_UNKNOWN_18, 0x0018, 0); /* null len?! 1800FC00 AAAAAAAA AAAAAAAA AAAAAAAA */ ++DEF_IE(100_IE_UNKNOWN_19, 0x0019, 256); /* huge: 1900FC00 9C1F00EA FEFFFFEA FEFFFFEA... */ ++DEF_IE(100_IE_INVAL_1A, 0x001A, -1); ++ ++DEF_IE(100_IE_DOT11_INVAL_1000, 0x1000, -1); ++DEF_IE(100_IE_DOT11_STATION_ID, 0x1001, 6); /* huge: 0110FC00 58B10E2F 03000000 00000000... */ ++DEF_IE(100_IE_DOT11_INVAL_1002, 0x1002, -1); ++DEF_IE(100_IE_DOT11_INVAL_1003, 0x1003, -1); ++DEF_IE(100_IE_DOT11_INVAL_1004, 0x1004, -1); ++DEF_IE(100_IE_DOT11_SHORT_RETRY_LIMIT, 0x1005, 1); ++DEF_IE(100_IE_DOT11_LONG_RETRY_LIMIT, 0x1006, 1); ++/* write only: */ ++DEF_IE(100_IE_DOT11_WEP_DEFAULT_KEY_WRITE, 0x1007, 32); ++DEF_IE(100_IE_DOT11_MAX_XMIT_MSDU_LIFETIME, 0x1008, 4); /* huge: 0810FC00 00020000 F4010000 00000000... */ ++/* undoc but returns something */ ++DEF_IE(100_IE_DOT11_GROUP_ADDR, 0x1009, 12); /* huge: 0910FC00 00000000 00000000 00000000... */ ++DEF_IE(100_IE_DOT11_CURRENT_REG_DOMAIN, 0x100a, 1); /* 0A10FC00 30AAAAAA AAAAAAAA AAAAAAAA */ ++DEF_IE(100_IE_DOT11_CURRENT_ANTENNA, 0x100b, 1); /* 0B10FC00 8FAAAAAA AAAAAAAA AAAAAAAA */ ++DEF_IE(100_IE_DOT11_INVAL_100C, 0x100c, -1); ++DEF_IE(100_IE_DOT11_TX_POWER_LEVEL, 0x100d, 2); /* 00000000 0100AAAA AAAAAAAA AAAAAAAA */ ++DEF_IE(100_IE_DOT11_CURRENT_CCA_MODE, 0x100e, 1); /* 0E10FC00 0DAAAAAA AAAAAAAA AAAAAAAA */ ++DEF_IE(100_IE_DOT11_ED_THRESHOLD, 0x100f, 4); /* 0F10FC00 70000000 AAAAAAAA AAAAAAAA */ ++/* set default key ID */ ++DEF_IE(100_IE_DOT11_WEP_DEFAULT_KEY_SET, 0x1010, 1); /* 1010FC00 00AAAAAA AAAAAAAA AAAAAAAA */ ++DEF_IE(100_IE_DOT11_INVAL_1011, 0x1011, -1); ++DEF_IE(100_IE_DOT11_INVAL_1012, 0x1012, -1); ++DEF_IE(100_IE_DOT11_INVAL_1013, 0x1013, -1); ++DEF_IE(100_IE_DOT11_UNKNOWN_1014, 0x1014, 256); /* huge */ ++DEF_IE(100_IE_DOT11_UNKNOWN_1015, 0x1015, 256); /* huge */ ++DEF_IE(100_IE_DOT11_UNKNOWN_1016, 0x1016, 256); /* huge */ ++DEF_IE(100_IE_DOT11_UNKNOWN_1017, 0x1017, 256); /* huge */ ++DEF_IE(100_IE_DOT11_UNKNOWN_1018, 0x1018, 256); /* huge */ ++DEF_IE(100_IE_DOT11_UNKNOWN_1019, 0x1019, 256); /* huge */ ++#endif ++ ++#if 0 ++/* Experimentally obtained on PCI acx111 Xterasys XN-2522g, fw 1.2.1.34 ++** -1 means that fw returned 'invalid IE' ++** 0400 0800 nnnn... are test read contents: u16 type, u16 len, data ++** (AA are poison bytes marking bytes not written by fw) ++** ++** Looks like acx111 fw reports real len! ++*/ ++DEF_IE(111_IE_INVAL_00, 0x0000, -1); ++DEF_IE(111_IE_INVAL_01, 0x0001, -1); ++DEF_IE(111_IE_POWER_MGMT, 0x0002, 12); ++/* write only, variable len: 12 + rxqueue_cnt*8 + txqueue_cnt*4: */ ++DEF_IE(111_IE_MEMORY_CONFIG, 0x0003, 24); ++DEF_IE(111_IE_BLOCK_SIZE, 0x0004, 8); /* 04000800 AA00AAAA AAAAAAAA */ ++/* variable len: 8 + rxqueue_cnt*8 + txqueue_cnt*8: */ ++DEF_IE(111_IE_QUEUE_HEAD, 0x0005, 24); ++DEF_IE(111_IE_RATE_FALLBACK, 0x0006, 1); ++/* acx100 name:WEP_OPTIONS */ ++/* said to have len:1 (not true, actually returns 12 bytes): */ ++DEF_IE(111_IE_RADIO_BAND, 0x0007, 12); /* 07000C00 AAAA1F00 FF03AAAA AAAAAAAA */ ++DEF_IE(111_IE_MEMORY_MAP, 0x0008, 48); ++/* said to have len:4, but gives INVAL on read: */ ++DEF_IE(111_IE_SCAN_STATUS, 0x0009, -1); ++DEF_IE(111_IE_ASSOC_ID, 0x000a, 2); ++/* write only, len is not known: */ ++DEF_IE(111_IE_UNKNOWN_0B, 0x000b, 0); ++/* read only, variable len. I see 67 byte reads: */ ++DEF_IE(111_IE_CONFIG_OPTIONS, 0x000c, 67); /* 0C004300 01160500 ... */ ++DEF_IE(111_IE_FWREV, 0x000d, 24); ++DEF_IE(111_IE_FCS_ERROR_COUNT, 0x000e, 4); ++DEF_IE(111_IE_MEDIUM_USAGE, 0x000f, 8); ++DEF_IE(111_IE_RXCONFIG, 0x0010, 4); ++DEF_IE(111_IE_QUEUE_THRESH, 0x0011, 12); ++DEF_IE(111_IE_BSS_POWER_SAVE, 0x0012, 1); ++/* read only, variable len. I see 240 byte reads: */ ++DEF_IE(111_IE_FIRMWARE_STATISTICS, 0x0013, 240); /* 1300F000 00000000 ... */ ++/* said to have len=17. looks like fw pads it to 20: */ ++DEF_IE(111_IE_INT_CONFIG, 0x0014, 20); /* 14001400 00000000 00000000 00000000 00000000 00000000 */ ++DEF_IE(111_IE_FEATURE_CONFIG, 0x0015, 8); ++/* said to be name:KEY_INDICATOR, len:4, but gives INVAL on read: */ ++DEF_IE(111_IE_KEY_CHOOSE, 0x0016, -1); ++/* said to have len:4, but in fact returns 8: */ ++DEF_IE(111_IE_MAX_USB_XFR, 0x0017, 8); /* 17000800 00014000 00000000 */ ++DEF_IE(111_IE_INVAL_18, 0x0018, -1); ++DEF_IE(111_IE_INVAL_19, 0x0019, -1); ++/* undoc but returns something: */ ++/* huh, fw indicates len=20 but uses 4 more bytes in buffer??? */ ++DEF_IE(111_IE_UNKNOWN_1A, 0x001A, 20); /* 1A001400 AA00AAAA 0000020F FF030000 00020000 00000007 04000000 */ ++ ++DEF_IE(111_IE_DOT11_INVAL_1000, 0x1000, -1); ++DEF_IE(111_IE_DOT11_STATION_ID, 0x1001, 6); ++DEF_IE(111_IE_DOT11_FRAG_THRESH, 0x1002, 2); ++/* acx100 only? gives INVAL on read: */ ++DEF_IE(111_IE_DOT11_BEACON_PERIOD, 0x1003, -1); ++/* said to be MAX_RECV_MSDU_LIFETIME: */ ++DEF_IE(111_IE_DOT11_DTIM_PERIOD, 0x1004, 4); ++DEF_IE(111_IE_DOT11_SHORT_RETRY_LIMIT, 0x1005, 1); ++DEF_IE(111_IE_DOT11_LONG_RETRY_LIMIT, 0x1006, 1); ++/* acx100 only? gives INVAL on read: */ ++DEF_IE(111_IE_DOT11_WEP_DEFAULT_KEY_WRITE, 0x1007, -1); ++DEF_IE(111_IE_DOT11_MAX_XMIT_MSDU_LIFETIME, 0x1008, 4); ++/* undoc but returns something. maybe it's 2 multicast MACs to listen to? */ ++DEF_IE(111_IE_DOT11_GROUP_ADDR, 0x1009, 12); /* 09100C00 00000000 00000000 00000000 */ ++DEF_IE(111_IE_DOT11_CURRENT_REG_DOMAIN, 0x100a, 1); ++DEF_IE(111_IE_DOT11_CURRENT_ANTENNA, 0x100b, 2); ++DEF_IE(111_IE_DOT11_INVAL_100C, 0x100c, -1); ++DEF_IE(111_IE_DOT11_TX_POWER_LEVEL, 0x100d, 1); ++/* said to have len=1 but gives INVAL on read: */ ++DEF_IE(111_IE_DOT11_CURRENT_CCA_MODE, 0x100e, -1); ++/* said to have len=4 but gives INVAL on read: */ ++DEF_IE(111_IE_DOT11_ED_THRESHOLD, 0x100f, -1); ++/* set default key ID. write only: */ ++DEF_IE(111_IE_DOT11_WEP_DEFAULT_KEY_SET, 0x1010, 1); ++/* undoc but returns something: */ ++DEF_IE(111_IE_DOT11_UNKNOWN_1011, 0x1011, 1); /* 11100100 20 */ ++DEF_IE(111_IE_DOT11_INVAL_1012, 0x1012, -1); ++DEF_IE(111_IE_DOT11_INVAL_1013, 0x1013, -1); ++#endif ++ ++ ++/*********************************************************************** ++**Information Frames Structures ++*/ ++ ++/* Used in beacon frames and the like */ ++#define DOT11RATEBYTE_1 (1*2) ++#define DOT11RATEBYTE_2 (2*2) ++#define DOT11RATEBYTE_5_5 (5*2+1) ++#define DOT11RATEBYTE_11 (11*2) ++#define DOT11RATEBYTE_22 (22*2) ++#define DOT11RATEBYTE_6_G (6*2) ++#define DOT11RATEBYTE_9_G (9*2) ++#define DOT11RATEBYTE_12_G (12*2) ++#define DOT11RATEBYTE_18_G (18*2) ++#define DOT11RATEBYTE_24_G (24*2) ++#define DOT11RATEBYTE_36_G (36*2) ++#define DOT11RATEBYTE_48_G (48*2) ++#define DOT11RATEBYTE_54_G (54*2) ++#define DOT11RATEBYTE_BASIC 0x80 /* flags rates included in basic rate set */ ++ ++ ++/*********************************************************************** ++** rxbuffer_t ++** ++** This is the format of rx data returned by acx ++*/ ++ ++/* I've hoped it's a 802.11 PHY header, but no... ++ * so far, I've seen on acx111: ++ * 0000 3a00 0000 0000 IBSS Beacons ++ * 0000 3c00 0000 0000 ESS Beacons ++ * 0000 2700 0000 0000 Probe requests ++ * --vda ++ */ ++typedef struct phy_hdr { ++ u8 unknown[4]; ++ u8 acx111_unknown[4]; ++} ACX_PACKED phy_hdr_t; ++ ++/* seems to be a bit similar to hfa384x_rx_frame. ++ * These fields are still not quite obvious, though. ++ * Some seem to have different meanings... */ ++ ++#define RXBUF_HDRSIZE 12 ++#define RXBUF_BYTES_RCVD(adev, rxbuf) \ ++ ((le16_to_cpu((rxbuf)->mac_cnt_rcvd) & 0xfff) - (adev)->phy_header_len) ++#define RXBUF_BYTES_USED(rxbuf) \ ++ ((le16_to_cpu((rxbuf)->mac_cnt_rcvd) & 0xfff) + RXBUF_HDRSIZE) ++/* USBism */ ++#define RXBUF_IS_TXSTAT(rxbuf) (le16_to_cpu((rxbuf)->mac_cnt_rcvd) & 0x8000) ++/* ++mac_cnt_rcvd: ++ 12 bits: length of frame from control field to first byte of FCS ++ 3 bits: reserved ++ 1 bit: 1 = it's a tx status info, not a rx packet (USB only) ++ ++mac_cnt_mblks: ++ 6 bits: number of memory block used to store frame in adapter memory ++ 1 bit: Traffic Indicator bit in TIM of received Beacon was set ++ ++mac_status: 1 byte (bitmap): ++ 7 Matching BSSID ++ 6 Matching SSID ++ 5 BDCST Address 1 field is a broadcast ++ 4 VBM received beacon frame has more than one set bit (?!) ++ 3 TIM Set bit representing this station is set in TIM of received beacon ++ 2 GROUP Address 1 is a multicast ++ 1 ADDR1 Address 1 matches our MAC ++ 0 FCSGD FSC is good ++ ++phy_stat_baseband: 1 byte (bitmap): ++ 7 Preamble frame had a long preamble ++ 6 PLCP Error CRC16 error in PLCP header ++ 5 Unsup_Mod unsupported modulation ++ 4 Selected Antenna antenna 1 was used to receive this frame ++ 3 PBCC/CCK frame used: 1=PBCC, 0=CCK modulation ++ 2 OFDM frame used OFDM modulation ++ 1 TI Protection protection frame was detected ++ 0 Reserved ++ ++phy_plcp_signal: 1 byte: ++ Receive PLCP Signal field from the Baseband Processor ++ ++phy_level: 1 byte: ++ receive AGC gain level (can be used to measure receive signal strength) ++ ++phy_snr: 1 byte: ++ estimated noise power of equalized receive signal ++ at input of FEC decoder (can be used to measure receive signal quality) ++ ++time: 4 bytes: ++ timestamp sampled from either the Access Manager TSF counter ++ or free-running microsecond counter when the MAC receives ++ first byte of PLCP header. ++*/ ++ ++typedef struct rxbuffer { ++ u16 mac_cnt_rcvd; /* only 12 bits are len! (0xfff) */ ++ u8 mac_cnt_mblks; ++ u8 mac_status; ++ u8 phy_stat_baseband; /* bit 0x80: used LNA (Low-Noise Amplifier) */ ++ u8 phy_plcp_signal; ++ u8 phy_level; /* PHY stat */ ++ u8 phy_snr; /* PHY stat */ ++ u32 time; /* timestamp upon MAC rcv first byte */ ++/* 4-byte (acx100) or 8-byte (acx111) phy header will be here ++** if RX_CFG1_INCLUDE_PHY_HDR is in effect: ++** phy_hdr_t phy */ ++ wlan_hdr_a3_t hdr_a3; ++ /* maximally sized data part of wlan packet */ ++ u8 data_a3[WLAN_A4FR_MAXLEN_WEP_FCS - WLAN_HDR_A3_LEN]; ++ /* can add hdr/data_a4 if needed */ ++} ACX_PACKED rxbuffer_t; ++ ++ ++/*--- Firmware statistics ----------------------------------------------------*/ ++ ++/* define a random 100 bytes more to catch firmware versions which ++ * provide a bigger struct */ ++#define FW_STATS_FUTURE_EXTENSION 100 ++ ++typedef struct fw_stats_tx { ++ u32 tx_desc_of; ++} ACX_PACKED fw_stats_tx_t; ++ ++typedef struct fw_stats_rx { ++ u32 rx_oom; ++ u32 rx_hdr_of; ++ u32 rx_hw_stuck; /* old: u32 rx_hdr_use_next */ ++ u32 rx_dropped_frame; ++ u32 rx_frame_ptr_err; ++ u32 rx_xfr_hint_trig; ++ u32 rx_aci_events; /* later versions only */ ++ u32 rx_aci_resets; /* later versions only */ ++} ACX_PACKED fw_stats_rx_t; ++ ++typedef struct fw_stats_dma { ++ u32 rx_dma_req; ++ u32 rx_dma_err; ++ u32 tx_dma_req; ++ u32 tx_dma_err; ++} ACX_PACKED fw_stats_dma_t; ++ ++typedef struct fw_stats_irq { ++ u32 cmd_cplt; ++ u32 fiq; ++ u32 rx_hdrs; ++ u32 rx_cmplt; ++ u32 rx_mem_of; ++ u32 rx_rdys; ++ u32 irqs; ++ u32 tx_procs; ++ u32 decrypt_done; ++ u32 dma_0_done; ++ u32 dma_1_done; ++ u32 tx_exch_complet; ++ u32 commands; ++ u32 rx_procs; ++ u32 hw_pm_mode_changes; ++ u32 host_acks; ++ u32 pci_pm; ++ u32 acm_wakeups; ++} ACX_PACKED fw_stats_irq_t; ++ ++typedef struct fw_stats_wep { ++ u32 wep_key_count; ++ u32 wep_default_key_count; ++ u32 dot11_def_key_mib; ++ u32 wep_key_not_found; ++ u32 wep_decrypt_fail; ++ u32 wep_pkt_decrypt; ++ u32 wep_decrypt_irqs; ++} ACX_PACKED fw_stats_wep_t; ++ ++typedef struct fw_stats_pwr { ++ u32 tx_start_ctr; ++ u32 no_ps_tx_too_short; ++ u32 rx_start_ctr; ++ u32 no_ps_rx_too_short; ++ u32 lppd_started; ++ u32 no_lppd_too_noisy; ++ u32 no_lppd_too_short; ++ u32 no_lppd_matching_frame; ++} ACX_PACKED fw_stats_pwr_t; ++ ++typedef struct fw_stats_mic { ++ u32 mic_rx_pkts; ++ u32 mic_calc_fail; ++} ACX_PACKED fw_stats_mic_t; ++ ++typedef struct fw_stats_aes { ++ u32 aes_enc_fail; ++ u32 aes_dec_fail; ++ u32 aes_enc_pkts; ++ u32 aes_dec_pkts; ++ u32 aes_enc_irq; ++ u32 aes_dec_irq; ++} ACX_PACKED fw_stats_aes_t; ++ ++typedef struct fw_stats_event { ++ u32 heartbeat; ++ u32 calibration; ++ u32 rx_mismatch; ++ u32 rx_mem_empty; ++ u32 rx_pool; ++ u32 oom_late; ++ u32 phy_tx_err; ++ u32 tx_stuck; ++} ACX_PACKED fw_stats_event_t; ++ ++/* mainly for size calculation only */ ++typedef struct fw_stats { ++ u16 type; ++ u16 len; ++ fw_stats_tx_t tx; ++ fw_stats_rx_t rx; ++ fw_stats_dma_t dma; ++ fw_stats_irq_t irq; ++ fw_stats_wep_t wep; ++ fw_stats_pwr_t pwr; ++ fw_stats_mic_t mic; ++ fw_stats_aes_t aes; ++ fw_stats_event_t evt; ++ u8 _padding[FW_STATS_FUTURE_EXTENSION]; ++} fw_stats_t; ++ ++/* Firmware version struct */ ++ ++typedef struct fw_ver { ++ u16 cmd; ++ u16 size; ++ char fw_id[20]; ++ u32 hw_id; ++} ACX_PACKED fw_ver_t; ++ ++#define FW_ID_SIZE 20 ++ ++typedef struct shared_queueindicator { ++ u32 indicator; ++ u16 host_lock; ++ u16 fw_lock; ++} ACX_PACKED queueindicator_t; ++ ++/*--- WEP stuff --------------------------------------------------------------*/ ++#define DOT11_MAX_DEFAULT_WEP_KEYS 4 ++ ++/* non-firmware struct, no packing necessary */ ++typedef struct wep_key { ++ size_t size; /* most often used member first */ ++ u8 index; ++ u8 key[29]; ++ u16 strange_filler; ++} wep_key_t; /* size = 264 bytes (33*8) */ ++/* FIXME: We don't have size 264! Or is there 2 bytes beyond the key ++ * (strange_filler)? */ ++ ++/* non-firmware struct, no packing necessary */ ++typedef struct key_struct { ++ u8 addr[ETH_ALEN]; /* 0x00 */ ++ u16 filler1; /* 0x06 */ ++ u32 filler2; /* 0x08 */ ++ u32 index; /* 0x0c */ ++ u16 len; /* 0x10 */ ++ u8 key[29]; /* 0x12; is this long enough??? */ ++} key_struct_t; /* size = 276. FIXME: where is the remaining space?? */ ++ ++ ++/*--- Client (peer) info -----------------------------------------------------*/ ++/* adev->sta_list[] is used for: ++** accumulating and processing of scan results ++** keeping client info in AP mode ++** keeping AP info in STA mode (AP is the only one 'client') ++** keeping peer info in ad-hoc mode ++** non-firmware struct --> no packing necessary */ ++enum { ++ CLIENT_EMPTY_SLOT_0 = 0, ++ CLIENT_EXIST_1 = 1, ++ CLIENT_AUTHENTICATED_2 = 2, ++ CLIENT_ASSOCIATED_3 = 3, ++ CLIENT_JOIN_CANDIDATE = 4 ++}; ++struct client { ++ /* most frequent access first */ ++ u8 used; /* misnamed, more like 'status' */ ++ struct client* next; ++ unsigned long mtime; /* last time we heard it, in jiffies */ ++ size_t essid_len; /* length of ESSID (without '\0') */ ++ u32 sir; /* Standard IR */ ++ u32 snr; /* Signal to Noise Ratio */ ++ u16 aid; /* association ID */ ++ u16 seq; /* from client's auth req */ ++ u16 auth_alg; /* from client's auth req */ ++ u16 cap_info; /* from client's assoc req */ ++ u16 rate_cap; /* what client supports (all rates) */ ++ u16 rate_bas; /* what client supports (basic rates) */ ++ u16 rate_cfg; /* what is allowed (by iwconfig etc) */ ++ u16 rate_cur; /* currently used rate mask */ ++ u8 rate_100; /* currently used rate byte (acx100 only) */ ++ u8 address[ETH_ALEN]; ++ u8 bssid[ETH_ALEN]; /* ad-hoc hosts can have bssid != mac */ ++ u8 channel; ++ u8 auth_step; ++ u8 ignore_count; ++ u8 fallback_count; ++ u8 stepup_count; ++ char essid[IW_ESSID_MAX_SIZE + 1]; /* ESSID and trailing '\0' */ ++/* FIXME: this one is too damn big */ ++ char challenge_text[WLAN_CHALLENGE_LEN]; ++}; ++ ++ ++/*********************************************************************** ++** Hardware structures ++*/ ++ ++/* An opaque typesafe helper type ++ * ++ * Some hardware fields are actually pointers, ++ * but they have to remain u32, since using ptr instead ++ * (8 bytes on 64bit systems!) would disrupt the fixed descriptor ++ * format the acx firmware expects in the non-user area. ++ * Since we cannot cram an 8 byte ptr into 4 bytes, we need to ++ * enforce that pointed to data remains in low memory ++ * (address value needs to fit in 4 bytes) on 64bit systems. ++ * ++ * This is easy to get wrong, thus we are using a small struct ++ * and special macros to access it. Macros will check for ++ * attempts to overflow an acx_ptr with value > 0xffffffff. ++ * ++ * Attempts to use acx_ptr without macros result in compile-time errors */ ++ ++typedef struct { ++ u32 v; ++} ACX_PACKED acx_ptr; ++ ++#if ACX_DEBUG ++#define CHECK32(n) BUG_ON(sizeof(n)>4 && (long)(n)>0xffffff00) ++#else ++#define CHECK32(n) ((void)0) ++#endif ++ ++/* acx_ptr <-> integer conversion */ ++#define cpu2acx(n) ({ CHECK32(n); ((acx_ptr){ .v = cpu_to_le32(n) }); }) ++#define acx2cpu(a) (le32_to_cpu(a.v)) ++ ++/* acx_ptr <-> pointer conversion */ ++#define ptr2acx(p) ({ CHECK32(p); ((acx_ptr){ .v = cpu_to_le32((u32)(long)(p)) }); }) ++#define acx2ptr(a) ((void*)le32_to_cpu(a.v)) ++ ++/* Values for rate field (acx100 only) */ ++#define RATE100_1 10 ++#define RATE100_2 20 ++#define RATE100_5 55 ++#define RATE100_11 110 ++#define RATE100_22 220 ++/* This bit denotes use of PBCC: ++** (PBCC encoding is usable with 11 and 22 Mbps speeds only) */ ++#define RATE100_PBCC511 0x80 ++ ++/* Bit values for rate111 field */ ++#define RATE111_1 0x0001 /* DBPSK */ ++#define RATE111_2 0x0002 /* DQPSK */ ++#define RATE111_5 0x0004 /* CCK or PBCC */ ++#define RATE111_6 0x0008 /* CCK-OFDM or OFDM */ ++#define RATE111_9 0x0010 /* CCK-OFDM or OFDM */ ++#define RATE111_11 0x0020 /* CCK or PBCC */ ++#define RATE111_12 0x0040 /* CCK-OFDM or OFDM */ ++#define RATE111_18 0x0080 /* CCK-OFDM or OFDM */ ++#define RATE111_22 0x0100 /* PBCC */ ++#define RATE111_24 0x0200 /* CCK-OFDM or OFDM */ ++#define RATE111_36 0x0400 /* CCK-OFDM or OFDM */ ++#define RATE111_48 0x0800 /* CCK-OFDM or OFDM */ ++#define RATE111_54 0x1000 /* CCK-OFDM or OFDM */ ++#define RATE111_RESERVED 0x2000 ++#define RATE111_PBCC511 0x4000 /* PBCC mod at 5.5 or 11Mbit (else CCK) */ ++#define RATE111_SHORTPRE 0x8000 /* short preamble */ ++/* Special 'try everything' value */ ++#define RATE111_ALL 0x1fff ++/* These bits denote acx100 compatible settings */ ++#define RATE111_ACX100_COMPAT 0x0127 ++/* These bits denote 802.11b compatible settings */ ++#define RATE111_80211B_COMPAT 0x0027 ++ ++/* Descriptor Ctl field bits ++ * init value is 0x8e, "idle" value is 0x82 (in idle tx descs) ++ */ ++#define DESC_CTL_SHORT_PREAMBLE 0x01 /* preamble type: 0 = long; 1 = short */ ++#define DESC_CTL_FIRSTFRAG 0x02 /* this is the 1st frag of the frame */ ++#define DESC_CTL_AUTODMA 0x04 ++#define DESC_CTL_RECLAIM 0x08 /* ready to reuse */ ++#define DESC_CTL_HOSTDONE 0x20 /* host has finished processing */ ++#define DESC_CTL_ACXDONE 0x40 /* acx has finished processing */ ++/* host owns the desc [has to be released last, AFTER modifying all other desc fields!] */ ++#define DESC_CTL_HOSTOWN 0x80 ++#define DESC_CTL_ACXDONE_HOSTOWN (DESC_CTL_ACXDONE | DESC_CTL_HOSTOWN) ++ ++/* Descriptor Status field ++ */ ++#define DESC_STATUS_FULL (1 << 31) ++ ++/* NB: some bits may be interesting for Monitor mode tx (aka Raw tx): */ ++#define DESC_CTL2_SEQ 0x01 /* don't increase sequence field */ ++#define DESC_CTL2_FCS 0x02 /* don't add the FCS */ ++#define DESC_CTL2_MORE_FRAG 0x04 ++#define DESC_CTL2_RETRY 0x08 /* don't increase retry field */ ++#define DESC_CTL2_POWER 0x10 /* don't increase power mgmt. field */ ++#define DESC_CTL2_RTS 0x20 /* do RTS/CTS magic before sending */ ++#define DESC_CTL2_WEP 0x40 /* encrypt this frame */ ++#define DESC_CTL2_DUR 0x80 /* don't increase duration field */ ++ ++/*********************************************************************** ++** PCI structures ++*/ ++/* IRQ Constants ++** (outside of "#ifdef PCI" because USB (mis)uses HOST_INT_SCAN_COMPLETE) */ ++#define HOST_INT_RX_DATA 0x0001 ++#define HOST_INT_TX_COMPLETE 0x0002 ++#define HOST_INT_TX_XFER 0x0004 ++#define HOST_INT_RX_COMPLETE 0x0008 ++#define HOST_INT_DTIM 0x0010 ++#define HOST_INT_BEACON 0x0020 ++#define HOST_INT_TIMER 0x0040 ++#define HOST_INT_KEY_NOT_FOUND 0x0080 ++#define HOST_INT_IV_ICV_FAILURE 0x0100 ++#define HOST_INT_CMD_COMPLETE 0x0200 ++#define HOST_INT_INFO 0x0400 ++#define HOST_INT_OVERFLOW 0x0800 ++#define HOST_INT_PROCESS_ERROR 0x1000 ++#define HOST_INT_SCAN_COMPLETE 0x2000 ++#define HOST_INT_FCS_THRESHOLD 0x4000 ++#define HOST_INT_UNKNOWN 0x8000 ++ ++/* Outside of "#ifdef PCI" because USB needs to know sizeof() ++** of txdesc and rxdesc: */ ++struct txdesc { ++ acx_ptr pNextDesc; /* pointer to next txdesc */ ++ acx_ptr HostMemPtr; /* 0x04 */ ++ acx_ptr AcxMemPtr; /* 0x08 */ ++ u32 tx_time; /* 0x0c */ ++ u16 total_length; /* 0x10 */ ++ u16 Reserved; /* 0x12 */ ++ ++/* The following 16 bytes do not change when acx100 owns the descriptor */ ++/* BUG: fw clears last byte of this area which is supposedly reserved ++** for driver use. amd64 blew up. We dare not use it now */ ++ u32 dummy[4]; ++ ++ u8 Ctl_8; /* 0x24, 8bit value */ ++ u8 Ctl2_8; /* 0x25, 8bit value */ ++ u8 error; /* 0x26 */ ++ u8 ack_failures; /* 0x27 */ ++ ++ union { ++ /* ++ * Packing doesn't work correctly on ARM unless unions are on ++ * 4 byte boundaries. ++ */ ++ struct { ++ u8 rts_failures; /* 0x28 */ ++ u8 rts_ok; /* 0x29 */ ++ u16 d1; ++ } ACX_PACKED rts; ++ struct { ++ u16 d1; ++ u8 rate; /* 0x2a */ ++ u8 queue_ctrl; /* 0x2b */ ++ } ACX_PACKED r1; ++ struct { ++ u16 d1; ++ u16 rate111; /* 0x2a */ ++ } ACX_PACKED r2; ++ } ACX_PACKED u; ++ u32 queue_info; /* 0x2c (acx100, reserved on acx111) */ ++} ACX_PACKED; /* size : 48 = 0x30 */ ++/* NB: acx111 txdesc structure is 4 byte larger */ ++/* All these 4 extra bytes are reserved. tx alloc code takes them into account */ ++ ++struct rxdesc { ++ acx_ptr pNextDesc; /* 0x00 */ ++ acx_ptr HostMemPtr; /* 0x04 */ ++ acx_ptr ACXMemPtr; /* 0x08 */ ++ u32 rx_time; /* 0x0c */ ++ u16 total_length; /* 0x10 */ ++ u16 WEP_length; /* 0x12 */ ++ u32 WEP_ofs; /* 0x14 */ ++ ++/* the following 16 bytes do not change when acx100 owns the descriptor */ ++ u8 driverWorkspace[16]; /* 0x18 */ ++ ++ u8 Ctl_8; ++ u8 rate; ++ u8 error; ++ u8 SNR; /* Signal-to-Noise Ratio */ ++ u8 RxLevel; ++ u8 queue_ctrl; ++ u16 unknown; ++ u32 unknown2; ++} ACX_PACKED; /* size 52 = 0x34 */ ++ ++#if defined(ACX_PCI) || defined(ACX_MEM) ++ ++/* Register I/O offsets */ ++#define ACX100_EEPROM_ID_OFFSET 0x380 ++ ++/* please add further ACX hardware register definitions only when ++ it turns out you need them in the driver, and please try to use ++ firmware functionality instead, since using direct I/O access instead ++ of letting the firmware do it might confuse the firmware's state ++ machine */ ++ ++/* ***** ABSOLUTELY ALWAYS KEEP OFFSETS IN SYNC WITH THE INITIALIZATION ++** OF THE I/O ARRAYS!!!! (grep for '^IO_ACX') ***** */ ++enum { ++ IO_ACX_SOFT_RESET = 0, ++ ++ IO_ACX_SLV_MEM_ADDR, ++ IO_ACX_SLV_MEM_DATA, ++ IO_ACX_SLV_MEM_CTL, ++ IO_ACX_SLV_END_CTL, ++ ++ IO_ACX_FEMR, /* Function Event Mask */ ++ ++ IO_ACX_INT_TRIG, ++ IO_ACX_IRQ_MASK, ++ IO_ACX_IRQ_STATUS_NON_DES, ++ IO_ACX_IRQ_STATUS_CLEAR, /* CLEAR = clear on read */ ++ IO_ACX_IRQ_ACK, ++ IO_ACX_HINT_TRIG, ++ ++ IO_ACX_ENABLE, ++ ++ IO_ACX_EEPROM_CTL, ++ IO_ACX_EEPROM_ADDR, ++ IO_ACX_EEPROM_DATA, ++ IO_ACX_EEPROM_CFG, ++ ++ IO_ACX_PHY_ADDR, ++ IO_ACX_PHY_DATA, ++ IO_ACX_PHY_CTL, ++ ++ IO_ACX_GPIO_OE, ++ ++ IO_ACX_GPIO_OUT, ++ ++ IO_ACX_CMD_MAILBOX_OFFS, ++ IO_ACX_INFO_MAILBOX_OFFS, ++ IO_ACX_EEPROM_INFORMATION, ++ ++ IO_ACX_EE_START, ++ IO_ACX_SOR_CFG, ++ IO_ACX_ECPU_CTRL ++}; ++/* ***** ABSOLUTELY ALWAYS KEEP OFFSETS IN SYNC WITH THE INITIALIZATION ++** OF THE I/O ARRAYS!!!! (grep for '^IO_ACX') ***** */ ++ ++/* Values for IO_ACX_INT_TRIG register: */ ++/* inform hw that rxdesc in queue needs processing */ ++#define INT_TRIG_RXPRC 0x08 ++/* inform hw that txdesc in queue needs processing */ ++#define INT_TRIG_TXPRC 0x04 ++/* ack that we received info from info mailbox */ ++#define INT_TRIG_INFOACK 0x02 ++/* inform hw that we have filled command mailbox */ ++#define INT_TRIG_CMD 0x01 ++ ++struct txhostdesc { ++ acx_ptr data_phy; /* 0x00 [u8 *] */ ++ u16 data_offset; /* 0x04 */ ++ u16 reserved; /* 0x06 */ ++ u16 Ctl_16; /* 16bit value, endianness!! */ ++ u16 length; /* 0x0a */ ++ acx_ptr desc_phy_next; /* 0x0c [txhostdesc *] */ ++ acx_ptr pNext; /* 0x10 [txhostdesc *] */ ++ u32 Status; /* 0x14, unused on Tx */ ++/* From here on you can use this area as you want (variable length, too!) */ ++ u8 *data; ++} ACX_PACKED; ++ ++struct rxhostdesc { ++ acx_ptr data_phy; /* 0x00 [rxbuffer_t *] */ ++ u16 data_offset; /* 0x04 */ ++ u16 reserved; /* 0x06 */ ++ u16 Ctl_16; /* 0x08; 16bit value, endianness!! */ ++ u16 length; /* 0x0a */ ++ acx_ptr desc_phy_next; /* 0x0c [rxhostdesc_t *] */ ++ acx_ptr pNext; /* 0x10 [rxhostdesc_t *] */ ++ u32 Status; /* 0x14 */ ++/* From here on you can use this area as you want (variable length, too!) */ ++ rxbuffer_t *data; ++} ACX_PACKED; ++ ++#endif /* ACX_PCI */ ++ ++/*********************************************************************** ++** USB structures and constants ++*/ ++#ifdef ACX_USB ++ ++/* Used for usb_txbuffer.desc field */ ++#define USB_TXBUF_TXDESC 0xA ++/* Size of header (everything up to data[]) */ ++#define USB_TXBUF_HDRSIZE 14 ++typedef struct usb_txbuffer { ++ u16 desc; ++ u16 mpdu_len; ++ u8 queue_index; ++ u8 rate; ++ u32 hostdata; ++ u8 ctrl1; ++ u8 ctrl2; ++ u16 data_len; ++ /* wlan packet content is placed here: */ ++ u8 data[WLAN_A4FR_MAXLEN_WEP_FCS]; ++} ACX_PACKED usb_txbuffer_t; ++ ++/* USB returns either rx packets (see rxbuffer) or ++** these "tx status" structs: */ ++typedef struct usb_txstatus { ++ u16 mac_cnt_rcvd; /* only 12 bits are len! (0xfff) */ ++ u8 queue_index; ++ u8 mac_status; /* seen 0x20 on tx failure */ ++ u32 hostdata; ++ u8 rate; ++ u8 ack_failures; ++ u8 rts_failures; ++ u8 rts_ok; ++} ACX_PACKED usb_txstatus_t; ++ ++typedef struct usb_tx { ++ unsigned busy:1; ++ struct urb *urb; ++ acx_device_t *adev; ++ /* actual USB bulk output data block is here: */ ++ usb_txbuffer_t bulkout; ++} usb_tx_t; ++ ++struct usb_rx_plain { ++ unsigned busy:1; ++ struct urb *urb; ++ acx_device_t *adev; ++ rxbuffer_t bulkin; ++}; ++ ++typedef struct usb_rx { ++ unsigned busy:1; ++ struct urb *urb; ++ acx_device_t *adev; ++ rxbuffer_t bulkin; ++ /* Make entire structure 4k. Report if it breaks something. */ ++ u8 padding[4*1024 - sizeof(struct usb_rx_plain)]; ++} usb_rx_t; ++#endif /* ACX_USB */ ++ ++ ++/* Config Option structs */ ++ ++typedef struct co_antennas { ++ u8 type; ++ u8 len; ++ u8 list[2]; ++} ACX_PACKED co_antennas_t; ++ ++typedef struct co_powerlevels { ++ u8 type; ++ u8 len; ++ u16 list[8]; ++} ACX_PACKED co_powerlevels_t; ++ ++typedef struct co_datarates { ++ u8 type; ++ u8 len; ++ u8 list[8]; ++} ACX_PACKED co_datarates_t; ++ ++typedef struct co_domains { ++ u8 type; ++ u8 len; ++ u8 list[6]; ++} ACX_PACKED co_domains_t; ++ ++typedef struct co_product_id { ++ u8 type; ++ u8 len; ++ u8 list[128]; ++} ACX_PACKED co_product_id_t; ++ ++typedef struct co_manuf_id { ++ u8 type; ++ u8 len; ++ u8 list[128]; ++} ACX_PACKED co_manuf_t; ++ ++typedef struct co_fixed { ++ char NVSv[8]; ++/* u16 NVS_vendor_offs; ACX111-only */ ++/* u16 unknown; ACX111-only */ ++ u8 MAC[6]; /* ACX100-only */ ++ u16 probe_delay; /* ACX100-only */ ++ u32 eof_memory; ++ u8 dot11CCAModes; ++ u8 dot11Diversity; ++ u8 dot11ShortPreambleOption; ++ u8 dot11PBCCOption; ++ u8 dot11ChannelAgility; ++ u8 dot11PhyType; /* FIXME: does 802.11 call it "dot11PHYType"? */ ++ u8 dot11TempType; ++ u8 table_count; ++} ACX_PACKED co_fixed_t; ++ ++typedef struct acx111_ie_configoption { ++ u16 type; ++ u16 len; ++/* Do not access below members directly, they are in fact variable length */ ++ co_fixed_t fixed; ++ co_antennas_t antennas; ++ co_powerlevels_t power_levels; ++ co_datarates_t data_rates; ++ co_domains_t domains; ++ co_product_id_t product_id; ++ co_manuf_t manufacturer; ++ u8 _padding[4]; ++} ACX_PACKED acx111_ie_configoption_t; ++ ++ ++/*********************************************************************** ++** Main acx per-device data structure ++*/ ++#define ACX_STATE_FW_LOADED 0x01 ++#define ACX_STATE_IFACE_UP 0x02 ++ ++/* MAC mode (BSS type) defines ++ * Note that they shouldn't be redefined, since they are also used ++ * during communication with firmware */ ++#define ACX_MODE_0_ADHOC 0 ++#define ACX_MODE_1_UNUSED 1 ++#define ACX_MODE_2_STA 2 ++#define ACX_MODE_3_AP 3 ++/* These are our own inventions. Sending these to firmware ++** makes it stop emitting beacons, which is exactly what we want ++** for these modes */ ++#define ACX_MODE_MONITOR 0xfe ++#define ACX_MODE_OFF 0xff ++/* 'Submode': identifies exact status of ADHOC/STA host */ ++#define ACX_STATUS_0_STOPPED 0 ++#define ACX_STATUS_1_SCANNING 1 ++#define ACX_STATUS_2_WAIT_AUTH 2 ++#define ACX_STATUS_3_AUTHENTICATED 3 ++#define ACX_STATUS_4_ASSOCIATED 4 ++ ++/* FIXME: this should be named something like struct acx_priv (typedef'd to ++ * acx_priv_t) */ ++ ++/* non-firmware struct, no packing necessary */ ++struct acx_device { ++ /* most frequent accesses first (dereferencing and cache line!) */ ++ ++ /*** Locking ***/ ++ /* FIXME: try to convert semaphore to more efficient mutex according ++ to Ingo Molnar's docs (but not before driver is in mainline or ++ pre-mutex Linux 2.6.10 is very outdated). */ ++ struct semaphore sem; ++ spinlock_t lock; ++#if defined(PARANOID_LOCKING) /* Lock debugging */ ++ const char *last_sem; ++ const char *last_lock; ++ unsigned long sem_time; ++ unsigned long lock_time; ++#endif ++#ifdef ACX_MEM ++ spinlock_t txbuf_lock; ++#endif ++ ++ /*** Linux network device ***/ ++ struct net_device *ndev; /* pointer to linux netdevice */ ++ ++ /*** Device statistics ***/ ++ struct net_device_stats stats; /* net device statistics */ ++#ifdef WIRELESS_EXT ++ struct iw_statistics wstats; /* wireless statistics */ ++#endif ++ /*** Power managment ***/ ++ struct pm_dev *pm; /* PM crap */ ++ ++ /*** Management timer ***/ ++ struct timer_list mgmt_timer; ++ ++ /*** Hardware identification ***/ ++ const char *chip_name; ++ u8 dev_type; ++ u8 chip_type; ++ u8 form_factor; ++ u8 radio_type; ++ u8 eeprom_version; ++ ++ /*** Config retrieved from EEPROM ***/ ++ char cfgopt_NVSv[8]; ++ u16 cfgopt_NVS_vendor_offs; ++ u8 cfgopt_MAC[6]; ++ u16 cfgopt_probe_delay; ++ u32 cfgopt_eof_memory; ++ u8 cfgopt_dot11CCAModes; ++ u8 cfgopt_dot11Diversity; ++ u8 cfgopt_dot11ShortPreambleOption; ++ u8 cfgopt_dot11PBCCOption; ++ u8 cfgopt_dot11ChannelAgility; ++ u8 cfgopt_dot11PhyType; ++ u8 cfgopt_dot11TempType; ++ co_antennas_t cfgopt_antennas; ++ co_powerlevels_t cfgopt_power_levels; ++ co_datarates_t cfgopt_data_rates; ++ co_domains_t cfgopt_domains; ++ co_product_id_t cfgopt_product_id; ++ co_manuf_t cfgopt_manufacturer; ++ ++ /*** Firmware identification ***/ ++ char firmware_version[FW_ID_SIZE+1]; ++ u32 firmware_numver; ++ u32 firmware_id; ++ const u16 *ie_len; ++ const u16 *ie_len_dot11; ++ ++ /*** Device state ***/ ++ u16 dev_state_mask; ++ u8 led_power; /* power LED status */ ++ u32 get_mask; /* mask of settings to fetch from the card */ ++ u32 set_mask; /* mask of settings to write to the card */ ++ ++ /* Barely used in USB case */ ++ u16 irq_status; ++ ++ u8 after_interrupt_jobs; /* mini job list for doing actions after an interrupt occurred */ ++ WORK_STRUCT after_interrupt_task; /* our task for after interrupt actions */ ++ ++ /*** scanning ***/ ++ u16 scan_count; /* number of times to do channel scan */ ++ u8 scan_mode; /* 0 == active, 1 == passive, 2 == background */ ++ u8 scan_rate; ++ u16 scan_duration; ++ u16 scan_probe_delay; ++#if WIRELESS_EXT > 15 ++ struct iw_spy_data spy_data; /* FIXME: needs to be implemented! */ ++#endif ++ ++ /*** Wireless network settings ***/ ++ /* copy of the device address (ifconfig hw ether) that we actually use ++ ** for 802.11; copied over from the network device's MAC address ++ ** (ifconfig) when it makes sense only */ ++ u8 dev_addr[MAX_ADDR_LEN]; ++ u8 bssid[ETH_ALEN]; /* the BSSID after having joined */ ++ u8 ap[ETH_ALEN]; /* The AP we want, FF:FF:FF:FF:FF:FF is any */ ++ u16 aid; /* The Association ID sent from the AP / last used AID if we're an AP */ ++ u16 mode; /* mode from iwconfig */ ++ int monitor_type; /* ARPHRD_IEEE80211 or ARPHRD_IEEE80211_PRISM */ ++ u16 status; /* 802.11 association status */ ++ u8 essid_active; /* specific ESSID active, or select any? */ ++ u8 essid_len; /* to avoid dozens of strlen() */ ++ /* INCLUDES \0 termination for easy printf - but many places ++ ** simply want the string data memcpy'd plus a length indicator! ++ ** Keep that in mind... */ ++ char essid[IW_ESSID_MAX_SIZE+1]; ++ /* essid we are going to use for association, in case of "essid 'any'" ++ ** and in case of hidden ESSID (use configured ESSID then) */ ++ char essid_for_assoc[IW_ESSID_MAX_SIZE+1]; ++ char nick[IW_ESSID_MAX_SIZE+1]; /* see essid! */ ++ u8 channel; ++ u8 reg_dom_id; /* reg domain setting */ ++ u16 reg_dom_chanmask; ++ u16 auth_or_assoc_retries; ++ u16 scan_retries; ++ unsigned long scan_start; /* YES, jiffies is defined as "unsigned long" */ ++ ++ /* stations known to us (if we're an ap) */ ++ client_t sta_list[32]; /* tab is larger than list, so that */ ++ client_t *sta_hash_tab[64]; /* hash collisions are not likely */ ++ client_t *ap_client; /* this one is our AP (STA mode only) */ ++ ++ int dup_count; ++ int nondup_count; ++ unsigned long dup_msg_expiry; ++ u16 last_seq_ctrl; /* duplicate packet detection */ ++ ++ /* 802.11 power save mode */ ++ u8 ps_wakeup_cfg; ++ u8 ps_listen_interval; ++ u8 ps_options; ++ u8 ps_hangover_period; ++ u32 ps_enhanced_transition_time; ++ u32 ps_beacon_rx_time; ++ ++ /*** PHY settings ***/ ++ u8 fallback_threshold; ++ u8 stepup_threshold; ++ u16 rate_basic; ++ u16 rate_oper; ++ u16 rate_bcast; ++ u16 rate_bcast100; ++ u8 rate_auto; /* false if "iwconfig rate N" (WITHOUT 'auto'!) */ ++ u8 preamble_mode; /* 0 == Long Preamble, 1 == Short, 2 == Auto */ ++ u8 preamble_cur; ++ ++ u8 tx_disabled; ++ u8 tx_level_dbm; ++ /* u8 tx_level_val; */ ++ /* u8 tx_level_auto; whether to do automatic power adjustment */ ++ ++ unsigned long recalib_time_last_success; ++ unsigned long recalib_time_last_attempt; ++ int recalib_failure_count; ++ int recalib_msg_ratelimit; ++ int retry_errors_msg_ratelimit; ++ ++ unsigned long brange_time_last_state_change; /* time the power LED was last changed */ ++ u8 brange_last_state; /* last state of the LED */ ++ u8 brange_max_quality; /* maximum quality that equates to full speed */ ++ ++ u8 sensitivity; ++ u8 antenna; /* antenna settings */ ++ u8 ed_threshold; /* energy detect threshold */ ++ u8 cca; /* clear channel assessment */ ++ ++ u16 rts_threshold; ++ u16 frag_threshold; ++ u32 short_retry; ++ u32 long_retry; ++ u16 msdu_lifetime; ++ u16 listen_interval; /* given in units of beacon interval */ ++ u32 beacon_interval; ++ ++ u16 capabilities; ++ u8 rate_supported_len; ++ u8 rate_supported[13]; ++ ++ /*** Encryption settings (WEP) ***/ ++ u32 auth_alg; /* used in transmit_authen1 */ ++ u8 wep_enabled; ++ u8 wep_restricted; ++ u8 wep_current_index; ++ wep_key_t wep_keys[DOT11_MAX_DEFAULT_WEP_KEYS]; /* the default WEP keys */ ++ key_struct_t wep_key_struct[10]; ++ ++ /*** Unknown ***/ ++ u8 dtim_interval; ++ ++#ifdef ACX_MEM ++ u32 acx_txbuf_start; ++ int acx_txbuf_numblocks; ++ u32 acx_txbuf_free; /* addr of head of free list */ ++ int acx_txbuf_blocks_free; /* how many are still open */ ++ queueindicator_t *acx_queue_indicator; ++#endif ++ ++ /*** Card Rx/Tx management ***/ ++ u16 rx_config_1; ++ u16 rx_config_2; ++ u16 memblocksize; ++ unsigned int tx_free; ++ unsigned int tx_head; /* keep as close as possible to Tx stuff below (cache line) */ ++ u16 phy_header_len; ++ ++/************************************************************************* ++ *** PCI/USB/... must be last or else hw agnostic code breaks horribly *** ++ *************************************************************************/ ++ ++ /* hack to let common code compile. FIXME */ ++ dma_addr_t rxhostdesc_startphy; ++ ++ /*** PCI stuff ***/ ++#if defined(ACX_PCI) || defined(ACX_MEM) ++ /* pointers to tx buffers, tx host descriptors (in host memory) ++ ** and tx descs in device memory */ ++ unsigned int tx_tail; ++ u8 *txbuf_start; ++ txhostdesc_t *txhostdesc_start; ++ txdesc_t *txdesc_start; /* points to PCI-mapped memory */ ++ dma_addr_t txbuf_startphy; ++ dma_addr_t txhostdesc_startphy; ++ /* sizes of above host memory areas */ ++ unsigned int txbuf_area_size; ++ unsigned int txhostdesc_area_size; ++ ++ unsigned int txdesc_size; /* size of txdesc; ACX111 = ACX100 + 4 */ ++ client_t *txc[TX_CNT]; ++ u16 txr[TX_CNT]; ++ ++ /* same for rx */ ++ unsigned int rx_tail; ++ rxbuffer_t *rxbuf_start; ++ rxhostdesc_t *rxhostdesc_start; ++ rxdesc_t *rxdesc_start; ++ /* physical addresses of above host memory areas */ ++ dma_addr_t rxbuf_startphy; ++ /* dma_addr_t rxhostdesc_startphy; */ ++ unsigned int rxbuf_area_size; ++ unsigned int rxhostdesc_area_size; ++ ++ u8 need_radio_fw; ++ u8 irqs_active; /* whether irq sending is activated */ ++ ++ const u16 *io; /* points to ACX100 or ACX111 PCI I/O register address set */ ++ ++#ifdef ACX_PCI ++ struct pci_dev *pdev; ++#endif ++#ifdef ACX_MEM ++ struct device *dev; ++#endif ++ ++#ifdef ACX_PCI ++ unsigned long membase; ++#endif ++#ifdef ACX_MEM ++ volatile u32 *membase; ++#endif ++ unsigned long membase2; ++#ifdef ACX_PCI ++ void __iomem *iobase; ++#endif ++#ifdef ACX_MEM ++ volatile u32 *iobase; ++#endif ++ void __iomem *iobase2; ++ /* command interface */ ++ u8 __iomem *cmd_area; ++ u8 __iomem *info_area; ++ ++ u16 irq_mask; /* interrupt types to mask out (not wanted) with many IRQs activated */ ++ u16 irq_mask_off; /* interrupt types to mask out (not wanted) with IRQs off */ ++ unsigned int irq_loops_this_jiffy; ++ unsigned long irq_last_jiffies; ++#endif ++ ++ /*** USB stuff ***/ ++#ifdef ACX_USB ++ struct usb_device *usbdev; ++ ++ rxbuffer_t rxtruncbuf; ++ ++ usb_tx_t *usb_tx; ++ usb_rx_t *usb_rx; ++ ++ int bulkinep; /* bulk-in endpoint */ ++ int bulkoutep; /* bulk-out endpoint */ ++ int rxtruncsize; ++#endif ++ ++}; ++ ++static inline acx_device_t* ++ndev2adev(struct net_device *ndev) ++{ ++ return netdev_priv(ndev); ++} ++ ++ ++/* For use with ACX1xx_IE_RXCONFIG */ ++/* bit description ++ * 13 include additional header (length etc.) *required* ++ * struct is defined in 'struct rxbuffer' ++ * is this bit acx100 only? does acx111 always put the header, ++ * and bit setting is irrelevant? --vda ++ * 10 receive frames only with SSID used in last join cmd ++ * 9 discard broadcast ++ * 8 receive packets for multicast address 1 ++ * 7 receive packets for multicast address 0 ++ * 6 discard all multicast packets ++ * 5 discard frames from foreign BSSID ++ * 4 discard frames with foreign destination MAC address ++ * 3 promiscuous mode (receive ALL frames, disable filter) ++ * 2 include FCS ++ * 1 include phy header ++ * 0 ??? ++ */ ++#define RX_CFG1_INCLUDE_RXBUF_HDR 0x2000 /* ACX100 only */ ++#define RX_CFG1_FILTER_SSID 0x0400 ++#define RX_CFG1_FILTER_BCAST 0x0200 ++#define RX_CFG1_RCV_MC_ADDR1 0x0100 ++#define RX_CFG1_RCV_MC_ADDR0 0x0080 ++#define RX_CFG1_FILTER_ALL_MULTI 0x0040 ++#define RX_CFG1_FILTER_BSSID 0x0020 ++#define RX_CFG1_FILTER_MAC 0x0010 ++#define RX_CFG1_RCV_PROMISCUOUS 0x0008 ++#define RX_CFG1_INCLUDE_FCS 0x0004 ++#define RX_CFG1_INCLUDE_PHY_HDR (WANT_PHY_HDR ? 0x0002 : 0) ++/* bit description ++ * 11 receive association requests etc. ++ * 10 receive authentication frames ++ * 9 receive beacon frames ++ * 8 receive contention free packets ++ * 7 receive control frames ++ * 6 receive data frames ++ * 5 receive broken frames ++ * 4 receive management frames ++ * 3 receive probe requests ++ * 2 receive probe responses ++ * 1 receive RTS/CTS/ACK frames ++ * 0 receive other ++ */ ++#define RX_CFG2_RCV_ASSOC_REQ 0x0800 ++#define RX_CFG2_RCV_AUTH_FRAMES 0x0400 ++#define RX_CFG2_RCV_BEACON_FRAMES 0x0200 ++#define RX_CFG2_RCV_CONTENTION_FREE 0x0100 ++#define RX_CFG2_RCV_CTRL_FRAMES 0x0080 ++#define RX_CFG2_RCV_DATA_FRAMES 0x0040 ++#define RX_CFG2_RCV_BROKEN_FRAMES 0x0020 ++#define RX_CFG2_RCV_MGMT_FRAMES 0x0010 ++#define RX_CFG2_RCV_PROBE_REQ 0x0008 ++#define RX_CFG2_RCV_PROBE_RESP 0x0004 ++#define RX_CFG2_RCV_ACK_FRAMES 0x0002 ++#define RX_CFG2_RCV_OTHER 0x0001 ++ ++/* For use with ACX1xx_IE_FEATURE_CONFIG */ ++#define FEATURE1_80MHZ_CLOCK 0x00000040L ++#define FEATURE1_4X 0x00000020L ++#define FEATURE1_LOW_RX 0x00000008L ++#define FEATURE1_EXTRA_LOW_RX 0x00000001L ++ ++#define FEATURE2_SNIFFER 0x00000080L ++#define FEATURE2_NO_TXCRYPT 0x00000001L ++ ++/*-- get and set mask values --*/ ++#define GETSET_LED_POWER 0x00000001L ++#define GETSET_STATION_ID 0x00000002L ++#define SET_TEMPLATES 0x00000004L ++#define SET_STA_LIST 0x00000008L ++#define GETSET_TX 0x00000010L ++#define GETSET_RX 0x00000020L ++#define SET_RXCONFIG 0x00000040L ++#define GETSET_ANTENNA 0x00000080L ++#define GETSET_SENSITIVITY 0x00000100L ++#define GETSET_TXPOWER 0x00000200L ++#define GETSET_ED_THRESH 0x00000400L ++#define GETSET_CCA 0x00000800L ++#define GETSET_POWER_80211 0x00001000L ++#define GETSET_RETRY 0x00002000L ++#define GETSET_REG_DOMAIN 0x00004000L ++#define GETSET_CHANNEL 0x00008000L ++/* Used when ESSID changes etc and we need to scan for AP anew */ ++#define GETSET_RESCAN 0x00010000L ++#define GETSET_MODE 0x00020000L ++#define GETSET_WEP 0x00040000L ++#define SET_WEP_OPTIONS 0x00080000L ++#define SET_MSDU_LIFETIME 0x00100000L ++#define SET_RATE_FALLBACK 0x00200000L ++ ++/* keep in sync with the above */ ++#define GETSET_ALL (0 \ ++/* GETSET_LED_POWER */ | 0x00000001L \ ++/* GETSET_STATION_ID */ | 0x00000002L \ ++/* SET_TEMPLATES */ | 0x00000004L \ ++/* SET_STA_LIST */ | 0x00000008L \ ++/* GETSET_TX */ | 0x00000010L \ ++/* GETSET_RX */ | 0x00000020L \ ++/* SET_RXCONFIG */ | 0x00000040L \ ++/* GETSET_ANTENNA */ | 0x00000080L \ ++/* GETSET_SENSITIVITY */| 0x00000100L \ ++/* GETSET_TXPOWER */ | 0x00000200L \ ++/* GETSET_ED_THRESH */ | 0x00000400L \ ++/* GETSET_CCA */ | 0x00000800L \ ++/* GETSET_POWER_80211 */| 0x00001000L \ ++/* GETSET_RETRY */ | 0x00002000L \ ++/* GETSET_REG_DOMAIN */ | 0x00004000L \ ++/* GETSET_CHANNEL */ | 0x00008000L \ ++/* GETSET_RESCAN */ | 0x00010000L \ ++/* GETSET_MODE */ | 0x00020000L \ ++/* GETSET_WEP */ | 0x00040000L \ ++/* SET_WEP_OPTIONS */ | 0x00080000L \ ++/* SET_MSDU_LIFETIME */ | 0x00100000L \ ++/* SET_RATE_FALLBACK */ | 0x00200000L \ ++ ) ++ ++ ++/*********************************************************************** ++** Firmware loading ++*/ ++#include <linux/firmware.h> /* request_firmware() */ ++#include <linux/pci.h> /* struct pci_device */ ++ ++ ++/*********************************************************************** ++*/ ++typedef struct acx100_ie_memblocksize { ++ u16 type; ++ u16 len; ++ u16 size; ++} ACX_PACKED acx100_ie_memblocksize_t; ++ ++typedef struct acx100_ie_queueconfig { ++ u16 type; ++ u16 len; ++ u32 AreaSize; ++ u32 RxQueueStart; ++ u8 QueueOptions; ++ u8 NumTxQueues; ++ u8 NumRxDesc; /* for USB only */ ++ u8 pad1; ++ u32 QueueEnd; ++ u32 HostQueueEnd; /* QueueEnd2 */ ++ u32 TxQueueStart; ++ u8 TxQueuePri; ++ u8 NumTxDesc; ++ u16 pad2; ++} ACX_PACKED acx100_ie_queueconfig_t; ++ ++typedef struct acx111_ie_queueconfig { ++ u16 type; ++ u16 len; ++ u32 tx_memory_block_address; ++ u32 rx_memory_block_address; ++ u32 rx1_queue_address; ++ u32 reserved1; ++ u32 tx1_queue_address; ++ u8 tx1_attributes; ++ u16 reserved2; ++ u8 reserved3; ++} ACX_PACKED acx111_ie_queueconfig_t; ++ ++typedef struct acx100_ie_memconfigoption { ++ u16 type; ++ u16 len; ++ u32 DMA_config; ++ acx_ptr pRxHostDesc; ++ u32 rx_mem; ++ u32 tx_mem; ++ u16 RxBlockNum; ++ u16 TxBlockNum; ++} ACX_PACKED acx100_ie_memconfigoption_t; ++ ++typedef struct acx111_ie_memoryconfig { ++ u16 type; ++ u16 len; ++ u16 no_of_stations; ++ u16 memory_block_size; ++ u8 tx_rx_memory_block_allocation; ++ u8 count_rx_queues; ++ u8 count_tx_queues; ++ u8 options; ++ u8 fragmentation; ++ u16 reserved1; ++ u8 reserved2; ++ ++ /* start of rx1 block */ ++ u8 rx_queue1_count_descs; ++ u8 rx_queue1_reserved1; ++ u8 rx_queue1_type; /* must be set to 7 */ ++ u8 rx_queue1_prio; /* must be set to 0 */ ++ acx_ptr rx_queue1_host_rx_start; ++ /* end of rx1 block */ ++ ++ /* start of tx1 block */ ++ u8 tx_queue1_count_descs; ++ u8 tx_queue1_reserved1; ++ u8 tx_queue1_reserved2; ++ u8 tx_queue1_attributes; ++ /* end of tx1 block */ ++} ACX_PACKED acx111_ie_memoryconfig_t; ++ ++typedef struct acx_ie_memmap { ++ u16 type; ++ u16 len; ++ u32 CodeStart; ++ u32 CodeEnd; ++ u32 WEPCacheStart; ++ u32 WEPCacheEnd; ++ u32 PacketTemplateStart; ++ u32 PacketTemplateEnd; ++ u32 QueueStart; ++ u32 QueueEnd; ++ u32 PoolStart; ++ u32 PoolEnd; ++} ACX_PACKED acx_ie_memmap_t; ++ ++typedef struct acx111_ie_feature_config { ++ u16 type; ++ u16 len; ++ u32 feature_options; ++ u32 data_flow_options; ++} ACX_PACKED acx111_ie_feature_config_t; ++ ++typedef struct acx111_ie_tx_level { ++ u16 type; ++ u16 len; ++ u8 level; ++} ACX_PACKED acx111_ie_tx_level_t; ++ ++#define PS_CFG_ENABLE 0x80 ++#define PS_CFG_PENDING 0x40 /* status flag when entering PS */ ++#define PS_CFG_WAKEUP_MODE_MASK 0x07 ++#define PS_CFG_WAKEUP_BY_HOST 0x03 ++#define PS_CFG_WAKEUP_EACH_ITVL 0x02 ++#define PS_CFG_WAKEUP_ON_DTIM 0x01 ++#define PS_CFG_WAKEUP_ALL_BEAC 0x00 ++ ++/* Enhanced PS mode: sleep until Rx Beacon w/ the STA's AID bit set ++** in the TIM; newer firmwares only(?) */ ++#define PS_OPT_ENA_ENHANCED_PS 0x04 ++#define PS_OPT_TX_PSPOLL 0x02 /* send PSPoll frame to fetch waiting frames from AP (on frame with matching AID) */ ++#define PS_OPT_STILL_RCV_BCASTS 0x01 ++ ++typedef struct acx100_ie_powersave { ++ u16 type; ++ u16 len; ++ u8 wakeup_cfg; ++ u8 listen_interval; /* for EACH_ITVL: wake up every "beacon units" interval */ ++ u8 options; ++ u8 hangover_period; /* remaining wake time after Tx MPDU w/ PS bit, in values of 1/1024 seconds */ ++ u16 enhanced_ps_transition_time; /* rem. wake time for Enh. PS */ ++} ACX_PACKED acx100_ie_powersave_t; ++ ++typedef struct acx111_ie_powersave { ++ u16 type; ++ u16 len; ++ u8 wakeup_cfg; ++ u8 listen_interval; /* for EACH_ITVL: wake up every "beacon units" interval */ ++ u8 options; ++ u8 hangover_period; /* remaining wake time after Tx MPDU w/ PS bit, in values of 1/1024 seconds */ ++ u32 beacon_rx_time; ++ u32 enhanced_ps_transition_time; /* rem. wake time for Enh. PS */ ++} ACX_PACKED acx111_ie_powersave_t; ++ ++ ++/*********************************************************************** ++** Commands and template structures ++*/ ++ ++/* ++** SCAN command structure ++** ++** even though acx100 scan rates match RATE100 constants, ++** acx111 ones do not match! Therefore we do not use RATE100 #defines */ ++#define ACX_SCAN_RATE_1 10 ++#define ACX_SCAN_RATE_2 20 ++#define ACX_SCAN_RATE_5 55 ++#define ACX_SCAN_RATE_11 110 ++#define ACX_SCAN_RATE_22 220 ++#define ACX_SCAN_RATE_PBCC 0x80 /* OR with this if needed */ ++#define ACX_SCAN_OPT_ACTIVE 0x00 /* a bit mask */ ++#define ACX_SCAN_OPT_PASSIVE 0x01 ++/* Background scan: we go into Power Save mode (by transmitting ++** NULL data frame to AP with the power mgmt bit set), do the scan, ++** and then exit Power Save mode. A plus is that AP buffers frames ++** for us while we do background scan. Thus we avoid frame losses. ++** Background scan can be active or passive, just like normal one */ ++#define ACX_SCAN_OPT_BACKGROUND 0x02 ++typedef struct acx100_scan { ++ u16 count; /* number of scans to do, 0xffff == continuous */ ++ u16 start_chan; ++ u16 flags; /* channel list mask; 0x8000 == all channels? */ ++ u8 max_rate; /* max. probe rate */ ++ u8 options; /* bit mask, see defines above */ ++ u16 chan_duration; ++ u16 max_probe_delay; ++} ACX_PACKED acx100_scan_t; /* length 0xc */ ++ ++#define ACX111_SCAN_RATE_6 0x0B ++#define ACX111_SCAN_RATE_9 0x0F ++#define ACX111_SCAN_RATE_12 0x0A ++#define ACX111_SCAN_RATE_18 0x0E ++#define ACX111_SCAN_RATE_24 0x09 ++#define ACX111_SCAN_RATE_36 0x0D ++#define ACX111_SCAN_RATE_48 0x08 ++#define ACX111_SCAN_RATE_54 0x0C ++#define ACX111_SCAN_OPT_5GHZ 0x04 /* else 2.4GHZ */ ++#define ACX111_SCAN_MOD_SHORTPRE 0x01 /* you can combine SHORTPRE and PBCC */ ++#define ACX111_SCAN_MOD_PBCC 0x80 ++#define ACX111_SCAN_MOD_OFDM 0x40 ++typedef struct acx111_scan { ++ u16 count; /* number of scans to do */ ++ u8 channel_list_select; /* 0: scan all channels, 1: from chan_list only */ ++ u16 reserved1; ++ u8 reserved2; ++ u8 rate; /* rate for probe requests (if active scan) */ ++ u8 options; /* bit mask, see defines above */ ++ u16 chan_duration; /* min time to wait for reply on one channel (in TU) */ ++ /* (active scan only) (802.11 section 11.1.3.2.2) */ ++ u16 max_probe_delay; /* max time to wait for reply on one channel (active scan) */ ++ /* time to listen on a channel (passive scan) */ ++ u8 modulation; ++ u8 channel_list[26]; /* bits 7:0 first byte: channels 8:1 */ ++ /* bits 7:0 second byte: channels 16:9 */ ++ /* 26 bytes is enough to cover 802.11a */ ++} ACX_PACKED acx111_scan_t; ++ ++ ++/* ++** Radio calibration command structure ++*/ ++typedef struct acx111_cmd_radiocalib { ++/* 0x80000000 == automatic calibration by firmware, according to interval; ++ * bits 0..3: select calibration methods to go through: ++ * calib based on DC, AfeDC, Tx mismatch, Tx equilization */ ++ u32 methods; ++ u32 interval; ++} ACX_PACKED acx111_cmd_radiocalib_t; ++ ++ ++/* ++** Packet template structures ++** ++** Packet templates store contents of Beacon, Probe response, Probe request, ++** Null data frame, and TIM data frame. Firmware automatically transmits ++** contents of template at appropriate time: ++** - Beacon: when configured as AP or Ad-hoc ++** - Probe response: when configured as AP or Ad-hoc, whenever ++** a Probe request frame is received ++** - Probe request: when host issues SCAN command (active) ++** - Null data frame: when entering 802.11 power save mode ++** - TIM data: at the end of Beacon frames (if no TIM template ++** is configured, then transmits default TIM) ++** NB: ++** - size field must be set to size of actual template ++** (NOT sizeof(struct) - templates are variable in length), ++** size field is not itself counted. ++** - members flagged with an asterisk must be initialized with host, ++** rest must be zero filled. ++** - variable length fields shown only in comments */ ++typedef struct acx_template_tim { ++ u16 size; ++ u8 tim_eid; /* 00 1 TIM IE ID * */ ++ u8 len; /* 01 1 Length * */ ++ u8 dtim_cnt; /* 02 1 DTIM Count */ ++ u8 dtim_period; /* 03 1 DTIM Period */ ++ u8 bitmap_ctrl; /* 04 1 Bitmap Control * (except bit0) */ ++ /* 05 n Partial Virtual Bitmap * */ ++ u8 variable[0x100 - 1-1-1-1-1]; ++} ACX_PACKED acx_template_tim_t; ++ ++typedef struct acx_template_probereq { ++ u16 size; ++ u16 fc; /* 00 2 fc * */ ++ u16 dur; /* 02 2 Duration */ ++ u8 da[6]; /* 04 6 Destination Address * */ ++ u8 sa[6]; /* 0A 6 Source Address * */ ++ u8 bssid[6]; /* 10 6 BSSID * */ ++ u16 seq; /* 16 2 Sequence Control */ ++ /* 18 n SSID * */ ++ /* nn n Supported Rates * */ ++ u8 variable[0x44 - 2-2-6-6-6-2]; ++} ACX_PACKED acx_template_probereq_t; ++ ++typedef struct acx_template_proberesp { ++ u16 size; ++ u16 fc; /* 00 2 fc * (bits [15:12] and [10:8] per 802.11 section 7.1.3.1) */ ++ u16 dur; /* 02 2 Duration */ ++ u8 da[6]; /* 04 6 Destination Address */ ++ u8 sa[6]; /* 0A 6 Source Address */ ++ u8 bssid[6]; /* 10 6 BSSID */ ++ u16 seq; /* 16 2 Sequence Control */ ++ u8 timestamp[8];/* 18 8 Timestamp */ ++ u16 beacon_interval; /* 20 2 Beacon Interval * */ ++ u16 cap; /* 22 2 Capability Information * */ ++ /* 24 n SSID * */ ++ /* nn n Supported Rates * */ ++ /* nn 1 DS Parameter Set * */ ++ u8 variable[0x54 - 2-2-6-6-6-2-8-2-2]; ++} ACX_PACKED acx_template_proberesp_t; ++#define acx_template_beacon_t acx_template_proberesp_t ++#define acx_template_beacon acx_template_proberesp ++ ++typedef struct acx_template_nullframe { ++ u16 size; ++ struct wlan_hdr_a3 hdr; ++} ACX_PACKED acx_template_nullframe_t; ++ ++ ++/* ++** JOIN command structure ++** ++** as opposed to acx100, acx111 dtim interval is AFTER rates_basic111. ++** NOTE: took me about an hour to get !@#$%^& packing right --> struct packing is eeeeevil... */ ++typedef struct acx_joinbss { ++ u8 bssid[ETH_ALEN]; ++ u16 beacon_interval; ++ union { ++ struct { ++ u8 dtim_interval; ++ u8 rates_basic; ++ u8 rates_supported; ++ /* ++ * ARM compiler doesn't pack correctly unless unions ++ * inside structures are multiples of 4 bytes. Ugh. ++ */ ++ u8 genfrm_txrate; /* generated frame (bcn, proberesp, RTS, PSpoll) tx rate */ ++ } ACX_PACKED acx100; ++ struct { ++ u16 rates_basic; ++ u8 dtim_interval; ++ u8 genfrm_txrate; /* generated frame (bcn, proberesp, RTS, PSpoll) tx rate */ ++ } ACX_PACKED acx111; ++ /* ++ * ARM compiler doesn't pack correctly unles unions are aligned on ++ * 4 byte boundaries and are multiples of 4 bytes. ++ */ ++ struct { ++ u8 d1; ++ u8 d2; ++ u8 d3; ++ u8 genfrm_txrate; ++ } ACX_PACKED txrate; ++ } ACX_PACKED u; ++ u8 genfrm_mod_pre; /* generated frame modulation/preamble: ++ ** bit7: PBCC, bit6: OFDM (else CCK/DQPSK/DBPSK) ++ ** bit5: short pre */ ++ u8 macmode; /* BSS Type, must be one of ACX_MODE_xxx */ ++ u8 channel; ++ u8 essid_len; ++ char essid[IW_ESSID_MAX_SIZE]; ++} ACX_PACKED acx_joinbss_t; ++ ++#define JOINBSS_RATES_1 0x01 ++#define JOINBSS_RATES_2 0x02 ++#define JOINBSS_RATES_5 0x04 ++#define JOINBSS_RATES_11 0x08 ++#define JOINBSS_RATES_22 0x10 ++ ++/* Looks like missing bits are used to indicate 11g rates! ++** (it follows from the fact that constants below match 1:1 to RATE111_nn) ++** This was actually seen! Look at that Assoc Request sent by acx111, ++** it _does_ contain 11g rates in basic set: ++01:30:20.070772 Beacon (xxx) [1.0* 2.0* 5.5* 11.0* 6.0* 9.0* 12.0* 18.0* 24.0* 36.0* 48.0* 54.0* Mbit] ESS CH: 1 ++01:30:20.074425 Authentication (Open System)-1: Succesful ++01:30:20.076539 Authentication (Open System)-2: ++01:30:20.076620 Acknowledgment ++01:30:20.088546 Assoc Request (xxx) [1.0* 2.0* 5.5* 6.0* 9.0* 11.0* 12.0* 18.0* 24.0* 36.0* 48.0* 54.0* Mbit] ++01:30:20.122413 Assoc Response AID(1) :: Succesful ++01:30:20.122679 Acknowledgment ++01:30:20.173204 Beacon (xxx) [1.0* 2.0* 5.5* 11.0* 6.0* 9.0* 12.0* 18.0* 24.0* 36.0* 48.0* 54.0* Mbit] ESS CH: 1 ++*/ ++#define JOINBSS_RATES_BASIC111_1 0x0001 ++#define JOINBSS_RATES_BASIC111_2 0x0002 ++#define JOINBSS_RATES_BASIC111_5 0x0004 ++#define JOINBSS_RATES_BASIC111_11 0x0020 ++#define JOINBSS_RATES_BASIC111_22 0x0100 ++ ++ ++/*********************************************************************** ++*/ ++typedef struct mem_read_write { ++ u16 addr; ++ u16 type; /* 0x0 int. RAM / 0xffff MAC reg. / 0x81 PHY RAM / 0x82 PHY reg.; or maybe it's actually 0x30 for MAC? Better verify it by writing and reading back and checking whether the value holds! */ ++ u32 len; ++ u32 data; ++} ACX_PACKED mem_read_write_t; ++ ++typedef struct firmware_image { ++ u32 chksum; ++ u32 size; ++ u8 data[1]; /* the byte array of the actual firmware... */ ++} ACX_PACKED firmware_image_t; ++ ++typedef struct acx_cmd_radioinit { ++ u32 offset; ++ u32 len; ++} ACX_PACKED acx_cmd_radioinit_t; ++ ++typedef struct acx100_ie_wep_options { ++ u16 type; ++ u16 len; ++ u16 NumKeys; /* max # of keys */ ++ u8 WEPOption; /* 0 == decrypt default key only, 1 == override decrypt */ ++ u8 Pad; /* used only for acx111 */ ++} ACX_PACKED acx100_ie_wep_options_t; ++ ++typedef struct ie_dot11WEPDefaultKey { ++ u16 type; ++ u16 len; ++ u8 action; ++ u8 keySize; ++ u8 defaultKeyNum; ++ u8 key[29]; /* check this! was Key[19] */ ++} ACX_PACKED ie_dot11WEPDefaultKey_t; ++ ++typedef struct acx111WEPDefaultKey { ++ u8 MacAddr[ETH_ALEN]; ++ u16 action; /* NOTE: this is a u16, NOT a u8!! */ ++ u16 reserved; ++ u8 keySize; ++ u8 type; ++ u8 index; ++ u8 defaultKeyNum; ++ u8 counter[6]; ++ u8 key[32]; /* up to 32 bytes (for TKIP!) */ ++} ACX_PACKED acx111WEPDefaultKey_t; ++ ++typedef struct ie_dot11WEPDefaultKeyID { ++ u16 type; ++ u16 len; ++ u8 KeyID; ++} ACX_PACKED ie_dot11WEPDefaultKeyID_t; ++ ++typedef struct acx100_cmd_wep_mgmt { ++ u8 MacAddr[ETH_ALEN]; ++ u16 Action; ++ u16 KeySize; ++ u8 Key[29]; /* 29*8 == 232bits == WEP256 */ ++} ACX_PACKED acx100_cmd_wep_mgmt_t; ++ ++typedef struct acx_ie_generic { ++ u16 type; ++ u16 len; ++ union { ++ /* Association ID IE: just a 16bit value: */ ++ u16 aid; ++ /* generic member for quick implementation of commands */ ++ u8 bytes[32]; ++ } ACX_PACKED m; ++} ACX_PACKED acx_ie_generic_t; ++ ++/*********************************************************************** ++*/ ++#define CHECK_SIZEOF(type,size) { \ ++ extern void BUG_bad_size_for_##type(void); \ ++ if (sizeof(type)!=(size)) BUG_bad_size_for_##type(); \ ++} ++ ++static inline void ++acx_struct_size_check(void) ++{ ++ CHECK_SIZEOF(txdesc_t, 0x30); ++ CHECK_SIZEOF(acx100_ie_memconfigoption_t, 24); ++ CHECK_SIZEOF(acx100_ie_queueconfig_t, 0x20); ++ CHECK_SIZEOF(acx_joinbss_t, 0x30); ++ /* IEs need 4 bytes for (type,len) tuple */ ++ CHECK_SIZEOF(acx111_ie_configoption_t, ACX111_IE_CONFIG_OPTIONS_LEN + 4); ++} ++ ++ ++/*********************************************************************** ++** Global data ++*/ ++extern const u8 acx_bitpos2ratebyte[]; ++extern const u8 acx_bitpos2rate100[]; ++ ++extern const u8 acx_reg_domain_ids[]; ++extern const char * const acx_reg_domain_strings[]; ++enum { ++ acx_reg_domain_ids_len = 8 ++}; ++ ++extern const struct iw_handler_def acx_ioctl_handler_def; +Index: linux-2.6.23/drivers/net/wireless/acx/common.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.23/drivers/net/wireless/acx/common.c 2008-01-20 21:13:40.000000000 +0000 +@@ -0,0 +1,7388 @@ ++/*********************************************************************** ++** Copyright (C) 2003 ACX100 Open Source Project ++** ++** The contents of this file are subject to the Mozilla Public ++** License Version 1.1 (the "License"); you may not use this file ++** except in compliance with the License. You may obtain a copy of ++** the License at http://www.mozilla.org/MPL/ ++** ++** Software distributed under the License is distributed on an "AS ++** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or ++** implied. See the License for the specific language governing ++** rights and limitations under the License. ++** ++** Alternatively, the contents of this file may be used under the ++** terms of the GNU Public License version 2 (the "GPL"), in which ++** case the provisions of the GPL are applicable instead of the ++** above. If you wish to allow the use of your version of this file ++** only under the terms of the GPL and not to allow others to use ++** your version of this file under the MPL, indicate your decision ++** by deleting the provisions above and replace them with the notice ++** and other provisions required by the GPL. If you do not delete ++** the provisions above, a recipient may use your version of this ++** file under either the MPL or the GPL. ++** --------------------------------------------------------------------- ++** Inquiries regarding the ACX100 Open Source Project can be ++** made directly to: ++** ++** acx100-users@lists.sf.net ++** http://acx100.sf.net ++** --------------------------------------------------------------------- ++*/ ++ ++#include <linux/version.h> ++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) ++#include <linux/config.h> ++#endif ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/sched.h> ++#include <linux/types.h> ++#include <linux/slab.h> ++#include <linux/delay.h> ++#include <linux/proc_fs.h> ++#include <linux/if_arp.h> ++#include <linux/rtnetlink.h> ++#include <linux/netdevice.h> ++#include <linux/etherdevice.h> ++#include <linux/wireless.h> ++#include <linux/pm.h> ++#include <linux/vmalloc.h> ++#include <net/iw_handler.h> ++ ++#include "acx_hw.h" ++#include "acx.h" ++ ++ ++/*********************************************************************** ++*/ ++static client_t *acx_l_sta_list_alloc(acx_device_t *adev); ++static client_t *acx_l_sta_list_get_from_hash(acx_device_t *adev, const u8 *address); ++ ++static int acx_l_process_data_frame_master(acx_device_t *adev, rxbuffer_t *rxbuf); ++static int acx_l_process_data_frame_client(acx_device_t *adev, rxbuffer_t *rxbuf); ++/* static int acx_l_process_NULL_frame(acx_device_t *adev, rxbuffer_t *rxbuf, int vala); */ ++static int acx_l_process_mgmt_frame(acx_device_t *adev, rxbuffer_t *rxbuf); ++static void acx_l_process_disassoc_from_sta(acx_device_t *adev, const wlan_fr_disassoc_t *req); ++static void acx_l_process_disassoc_from_ap(acx_device_t *adev, const wlan_fr_disassoc_t *req); ++static void acx_l_process_deauth_from_sta(acx_device_t *adev, const wlan_fr_deauthen_t *req); ++static void acx_l_process_deauth_from_ap(acx_device_t *adev, const wlan_fr_deauthen_t *req); ++static int acx_l_process_probe_response(acx_device_t *adev, wlan_fr_proberesp_t *req, const rxbuffer_t *rxbuf); ++static int acx_l_process_assocresp(acx_device_t *adev, const wlan_fr_assocresp_t *req); ++static int acx_l_process_reassocresp(acx_device_t *adev, const wlan_fr_reassocresp_t *req); ++static int acx_l_process_authen(acx_device_t *adev, const wlan_fr_authen_t *req); ++static int acx_l_transmit_assocresp(acx_device_t *adev, const wlan_fr_assocreq_t *req); ++static int acx_l_transmit_reassocresp(acx_device_t *adev, const wlan_fr_reassocreq_t *req); ++static int acx_l_transmit_deauthen(acx_device_t *adev, const u8 *addr, u16 reason); ++static int acx_l_transmit_authen1(acx_device_t *adev); ++static int acx_l_transmit_authen2(acx_device_t *adev, const wlan_fr_authen_t *req, client_t *clt); ++static int acx_l_transmit_authen3(acx_device_t *adev, const wlan_fr_authen_t *req); ++static int acx_l_transmit_authen4(acx_device_t *adev, const wlan_fr_authen_t *req); ++static int acx_l_transmit_assoc_req(acx_device_t *adev); ++ ++ ++/*********************************************************************** ++*/ ++#if ACX_DEBUG ++unsigned int acx_debug /* will add __read_mostly later */ = ACX_DEFAULT_MSG; ++/* parameter is 'debug', corresponding var is acx_debug */ ++module_param_named(debug, acx_debug, uint, 0); ++MODULE_PARM_DESC(debug, "Debug level mask (see L_xxx constants)"); ++#endif ++ ++#ifdef MODULE_LICENSE ++MODULE_LICENSE("Dual MPL/GPL"); ++#endif ++/* USB had this: MODULE_AUTHOR("Martin Wawro <martin.wawro AT uni-dortmund.de>"); */ ++MODULE_AUTHOR("ACX100 Open Source Driver development team"); ++MODULE_DESCRIPTION("Driver for TI ACX1xx based wireless cards (CardBus/PCI/USB)"); ++ ++ ++/*********************************************************************** ++*/ ++/* Probably a number of acx's intermediate buffers for USB transfers, ++** not to be confused with number of descriptors in tx/rx rings ++** (which are not directly accessible to host in USB devices) */ ++#define USB_RX_CNT 10 ++#define USB_TX_CNT 10 ++ ++ ++/*********************************************************************** ++*/ ++ ++/* minutes to wait until next radio recalibration: */ ++#define RECALIB_PAUSE 5 ++ ++/* Please keep acx_reg_domain_ids_len in sync... */ ++const u8 acx_reg_domain_ids[acx_reg_domain_ids_len] = ++ { 0x10, 0x20, 0x30, 0x31, 0x32, 0x40, 0x41, 0x51 }; ++static const u16 reg_domain_channel_masks[acx_reg_domain_ids_len] = ++#ifdef ACX_ALLOW_ALLCHANNELS ++ { 0x3fff, 0x07ff, 0x1fff, 0x0600, 0x1e00, 0x2000, 0x3fff, 0x01fc }; ++#else ++ { 0x07ff, 0x07ff, 0x1fff, 0x0600, 0x1e00, 0x2000, 0x3fff, 0x01fc }; ++#endif ++const char * const ++acx_reg_domain_strings[] = { ++ /* 0 */ " 1-11 FCC (USA)", ++ /* 1 */ " 1-11 DOC/IC (Canada)", ++/* BTW: WLAN use in ETSI is regulated by ETSI standard EN 300 328-2 V1.1.2 */ ++ /* 2 */ " 1-13 ETSI (Europe)", ++ /* 3 */ "10-11 Spain", ++ /* 4 */ "10-13 France", ++ /* 5 */ " 14 MKK (Japan)", ++ /* 6 */ " 1-14 MKK1", ++ /* 7 */ " 3-9 Israel (not all firmware versions)", ++ NULL /* needs to remain as last entry */ ++}; ++ ++ ++ ++/*********************************************************************** ++** Debugging support ++*/ ++#ifdef PARANOID_LOCKING ++static unsigned max_lock_time; ++static unsigned max_sem_time; ++ ++void ++acx_lock_unhold() { max_lock_time = 0; } ++void ++acx_sem_unhold() { max_sem_time = 0; } ++ ++static inline const char* ++sanitize_str(const char *s) ++{ ++ const char* t = strrchr(s, '/'); ++ if (t) return t + 1; ++ return s; ++} ++ ++void ++acx_lock_debug(acx_device_t *adev, const char* where) ++{ ++ unsigned int count = 100*1000*1000; ++ where = sanitize_str(where); ++ while (--count) { ++ if (!spin_is_locked(&adev->lock)) break; ++ cpu_relax(); ++ } ++ if (!count) { ++ printk(KERN_EMERG "LOCKUP: already taken at %s!\n", adev->last_lock); ++ BUG(); ++ } ++ adev->last_lock = where; ++ rdtscl(adev->lock_time); ++} ++void ++acx_unlock_debug(acx_device_t *adev, const char* where) ++{ ++#ifdef SMP ++ if (!spin_is_locked(&adev->lock)) { ++ where = sanitize_str(where); ++ printk(KERN_EMERG "STRAY UNLOCK at %s!\n", where); ++ BUG(); ++ } ++#endif ++ if (acx_debug & L_LOCK) { ++ unsigned long diff; ++ rdtscl(diff); ++ diff -= adev->lock_time; ++ if (diff > max_lock_time) { ++ where = sanitize_str(where); ++ printk("max lock hold time %ld CPU ticks from %s " ++ "to %s\n", diff, adev->last_lock, where); ++ max_lock_time = diff; ++ } ++ } ++} ++void ++acx_down_debug(acx_device_t *adev, const char* where) ++{ ++ int sem_count; ++ unsigned long timeout = jiffies + 5*HZ; ++ ++ where = sanitize_str(where); ++ ++ for (;;) { ++ sem_count = atomic_read(&adev->sem.count); ++ if (sem_count) break; ++ if (time_after(jiffies, timeout)) ++ break; ++ msleep(5); ++ } ++ if (!sem_count) { ++ printk(KERN_EMERG "D STATE at %s! last sem at %s\n", ++ where, adev->last_sem); ++ dump_stack(); ++ } ++ adev->last_sem = where; ++ adev->sem_time = jiffies; ++ down(&adev->sem); ++ if (acx_debug & L_LOCK) { ++ printk("%s: sem_down %d -> %d\n", ++ where, sem_count, atomic_read(&adev->sem.count)); ++ } ++} ++void ++acx_up_debug(acx_device_t *adev, const char* where) ++{ ++ int sem_count = atomic_read(&adev->sem.count); ++ if (sem_count) { ++ where = sanitize_str(where); ++ printk(KERN_EMERG "STRAY UP at %s! sem.count=%d\n", where, sem_count); ++ dump_stack(); ++ } ++ if (acx_debug & L_LOCK) { ++ unsigned long diff = jiffies - adev->sem_time; ++ if (diff > max_sem_time) { ++ where = sanitize_str(where); ++ printk("max sem hold time %ld jiffies from %s " ++ "to %s\n", diff, adev->last_sem, where); ++ max_sem_time = diff; ++ } ++ } ++ up(&adev->sem); ++ if (acx_debug & L_LOCK) { ++ where = sanitize_str(where); ++ printk("%s: sem_up %d -> %d\n", ++ where, sem_count, atomic_read(&adev->sem.count)); ++ } ++} ++#endif /* PARANOID_LOCKING */ ++ ++ ++/*********************************************************************** ++*/ ++#if ACX_DEBUG > 1 ++ ++static int acx_debug_func_indent; ++#define DEBUG_TSC 0 ++#define FUNC_INDENT_INCREMENT 2 ++ ++#if DEBUG_TSC ++#define TIMESTAMP(d) unsigned long d; rdtscl(d) ++#else ++#define TIMESTAMP(d) unsigned long d = jiffies ++#endif ++ ++static const char ++spaces[] = " " " "; /* Nx10 spaces */ ++ ++void ++log_fn_enter(const char *funcname) ++{ ++ int indent; ++ TIMESTAMP(d); ++ ++ indent = acx_debug_func_indent; ++ if (indent >= sizeof(spaces)) ++ indent = sizeof(spaces)-1; ++ ++ printk("%08ld %s==> %s\n", ++ d % 100000000, ++ spaces + (sizeof(spaces)-1) - indent, ++ funcname ++ ); ++ ++ acx_debug_func_indent += FUNC_INDENT_INCREMENT; ++} ++void ++log_fn_exit(const char *funcname) ++{ ++ int indent; ++ TIMESTAMP(d); ++ ++ acx_debug_func_indent -= FUNC_INDENT_INCREMENT; ++ ++ indent = acx_debug_func_indent; ++ if (indent >= sizeof(spaces)) ++ indent = sizeof(spaces)-1; ++ ++ printk("%08ld %s<== %s\n", ++ d % 100000000, ++ spaces + (sizeof(spaces)-1) - indent, ++ funcname ++ ); ++} ++void ++log_fn_exit_v(const char *funcname, int v) ++{ ++ int indent; ++ TIMESTAMP(d); ++ ++ acx_debug_func_indent -= FUNC_INDENT_INCREMENT; ++ ++ indent = acx_debug_func_indent; ++ if (indent >= sizeof(spaces)) ++ indent = sizeof(spaces)-1; ++ ++ printk("%08ld %s<== %s: %08X\n", ++ d % 100000000, ++ spaces + (sizeof(spaces)-1) - indent, ++ funcname, ++ v ++ ); ++} ++#endif /* ACX_DEBUG > 1 */ ++ ++ ++/*********************************************************************** ++** Basically a msleep with logging ++*/ ++void ++acx_s_msleep(int ms) ++{ ++ FN_ENTER; ++ msleep(ms); ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** Not inlined: it's larger than it seems ++*/ ++void ++acx_print_mac(const char *head, const u8 *mac, const char *tail) ++{ ++ printk("%s"MACSTR"%s", head, MAC(mac), tail); ++} ++ ++ ++/*********************************************************************** ++** acx_get_status_name ++*/ ++static const char* ++acx_get_status_name(u16 status) ++{ ++ static const char * const str[] = { ++ "STOPPED", "SCANNING", "WAIT_AUTH", ++ "AUTHENTICATED", "ASSOCIATED", "INVALID??" ++ }; ++ if (status > VEC_SIZE(str)-1) ++ status = VEC_SIZE(str)-1; ++ ++ return str[status]; ++} ++ ++ ++/*********************************************************************** ++** acx_get_packet_type_string ++*/ ++#if ACX_DEBUG ++const char* ++acx_get_packet_type_string(u16 fc) ++{ ++ static const char * const mgmt_arr[] = { ++ "MGMT/AssocReq", "MGMT/AssocResp", "MGMT/ReassocReq", ++ "MGMT/ReassocResp", "MGMT/ProbeReq", "MGMT/ProbeResp", ++ "MGMT/UNKNOWN", "MGMT/UNKNOWN", "MGMT/Beacon", "MGMT/ATIM", ++ "MGMT/Disassoc", "MGMT/Authen", "MGMT/Deauthen" ++ }; ++ static const char * const ctl_arr[] = { ++ "CTL/PSPoll", "CTL/RTS", "CTL/CTS", "CTL/Ack", "CTL/CFEnd", ++ "CTL/CFEndCFAck" ++ }; ++ static const char * const data_arr[] = { ++ "DATA/DataOnly", "DATA/Data CFAck", "DATA/Data CFPoll", ++ "DATA/Data CFAck/CFPoll", "DATA/Null", "DATA/CFAck", ++ "DATA/CFPoll", "DATA/CFAck/CFPoll" ++ }; ++ const char *str; ++ u8 fstype = (WF_FC_FSTYPE & fc) >> 4; ++ u8 ctl; ++ ++ switch (WF_FC_FTYPE & fc) { ++ case WF_FTYPE_MGMT: ++ if (fstype < VEC_SIZE(mgmt_arr)) ++ str = mgmt_arr[fstype]; ++ else ++ str = "MGMT/UNKNOWN"; ++ break; ++ case WF_FTYPE_CTL: ++ ctl = fstype - 0x0a; ++ if (ctl < VEC_SIZE(ctl_arr)) ++ str = ctl_arr[ctl]; ++ else ++ str = "CTL/UNKNOWN"; ++ break; ++ case WF_FTYPE_DATA: ++ if (fstype < VEC_SIZE(data_arr)) ++ str = data_arr[fstype]; ++ else ++ str = "DATA/UNKNOWN"; ++ break; ++ default: ++ str = "UNKNOWN"; ++ break; ++ } ++ return str; ++} ++#endif ++ ++ ++/*********************************************************************** ++** acx_wlan_reason_str ++*/ ++static inline const char* ++acx_wlan_reason_str(u16 reason) ++{ ++ static const char* const reason_str[] = { ++ /* 0 */ "?", ++ /* 1 */ "unspecified", ++ /* 2 */ "prev auth is not valid", ++ /* 3 */ "leaving BBS", ++ /* 4 */ "due to inactivity", ++ /* 5 */ "AP is busy", ++ /* 6 */ "got class 2 frame from non-auth'ed STA", ++ /* 7 */ "got class 3 frame from non-assoc'ed STA", ++ /* 8 */ "STA has left BSS", ++ /* 9 */ "assoc without auth is not allowed", ++ /* 10 */ "bad power setting (802.11h)", ++ /* 11 */ "bad channel (802.11i)", ++ /* 12 */ "?", ++ /* 13 */ "invalid IE", ++ /* 14 */ "MIC failure", ++ /* 15 */ "four-way handshake timeout", ++ /* 16 */ "group key handshake timeout", ++ /* 17 */ "IE is different", ++ /* 18 */ "invalid group cipher", ++ /* 19 */ "invalid pairwise cipher", ++ /* 20 */ "invalid AKMP", ++ /* 21 */ "unsupported RSN version", ++ /* 22 */ "invalid RSN IE cap", ++ /* 23 */ "802.1x failed", ++ /* 24 */ "cipher suite rejected" ++ }; ++ return reason < VEC_SIZE(reason_str) ? reason_str[reason] : "?"; ++} ++ ++ ++/*********************************************************************** ++** acx_cmd_status_str ++*/ ++const char* ++acx_cmd_status_str(unsigned int state) ++{ ++ static const char * const cmd_error_strings[] = { ++ "Idle", ++ "Success", ++ "Unknown Command", ++ "Invalid Information Element", ++ "Channel rejected", ++ "Channel invalid in current regulatory domain", ++ "MAC invalid", ++ "Command rejected (read-only information element)", ++ "Command rejected", ++ "Already asleep", ++ "TX in progress", ++ "Already awake", ++ "Write only", ++ "RX in progress", ++ "Invalid parameter", ++ "Scan in progress", ++ "Failed" ++ }; ++ return state < VEC_SIZE(cmd_error_strings) ? ++ cmd_error_strings[state] : "?"; ++} ++ ++ ++/*********************************************************************** ++** get_status_string ++*/ ++static inline const char* ++get_status_string(unsigned int status) ++{ ++ /* A bit shortened, but hopefully still understandable */ ++ static const char * const status_str[] = { ++ /* 0 */ "Successful", ++ /* 1 */ "Unspecified failure", ++ /* 2 */ "reserved", ++ /* 3 */ "reserved", ++ /* 4 */ "reserved", ++ /* 5 */ "reserved", ++ /* 6 */ "reserved", ++ /* 7 */ "reserved", ++ /* 8 */ "reserved", ++ /* 9 */ "reserved", ++ /*10 */ "Cannot support all requested capabilities in Capability Information field", ++ /*11 */ "Reassoc denied (reason outside of 802.11b scope)", ++ /*12 */ "Assoc denied (reason outside of 802.11b scope) -- maybe MAC filtering by peer?", ++ /*13 */ "Responding station doesnt support specified auth algorithm -- maybe WEP auth Open vs. Restricted?", ++ /*14 */ "Auth rejected: wrong transaction sequence number", ++ /*15 */ "Auth rejected: challenge failure", ++ /*16 */ "Auth rejected: timeout for next frame in sequence", ++ /*17 */ "Assoc denied: too many STAs on this AP", ++ /*18 */ "Assoc denied: requesting STA doesnt support all data rates in basic set", ++ /*19 */ "Assoc denied: requesting STA doesnt support Short Preamble", ++ /*20 */ "Assoc denied: requesting STA doesnt support PBCC Modulation", ++ /*21 */ "Assoc denied: requesting STA doesnt support Channel Agility" ++ /*22 */ "reserved", ++ /*23 */ "reserved", ++ /*24 */ "reserved", ++ /*25 */ "Assoc denied: requesting STA doesnt support Short Slot Time", ++ /*26 */ "Assoc denied: requesting STA doesnt support DSSS-OFDM" ++ }; ++ ++ return status_str[status < VEC_SIZE(status_str) ? status : 2]; ++} ++ ++ ++/*********************************************************************** ++*/ ++void ++acx_log_bad_eid(wlan_hdr_t* hdr, int len, wlan_ie_t* ie_ptr) ++{ ++ if (acx_debug & L_ASSOC) { ++ int offset = (u8*)ie_ptr - (u8*)hdr; ++ printk("acx: unknown EID %d in mgmt frame at offset %d. IE: ", ++ ie_ptr->eid, offset); ++ /* IE len can be bogus, IE can extend past packet end. Oh well... */ ++ acx_dump_bytes(ie_ptr, ie_ptr->len + 2); ++ if (acx_debug & L_DATA) { ++ printk("frame (%s): ", ++ acx_get_packet_type_string(le16_to_cpu(hdr->fc))); ++ acx_dump_bytes(hdr, len); ++ } ++ } ++} ++ ++ ++/*********************************************************************** ++*/ ++#if ACX_DEBUG ++void ++acx_dump_bytes(const void *data, int num) ++{ ++ const u8* ptr = (const u8*)data; ++ ++ if (num <= 0) { ++ printk("\n"); ++ return; ++ } ++ ++ while (num >= 16) { ++ printk( "%02X %02X %02X %02X %02X %02X %02X %02X " ++ "%02X %02X %02X %02X %02X %02X %02X %02X\n", ++ ptr[0], ptr[1], ptr[2], ptr[3], ++ ptr[4], ptr[5], ptr[6], ptr[7], ++ ptr[8], ptr[9], ptr[10], ptr[11], ++ ptr[12], ptr[13], ptr[14], ptr[15]); ++ num -= 16; ++ ptr += 16; ++ } ++ if (num > 0) { ++ while (--num > 0) ++ printk("%02X ", *ptr++); ++ printk("%02X\n", *ptr); ++ } ++} ++#endif ++ ++ ++/*********************************************************************** ++** acx_s_get_firmware_version ++*/ ++void ++acx_s_get_firmware_version(acx_device_t *adev) ++{ ++ fw_ver_t fw; ++ u8 hexarr[4] = { 0, 0, 0, 0 }; ++ int hexidx = 0, val = 0; ++ const char *num; ++ char c; ++ ++ FN_ENTER; ++ ++ memset(fw.fw_id, 'E', FW_ID_SIZE); ++ acx_s_interrogate(adev, &fw, ACX1xx_IE_FWREV); ++ memcpy(adev->firmware_version, fw.fw_id, FW_ID_SIZE); ++ adev->firmware_version[FW_ID_SIZE] = '\0'; ++ ++ log(L_DEBUG, "fw_ver: fw_id='%s' hw_id=%08X\n", ++ adev->firmware_version, fw.hw_id); ++ ++ if (strncmp(fw.fw_id, "Rev ", 4) != 0) { ++ printk("acx: strange firmware version string " ++ "'%s', please report\n", adev->firmware_version); ++ adev->firmware_numver = 0x01090407; /* assume 1.9.4.7 */ ++ } else { ++ num = &fw.fw_id[4]; ++ while (1) { ++ c = *num++; ++ if ((c == '.') || (c == '\0')) { ++ hexarr[hexidx++] = val; ++ if ((hexidx > 3) || (c == '\0')) /* end? */ ++ break; ++ val = 0; ++ continue; ++ } ++ if ((c >= '0') && (c <= '9')) ++ c -= '0'; ++ else ++ c = c - 'a' + (char)10; ++ val = val*16 + c; ++ } ++ ++ adev->firmware_numver = (u32)( ++ (hexarr[0] << 24) | (hexarr[1] << 16) ++ | (hexarr[2] << 8) | hexarr[3]); ++ log(L_DEBUG, "firmware_numver 0x%08X\n", adev->firmware_numver); ++ } ++ if (IS_ACX111(adev)) { ++ if (adev->firmware_numver == 0x00010011) { ++ /* This one does not survive floodpinging */ ++ printk("acx: firmware '%s' is known to be buggy, " ++ "please upgrade\n", adev->firmware_version); ++ } ++ } ++ ++ adev->firmware_id = le32_to_cpu(fw.hw_id); ++ ++ /* we're able to find out more detailed chip names now */ ++ switch (adev->firmware_id & 0xffff0000) { ++ case 0x01010000: ++ case 0x01020000: ++ adev->chip_name = "TNETW1100A"; ++ break; ++ case 0x01030000: ++ adev->chip_name = "TNETW1100B"; ++ break; ++ case 0x03000000: ++ case 0x03010000: ++ adev->chip_name = "TNETW1130"; ++ break; ++ case 0x04030000: /* 0x04030101 is TNETW1450 */ ++ adev->chip_name = "TNETW1450"; ++ break; ++ default: ++ printk("acx: unknown chip ID 0x%08X, " ++ "please report\n", adev->firmware_id); ++ break; ++ } ++ ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acx_display_hardware_details ++** ++** Displays hw/fw version, radio type etc... ++*/ ++void ++acx_display_hardware_details(acx_device_t *adev) ++{ ++ const char *radio_str, *form_str; ++ ++ FN_ENTER; ++ ++ switch (adev->radio_type) { ++ case RADIO_MAXIM_0D: ++ radio_str = "Maxim"; ++ break; ++ case RADIO_RFMD_11: ++ radio_str = "RFMD"; ++ break; ++ case RADIO_RALINK_15: ++ radio_str = "Ralink"; ++ break; ++ case RADIO_RADIA_16: ++ radio_str = "Radia"; ++ break; ++ case RADIO_UNKNOWN_17: ++ /* TI seems to have a radio which is ++ * additionally 802.11a capable, too */ ++ radio_str = "802.11a/b/g radio?! Please report"; ++ break; ++ case RADIO_UNKNOWN_19: ++ radio_str = "A radio used by Safecom cards?! Please report"; ++ break; ++ case RADIO_UNKNOWN_1B: ++ radio_str = "An unknown radio used by TNETW1450 USB adapters"; ++ break; ++ default: ++ radio_str = "UNKNOWN, please report radio type name!"; ++ break; ++ } ++ ++ switch (adev->form_factor) { ++ case 0x00: ++ form_str = "unspecified"; ++ break; ++ case 0x01: ++ form_str = "(mini-)PCI / CardBus"; ++ break; ++ case 0x02: ++ form_str = "USB"; ++ break; ++ case 0x03: ++ form_str = "Compact Flash"; ++ break; ++ default: ++ form_str = "UNKNOWN, please report"; ++ break; ++ } ++ ++ printk("acx: === chipset %s, radio type 0x%02X (%s), " ++ "form factor 0x%02X (%s), EEPROM version 0x%02X: " ++ "uploaded firmware '%s' ===\n", ++ adev->chip_name, adev->radio_type, radio_str, ++ adev->form_factor, form_str, adev->eeprom_version, ++ adev->firmware_version); ++ ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++*/ ++int ++acx_e_change_mtu(struct net_device *ndev, int mtu) ++{ ++ enum { ++ MIN_MTU = 256, ++ MAX_MTU = WLAN_DATA_MAXLEN - (ETH_HLEN) ++ }; ++ ++ if (mtu < MIN_MTU || mtu > MAX_MTU) ++ return -EINVAL; ++ ++ ndev->mtu = mtu; ++ return 0; ++} ++ ++ ++/*********************************************************************** ++** acx_e_get_stats, acx_e_get_wireless_stats ++*/ ++struct net_device_stats* ++acx_e_get_stats(struct net_device *ndev) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ return &adev->stats; ++} ++ ++struct iw_statistics* ++acx_e_get_wireless_stats(struct net_device *ndev) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ return &adev->wstats; ++} ++ ++ ++/*********************************************************************** ++** maps acx111 tx descr rate field to acx100 one ++*/ ++const u8 ++acx_bitpos2rate100[] = { ++ RATE100_1 ,/* 0 */ ++ RATE100_2 ,/* 1 */ ++ RATE100_5 ,/* 2 */ ++ RATE100_2 ,/* 3, should not happen */ ++ RATE100_2 ,/* 4, should not happen */ ++ RATE100_11 ,/* 5 */ ++ RATE100_2 ,/* 6, should not happen */ ++ RATE100_2 ,/* 7, should not happen */ ++ RATE100_22 ,/* 8 */ ++ RATE100_2 ,/* 9, should not happen */ ++ RATE100_2 ,/* 10, should not happen */ ++ RATE100_2 ,/* 11, should not happen */ ++ RATE100_2 ,/* 12, should not happen */ ++ RATE100_2 ,/* 13, should not happen */ ++ RATE100_2 ,/* 14, should not happen */ ++ RATE100_2 ,/* 15, should not happen */ ++}; ++ ++u8 ++acx_rate111to100(u16 r) { ++ return acx_bitpos2rate100[highest_bit(r)]; ++} ++ ++ ++/*********************************************************************** ++** Calculate level like the feb 2003 windows driver seems to do ++*/ ++static u8 ++acx_signal_to_winlevel(u8 rawlevel) ++{ ++ /* u8 winlevel = (u8) (0.5 + 0.625 * rawlevel); */ ++ u8 winlevel = ((4 + (rawlevel * 5)) / 8); ++ ++ if (winlevel > 100) ++ winlevel = 100; ++ return winlevel; ++} ++ ++u8 ++acx_signal_determine_quality(u8 signal, u8 noise) ++{ ++ int qual; ++ ++ qual = (((signal - 30) * 100 / 70) + (100 - noise * 4)) / 2; ++ ++ if (qual > 100) ++ return 100; ++ if (qual < 0) ++ return 0; ++ return qual; ++} ++ ++ ++/*********************************************************************** ++** Interrogate/configure commands ++*/ ++ ++/* FIXME: the lengths given here probably aren't always correct. ++ * They should be gradually replaced by proper "sizeof(acx1XX_ie_XXXX)-4", ++ * unless the firmware actually expects a different length than the struct length */ ++static const u16 ++acx100_ie_len[] = { ++ 0, ++ ACX100_IE_ACX_TIMER_LEN, ++ sizeof(acx100_ie_powersave_t)-4, /* is that 6 or 8??? */ ++ ACX1xx_IE_QUEUE_CONFIG_LEN, ++ ACX100_IE_BLOCK_SIZE_LEN, ++ ACX1xx_IE_MEMORY_CONFIG_OPTIONS_LEN, ++ ACX1xx_IE_RATE_FALLBACK_LEN, ++ ACX100_IE_WEP_OPTIONS_LEN, ++ ACX1xx_IE_MEMORY_MAP_LEN, /* ACX1xx_IE_SSID_LEN, */ ++ 0, ++ ACX1xx_IE_ASSOC_ID_LEN, ++ 0, ++ ACX111_IE_CONFIG_OPTIONS_LEN, ++ ACX1xx_IE_FWREV_LEN, ++ ACX1xx_IE_FCS_ERROR_COUNT_LEN, ++ ACX1xx_IE_MEDIUM_USAGE_LEN, ++ ACX1xx_IE_RXCONFIG_LEN, ++ 0, ++ 0, ++ sizeof(fw_stats_t)-4, ++ 0, ++ ACX1xx_IE_FEATURE_CONFIG_LEN, ++ ACX111_IE_KEY_CHOOSE_LEN, ++ ACX1FF_IE_MISC_CONFIG_TABLE_LEN, ++ ACX1FF_IE_WONE_CONFIG_LEN, ++ 0, ++ ACX1FF_IE_TID_CONFIG_LEN, ++ 0, ++ 0, ++ 0, ++ ACX1FF_IE_CALIB_ASSESSMENT_LEN, ++ ACX1FF_IE_BEACON_FILTER_OPTIONS_LEN, ++ ACX1FF_IE_LOW_RSSI_THRESH_OPT_LEN, ++ ACX1FF_IE_NOISE_HISTOGRAM_RESULTS_LEN, ++ 0, ++ ACX1FF_IE_PACKET_DETECT_THRESH_LEN, ++ ACX1FF_IE_TX_CONFIG_OPTIONS_LEN, ++ ACX1FF_IE_CCA_THRESHOLD_LEN, ++ ACX1FF_IE_EVENT_MASK_LEN, ++ ACX1FF_IE_DTIM_PERIOD_LEN, ++ 0, ++ ACX1FF_IE_ACI_CONFIG_SET_LEN, ++ 0, ++ 0, ++ 0, ++ 0, ++ 0, ++ 0, ++ ACX1FF_IE_EEPROM_VER_LEN, ++}; ++ ++static const u16 ++acx100_ie_len_dot11[] = { ++ 0, ++ ACX1xx_IE_DOT11_STATION_ID_LEN, ++ 0, ++ ACX100_IE_DOT11_BEACON_PERIOD_LEN, ++ ACX1xx_IE_DOT11_DTIM_PERIOD_LEN, ++ ACX1xx_IE_DOT11_SHORT_RETRY_LIMIT_LEN, ++ ACX1xx_IE_DOT11_LONG_RETRY_LIMIT_LEN, ++ ACX100_IE_DOT11_WEP_DEFAULT_KEY_WRITE_LEN, ++ ACX1xx_IE_DOT11_MAX_XMIT_MSDU_LIFETIME_LEN, ++ 0, ++ ACX1xx_IE_DOT11_CURRENT_REG_DOMAIN_LEN, ++ ACX1xx_IE_DOT11_CURRENT_ANTENNA_LEN, ++ 0, ++ ACX1xx_IE_DOT11_TX_POWER_LEVEL_LEN, ++ ACX1xx_IE_DOT11_CURRENT_CCA_MODE_LEN, ++ ACX100_IE_DOT11_ED_THRESHOLD_LEN, ++ ACX1xx_IE_DOT11_WEP_DEFAULT_KEY_SET_LEN, ++ 0, ++ 0, ++ 0, ++}; ++ ++static const u16 ++acx111_ie_len[] = { ++ 0, ++ ACX100_IE_ACX_TIMER_LEN, ++ sizeof(acx111_ie_powersave_t)-4, ++ ACX1xx_IE_QUEUE_CONFIG_LEN, ++ ACX100_IE_BLOCK_SIZE_LEN, ++ ACX1xx_IE_MEMORY_CONFIG_OPTIONS_LEN, ++ ACX1xx_IE_RATE_FALLBACK_LEN, ++ ACX100_IE_WEP_OPTIONS_LEN, ++ ACX1xx_IE_MEMORY_MAP_LEN, /* ACX1xx_IE_SSID_LEN, */ ++ 0, ++ ACX1xx_IE_ASSOC_ID_LEN, ++ 0, ++ ACX111_IE_CONFIG_OPTIONS_LEN, ++ ACX1xx_IE_FWREV_LEN, ++ ACX1xx_IE_FCS_ERROR_COUNT_LEN, ++ ACX1xx_IE_MEDIUM_USAGE_LEN, ++ ACX1xx_IE_RXCONFIG_LEN, ++ 0, ++ 0, ++ sizeof(fw_stats_t)-4, ++ 0, ++ ACX1xx_IE_FEATURE_CONFIG_LEN, ++ ACX111_IE_KEY_CHOOSE_LEN, ++ ACX1FF_IE_MISC_CONFIG_TABLE_LEN, ++ ACX1FF_IE_WONE_CONFIG_LEN, ++ 0, ++ ACX1FF_IE_TID_CONFIG_LEN, ++ 0, ++ 0, ++ 0, ++ ACX1FF_IE_CALIB_ASSESSMENT_LEN, ++ ACX1FF_IE_BEACON_FILTER_OPTIONS_LEN, ++ ACX1FF_IE_LOW_RSSI_THRESH_OPT_LEN, ++ ACX1FF_IE_NOISE_HISTOGRAM_RESULTS_LEN, ++ 0, ++ ACX1FF_IE_PACKET_DETECT_THRESH_LEN, ++ ACX1FF_IE_TX_CONFIG_OPTIONS_LEN, ++ ACX1FF_IE_CCA_THRESHOLD_LEN, ++ ACX1FF_IE_EVENT_MASK_LEN, ++ ACX1FF_IE_DTIM_PERIOD_LEN, ++ 0, ++ ACX1FF_IE_ACI_CONFIG_SET_LEN, ++ 0, ++ 0, ++ 0, ++ 0, ++ 0, ++ 0, ++ ACX1FF_IE_EEPROM_VER_LEN, ++}; ++ ++static const u16 ++acx111_ie_len_dot11[] = { ++ 0, ++ ACX1xx_IE_DOT11_STATION_ID_LEN, ++ 0, ++ ACX100_IE_DOT11_BEACON_PERIOD_LEN, ++ ACX1xx_IE_DOT11_DTIM_PERIOD_LEN, ++ ACX1xx_IE_DOT11_SHORT_RETRY_LIMIT_LEN, ++ ACX1xx_IE_DOT11_LONG_RETRY_LIMIT_LEN, ++ ACX100_IE_DOT11_WEP_DEFAULT_KEY_WRITE_LEN, ++ ACX1xx_IE_DOT11_MAX_XMIT_MSDU_LIFETIME_LEN, ++ 0, ++ ACX1xx_IE_DOT11_CURRENT_REG_DOMAIN_LEN, ++ ACX1xx_IE_DOT11_CURRENT_ANTENNA_LEN, ++ 0, ++ ACX1xx_IE_DOT11_TX_POWER_LEVEL_LEN, ++ ACX1xx_IE_DOT11_CURRENT_CCA_MODE_LEN, ++ ACX100_IE_DOT11_ED_THRESHOLD_LEN, ++ ACX1xx_IE_DOT11_WEP_DEFAULT_KEY_SET_LEN, ++ 0, ++ 0, ++ 0, ++}; ++ ++ ++#undef FUNC ++#define FUNC "configure" ++#if !ACX_DEBUG ++int ++acx_s_configure(acx_device_t *adev, void *pdr, int type) ++{ ++#else ++int ++acx_s_configure_debug(acx_device_t *adev, void *pdr, int type, const char* typestr) ++{ ++#endif ++ u16 len; ++ int res; ++ ++ if (type < 0x1000) ++ len = adev->ie_len[type]; ++ else ++ len = adev->ie_len_dot11[type - 0x1000]; ++ ++ log(L_CTL, FUNC"(type:%s,len:%u)\n", typestr, len); ++ if (unlikely(!len)) { ++ log(L_DEBUG, "zero-length type %s?!\n", typestr); ++ } ++ ++ ((acx_ie_generic_t *)pdr)->type = cpu_to_le16(type); ++ ((acx_ie_generic_t *)pdr)->len = cpu_to_le16(len); ++ res = acx_s_issue_cmd(adev, ACX1xx_CMD_CONFIGURE, pdr, len + 4); ++ if (unlikely(OK != res)) { ++#if ACX_DEBUG ++ printk("%s: "FUNC"(type:%s) FAILED\n", adev->ndev->name, typestr); ++#else ++ printk("%s: "FUNC"(type:0x%X) FAILED\n", adev->ndev->name, type); ++#endif ++ /* dump_stack() is already done in issue_cmd() */ ++ } ++ return res; ++} ++ ++#undef FUNC ++#define FUNC "interrogate" ++#if !ACX_DEBUG ++int ++acx_s_interrogate(acx_device_t *adev, void *pdr, int type) ++{ ++#else ++int ++acx_s_interrogate_debug(acx_device_t *adev, void *pdr, int type, ++ const char* typestr) ++{ ++#endif ++ u16 len; ++ int res; ++ ++ /* FIXME: no check whether this exceeds the array yet. ++ * We should probably remember the number of entries... */ ++ if (type < 0x1000) ++ len = adev->ie_len[type]; ++ else ++ len = adev->ie_len_dot11[type-0x1000]; ++ ++ log(L_CTL, FUNC"(type:%s,len:%u)\n", typestr, len); ++ ++ ((acx_ie_generic_t *)pdr)->type = cpu_to_le16(type); ++ ((acx_ie_generic_t *)pdr)->len = cpu_to_le16(len); ++ res = acx_s_issue_cmd(adev, ACX1xx_CMD_INTERROGATE, pdr, len + 4); ++ if (unlikely(OK != res)) { ++#if ACX_DEBUG ++ printk("%s: "FUNC"(type:%s) FAILED\n", adev->ndev->name, typestr); ++#else ++ printk("%s: "FUNC"(type:0x%X) FAILED\n", adev->ndev->name, type); ++#endif ++ /* dump_stack() is already done in issue_cmd() */ ++ } ++ return res; ++} ++ ++#if CMD_DISCOVERY ++void ++great_inquisitor(acx_device_t *adev) ++{ ++ static struct { ++ u16 type; ++ u16 len; ++ /* 0x200 was too large here: */ ++ u8 data[0x100 - 4]; ++ } ACX_PACKED ie; ++ u16 type; ++ ++ FN_ENTER; ++ ++ /* 0..0x20, 0x1000..0x1020 */ ++ for (type = 0; type <= 0x1020; type++) { ++ if (type == 0x21) ++ type = 0x1000; ++ ie.type = cpu_to_le16(type); ++ ie.len = cpu_to_le16(sizeof(ie) - 4); ++ acx_s_issue_cmd(adev, ACX1xx_CMD_INTERROGATE, &ie, sizeof(ie)); ++ } ++ FN_EXIT0; ++} ++#endif ++ ++ ++#ifdef CONFIG_PROC_FS ++/*********************************************************************** ++** /proc files ++*/ ++/*********************************************************************** ++** acx_l_proc_output ++** Generate content for our /proc entry ++** ++** Arguments: ++** buf is a pointer to write output to ++** adev is the usual pointer to our private struct acx_device ++** Returns: ++** number of bytes actually written to buf ++** Side effects: ++** none ++*/ ++static int ++acx_l_proc_output(char *buf, acx_device_t *adev) ++{ ++ char *p = buf; ++ int i; ++ ++ FN_ENTER; ++ ++ p += sprintf(p, ++ "acx driver version:\t\t" ACX_RELEASE "\n" ++ "Wireless extension version:\t" STRING(WIRELESS_EXT) "\n" ++ "chip name:\t\t\t%s (0x%08X)\n" ++ "radio type:\t\t\t0x%02X\n" ++ "form factor:\t\t\t0x%02X\n" ++ "EEPROM version:\t\t\t0x%02X\n" ++ "firmware version:\t\t%s (0x%08X)\n", ++ adev->chip_name, adev->firmware_id, ++ adev->radio_type, ++ adev->form_factor, ++ adev->eeprom_version, ++ adev->firmware_version, adev->firmware_numver); ++ ++ for (i = 0; i < VEC_SIZE(adev->sta_list); i++) { ++ struct client *bss = &adev->sta_list[i]; ++ if (!bss->used) continue; ++ p += sprintf(p, "BSS %u BSSID "MACSTR" ESSID %s channel %u " ++ "Cap 0x%X SIR %u SNR %u\n", ++ i, MAC(bss->bssid), (char*)bss->essid, bss->channel, ++ bss->cap_info, bss->sir, bss->snr); ++ } ++ p += sprintf(p, "status:\t\t\t%u (%s)\n", ++ adev->status, acx_get_status_name(adev->status)); ++ ++ FN_EXIT1(p - buf); ++ return p - buf; ++} ++ ++ ++/*********************************************************************** ++*/ ++static int ++acx_s_proc_diag_output(char *buf, acx_device_t *adev) ++{ ++ char *p = buf; ++ unsigned long flags; ++ unsigned int len = 0, partlen; ++ u32 temp1, temp2; ++ u8 *st, *st_end; ++#ifdef __BIG_ENDIAN ++ u8 *st2; ++#endif ++ fw_stats_t *fw_stats; ++ char *part_str = NULL; ++ fw_stats_tx_t *tx = NULL; ++ fw_stats_rx_t *rx = NULL; ++ fw_stats_dma_t *dma = NULL; ++ fw_stats_irq_t *irq = NULL; ++ fw_stats_wep_t *wep = NULL; ++ fw_stats_pwr_t *pwr = NULL; ++ fw_stats_mic_t *mic = NULL; ++ fw_stats_aes_t *aes = NULL; ++ fw_stats_event_t *evt = NULL; ++ ++ FN_ENTER; ++ ++ acx_lock(adev, flags); ++ ++#if defined (ACX_MEM) ++ p = acxmem_s_proc_diag_output(p, adev); ++#else ++ if (IS_PCI(adev)) ++ p = acxpci_s_proc_diag_output(p, adev); ++#endif ++ ++ p += sprintf(p, ++ "\n" ++ "** network status **\n" ++ "dev_state_mask 0x%04X\n" ++ "status %u (%s), " ++ "mode %u, channel %u, " ++ "reg_dom_id 0x%02X, reg_dom_chanmask 0x%04X, ", ++ adev->dev_state_mask, ++ adev->status, acx_get_status_name(adev->status), ++ adev->mode, adev->channel, ++ adev->reg_dom_id, adev->reg_dom_chanmask ++ ); ++ p += sprintf(p, ++ "ESSID \"%s\", essid_active %d, essid_len %d, " ++ "essid_for_assoc \"%s\", nick \"%s\"\n" ++ "WEP ena %d, restricted %d, idx %d\n", ++ adev->essid, adev->essid_active, (int)adev->essid_len, ++ adev->essid_for_assoc, adev->nick, ++ adev->wep_enabled, adev->wep_restricted, ++ adev->wep_current_index); ++ p += sprintf(p, "dev_addr "MACSTR"\n", MAC(adev->dev_addr)); ++ p += sprintf(p, "bssid "MACSTR"\n", MAC(adev->bssid)); ++ p += sprintf(p, "ap_filter "MACSTR"\n", MAC(adev->ap)); ++ ++ p += sprintf(p, ++ "\n" ++ "** PHY status **\n" ++ "tx_disabled %d, tx_level_dbm %d\n" /* "tx_level_val %d, tx_level_auto %d\n" */ ++ "sensitivity %d, antenna 0x%02X, ed_threshold %d, cca %d, preamble_mode %d\n" ++ "rate_basic 0x%04X, rate_oper 0x%04X\n" ++ "rts_threshold %d, frag_threshold %d, short_retry %d, long_retry %d\n" ++ "msdu_lifetime %d, listen_interval %d, beacon_interval %d\n", ++ adev->tx_disabled, adev->tx_level_dbm, /* adev->tx_level_val, adev->tx_level_auto, */ ++ adev->sensitivity, adev->antenna, adev->ed_threshold, adev->cca, adev->preamble_mode, ++ adev->rate_basic, adev->rate_oper, ++ adev->rts_threshold, adev->frag_threshold, adev->short_retry, adev->long_retry, ++ adev->msdu_lifetime, adev->listen_interval, adev->beacon_interval); ++ ++ acx_unlock(adev, flags); ++ ++ p += sprintf(p, ++ "\n" ++ "** Firmware **\n" ++ "NOTE: version dependent statistics layout, " ++ "please report if you suspect wrong parsing!\n" ++ "\n" ++ "version \"%s\"\n", adev->firmware_version); ++ ++ /* TODO: may replace kmalloc/memset with kzalloc once ++ * Linux 2.6.14 is widespread */ ++ fw_stats = kmalloc(sizeof(*fw_stats), GFP_KERNEL); ++ if (!fw_stats) { ++ FN_EXIT1(0); ++ return 0; ++ } ++ memset(fw_stats, 0, sizeof(*fw_stats)); ++ ++ st = (u8 *)fw_stats; ++ ++ part_str = "statistics query command"; ++ ++ if (OK != acx_s_interrogate(adev, st, ACX1xx_IE_FIRMWARE_STATISTICS)) ++ goto fw_stats_end; ++ ++ st += sizeof(u16); ++ len = *(u16 *)st; ++ ++ if (len > sizeof(*fw_stats)) { ++ p += sprintf(p, ++ "firmware version with bigger fw_stats struct detected\n" ++ "(%u vs. %u), please report\n", len, sizeof(fw_stats_t)); ++ if (len > sizeof(*fw_stats)) { ++ p += sprintf(p, "struct size exceeded allocation!\n"); ++ len = sizeof(*fw_stats); ++ } ++ } ++ st += sizeof(u16); ++ st_end = st - 2*sizeof(u16) + len; ++ ++#ifdef __BIG_ENDIAN ++ /* let's make one bold assumption here: ++ * (hopefully!) *all* statistics fields are u32 only, ++ * thus if we need to make endianness corrections ++ * we can simply do them in one go, in advance */ ++ st2 = (u8 *)fw_stats; ++ for (temp1 = 0; temp1 < len; temp1 += 4, st2 += 4) ++ *(u32 *)st2 = le32_to_cpu(*(u32 *)st2); ++#endif ++ ++ part_str = "Rx/Tx"; ++ ++ /* directly at end of a struct part? --> no error! */ ++ if (st == st_end) ++ goto fw_stats_end; ++ ++ tx = (fw_stats_tx_t *)st; ++ st += sizeof(fw_stats_tx_t); ++ rx = (fw_stats_rx_t *)st; ++ st += sizeof(fw_stats_rx_t); ++ partlen = sizeof(fw_stats_tx_t) + sizeof(fw_stats_rx_t); ++ ++ if (IS_ACX100(adev)) { ++ /* at least ACX100 PCI F/W 1.9.8.b ++ * and ACX100 USB F/W 1.0.7-USB ++ * don't have those two fields... */ ++ st -= 2*sizeof(u32); ++ ++ /* our parsing doesn't quite match this firmware yet, ++ * log failure */ ++ if (st > st_end) ++ goto fw_stats_fail; ++ temp1 = temp2 = 999999999; ++ } else { ++ if (st > st_end) ++ goto fw_stats_fail; ++ temp1 = rx->rx_aci_events; ++ temp2 = rx->rx_aci_resets; ++ } ++ ++ p += sprintf(p, ++ "%s:\n" ++ " tx_desc_overfl %u\n" ++ " rx_OutOfMem %u, rx_hdr_overfl %u, rx_hw_stuck %u\n" ++ " rx_dropped_frame %u, rx_frame_ptr_err %u, rx_xfr_hint_trig %u\n" ++ " rx_aci_events %u, rx_aci_resets %u\n", ++ part_str, ++ tx->tx_desc_of, ++ rx->rx_oom, ++ rx->rx_hdr_of, ++ rx->rx_hw_stuck, ++ rx->rx_dropped_frame, ++ rx->rx_frame_ptr_err, ++ rx->rx_xfr_hint_trig, ++ temp1, ++ temp2); ++ ++ part_str = "DMA"; ++ ++ if (st == st_end) ++ goto fw_stats_end; ++ ++ dma = (fw_stats_dma_t *)st; ++ partlen = sizeof(fw_stats_dma_t); ++ st += partlen; ++ ++ if (st > st_end) ++ goto fw_stats_fail; ++ ++ p += sprintf(p, ++ "%s:\n" ++ " rx_dma_req %u, rx_dma_err %u, tx_dma_req %u, tx_dma_err %u\n", ++ part_str, ++ dma->rx_dma_req, ++ dma->rx_dma_err, ++ dma->tx_dma_req, ++ dma->tx_dma_err); ++ ++ part_str = "IRQ"; ++ ++ if (st == st_end) ++ goto fw_stats_end; ++ ++ irq = (fw_stats_irq_t *)st; ++ partlen = sizeof(fw_stats_irq_t); ++ st += partlen; ++ ++ if (st > st_end) ++ goto fw_stats_fail; ++ ++ p += sprintf(p, ++ "%s:\n" ++ " cmd_cplt %u, fiq %u\n" ++ " rx_hdrs %u, rx_cmplt %u, rx_mem_overfl %u, rx_rdys %u\n" ++ " irqs %u, tx_procs %u, decrypt_done %u\n" ++ " dma_0_done %u, dma_1_done %u, tx_exch_complet %u\n" ++ " commands %u, rx_procs %u, hw_pm_mode_changes %u\n" ++ " host_acks %u, pci_pm %u, acm_wakeups %u\n", ++ part_str, ++ irq->cmd_cplt, ++ irq->fiq, ++ irq->rx_hdrs, ++ irq->rx_cmplt, ++ irq->rx_mem_of, ++ irq->rx_rdys, ++ irq->irqs, ++ irq->tx_procs, ++ irq->decrypt_done, ++ irq->dma_0_done, ++ irq->dma_1_done, ++ irq->tx_exch_complet, ++ irq->commands, ++ irq->rx_procs, ++ irq->hw_pm_mode_changes, ++ irq->host_acks, ++ irq->pci_pm, ++ irq->acm_wakeups); ++ ++ part_str = "WEP"; ++ ++ if (st == st_end) ++ goto fw_stats_end; ++ ++ wep = (fw_stats_wep_t *)st; ++ partlen = sizeof(fw_stats_wep_t); ++ st += partlen; ++ ++ if ( ++ (IS_PCI(adev) && IS_ACX100(adev)) ++ || (IS_USB(adev) && IS_ACX100(adev)) ++ || (IS_MEM(adev) && IS_ACX100(adev)) ++ ) { ++ /* at least ACX100 PCI F/W 1.9.8.b, ++ * ACX100 USB F/W 1.0.7-USB ++ * and ACX100 Generic Slave F/W 1.10.7.K ++ * don't have those two fields... ++ */ ++ st -= 2*sizeof(u32); ++ if (st > st_end) ++ goto fw_stats_fail; ++ temp1 = temp2 = 999999999; ++ } else { ++ if (st > st_end) ++ goto fw_stats_fail; ++ temp1 = wep->wep_pkt_decrypt; ++ temp2 = wep->wep_decrypt_irqs; ++ } ++ ++ p += sprintf(p, ++ "%s:\n" ++ " wep_key_count %u, wep_default_key_count %u, dot11_def_key_mib %u\n" ++ " wep_key_not_found %u, wep_decrypt_fail %u\n" ++ " wep_pkt_decrypt %u, wep_decrypt_irqs %u\n", ++ part_str, ++ wep->wep_key_count, ++ wep->wep_default_key_count, ++ wep->dot11_def_key_mib, ++ wep->wep_key_not_found, ++ wep->wep_decrypt_fail, ++ temp1, ++ temp2); ++ ++ part_str = "power"; ++ ++ if (st == st_end) ++ goto fw_stats_end; ++ ++ pwr = (fw_stats_pwr_t *)st; ++ partlen = sizeof(fw_stats_pwr_t); ++ st += partlen; ++ ++ if (st > st_end) ++ goto fw_stats_fail; ++ ++ p += sprintf(p, ++ "%s:\n" ++ " tx_start_ctr %u, no_ps_tx_too_short %u\n" ++ " rx_start_ctr %u, no_ps_rx_too_short %u\n" ++ " lppd_started %u\n" ++ " no_lppd_too_noisy %u, no_lppd_too_short %u, no_lppd_matching_frame %u\n", ++ part_str, ++ pwr->tx_start_ctr, ++ pwr->no_ps_tx_too_short, ++ pwr->rx_start_ctr, ++ pwr->no_ps_rx_too_short, ++ pwr->lppd_started, ++ pwr->no_lppd_too_noisy, ++ pwr->no_lppd_too_short, ++ pwr->no_lppd_matching_frame); ++ ++ part_str = "MIC"; ++ ++ if (st == st_end) ++ goto fw_stats_end; ++ ++ mic = (fw_stats_mic_t *)st; ++ partlen = sizeof(fw_stats_mic_t); ++ st += partlen; ++ ++ if (st > st_end) ++ goto fw_stats_fail; ++ ++ p += sprintf(p, ++ "%s:\n" ++ " mic_rx_pkts %u, mic_calc_fail %u\n", ++ part_str, ++ mic->mic_rx_pkts, ++ mic->mic_calc_fail); ++ ++ part_str = "AES"; ++ ++ if (st == st_end) ++ goto fw_stats_end; ++ ++ aes = (fw_stats_aes_t *)st; ++ partlen = sizeof(fw_stats_aes_t); ++ st += partlen; ++ ++ if (st > st_end) ++ goto fw_stats_fail; ++ ++ p += sprintf(p, ++ "%s:\n" ++ " aes_enc_fail %u, aes_dec_fail %u\n" ++ " aes_enc_pkts %u, aes_dec_pkts %u\n" ++ " aes_enc_irq %u, aes_dec_irq %u\n", ++ part_str, ++ aes->aes_enc_fail, ++ aes->aes_dec_fail, ++ aes->aes_enc_pkts, ++ aes->aes_dec_pkts, ++ aes->aes_enc_irq, ++ aes->aes_dec_irq); ++ ++ part_str = "event"; ++ ++ if (st == st_end) ++ goto fw_stats_end; ++ ++ evt = (fw_stats_event_t *)st; ++ partlen = sizeof(fw_stats_event_t); ++ st += partlen; ++ ++ if (st > st_end) ++ goto fw_stats_fail; ++ ++ p += sprintf(p, ++ "%s:\n" ++ " heartbeat %u, calibration %u\n" ++ " rx_mismatch %u, rx_mem_empty %u, rx_pool %u\n" ++ " oom_late %u\n" ++ " phy_tx_err %u, tx_stuck %u\n", ++ part_str, ++ evt->heartbeat, ++ evt->calibration, ++ evt->rx_mismatch, ++ evt->rx_mem_empty, ++ evt->rx_pool, ++ evt->oom_late, ++ evt->phy_tx_err, ++ evt->tx_stuck); ++ ++ if (st < st_end) ++ goto fw_stats_bigger; ++ ++ goto fw_stats_end; ++ ++fw_stats_fail: ++ st -= partlen; ++ p += sprintf(p, ++ "failed at %s part (size %u), offset %u (struct size %u), " ++ "please report\n", part_str, partlen, ++ (int)st - (int)fw_stats, len); ++ ++fw_stats_bigger: ++ for (; st < st_end; st += 4) ++ p += sprintf(p, ++ "UNKN%3d: %u\n", (int)st - (int)fw_stats, *(u32 *)st); ++ ++fw_stats_end: ++ kfree(fw_stats); ++ ++ FN_EXIT1(p - buf); ++ return p - buf; ++} ++ ++ ++/*********************************************************************** ++*/ ++static int ++acx_s_proc_phy_output(char *buf, acx_device_t *adev) ++{ ++ char *p = buf; ++ int i; ++ ++ FN_ENTER; ++ ++ /* ++ if (RADIO_RFMD_11 != adev->radio_type) { ++ printk("sorry, not yet adapted for radio types " ++ "other than RFMD, please verify " ++ "PHY size etc. first!\n"); ++ goto end; ++ } ++ */ ++ ++ /* The PHY area is only 0x80 bytes long; further pages after that ++ * only have some page number registers with altered value, ++ * all other registers remain the same. */ ++ for (i = 0; i < 0x80; i++) { ++ acx_s_read_phy_reg(adev, i, p++); ++ } ++ ++ FN_EXIT1(p - buf); ++ return p - buf; ++} ++ ++ ++/*********************************************************************** ++** acx_e_read_proc_XXXX ++** Handle our /proc entry ++** ++** Arguments: ++** standard kernel read_proc interface ++** Returns: ++** number of bytes written to buf ++** Side effects: ++** none ++*/ ++static int ++acx_e_read_proc(char *buf, char **start, off_t offset, int count, ++ int *eof, void *data) ++{ ++ acx_device_t *adev = (acx_device_t*)data; ++ unsigned long flags; ++ int length; ++ ++ FN_ENTER; ++ ++ acx_sem_lock(adev); ++ acx_lock(adev, flags); ++ /* fill buf */ ++ length = acx_l_proc_output(buf, adev); ++ acx_unlock(adev, flags); ++ acx_sem_unlock(adev); ++ ++ /* housekeeping */ ++ if (length <= offset + count) ++ *eof = 1; ++ *start = buf + offset; ++ length -= offset; ++ if (length > count) ++ length = count; ++ if (length < 0) ++ length = 0; ++ FN_EXIT1(length); ++ return length; ++} ++ ++static char _buf[32768]; ++static int ++acx_e_read_proc_diag(char *buf, char **start, off_t offset, int count, ++ int *eof, void *data) ++{ ++ acx_device_t *adev = (acx_device_t*)data; ++ int length; ++ ++ FN_ENTER; ++ ++ acx_sem_lock(adev); ++ /* fill buf */ ++ length = acx_s_proc_diag_output(_buf, adev); ++ acx_sem_unlock(adev); ++ ++ memcpy(buf, _buf + offset, count); ++ ++ /* housekeeping */ ++ if (length <= offset + count) ++ *eof = 1; ++ *start = count; ++ length -= offset; ++ if (length > count) ++ length = count; ++ if (length < 0) ++ length = 0; ++ FN_EXIT1(length); ++ return length; ++} ++ ++static int ++acx_e_read_proc_eeprom(char *buf, char **start, off_t offset, int count, ++ int *eof, void *data) ++{ ++ acx_device_t *adev = (acx_device_t*)data; ++ int length; ++ ++ FN_ENTER; ++ ++ /* fill buf */ ++ length = 0; ++#if defined (ACX_MEM) ++ acx_sem_lock(adev); ++ length = acxmem_proc_eeprom_output(buf, adev); ++ acx_sem_unlock(adev); ++#else ++ if (IS_PCI(adev)) { ++ acx_sem_lock(adev); ++ length = acxpci_proc_eeprom_output(buf, adev); ++ acx_sem_unlock(adev); ++ } ++#endif ++ ++ /* housekeeping */ ++ if (length <= offset + count) ++ *eof = 1; ++ *start = buf + offset; ++ length -= offset; ++ if (length > count) ++ length = count; ++ if (length < 0) ++ length = 0; ++ FN_EXIT1(length); ++ return length; ++} ++ ++static int ++acx_e_read_proc_phy(char *buf, char **start, off_t offset, int count, ++ int *eof, void *data) ++{ ++ acx_device_t *adev = (acx_device_t*)data; ++ int length; ++ ++ FN_ENTER; ++ ++ acx_sem_lock(adev); ++ /* fill buf */ ++ length = acx_s_proc_phy_output(buf, adev); ++ acx_sem_unlock(adev); ++ ++ /* housekeeping */ ++ if (length <= offset + count) ++ *eof = 1; ++ *start = buf + offset; ++ length -= offset; ++ if (length > count) ++ length = count; ++ if (length < 0) ++ length = 0; ++ FN_EXIT1(length); ++ return length; ++} ++ ++ ++/*********************************************************************** ++** /proc files registration ++*/ ++static const char * const ++proc_files[] = { "", "_diag", "_eeprom", "_phy" }; ++ ++static read_proc_t * const ++proc_funcs[] = { ++ acx_e_read_proc, ++ acx_e_read_proc_diag, ++ acx_e_read_proc_eeprom, ++ acx_e_read_proc_phy ++}; ++ ++static int ++manage_proc_entries(const struct net_device *ndev, int remove) ++{ ++ acx_device_t *adev = ndev2adev((struct net_device *)ndev); ++ char procbuf[80]; ++ int i; ++ ++ for (i = 0; i < VEC_SIZE(proc_files); i++) { ++ snprintf(procbuf, sizeof(procbuf), ++ "driver/acx_%s%s", ndev->name, proc_files[i]); ++ log(L_INIT, "%sing /proc entry %s\n", ++ remove ? "remov" : "creat", procbuf); ++ if (!remove) { ++ if (!create_proc_read_entry(procbuf, 0, 0, proc_funcs[i], adev)) { ++ printk("acx: cannot register /proc entry %s\n", procbuf); ++ return NOT_OK; ++ } ++ } else { ++ remove_proc_entry(procbuf, NULL); ++ } ++ } ++ return OK; ++} ++ ++int ++acx_proc_register_entries(const struct net_device *ndev) ++{ ++ return manage_proc_entries(ndev, 0); ++} ++ ++int ++acx_proc_unregister_entries(const struct net_device *ndev) ++{ ++ return manage_proc_entries(ndev, 1); ++} ++#endif /* CONFIG_PROC_FS */ ++ ++ ++/*********************************************************************** ++** acx_cmd_join_bssid ++** ++** Common code for both acx100 and acx111. ++*/ ++/* NB: does NOT match RATE100_nn but matches ACX[111]_SCAN_RATE_n */ ++static const u8 ++bitpos2genframe_txrate[] = { ++ 10, /* 0. 1 Mbit/s */ ++ 20, /* 1. 2 Mbit/s */ ++ 55, /* 2. 5.5 Mbit/s */ ++ 0x0B, /* 3. 6 Mbit/s */ ++ 0x0F, /* 4. 9 Mbit/s */ ++ 110, /* 5. 11 Mbit/s */ ++ 0x0A, /* 6. 12 Mbit/s */ ++ 0x0E, /* 7. 18 Mbit/s */ ++ 220, /* 8. 22 Mbit/s */ ++ 0x09, /* 9. 24 Mbit/s */ ++ 0x0D, /* 10. 36 Mbit/s */ ++ 0x08, /* 11. 48 Mbit/s */ ++ 0x0C, /* 12. 54 Mbit/s */ ++ 10, /* 13. 1 Mbit/s, should never happen */ ++ 10, /* 14. 1 Mbit/s, should never happen */ ++ 10, /* 15. 1 Mbit/s, should never happen */ ++}; ++ ++/* Looks scary, eh? ++** Actually, each one compiled into one AND and one SHIFT, ++** 31 bytes in x86 asm (more if uints are replaced by u16/u8) */ ++static inline unsigned int ++rate111to5bits(unsigned int rate) ++{ ++ return (rate & 0x7) ++ | ( (rate & RATE111_11) / (RATE111_11/JOINBSS_RATES_11) ) ++ | ( (rate & RATE111_22) / (RATE111_22/JOINBSS_RATES_22) ) ++ ; ++} ++ ++static void ++acx_s_cmd_join_bssid(acx_device_t *adev, const u8 *bssid) ++{ ++ acx_joinbss_t tmp; ++ int dtim_interval; ++ int i; ++ ++ if (mac_is_zero(bssid)) ++ return; ++ ++ FN_ENTER; ++ ++ dtim_interval = (ACX_MODE_0_ADHOC == adev->mode) ? ++ 1 : adev->dtim_interval; ++ ++ memset(&tmp, 0, sizeof(tmp)); ++ ++ for (i = 0; i < ETH_ALEN; i++) { ++ tmp.bssid[i] = bssid[ETH_ALEN-1 - i]; ++ } ++ ++ tmp.beacon_interval = cpu_to_le16(adev->beacon_interval); ++ ++ /* Basic rate set. Control frame responses (such as ACK or CTS frames) ++ ** are sent with one of these rates */ ++ if (IS_ACX111(adev)) { ++ /* It was experimentally determined that rates_basic ++ ** can take 11g rates as well, not only rates ++ ** defined with JOINBSS_RATES_BASIC111_nnn. ++ ** Just use RATE111_nnn constants... */ ++ tmp.u.acx111.dtim_interval = dtim_interval; ++ tmp.u.acx111.rates_basic = cpu_to_le16(adev->rate_basic); ++ log(L_ASSOC, "rates_basic:%04X, rates_supported:%04X\n", ++ adev->rate_basic, adev->rate_oper); ++ } else { ++ tmp.u.acx100.dtim_interval = dtim_interval; ++ tmp.u.acx100.rates_basic = rate111to5bits(adev->rate_basic); ++ tmp.u.acx100.rates_supported = rate111to5bits(adev->rate_oper); ++ log(L_ASSOC, "rates_basic:%04X->%02X, " ++ "rates_supported:%04X->%02X\n", ++ adev->rate_basic, tmp.u.acx100.rates_basic, ++ adev->rate_oper, tmp.u.acx100.rates_supported); ++ } ++ ++ /* Setting up how Beacon, Probe Response, RTS, and PS-Poll frames ++ ** will be sent (rate/modulation/preamble) */ ++ tmp.u.txrate.genfrm_txrate = bitpos2genframe_txrate[lowest_bit(adev->rate_basic)]; ++ tmp.genfrm_mod_pre = 0; /* FIXME: was = adev->capab_short (which was always 0); */ ++ /* we can use short pre *if* all peers can understand it */ ++ /* FIXME #2: we need to correctly set PBCC/OFDM bits here too */ ++ ++ /* we switch fw to STA mode in MONITOR mode, it seems to be ++ ** the only mode where fw does not emit beacons by itself ++ ** but allows us to send anything (we really want to retain ++ ** ability to tx arbitrary frames in MONITOR mode) ++ */ ++ tmp.macmode = (adev->mode != ACX_MODE_MONITOR ? adev->mode : ACX_MODE_2_STA); ++ tmp.channel = adev->channel; ++ tmp.essid_len = adev->essid_len; ++ /* NOTE: the code memcpy'd essid_len + 1 before, which is WRONG! */ ++ memcpy(tmp.essid, adev->essid, tmp.essid_len); ++ acx_s_issue_cmd(adev, ACX1xx_CMD_JOIN, &tmp, tmp.essid_len + 0x11); ++ ++ log(L_ASSOC|L_DEBUG, "BSS_Type = %u\n", tmp.macmode); ++ acxlog_mac(L_ASSOC|L_DEBUG, "JoinBSSID MAC:", adev->bssid, "\n"); ++ ++ acx_update_capabilities(adev); ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acx_s_cmd_start_scan ++** ++** Issue scan command to the hardware ++** ++** unified function for both ACX111 and ACX100 ++*/ ++static void ++acx_s_scan_chan(acx_device_t *adev) ++{ ++ union { ++ acx111_scan_t acx111; ++ acx100_scan_t acx100; ++ } s; ++ ++ FN_ENTER; ++ ++ memset(&s, 0, sizeof(s)); ++ ++ /* first common positions... */ ++ ++ s.acx111.count = cpu_to_le16(adev->scan_count); ++ s.acx111.rate = adev->scan_rate; ++ s.acx111.options = adev->scan_mode; ++ s.acx111.chan_duration = cpu_to_le16(adev->scan_duration); ++ s.acx111.max_probe_delay = cpu_to_le16(adev->scan_probe_delay); ++ ++ /* ...then differences */ ++ ++ if (IS_ACX111(adev)) { ++ s.acx111.channel_list_select = 0; /* scan every allowed channel */ ++ /*s.acx111.channel_list_select = 1;*/ /* scan given channels */ ++ /*s.acx111.modulation = 0x40;*/ /* long preamble? OFDM? -> only for active scan */ ++ s.acx111.modulation = 0; ++ /*s.acx111.channel_list[0] = 6; ++ s.acx111.channel_list[1] = 4;*/ ++ } else { ++ s.acx100.start_chan = cpu_to_le16(1); ++ s.acx100.flags = cpu_to_le16(0x8000); ++ } ++ ++ acx_s_issue_cmd(adev, ACX1xx_CMD_SCAN, &s, sizeof(s)); ++ FN_EXIT0; ++} ++ ++ ++void ++acx_s_cmd_start_scan(acx_device_t *adev) ++{ ++ /* time_before check is 'just in case' thing */ ++ if (!(adev->irq_status & HOST_INT_SCAN_COMPLETE) ++ && time_before(jiffies, adev->scan_start + 10*HZ) ++ ) { ++ log(L_INIT, "start_scan: seems like previous scan " ++ "is still running. Not starting anew. Please report\n"); ++ return; ++ } ++ ++ log(L_INIT, "starting radio scan\n"); ++ /* remember that fw is commanded to do scan */ ++ adev->scan_start = jiffies; ++ CLEAR_BIT(adev->irq_status, HOST_INT_SCAN_COMPLETE); ++ /* issue it */ ++ acx_s_scan_chan(adev); ++} ++ ++ ++/*********************************************************************** ++** acx111 feature config ++*/ ++static int ++acx111_s_get_feature_config(acx_device_t *adev, ++ u32 *feature_options, u32 *data_flow_options) ++{ ++ struct acx111_ie_feature_config feat; ++ ++ if (!IS_ACX111(adev)) { ++ return NOT_OK; ++ } ++ ++ memset(&feat, 0, sizeof(feat)); ++ ++ if (OK != acx_s_interrogate(adev, &feat, ACX1xx_IE_FEATURE_CONFIG)) { ++ return NOT_OK; ++ } ++ log(L_DEBUG, ++ "got Feature option:0x%X, DataFlow option: 0x%X\n", ++ feat.feature_options, ++ feat.data_flow_options); ++ ++ if (feature_options) ++ *feature_options = le32_to_cpu(feat.feature_options); ++ if (data_flow_options) ++ *data_flow_options = le32_to_cpu(feat.data_flow_options); ++ ++ return OK; ++} ++ ++static int ++acx111_s_set_feature_config(acx_device_t *adev, ++ u32 feature_options, u32 data_flow_options, ++ unsigned int mode /* 0 == remove, 1 == add, 2 == set */) ++{ ++ struct acx111_ie_feature_config feat; ++ ++ if (!IS_ACX111(adev)) { ++ return NOT_OK; ++ } ++ ++ if ((mode < 0) || (mode > 2)) ++ return NOT_OK; ++ ++ if (mode != 2) ++ /* need to modify old data */ ++ acx111_s_get_feature_config(adev, &feat.feature_options, &feat.data_flow_options); ++ else { ++ /* need to set a completely new value */ ++ feat.feature_options = 0; ++ feat.data_flow_options = 0; ++ } ++ ++ if (mode == 0) { /* remove */ ++ CLEAR_BIT(feat.feature_options, cpu_to_le32(feature_options)); ++ CLEAR_BIT(feat.data_flow_options, cpu_to_le32(data_flow_options)); ++ } else { /* add or set */ ++ SET_BIT(feat.feature_options, cpu_to_le32(feature_options)); ++ SET_BIT(feat.data_flow_options, cpu_to_le32(data_flow_options)); ++ } ++ ++ log(L_DEBUG, ++ "old: feature 0x%08X dataflow 0x%08X. mode: %u\n" ++ "new: feature 0x%08X dataflow 0x%08X\n", ++ feature_options, data_flow_options, mode, ++ le32_to_cpu(feat.feature_options), ++ le32_to_cpu(feat.data_flow_options)); ++ ++ if (OK != acx_s_configure(adev, &feat, ACX1xx_IE_FEATURE_CONFIG)) { ++ return NOT_OK; ++ } ++ ++ return OK; ++} ++ ++static inline int ++acx111_s_feature_off(acx_device_t *adev, u32 f, u32 d) ++{ ++ return acx111_s_set_feature_config(adev, f, d, 0); ++} ++static inline int ++acx111_s_feature_on(acx_device_t *adev, u32 f, u32 d) ++{ ++ return acx111_s_set_feature_config(adev, f, d, 1); ++} ++static inline int ++acx111_s_feature_set(acx_device_t *adev, u32 f, u32 d) ++{ ++ return acx111_s_set_feature_config(adev, f, d, 2); ++} ++ ++ ++/*********************************************************************** ++** acx100_s_init_memory_pools ++*/ ++static int ++acx100_s_init_memory_pools(acx_device_t *adev, const acx_ie_memmap_t *mmt) ++{ ++ acx100_ie_memblocksize_t MemoryBlockSize; ++ acx100_ie_memconfigoption_t MemoryConfigOption; ++ int TotalMemoryBlocks; ++ int RxBlockNum; ++ int TotalRxBlockSize; ++ int TxBlockNum; ++ int TotalTxBlockSize; ++ ++ FN_ENTER; ++ ++ /* Let's see if we can follow this: ++ first we select our memory block size (which I think is ++ completely arbitrary) */ ++ MemoryBlockSize.size = cpu_to_le16(adev->memblocksize); ++ ++ /* Then we alert the card to our decision of block size */ ++ if (OK != acx_s_configure(adev, &MemoryBlockSize, ACX100_IE_BLOCK_SIZE)) { ++ goto bad; ++ } ++ ++ /* We figure out how many total blocks we can create, using ++ the block size we chose, and the beginning and ending ++ memory pointers, i.e.: end-start/size */ ++ TotalMemoryBlocks = (le32_to_cpu(mmt->PoolEnd) - le32_to_cpu(mmt->PoolStart)) / adev->memblocksize; ++ ++ log(L_DEBUG, "TotalMemoryBlocks=%u (%u bytes)\n", ++ TotalMemoryBlocks, TotalMemoryBlocks*adev->memblocksize); ++ ++ /* MemoryConfigOption.DMA_config bitmask: ++ access to ACX memory is to be done: ++ 0x00080000 using PCI conf space?! ++ 0x00040000 using IO instructions? ++ 0x00000000 using memory access instructions ++ 0x00020000 using local memory block linked list (else what?) ++ 0x00010000 using host indirect descriptors (else host must access ACX memory?) ++ */ ++#if defined (ACX_MEM) ++ /* ++ * ACX ignores DMA_config for generic slave mode. ++ */ ++ MemoryConfigOption.DMA_config = 0; ++ /* Declare start of the Rx host pool */ ++ MemoryConfigOption.pRxHostDesc = cpu2acx(0); ++ log(L_DEBUG, "pRxHostDesc 0x%08X, rxhostdesc_startphy 0x%lX\n", ++ acx2cpu(MemoryConfigOption.pRxHostDesc), ++ (long)adev->rxhostdesc_startphy); ++#else ++ if (IS_PCI(adev)) { ++ MemoryConfigOption.DMA_config = cpu_to_le32(0x30000); ++ /* Declare start of the Rx host pool */ ++ MemoryConfigOption.pRxHostDesc = cpu2acx(adev->rxhostdesc_startphy); ++ log(L_DEBUG, "pRxHostDesc 0x%08X, rxhostdesc_startphy 0x%lX\n", ++ acx2cpu(MemoryConfigOption.pRxHostDesc), ++ (long)adev->rxhostdesc_startphy); ++ } else { ++ MemoryConfigOption.DMA_config = cpu_to_le32(0x20000); ++ } ++#endif ++ ++ /* 50% of the allotment of memory blocks go to tx descriptors */ ++ TxBlockNum = TotalMemoryBlocks / 2; ++ MemoryConfigOption.TxBlockNum = cpu_to_le16(TxBlockNum); ++ ++ /* and 50% go to the rx descriptors */ ++ RxBlockNum = TotalMemoryBlocks - TxBlockNum; ++ MemoryConfigOption.RxBlockNum = cpu_to_le16(RxBlockNum); ++ ++ /* size of the tx and rx descriptor queues */ ++ TotalTxBlockSize = TxBlockNum * adev->memblocksize; ++ TotalRxBlockSize = RxBlockNum * adev->memblocksize; ++ log(L_DEBUG, "TxBlockNum %u RxBlockNum %u TotalTxBlockSize %u " ++ "TotalTxBlockSize %u\n", TxBlockNum, RxBlockNum, ++ TotalTxBlockSize, TotalRxBlockSize); ++ ++ ++ /* align the tx descriptor queue to an alignment of 0x20 (32 bytes) */ ++ MemoryConfigOption.rx_mem = ++ cpu_to_le32((le32_to_cpu(mmt->PoolStart) + 0x1f) & ~0x1f); ++ ++ /* align the rx descriptor queue to units of 0x20 ++ * and offset it by the tx descriptor queue */ ++ MemoryConfigOption.tx_mem = ++ cpu_to_le32((le32_to_cpu(mmt->PoolStart) + TotalRxBlockSize + 0x1f) & ~0x1f); ++ log(L_DEBUG, "rx_mem %08X rx_mem %08X\n", ++ MemoryConfigOption.tx_mem, MemoryConfigOption.rx_mem); ++ ++ /* alert the device to our decision */ ++ if (OK != acx_s_configure(adev, &MemoryConfigOption, ACX1xx_IE_MEMORY_CONFIG_OPTIONS)) { ++ goto bad; ++ } ++ ++ /* and tell the device to kick it into gear */ ++ if (OK != acx_s_issue_cmd(adev, ACX100_CMD_INIT_MEMORY, NULL, 0)) { ++ goto bad; ++ } ++#ifdef ACX_MEM ++ /* ++ * slave memory interface has to manage the transmit pools for the ACX, ++ * so it needs to know what we chose here. ++ */ ++ adev->acx_txbuf_start = MemoryConfigOption.tx_mem; ++ adev->acx_txbuf_numblocks = MemoryConfigOption.TxBlockNum; ++#endif ++ ++ FN_EXIT1(OK); ++ return OK; ++bad: ++ FN_EXIT1(NOT_OK); ++ return NOT_OK; ++} ++ ++ ++/*********************************************************************** ++** acx100_s_create_dma_regions ++** ++** Note that this fn messes up heavily with hardware, but we cannot ++** lock it (we need to sleep). Not a problem since IRQs can't happen ++*/ ++static int ++acx100_s_create_dma_regions(acx_device_t *adev) ++{ ++ acx100_ie_queueconfig_t queueconf; ++ acx_ie_memmap_t memmap; ++ int res = NOT_OK; ++ u32 tx_queue_start, rx_queue_start; ++ ++ FN_ENTER; ++ ++ /* read out the acx100 physical start address for the queues */ ++ if (OK != acx_s_interrogate(adev, &memmap, ACX1xx_IE_MEMORY_MAP)) { ++ goto fail; ++ } ++ ++ tx_queue_start = le32_to_cpu(memmap.QueueStart); ++ rx_queue_start = tx_queue_start + TX_CNT * sizeof(txdesc_t); ++ ++ log(L_DEBUG, "initializing Queue Indicator\n"); ++ ++ memset(&queueconf, 0, sizeof(queueconf)); ++ ++ /* Not needed for PCI or slave memory, so we can avoid setting them altogether */ ++ if (IS_USB(adev)) { ++ queueconf.NumTxDesc = USB_TX_CNT; ++ queueconf.NumRxDesc = USB_RX_CNT; ++ } ++ ++ /* calculate size of queues */ ++ queueconf.AreaSize = cpu_to_le32( ++ TX_CNT * sizeof(txdesc_t) + ++ RX_CNT * sizeof(rxdesc_t) + 8 ++ ); ++ queueconf.NumTxQueues = 1; /* number of tx queues */ ++ /* sets the beginning of the tx descriptor queue */ ++ queueconf.TxQueueStart = memmap.QueueStart; ++ /* done by memset: queueconf.TxQueuePri = 0; */ ++ queueconf.RxQueueStart = cpu_to_le32(rx_queue_start); ++ queueconf.QueueOptions = 1; /* auto reset descriptor */ ++ /* sets the end of the rx descriptor queue */ ++ queueconf.QueueEnd = cpu_to_le32( ++ rx_queue_start + RX_CNT * sizeof(rxdesc_t) ++ ); ++ /* sets the beginning of the next queue */ ++ queueconf.HostQueueEnd = cpu_to_le32(le32_to_cpu(queueconf.QueueEnd) + 8); ++ if (OK != acx_s_configure(adev, &queueconf, ACX1xx_IE_QUEUE_CONFIG)) { ++ goto fail; ++ } ++ ++#if defined (ACX_MEM) ++ /* sets the beginning of the rx descriptor queue, after the tx descrs */ ++ adev->acx_queue_indicator = ++ (queueindicator_t *) le32_to_cpu (queueconf.QueueEnd); ++ if (OK != acxmem_s_create_hostdesc_queues(adev)) ++ goto fail; ++ ++ acxmem_create_desc_queues(adev, tx_queue_start, rx_queue_start); ++#else ++ if (IS_PCI(adev)) { ++ /* sets the beginning of the rx descriptor queue, after the tx descrs */ ++ if (OK != acxpci_s_create_hostdesc_queues(adev)) ++ goto fail; ++ acxpci_create_desc_queues(adev, tx_queue_start, rx_queue_start); ++ } ++#endif ++ ++ if (OK != acx_s_interrogate(adev, &memmap, ACX1xx_IE_MEMORY_MAP)) { ++ goto fail; ++ } ++ ++ /* ++ * Have to make sure we skip past the Queue Indicator (QueueEnd) and Host Queue Indicator ++ * maps, each of which are 8 bytes and follow immediately after the transmit and ++ * receive queues. ++ */ ++ memmap.PoolStart = cpu_to_le32( ++ (le32_to_cpu(memmap.QueueEnd) + 4 + 0x1f) & ~0x1f ++ ); ++ ++ if (OK != acx_s_configure(adev, &memmap, ACX1xx_IE_MEMORY_MAP)) { ++ goto fail; ++ } ++ ++ if (OK != acx100_s_init_memory_pools(adev, &memmap)) { ++ goto fail; ++ } ++ ++ res = OK; ++ goto end; ++ ++fail: ++ acx_s_msleep(1000); /* ? */ ++#if defined (ACX_MEM) ++ acxmem_free_desc_queues(adev); ++#else ++ if (IS_PCI(adev)) ++ acxpci_free_desc_queues(adev); ++#endif ++end: ++ FN_EXIT1(res); ++ return res; ++} ++ ++ ++/*********************************************************************** ++** acx111_s_create_dma_regions ++** ++** Note that this fn messes heavily with hardware, but we cannot ++** lock it (we need to sleep). Not a problem since IRQs can't happen ++*/ ++#define ACX111_PERCENT(percent) ((percent)/5) ++ ++static int ++acx111_s_create_dma_regions(acx_device_t *adev) ++{ ++ struct acx111_ie_memoryconfig memconf; ++ struct acx111_ie_queueconfig queueconf; ++ u32 tx_queue_start, rx_queue_start; ++ ++ FN_ENTER; ++ ++ /* Calculate memory positions and queue sizes */ ++ ++ /* Set up our host descriptor pool + data pool */ ++#if defined (ACX_MEM) ++ if (OK != acxmem_s_create_hostdesc_queues(adev)) ++ goto fail; ++#else ++ if (IS_PCI(adev)) { ++ if (OK != acxpci_s_create_hostdesc_queues(adev)) ++ goto fail; ++ } ++#endif ++ ++ memset(&memconf, 0, sizeof(memconf)); ++ /* the number of STAs (STA contexts) to support ++ ** NB: was set to 1 and everything seemed to work nevertheless... */ ++ memconf.no_of_stations = cpu_to_le16(VEC_SIZE(adev->sta_list)); ++ /* specify the memory block size. Default is 256 */ ++ memconf.memory_block_size = cpu_to_le16(adev->memblocksize); ++ /* let's use 50%/50% for tx/rx (specify percentage, units of 5%) */ ++ memconf.tx_rx_memory_block_allocation = ACX111_PERCENT(50); ++ /* set the count of our queues ++ ** NB: struct acx111_ie_memoryconfig shall be modified ++ ** if we ever will switch to more than one rx and/or tx queue */ ++ memconf.count_rx_queues = 1; ++ memconf.count_tx_queues = 1; ++ /* 0 == Busmaster Indirect Memory Organization, which is what we want ++ * (using linked host descs with their allocated mem). ++ * 2 == Generic Bus Slave */ ++ /* done by memset: memconf.options = 0; */ ++ /* let's use 25% for fragmentations and 75% for frame transfers ++ * (specified in units of 5%) */ ++ memconf.fragmentation = ACX111_PERCENT(75); ++ /* Rx descriptor queue config */ ++ memconf.rx_queue1_count_descs = RX_CNT; ++ memconf.rx_queue1_type = 7; /* must be set to 7 */ ++ /* done by memset: memconf.rx_queue1_prio = 0; low prio */ ++#if defined (ACX_MEM) ++ memconf.rx_queue1_host_rx_start = cpu2acx(adev->rxhostdesc_startphy); ++#else ++ if (IS_PCI(adev)) { ++ memconf.rx_queue1_host_rx_start = cpu2acx(adev->rxhostdesc_startphy); ++ } ++#endif ++ /* Tx descriptor queue config */ ++ memconf.tx_queue1_count_descs = TX_CNT; ++ /* done by memset: memconf.tx_queue1_attributes = 0; lowest priority */ ++ ++ /* NB1: this looks wrong: (memconf,ACX1xx_IE_QUEUE_CONFIG), ++ ** (queueconf,ACX1xx_IE_MEMORY_CONFIG_OPTIONS) look swapped, eh? ++ ** But it is actually correct wrt IE numbers. ++ ** NB2: sizeof(memconf) == 28 == 0x1c but configure(ACX1xx_IE_QUEUE_CONFIG) ++ ** writes 0x20 bytes (because same IE for acx100 uses struct acx100_ie_queueconfig ++ ** which is 4 bytes larger. what a mess. TODO: clean it up) */ ++ if (OK != acx_s_configure(adev, &memconf, ACX1xx_IE_QUEUE_CONFIG)) { ++ goto fail; ++ } ++ ++ acx_s_interrogate(adev, &queueconf, ACX1xx_IE_MEMORY_CONFIG_OPTIONS); ++ ++ tx_queue_start = le32_to_cpu(queueconf.tx1_queue_address); ++ rx_queue_start = le32_to_cpu(queueconf.rx1_queue_address); ++ ++ log(L_INIT, "dump queue head (from card):\n" ++ "len: %u\n" ++ "tx_memory_block_address: %X\n" ++ "rx_memory_block_address: %X\n" ++ "tx1_queue address: %X\n" ++ "rx1_queue address: %X\n", ++ le16_to_cpu(queueconf.len), ++ le32_to_cpu(queueconf.tx_memory_block_address), ++ le32_to_cpu(queueconf.rx_memory_block_address), ++ tx_queue_start, ++ rx_queue_start); ++ ++#if defined (ACX_MEM) ++ acxmem_create_desc_queues(adev, tx_queue_start, rx_queue_start); ++#else ++ if (IS_PCI(adev)) ++ acxpci_create_desc_queues(adev, tx_queue_start, rx_queue_start); ++#endif ++ ++ FN_EXIT1(OK); ++ return OK; ++fail: ++#if defined (ACX_MEM) ++ acxmem_free_desc_queues(adev); ++#else ++ if (IS_PCI(adev)) ++ acxpci_free_desc_queues(adev); ++#endif ++ ++ FN_EXIT1(NOT_OK); ++ return NOT_OK; ++} ++ ++ ++/*********************************************************************** ++*/ ++static void ++acx_s_initialize_rx_config(acx_device_t *adev) ++{ ++ struct { ++ u16 id; ++ u16 len; ++ u16 rx_cfg1; ++ u16 rx_cfg2; ++ } ACX_PACKED cfg; ++ ++ switch (adev->mode) { ++ case ACX_MODE_OFF: ++ adev->rx_config_1 = (u16) (0 ++ /* | RX_CFG1_INCLUDE_RXBUF_HDR */ ++ /* | RX_CFG1_FILTER_SSID */ ++ /* | RX_CFG1_FILTER_BCAST */ ++ /* | RX_CFG1_RCV_MC_ADDR1 */ ++ /* | RX_CFG1_RCV_MC_ADDR0 */ ++ /* | RX_CFG1_FILTER_ALL_MULTI */ ++ /* | RX_CFG1_FILTER_BSSID */ ++ /* | RX_CFG1_FILTER_MAC */ ++ /* | RX_CFG1_RCV_PROMISCUOUS */ ++ /* | RX_CFG1_INCLUDE_FCS */ ++ /* | RX_CFG1_INCLUDE_PHY_HDR */ ++ ); ++ adev->rx_config_2 = (u16) (0 ++ /*| RX_CFG2_RCV_ASSOC_REQ */ ++ /*| RX_CFG2_RCV_AUTH_FRAMES */ ++ /*| RX_CFG2_RCV_BEACON_FRAMES */ ++ /*| RX_CFG2_RCV_CONTENTION_FREE */ ++ /*| RX_CFG2_RCV_CTRL_FRAMES */ ++ /*| RX_CFG2_RCV_DATA_FRAMES */ ++ /*| RX_CFG2_RCV_BROKEN_FRAMES */ ++ /*| RX_CFG2_RCV_MGMT_FRAMES */ ++ /*| RX_CFG2_RCV_PROBE_REQ */ ++ /*| RX_CFG2_RCV_PROBE_RESP */ ++ /*| RX_CFG2_RCV_ACK_FRAMES */ ++ /*| RX_CFG2_RCV_OTHER */ ++ ); ++ break; ++ case ACX_MODE_MONITOR: ++ adev->rx_config_1 = (u16) (0 ++ /* | RX_CFG1_INCLUDE_RXBUF_HDR */ ++ /* | RX_CFG1_FILTER_SSID */ ++ /* | RX_CFG1_FILTER_BCAST */ ++ /* | RX_CFG1_RCV_MC_ADDR1 */ ++ /* | RX_CFG1_RCV_MC_ADDR0 */ ++ /* | RX_CFG1_FILTER_ALL_MULTI */ ++ /* | RX_CFG1_FILTER_BSSID */ ++ /* | RX_CFG1_FILTER_MAC */ ++ | RX_CFG1_RCV_PROMISCUOUS ++ /* | RX_CFG1_INCLUDE_FCS */ ++ /* | RX_CFG1_INCLUDE_PHY_HDR */ ++ ); ++ adev->rx_config_2 = (u16) (0 ++ | RX_CFG2_RCV_ASSOC_REQ ++ | RX_CFG2_RCV_AUTH_FRAMES ++ | RX_CFG2_RCV_BEACON_FRAMES ++ | RX_CFG2_RCV_CONTENTION_FREE ++ | RX_CFG2_RCV_CTRL_FRAMES ++ | RX_CFG2_RCV_DATA_FRAMES ++ | RX_CFG2_RCV_BROKEN_FRAMES ++ | RX_CFG2_RCV_MGMT_FRAMES ++ | RX_CFG2_RCV_PROBE_REQ ++ | RX_CFG2_RCV_PROBE_RESP ++ | RX_CFG2_RCV_ACK_FRAMES ++ | RX_CFG2_RCV_OTHER ++ ); ++ break; ++ default: ++ adev->rx_config_1 = (u16) (0 ++ /* | RX_CFG1_INCLUDE_RXBUF_HDR */ ++ /* | RX_CFG1_FILTER_SSID */ ++ /* | RX_CFG1_FILTER_BCAST */ ++ /* | RX_CFG1_RCV_MC_ADDR1 */ ++ /* | RX_CFG1_RCV_MC_ADDR0 */ ++ /* | RX_CFG1_FILTER_ALL_MULTI */ ++ /* | RX_CFG1_FILTER_BSSID */ ++ | RX_CFG1_FILTER_MAC ++ /* | RX_CFG1_RCV_PROMISCUOUS */ ++ /* | RX_CFG1_INCLUDE_FCS */ ++ /* | RX_CFG1_INCLUDE_PHY_HDR */ ++ ); ++ adev->rx_config_2 = (u16) (0 ++ | RX_CFG2_RCV_ASSOC_REQ ++ | RX_CFG2_RCV_AUTH_FRAMES ++ | RX_CFG2_RCV_BEACON_FRAMES ++ | RX_CFG2_RCV_CONTENTION_FREE ++ | RX_CFG2_RCV_CTRL_FRAMES ++ | RX_CFG2_RCV_DATA_FRAMES ++ /*| RX_CFG2_RCV_BROKEN_FRAMES */ ++ | RX_CFG2_RCV_MGMT_FRAMES ++ | RX_CFG2_RCV_PROBE_REQ ++ | RX_CFG2_RCV_PROBE_RESP ++ /*| RX_CFG2_RCV_ACK_FRAMES */ ++ | RX_CFG2_RCV_OTHER ++ ); ++ break; ++ } ++ adev->rx_config_1 |= RX_CFG1_INCLUDE_RXBUF_HDR; ++ ++ if ((adev->rx_config_1 & RX_CFG1_INCLUDE_PHY_HDR) ++ || (adev->firmware_numver >= 0x02000000)) ++ adev->phy_header_len = IS_ACX111(adev) ? 8 : 4; ++ else ++ adev->phy_header_len = 0; ++ ++ log(L_INIT, "setting RXconfig to %04X:%04X\n", ++ adev->rx_config_1, adev->rx_config_2); ++ cfg.rx_cfg1 = cpu_to_le16(adev->rx_config_1); ++ cfg.rx_cfg2 = cpu_to_le16(adev->rx_config_2); ++ acx_s_configure(adev, &cfg, ACX1xx_IE_RXCONFIG); ++} ++ ++ ++/*********************************************************************** ++** acx_s_set_defaults ++*/ ++void ++acx_s_set_defaults(acx_device_t *adev) ++{ ++ unsigned long flags; ++ ++ FN_ENTER; ++ ++ /* do it before getting settings, prevent bogus channel 0 warning */ ++ adev->channel = 1; ++ ++ /* query some settings from the card. ++ * NOTE: for some settings, e.g. CCA and ED (ACX100!), an initial ++ * query is REQUIRED, otherwise the card won't work correctly! */ ++ adev->get_mask = GETSET_ANTENNA|GETSET_SENSITIVITY|GETSET_STATION_ID|GETSET_REG_DOMAIN; ++ /* Only ACX100 supports ED and CCA */ ++ if (IS_ACX100(adev)) ++ adev->get_mask |= GETSET_CCA|GETSET_ED_THRESH; ++ ++ acx_s_update_card_settings(adev); ++ ++ acx_lock(adev, flags); ++ ++ /* set our global interrupt mask */ ++#if defined (ACX_MEM) ++ acxmem_set_interrupt_mask(adev); ++#else ++ if (IS_PCI(adev)) ++ acxpci_set_interrupt_mask(adev); ++#endif ++ ++ adev->led_power = 1; /* LED is active on startup */ ++ adev->brange_max_quality = 60; /* LED blink max quality is 60 */ ++ adev->brange_time_last_state_change = jiffies; ++ ++ /* copy the MAC address we just got from the card ++ * into our MAC address used during current 802.11 session */ ++ MAC_COPY(adev->dev_addr, adev->ndev->dev_addr); ++ MAC_BCAST(adev->ap); ++ ++ adev->essid_len = ++ snprintf(adev->essid, sizeof(adev->essid), "STA%02X%02X%02X", ++ adev->dev_addr[3], adev->dev_addr[4], adev->dev_addr[5]); ++ adev->essid_active = 1; ++ ++ /* we have a nick field to waste, so why not abuse it ++ * to announce the driver version? ;-) */ ++ strncpy(adev->nick, "acx " ACX_RELEASE, IW_ESSID_MAX_SIZE); ++ ++#if defined (ACX_MEM) ++ adev->reg_dom_id = adev->cfgopt_domains.list[0]; ++#else ++ if (IS_PCI(adev)) { /* FIXME: this should be made to apply to USB, too! */ ++ /* first regulatory domain entry in EEPROM == default reg. domain */ ++ adev->reg_dom_id = adev->cfgopt_domains.list[0]; ++ } ++#endif ++ ++ /* 0xffff would be better, but then we won't get a "scan complete" ++ * interrupt, so our current infrastructure will fail: */ ++ adev->scan_count = 1; ++ adev->scan_mode = ACX_SCAN_OPT_ACTIVE; ++ adev->scan_duration = 100; ++ adev->scan_probe_delay = 200; ++ /* reported to break scanning: adev->scan_probe_delay = adev->cfgopt_probe_delay; */ ++ adev->scan_rate = ACX_SCAN_RATE_1; ++ ++ adev->mode = ACX_MODE_2_STA; ++ adev->auth_alg = WLAN_AUTH_ALG_OPENSYSTEM; ++ adev->listen_interval = 100; ++ adev->beacon_interval = DEFAULT_BEACON_INTERVAL; ++ adev->dtim_interval = DEFAULT_DTIM_INTERVAL; ++ ++ adev->msdu_lifetime = DEFAULT_MSDU_LIFETIME; ++ ++ adev->rts_threshold = DEFAULT_RTS_THRESHOLD; ++ adev->frag_threshold = 2346; ++ ++ /* use standard default values for retry limits */ ++ adev->short_retry = 7; /* max. retries for (short) non-RTS packets */ ++ adev->long_retry = 4; /* max. retries for long (RTS) packets */ ++ ++ adev->preamble_mode = 2; /* auto */ ++ adev->fallback_threshold = 3; ++ adev->stepup_threshold = 10; ++ adev->rate_bcast = RATE111_1; ++ adev->rate_bcast100 = RATE100_1; ++ adev->rate_basic = RATE111_1 | RATE111_2; ++ adev->rate_auto = 1; ++ if (IS_ACX111(adev)) { ++ adev->rate_oper = RATE111_ALL; ++ } else { ++ adev->rate_oper = RATE111_ACX100_COMPAT; ++ } ++ ++ /* Supported Rates element - the rates here are given in units of ++ * 500 kbit/s, plus 0x80 added. See 802.11-1999.pdf item 7.3.2.2 */ ++ acx_l_update_ratevector(adev); ++ ++ /* set some more defaults */ ++ if (IS_ACX111(adev)) { ++ /* 30mW (15dBm) is default, at least in my acx111 card: */ ++ adev->tx_level_dbm = 15; ++ } else { ++ /* don't use max. level, since it might be dangerous ++ * (e.g. WRT54G people experience ++ * excessive Tx power damage!) */ ++ adev->tx_level_dbm = 18; ++ /* ++ * Lower power for the iPaq hx4700 ++ */ ++ if (IS_MEM(adev)) { ++ adev->tx_level_dbm = 14; ++ } ++ } ++ /* adev->tx_level_auto = 1; */ ++ if (IS_ACX111(adev)) { ++ /* start with sensitivity level 1 out of 3: */ ++ adev->sensitivity = 1; ++ } ++ ++/* #define ENABLE_POWER_SAVE */ ++#ifdef ENABLE_POWER_SAVE ++ adev->ps_wakeup_cfg = PS_CFG_ENABLE | PS_CFG_WAKEUP_ALL_BEAC; ++ adev->ps_listen_interval = 1; ++ adev->ps_options = PS_OPT_ENA_ENHANCED_PS | PS_OPT_TX_PSPOLL | PS_OPT_STILL_RCV_BCASTS; ++ adev->ps_hangover_period = 30; ++ adev->ps_enhanced_transition_time = 0; ++#else ++ adev->ps_wakeup_cfg = 0; ++ adev->ps_listen_interval = 0; ++ adev->ps_options = 0; ++ adev->ps_hangover_period = 0; ++ adev->ps_enhanced_transition_time = 0; ++#endif ++ ++ /* These settings will be set in fw on ifup */ ++ adev->set_mask = 0 ++ | GETSET_RETRY ++ | SET_MSDU_LIFETIME ++ /* configure card to do rate fallback when in auto rate mode */ ++ | SET_RATE_FALLBACK ++ | SET_RXCONFIG ++ | GETSET_TXPOWER ++ /* better re-init the antenna value we got above */ ++ | GETSET_ANTENNA ++#if POWER_SAVE_80211 ++ | GETSET_POWER_80211 ++#endif ++ ; ++ ++ acx_unlock(adev, flags); ++ acx_lock_unhold(); /* hold time 844814 CPU ticks @2GHz */ ++ ++ acx_s_initialize_rx_config(adev); ++ ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** FIXME: this should be solved in a general way for all radio types ++** by decoding the radio firmware module, ++** since it probably has some standard structure describing how to ++** set the power level of the radio module which it controls. ++** Or maybe not, since the radio module probably has a function interface ++** instead which then manages Tx level programming :-\ ++*/ ++static int ++acx111_s_set_tx_level(acx_device_t *adev, u8 level_dbm) ++{ ++ struct acx111_ie_tx_level tx_level; ++ ++ /* my acx111 card has two power levels in its configoptions (== EEPROM): ++ * 1 (30mW) [15dBm] ++ * 2 (10mW) [10dBm] ++ * For now, just assume all other acx111 cards have the same. ++ * FIXME: Ideally we would query it here, but we first need a ++ * standard way to query individual configoptions easily. ++ * Well, now we have proper cfgopt txpower variables, but this still ++ * hasn't been done yet, since it also requires dBm <-> mW conversion here... */ ++ if (level_dbm <= 12) { ++ tx_level.level = 2; /* 10 dBm */ ++ adev->tx_level_dbm = 10; ++ } else { ++ tx_level.level = 1; /* 15 dBm */ ++ adev->tx_level_dbm = 15; ++ } ++ if (level_dbm != adev->tx_level_dbm) ++ log(L_INIT, "acx111 firmware has specific " ++ "power levels only: adjusted %d dBm to %d dBm!\n", ++ level_dbm, adev->tx_level_dbm); ++ ++ return acx_s_configure(adev, &tx_level, ACX1xx_IE_DOT11_TX_POWER_LEVEL); ++} ++ ++static int ++acx_s_set_tx_level(acx_device_t *adev, u8 level_dbm) ++{ ++ if (IS_ACX111(adev)) { ++ return acx111_s_set_tx_level(adev, level_dbm); ++ } ++#if defined (ACX_MEM) ++ return acx100mem_s_set_tx_level(adev, level_dbm); ++#else ++ if (IS_PCI(adev)) { ++ return acx100pci_s_set_tx_level(adev, level_dbm); ++ } ++#endif ++ return OK; ++} ++ ++ ++/*********************************************************************** ++*/ ++#ifdef UNUSED ++/* Returns the current tx level (ACX111) */ ++static u8 ++acx111_s_get_tx_level(acx_device_t *adev) ++{ ++ struct acx111_ie_tx_level tx_level; ++ ++ tx_level.level = 0; ++ acx_s_interrogate(adev, &tx_level, ACX1xx_IE_DOT11_TX_POWER_LEVEL); ++ return tx_level.level; ++} ++#endif ++ ++ ++/*********************************************************************** ++** acx_l_rxmonitor ++** Called from IRQ context only ++*/ ++static void ++acx_l_rxmonitor(acx_device_t *adev, const rxbuffer_t *rxbuf) ++{ ++ wlansniffrm_t *msg; ++ struct sk_buff *skb; ++ void *datap; ++ unsigned int skb_len; ++ int payload_offset; ++ ++ FN_ENTER; ++ ++ /* we are in big luck: the acx100 doesn't modify any of the fields */ ++ /* in the 802.11 frame. just pass this packet into the PF_PACKET */ ++ /* subsystem. yeah. */ ++ payload_offset = ((u8*)acx_get_wlan_hdr(adev, rxbuf) - (u8*)rxbuf); ++ skb_len = RXBUF_BYTES_USED(rxbuf) - payload_offset; ++ ++ /* sanity check */ ++ if (unlikely(skb_len > WLAN_A4FR_MAXLEN_WEP)) { ++ printk("%s: monitor mode panic: oversized frame!\n", ++ adev->ndev->name); ++ goto end; ++ } ++ ++ if (adev->ndev->type == ARPHRD_IEEE80211_PRISM) ++ skb_len += sizeof(*msg); ++ ++ /* allocate skb */ ++ skb = dev_alloc_skb(skb_len); ++ if (unlikely(!skb)) { ++ printk("%s: no memory for skb (%u bytes)\n", ++ adev->ndev->name, skb_len); ++ goto end; ++ } ++ ++ skb_put(skb, skb_len); ++ ++ if (adev->ndev->type == ARPHRD_IEEE80211) { ++ /* when in raw 802.11 mode, just copy frame as-is */ ++ datap = skb->data; ++ } else if (adev->ndev->type == ARPHRD_IEEE80211_PRISM) { ++ /* emulate prism header */ ++ msg = (wlansniffrm_t*)skb->data; ++ datap = msg + 1; ++ ++ msg->msgcode = WLANSNIFFFRM; ++ msg->msglen = sizeof(*msg); ++ strncpy(msg->devname, adev->ndev->name, sizeof(msg->devname)-1); ++ msg->devname[sizeof(msg->devname)-1] = '\0'; ++ ++ msg->hosttime.did = WLANSNIFFFRM_hosttime; ++ msg->hosttime.status = WLANITEM_STATUS_data_ok; ++ msg->hosttime.len = 4; ++ msg->hosttime.data = jiffies; ++ ++ msg->mactime.did = WLANSNIFFFRM_mactime; ++ msg->mactime.status = WLANITEM_STATUS_data_ok; ++ msg->mactime.len = 4; ++ msg->mactime.data = rxbuf->time; ++ ++ msg->channel.did = WLANSNIFFFRM_channel; ++ msg->channel.status = WLANITEM_STATUS_data_ok; ++ msg->channel.len = 4; ++ msg->channel.data = adev->channel; ++ ++ msg->rssi.did = WLANSNIFFFRM_rssi; ++ msg->rssi.status = WLANITEM_STATUS_no_value; ++ msg->rssi.len = 4; ++ msg->rssi.data = 0; ++ ++ msg->sq.did = WLANSNIFFFRM_sq; ++ msg->sq.status = WLANITEM_STATUS_no_value; ++ msg->sq.len = 4; ++ msg->sq.data = 0; ++ ++ msg->signal.did = WLANSNIFFFRM_signal; ++ msg->signal.status = WLANITEM_STATUS_data_ok; ++ msg->signal.len = 4; ++ msg->signal.data = rxbuf->phy_snr; ++ ++ msg->noise.did = WLANSNIFFFRM_noise; ++ msg->noise.status = WLANITEM_STATUS_data_ok; ++ msg->noise.len = 4; ++ msg->noise.data = rxbuf->phy_level; ++ ++ msg->rate.did = WLANSNIFFFRM_rate; ++ msg->rate.status = WLANITEM_STATUS_data_ok; ++ msg->rate.len = 4; ++ msg->rate.data = rxbuf->phy_plcp_signal / 5; ++ ++ msg->istx.did = WLANSNIFFFRM_istx; ++ msg->istx.status = WLANITEM_STATUS_data_ok; ++ msg->istx.len = 4; ++ msg->istx.data = 0; /* tx=0: it's not a tx packet */ ++ ++ skb_len -= sizeof(*msg); ++ ++ msg->frmlen.did = WLANSNIFFFRM_signal; ++ msg->frmlen.status = WLANITEM_STATUS_data_ok; ++ msg->frmlen.len = 4; ++ msg->frmlen.data = skb_len; ++ } else { ++ printk("acx: unsupported netdev type %d!\n", adev->ndev->type); ++ dev_kfree_skb(skb); ++ return; ++ } ++ ++ /* sanity check (keep it here) */ ++ if (unlikely((int)skb_len < 0)) { ++ printk("acx: skb_len=%d. Driver bug, please report\n", (int)skb_len); ++ dev_kfree_skb(skb); ++ return; ++ } ++ memcpy(datap, ((unsigned char*)rxbuf)+payload_offset, skb_len); ++ ++ skb->dev = adev->ndev; ++ skb->dev->last_rx = jiffies; ++ ++ skb_reset_mac_header(skb); ++ skb->ip_summed = CHECKSUM_NONE; ++ skb->pkt_type = PACKET_OTHERHOST; ++ skb->protocol = htons(ETH_P_80211_RAW); ++ netif_rx(skb); ++ ++ adev->stats.rx_packets++; ++ adev->stats.rx_bytes += skb->len; ++ ++end: ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acx_l_rx_ieee802_11_frame ++** ++** Called from IRQ context only ++*/ ++ ++/* All these contortions are for saner dup logging ++** ++** We want: (a) to know about excessive dups ++** (b) to not spam kernel log about occasional dups ++** ++** 1/64 threshold was chosen by running "ping -A" ++** It gave "rx: 59 DUPs in 2878 packets" only with 4 parallel ++** "ping -A" streams running. */ ++/* 2005-10-11: bumped up to 1/8 ++** subtract a $smallint from dup_count in order to ++** avoid "2 DUPs in 19 packets" messages */ ++static inline int ++acx_l_handle_dup(acx_device_t *adev, u16 seq) ++{ ++ if (adev->dup_count) { ++ adev->nondup_count++; ++ if (time_after(jiffies, adev->dup_msg_expiry)) { ++ /* Log only if more than 1 dup in 64 packets */ ++ if (adev->nondup_count/8 < adev->dup_count-5) { ++ printk(KERN_INFO "%s: rx: %d DUPs in " ++ "%d packets received in 10 secs\n", ++ adev->ndev->name, ++ adev->dup_count, ++ adev->nondup_count); ++ } ++ adev->dup_count = 0; ++ adev->nondup_count = 0; ++ } ++ } ++ if (unlikely(seq == adev->last_seq_ctrl)) { ++ if (!adev->dup_count++) ++ adev->dup_msg_expiry = jiffies + 10*HZ; ++ adev->stats.rx_errors++; ++ return 1; /* a dup */ ++ } ++ adev->last_seq_ctrl = seq; ++ return 0; ++} ++ ++static int ++acx_l_rx_ieee802_11_frame(acx_device_t *adev, rxbuffer_t *rxbuf) ++{ ++ unsigned int ftype, fstype; ++ const wlan_hdr_t *hdr; ++ int result = NOT_OK; ++ ++ FN_ENTER; ++ ++ hdr = acx_get_wlan_hdr(adev, rxbuf); ++ ++ /* see IEEE 802.11-1999.pdf chapter 7 "MAC frame formats" */ ++ if (unlikely((hdr->fc & WF_FC_PVERi) != 0)) { ++ printk_ratelimited(KERN_INFO "rx: unsupported 802.11 protocol\n"); ++ goto end; ++ } ++ ++ ftype = hdr->fc & WF_FC_FTYPEi; ++ fstype = hdr->fc & WF_FC_FSTYPEi; ++ ++ switch (ftype) { ++ /* check data frames first, for speed */ ++ case WF_FTYPE_DATAi: ++ switch (fstype) { ++ case WF_FSTYPE_DATAONLYi: ++ if (acx_l_handle_dup(adev, hdr->seq)) ++ break; /* a dup, simply discard it */ ++ ++ /* TODO: ++ if (WF_FC_FROMTODSi == (hdr->fc & WF_FC_FROMTODSi)) { ++ result = acx_l_process_data_frame_wds(adev, rxbuf); ++ break; ++ } ++ */ ++ ++ switch (adev->mode) { ++ case ACX_MODE_3_AP: ++ result = acx_l_process_data_frame_master(adev, rxbuf); ++ break; ++ case ACX_MODE_0_ADHOC: ++ case ACX_MODE_2_STA: ++ result = acx_l_process_data_frame_client(adev, rxbuf); ++ break; ++ } ++ case WF_FSTYPE_DATA_CFACKi: ++ case WF_FSTYPE_DATA_CFPOLLi: ++ case WF_FSTYPE_DATA_CFACK_CFPOLLi: ++ case WF_FSTYPE_CFPOLLi: ++ case WF_FSTYPE_CFACK_CFPOLLi: ++ /* see above. ++ acx_process_class_frame(adev, rxbuf, 3); */ ++ break; ++ case WF_FSTYPE_NULLi: ++ /* acx_l_process_NULL_frame(adev, rxbuf, 3); */ ++ break; ++ /* FIXME: same here, see above */ ++ case WF_FSTYPE_CFACKi: ++ default: ++ break; ++ } ++ break; ++ case WF_FTYPE_MGMTi: ++ result = acx_l_process_mgmt_frame(adev, rxbuf); ++ break; ++ case WF_FTYPE_CTLi: ++ if (fstype == WF_FSTYPE_PSPOLLi) ++ result = OK; ++ /* this call is irrelevant, since ++ * acx_process_class_frame is a stub, so return ++ * immediately instead. ++ * return acx_process_class_frame(adev, rxbuf, 3); */ ++ break; ++ default: ++ break; ++ } ++end: ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++** acx_l_process_rxbuf ++** ++** NB: used by USB code also ++*/ ++void ++acx_l_process_rxbuf(acx_device_t *adev, rxbuffer_t *rxbuf) ++{ ++ struct wlan_hdr *hdr; ++ unsigned int qual; ++ int buf_len; ++ u16 fc; ++ ++ hdr = acx_get_wlan_hdr(adev, rxbuf); ++ fc = le16_to_cpu(hdr->fc); ++ /* length of frame from control field to first byte of FCS */ ++ buf_len = RXBUF_BYTES_RCVD(adev, rxbuf); ++ ++ if ( ((WF_FC_FSTYPE & fc) != WF_FSTYPE_BEACON) ++ || (acx_debug & L_XFER_BEACON) ++ ) { ++ log(L_XFER|L_DATA, "rx: %s " ++ "time:%u len:%u signal:%u SNR:%u macstat:%02X " ++ "phystat:%02X phyrate:%u status:%u\n", ++ acx_get_packet_type_string(fc), ++ le32_to_cpu(rxbuf->time), ++ buf_len, ++ acx_signal_to_winlevel(rxbuf->phy_level), ++ acx_signal_to_winlevel(rxbuf->phy_snr), ++ rxbuf->mac_status, ++ rxbuf->phy_stat_baseband, ++ rxbuf->phy_plcp_signal, ++ adev->status); ++ } ++ ++ if (unlikely(acx_debug & L_DATA)) { ++ printk("rx: 802.11 buf[%u]: ", buf_len); ++ acx_dump_bytes(hdr, buf_len); ++ } ++ ++ /* FIXME: should check for Rx errors (rxbuf->mac_status? ++ * discard broken packets - but NOT for monitor!) ++ * and update Rx packet statistics here */ ++ ++ if (unlikely(adev->mode == ACX_MODE_MONITOR)) { ++ acx_l_rxmonitor(adev, rxbuf); ++ } else if (likely(buf_len >= WLAN_HDR_A3_LEN)) { ++ acx_l_rx_ieee802_11_frame(adev, rxbuf); ++ } else { ++ log(L_DEBUG|L_XFER|L_DATA, ++ "rx: NOT receiving packet (%s): " ++ "size too small (%u)\n", ++ acx_get_packet_type_string(fc), ++ buf_len); ++ } ++ ++ /* Now check Rx quality level, AFTER processing packet. ++ * I tried to figure out how to map these levels to dBm ++ * values, but for the life of me I really didn't ++ * manage to get it. Either these values are not meant to ++ * be expressed in dBm, or it's some pretty complicated ++ * calculation. */ ++ ++#ifdef FROM_SCAN_SOURCE_ONLY ++ /* only consider packets originating from the MAC ++ * address of the device that's managing our BSSID. ++ * Disable it for now, since it removes information (levels ++ * from different peers) and slows the Rx path. */ ++ if (adev->ap_client ++ && mac_is_equal(hdr->a2, adev->ap_client->address)) { ++#endif ++ adev->wstats.qual.level = acx_signal_to_winlevel(rxbuf->phy_level); ++ adev->wstats.qual.noise = acx_signal_to_winlevel(rxbuf->phy_snr); ++#ifndef OLD_QUALITY ++ qual = acx_signal_determine_quality(adev->wstats.qual.level, ++ adev->wstats.qual.noise); ++#else ++ qual = (adev->wstats.qual.noise <= 100) ? ++ 100 - adev->wstats.qual.noise : 0; ++#endif ++ adev->wstats.qual.qual = qual; ++ adev->wstats.qual.updated = 7; /* all 3 indicators updated */ ++#ifdef FROM_SCAN_SOURCE_ONLY ++ } ++#endif ++} ++ ++ ++/*********************************************************************** ++** acx_l_handle_txrate_auto ++** ++** Theory of operation: ++** client->rate_cap is a bitmask of rates client is capable of. ++** client->rate_cfg is a bitmask of allowed (configured) rates. ++** It is set as a result of iwconfig rate N [auto] ++** or iwpriv set_rates "N,N,N N,N,N" commands. ++** It can be fixed (e.g. 0x0080 == 18Mbit only), ++** auto (0x00ff == 18Mbit or any lower value), ++** and code handles any bitmask (0x1081 == try 54Mbit,18Mbit,1Mbit _only_). ++** ++** client->rate_cur is a value for rate111 field in tx descriptor. ++** It is always set to txrate_cfg sans zero or more most significant ++** bits. This routine handles selection of new rate_cur value depending on ++** outcome of last tx event. ++** ++** client->rate_100 is a precalculated rate value for acx100 ++** (we can do without it, but will need to calculate it on each tx). ++** ++** You cannot configure mixed usage of 5.5 and/or 11Mbit rate ++** with PBCC and CCK modulation. Either both at CCK or both at PBCC. ++** In theory you can implement it, but so far it is considered not worth doing. ++** ++** 22Mbit, of course, is PBCC always. */ ++ ++/* maps acx100 tx descr rate field to acx111 one */ ++static u16 ++rate100to111(u8 r) ++{ ++ switch (r) { ++ case RATE100_1: return RATE111_1; ++ case RATE100_2: return RATE111_2; ++ case RATE100_5: ++ case (RATE100_5 | RATE100_PBCC511): return RATE111_5; ++ case RATE100_11: ++ case (RATE100_11 | RATE100_PBCC511): return RATE111_11; ++ case RATE100_22: return RATE111_22; ++ default: ++ printk("acx: unexpected acx100 txrate: %u! " ++ "Please report\n", r); ++ return RATE111_1; ++ } ++} ++ ++ ++void ++acx_l_handle_txrate_auto(acx_device_t *adev, struct client *txc, ++ u16 cur, u8 rate100, u16 rate111, ++ u8 error, int pkts_to_ignore) ++{ ++ u16 sent_rate; ++ int slower_rate_was_used; ++ ++ /* vda: hmm. current code will do this: ++ ** 1. send packets at 11 Mbit, stepup++ ++ ** 2. will try to send at 22Mbit. hardware will see no ACK, ++ ** retries at 11Mbit, success. code notes that used rate ++ ** is lower. stepup = 0, fallback++ ++ ** 3. repeat step 2 fallback_count times. Fall back to ++ ** 11Mbit. go to step 1. ++ ** If stepup_count is large (say, 16) and fallback_count ++ ** is small (3), this wouldn't be too bad wrt throughput */ ++ ++ if (unlikely(!cur)) { ++ printk("acx: BUG! ratemask is empty\n"); ++ return; /* or else we may lock up the box */ ++ } ++ ++ /* do some preparations, i.e. calculate the one rate that was ++ * used to send this packet */ ++ if (IS_ACX111(adev)) { ++ sent_rate = 1 << highest_bit(rate111 & RATE111_ALL); ++ } else { ++ sent_rate = rate100to111(rate100); ++ } ++ /* sent_rate has only one bit set now, corresponding to tx rate ++ * which was used by hardware to tx this particular packet */ ++ ++ /* now do the actual auto rate management */ ++ log(L_XFER, "tx: %sclient=%p/"MACSTR" used=%04X cur=%04X cfg=%04X " ++ "__=%u/%u ^^=%u/%u\n", ++ (txc->ignore_count > 0) ? "[IGN] " : "", ++ txc, MAC(txc->address), sent_rate, cur, txc->rate_cfg, ++ txc->fallback_count, adev->fallback_threshold, ++ txc->stepup_count, adev->stepup_threshold ++ ); ++ ++ /* we need to ignore old packets already in the tx queue since ++ * they use older rate bytes configured before our last rate change, ++ * otherwise our mechanism will get confused by interpreting old data. ++ * Do it after logging above */ ++ if (txc->ignore_count) { ++ txc->ignore_count--; ++ return; ++ } ++ ++ /* true only if the only nonzero bit in sent_rate is ++ ** less significant than highest nonzero bit in cur */ ++ slower_rate_was_used = ( cur > ((sent_rate<<1)-1) ); ++ ++ if (slower_rate_was_used || error) { ++ txc->stepup_count = 0; ++ if (++txc->fallback_count <= adev->fallback_threshold) ++ return; ++ txc->fallback_count = 0; ++ ++ /* clear highest 1 bit in cur */ ++ sent_rate = RATE111_54; ++ while (!(cur & sent_rate)) sent_rate >>= 1; ++ CLEAR_BIT(cur, sent_rate); ++ if (!cur) /* we can't disable all rates! */ ++ cur = sent_rate; ++ log(L_XFER, "tx: falling back to ratemask %04X\n", cur); ++ ++ } else { /* there was neither lower rate nor error */ ++ txc->fallback_count = 0; ++ if (++txc->stepup_count <= adev->stepup_threshold) ++ return; ++ txc->stepup_count = 0; ++ ++ /* Sanitize. Sort of not needed, but I dont trust hw that much... ++ ** what if it can report bogus tx rates sometimes? */ ++ while (!(cur & sent_rate)) sent_rate >>= 1; ++ ++ /* try to find a higher sent_rate that isn't yet in our ++ * current set, but is an allowed cfg */ ++ while (1) { ++ sent_rate <<= 1; ++ if (sent_rate > txc->rate_cfg) ++ /* no higher rates allowed by config */ ++ return; ++ if (!(cur & sent_rate) && (txc->rate_cfg & sent_rate)) ++ /* found */ ++ break; ++ /* not found, try higher one */ ++ } ++ SET_BIT(cur, sent_rate); ++ log(L_XFER, "tx: stepping up to ratemask %04X\n", cur); ++ } ++ ++ txc->rate_cur = cur; ++ txc->ignore_count = pkts_to_ignore; ++ /* calculate acx100 style rate byte if needed */ ++ if (IS_ACX100(adev)) { ++ txc->rate_100 = acx_bitpos2rate100[highest_bit(cur)]; ++ } ++} ++ ++ ++/*********************************************************************** ++** acx_i_start_xmit ++** ++** Called by network core. Can be called outside of process context. ++*/ ++int ++acx_i_start_xmit(struct sk_buff *skb, struct net_device *ndev) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ tx_t *tx; ++ void *txbuf; ++ unsigned long flags; ++ int txresult = NOT_OK; ++ int len; ++ ++ FN_ENTER; ++ ++ if (unlikely(!skb)) { ++ /* indicate success */ ++ txresult = OK; ++ goto end_no_unlock; ++ } ++ if (unlikely(!adev)) { ++ goto end_no_unlock; ++ } ++ ++ acx_lock(adev, flags); ++ ++ if (unlikely(!(adev->dev_state_mask & ACX_STATE_IFACE_UP))) { ++ goto end; ++ } ++ if (unlikely(adev->mode == ACX_MODE_OFF)) { ++ goto end; ++ } ++ if (unlikely(acx_queue_stopped(ndev))) { ++ log(L_DEBUG, "%s: called when queue stopped\n", __func__); ++ goto end; ++ } ++ if (unlikely(ACX_STATUS_4_ASSOCIATED != adev->status)) { ++ log(L_XFER, "trying to xmit, but not associated yet: " ++ "aborting...\n"); ++ /* silently drop the packet, since we're not connected yet */ ++ txresult = OK; ++ /* ...but indicate an error nevertheless */ ++ adev->stats.tx_errors++; ++ goto end; ++ } ++ ++ tx = acx_l_alloc_tx(adev); ++ if (unlikely(!tx)) { ++#ifndef ACX_MEM ++ /* ++ * generic slave interface has to make do with the tiny amount, around ++ * 7k, of transmit buffer space on the ACX itself. It is likely this will ++ * frequently be full. ++ */ ++ printk_ratelimited("%s: start_xmit: txdesc ring is full, " ++ "dropping tx\n", ndev->name); ++#endif ++ txresult = NOT_OK; ++ goto end; ++ } ++ ++ txbuf = acx_l_get_txbuf(adev, tx); ++ if (unlikely(!txbuf)) { ++ /* Card was removed */ ++ txresult = NOT_OK; ++ acx_l_dealloc_tx(adev, tx); ++ goto end; ++ } ++ len = acx_ether_to_txbuf(adev, txbuf, skb); ++ if (unlikely(len < 0)) { ++ /* Error in packet conversion */ ++ txresult = NOT_OK; ++ acx_l_dealloc_tx(adev, tx); ++ goto end; ++ } ++ acx_l_tx_data(adev, tx, len); ++ ndev->trans_start = jiffies; ++ ++ txresult = OK; ++ adev->stats.tx_packets++; ++ adev->stats.tx_bytes += skb->len; ++ ++end: ++ acx_unlock(adev, flags); ++ ++end_no_unlock: ++ if ((txresult == OK) && skb) ++ dev_kfree_skb_any(skb); ++ ++ FN_EXIT1(txresult); ++ return txresult; ++} ++ ++ ++/*********************************************************************** ++** acx_l_update_ratevector ++** ++** Updates adev->rate_supported[_len] according to rate_{basic,oper} ++*/ ++const u8 ++acx_bitpos2ratebyte[] = { ++ DOT11RATEBYTE_1, ++ DOT11RATEBYTE_2, ++ DOT11RATEBYTE_5_5, ++ DOT11RATEBYTE_6_G, ++ DOT11RATEBYTE_9_G, ++ DOT11RATEBYTE_11, ++ DOT11RATEBYTE_12_G, ++ DOT11RATEBYTE_18_G, ++ DOT11RATEBYTE_22, ++ DOT11RATEBYTE_24_G, ++ DOT11RATEBYTE_36_G, ++ DOT11RATEBYTE_48_G, ++ DOT11RATEBYTE_54_G, ++}; ++ ++void ++acx_l_update_ratevector(acx_device_t *adev) ++{ ++ u16 bcfg = adev->rate_basic; ++ u16 ocfg = adev->rate_oper; ++ u8 *supp = adev->rate_supported; ++ const u8 *dot11 = acx_bitpos2ratebyte; ++ ++ FN_ENTER; ++ ++ while (ocfg) { ++ if (ocfg & 1) { ++ *supp = *dot11; ++ if (bcfg & 1) { ++ *supp |= 0x80; ++ } ++ supp++; ++ } ++ dot11++; ++ ocfg >>= 1; ++ bcfg >>= 1; ++ } ++ adev->rate_supported_len = supp - adev->rate_supported; ++ if (acx_debug & L_ASSOC) { ++ printk("new ratevector: "); ++ acx_dump_bytes(adev->rate_supported, adev->rate_supported_len); ++ } ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acx_l_sta_list_init ++*/ ++static void ++acx_l_sta_list_init(acx_device_t *adev) ++{ ++ FN_ENTER; ++ memset(adev->sta_hash_tab, 0, sizeof(adev->sta_hash_tab)); ++ memset(adev->sta_list, 0, sizeof(adev->sta_list)); ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acx_l_sta_list_get_from_hash ++*/ ++static inline client_t* ++acx_l_sta_list_get_from_hash(acx_device_t *adev, const u8 *address) ++{ ++ return adev->sta_hash_tab[address[5] % VEC_SIZE(adev->sta_hash_tab)]; ++} ++ ++ ++/*********************************************************************** ++** acx_l_sta_list_get ++*/ ++client_t* ++acx_l_sta_list_get(acx_device_t *adev, const u8 *address) ++{ ++ client_t *client; ++ FN_ENTER; ++ client = acx_l_sta_list_get_from_hash(adev, address); ++ while (client) { ++ if (mac_is_equal(address, client->address)) { ++ client->mtime = jiffies; ++ break; ++ } ++ client = client->next; ++ } ++ FN_EXIT0; ++ return client; ++} ++ ++ ++/*********************************************************************** ++** acx_l_sta_list_del ++*/ ++void ++acx_l_sta_list_del(acx_device_t *adev, client_t *victim) ++{ ++ client_t *client, *next; ++ ++ client = acx_l_sta_list_get_from_hash(adev, victim->address); ++ next = client; ++ /* tricky. next = client on first iteration only, ++ ** on all other iters next = client->next */ ++ while (next) { ++ if (next == victim) { ++ client->next = victim->next; ++ /* Overkill */ ++ memset(victim, 0, sizeof(*victim)); ++ break; ++ } ++ client = next; ++ next = client->next; ++ } ++} ++ ++ ++/*********************************************************************** ++** acx_l_sta_list_alloc ++** ++** Never fails - will evict oldest client if needed ++*/ ++static client_t* ++acx_l_sta_list_alloc(acx_device_t *adev) ++{ ++ int i; ++ unsigned long age, oldest_age; ++ client_t *client, *oldest; ++ ++ FN_ENTER; ++ ++ oldest = &adev->sta_list[0]; ++ oldest_age = 0; ++ for (i = 0; i < VEC_SIZE(adev->sta_list); i++) { ++ client = &adev->sta_list[i]; ++ ++ if (!client->used) { ++ goto found; ++ } else { ++ age = jiffies - client->mtime; ++ if (oldest_age < age) { ++ oldest_age = age; ++ oldest = client; ++ } ++ } ++ } ++ acx_l_sta_list_del(adev, oldest); ++ client = oldest; ++found: ++ memset(client, 0, sizeof(*client)); ++ FN_EXIT0; ++ return client; ++} ++ ++ ++/*********************************************************************** ++** acx_l_sta_list_add ++** ++** Never fails - will evict oldest client if needed ++*/ ++/* In case we will reimplement it differently... */ ++#define STA_LIST_ADD_CAN_FAIL 0 ++ ++static client_t* ++acx_l_sta_list_add(acx_device_t *adev, const u8 *address) ++{ ++ client_t *client; ++ int index; ++ ++ FN_ENTER; ++ ++ client = acx_l_sta_list_alloc(adev); ++ ++ client->mtime = jiffies; ++ MAC_COPY(client->address, address); ++ client->used = CLIENT_EXIST_1; ++ client->auth_alg = WLAN_AUTH_ALG_SHAREDKEY; ++ client->auth_step = 1; ++ /* give some tentative peer rate values ++ ** (needed because peer may do auth without probing us first, ++ ** thus we'll have no idea of peer's ratevector yet). ++ ** Will be overwritten by scanning or assoc code */ ++ client->rate_cap = adev->rate_basic; ++ client->rate_cfg = adev->rate_basic; ++ client->rate_cur = 1 << lowest_bit(adev->rate_basic); ++ ++ index = address[5] % VEC_SIZE(adev->sta_hash_tab); ++ client->next = adev->sta_hash_tab[index]; ++ adev->sta_hash_tab[index] = client; ++ ++ acxlog_mac(L_ASSOC, "sta_list_add: sta=", address, "\n"); ++ ++ FN_EXIT0; ++ return client; ++} ++ ++ ++/*********************************************************************** ++** acx_l_sta_list_get_or_add ++** ++** Never fails - will evict oldest client if needed ++*/ ++static client_t* ++acx_l_sta_list_get_or_add(acx_device_t *adev, const u8 *address) ++{ ++ client_t *client = acx_l_sta_list_get(adev, address); ++ if (!client) ++ client = acx_l_sta_list_add(adev, address); ++ return client; ++} ++ ++ ++/*********************************************************************** ++** acx_set_status ++** ++** This function is called in many atomic regions, must not sleep ++** ++** This function does not need locking UNLESS you call it ++** as acx_set_status(ACX_STATUS_4_ASSOCIATED), bacause this can ++** wake queue. This can race with stop_queue elsewhere. ++** See acx_stop_queue comment. */ ++void ++acx_set_status(acx_device_t *adev, u16 new_status) ++{ ++#define QUEUE_OPEN_AFTER_ASSOC 1 /* this really seems to be needed now */ ++ u16 old_status = adev->status; ++ ++ FN_ENTER; ++ ++ log(L_ASSOC, "%s(%d):%s\n", ++ __func__, new_status, acx_get_status_name(new_status)); ++ ++ /* wireless_send_event never sleeps */ ++ if (ACX_STATUS_4_ASSOCIATED == new_status) { ++ union iwreq_data wrqu; ++ ++ wrqu.data.length = 0; ++ wrqu.data.flags = 0; ++ wireless_send_event(adev->ndev, SIOCGIWSCAN, &wrqu, NULL); ++ ++ wrqu.data.length = 0; ++ wrqu.data.flags = 0; ++ MAC_COPY(wrqu.ap_addr.sa_data, adev->bssid); ++ wrqu.ap_addr.sa_family = ARPHRD_ETHER; ++ wireless_send_event(adev->ndev, SIOCGIWAP, &wrqu, NULL); ++ } else { ++ union iwreq_data wrqu; ++ ++ /* send event with empty BSSID to indicate we're not associated */ ++ MAC_ZERO(wrqu.ap_addr.sa_data); ++ wrqu.ap_addr.sa_family = ARPHRD_ETHER; ++ wireless_send_event(adev->ndev, SIOCGIWAP, &wrqu, NULL); ++ } ++ ++ adev->status = new_status; ++ ++ switch (new_status) { ++ case ACX_STATUS_1_SCANNING: ++ adev->scan_retries = 0; ++ /* 1.0 s initial scan time */ ++ acx_set_timer(adev, 1000000); ++ break; ++ case ACX_STATUS_2_WAIT_AUTH: ++ case ACX_STATUS_3_AUTHENTICATED: ++ adev->auth_or_assoc_retries = 0; ++ acx_set_timer(adev, 1500000); /* 1.5 s */ ++ break; ++ } ++ ++#if QUEUE_OPEN_AFTER_ASSOC ++ if (new_status == ACX_STATUS_4_ASSOCIATED) { ++ if (old_status < ACX_STATUS_4_ASSOCIATED) { ++ /* ah, we're newly associated now, ++ * so let's indicate carrier */ ++ acx_carrier_on(adev->ndev, "after association"); ++ acx_wake_queue(adev->ndev, "after association"); ++ } ++ } else { ++ /* not associated any more, so let's kill carrier */ ++ if (old_status >= ACX_STATUS_4_ASSOCIATED) { ++ acx_carrier_off(adev->ndev, "after losing association"); ++ acx_stop_queue(adev->ndev, "after losing association"); ++ } ++ } ++#endif ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acx_i_timer ++** ++** Fires up periodically. Used to kick scan/auth/assoc if something goes wrong ++*/ ++void ++acx_i_timer(unsigned long address) ++{ ++ unsigned long flags; ++ acx_device_t *adev = (acx_device_t*)address; ++ ++ FN_ENTER; ++ ++ acx_lock(adev, flags); ++ ++ log(L_DEBUG|L_ASSOC, "%s: adev->status=%d (%s)\n", ++ __func__, adev->status, acx_get_status_name(adev->status)); ++ ++ switch (adev->status) { ++ case ACX_STATUS_1_SCANNING: ++ /* was set to 0 by set_status() */ ++ if (++adev->scan_retries < 7) { ++ acx_set_timer(adev, 1000000); ++ /* used to interrogate for scan status. ++ ** We rely on SCAN_COMPLETE IRQ instead */ ++ log(L_ASSOC, "continuing scan (%d sec)\n", ++ adev->scan_retries); ++ } else { ++ log(L_ASSOC, "stopping scan\n"); ++ /* send stop_scan cmd when we leave the interrupt context, ++ * and make a decision what to do next (COMPLETE_SCAN) */ ++ acx_schedule_task(adev, ++ ACX_AFTER_IRQ_CMD_STOP_SCAN + ACX_AFTER_IRQ_COMPLETE_SCAN); ++ } ++ break; ++ case ACX_STATUS_2_WAIT_AUTH: ++ /* was set to 0 by set_status() */ ++ if (++adev->auth_or_assoc_retries < 10) { ++ log(L_ASSOC, "resend authen1 request (attempt %d)\n", ++ adev->auth_or_assoc_retries + 1); ++ acx_l_transmit_authen1(adev); ++ } else { ++ /* time exceeded: fall back to scanning mode */ ++ log(L_ASSOC, ++ "authen1 request reply timeout, giving up\n"); ++ /* we are a STA, need to find AP anyhow */ ++ acx_set_status(adev, ACX_STATUS_1_SCANNING); ++ acx_schedule_task(adev, ACX_AFTER_IRQ_RESTART_SCAN); ++ } ++ /* used to be 1500000, but some other driver uses 2.5s */ ++ acx_set_timer(adev, 2500000); ++ break; ++ case ACX_STATUS_3_AUTHENTICATED: ++ /* was set to 0 by set_status() */ ++ if (++adev->auth_or_assoc_retries < 10) { ++ log(L_ASSOC, "resend assoc request (attempt %d)\n", ++ adev->auth_or_assoc_retries + 1); ++ acx_l_transmit_assoc_req(adev); ++ } else { ++ /* time exceeded: give up */ ++ log(L_ASSOC, ++ "association request reply timeout, giving up\n"); ++ /* we are a STA, need to find AP anyhow */ ++ acx_set_status(adev, ACX_STATUS_1_SCANNING); ++ acx_schedule_task(adev, ACX_AFTER_IRQ_RESTART_SCAN); ++ } ++ acx_set_timer(adev, 2500000); /* see above */ ++ break; ++ case ACX_STATUS_4_ASSOCIATED: ++ default: ++ break; ++ } ++ ++ acx_unlock(adev, flags); ++ ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acx_set_timer ++** ++** Sets the 802.11 state management timer's timeout. ++*/ ++void ++acx_set_timer(acx_device_t *adev, int timeout_us) ++{ ++ FN_ENTER; ++ ++ log(L_DEBUG|L_IRQ, "%s(%u ms)\n", __func__, timeout_us/1000); ++ if (!(adev->dev_state_mask & ACX_STATE_IFACE_UP)) { ++ printk("attempt to set the timer " ++ "when the card interface is not up!\n"); ++ goto end; ++ } ++ ++ /* first check if the timer was already initialized, THEN modify it */ ++ if (adev->mgmt_timer.function) { ++ mod_timer(&adev->mgmt_timer, ++ jiffies + (timeout_us * HZ / 1000000)); ++ } ++end: ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acx_l_transmit_assocresp ++** ++** We are an AP here ++*/ ++static const u8 ++dot11ratebyte[] = { ++ DOT11RATEBYTE_1, ++ DOT11RATEBYTE_2, ++ DOT11RATEBYTE_5_5, ++ DOT11RATEBYTE_6_G, ++ DOT11RATEBYTE_9_G, ++ DOT11RATEBYTE_11, ++ DOT11RATEBYTE_12_G, ++ DOT11RATEBYTE_18_G, ++ DOT11RATEBYTE_22, ++ DOT11RATEBYTE_24_G, ++ DOT11RATEBYTE_36_G, ++ DOT11RATEBYTE_48_G, ++ DOT11RATEBYTE_54_G, ++}; ++ ++static inline int ++find_pos(const u8 *p, int size, u8 v) ++{ ++ int i; ++ for (i = 0; i < size; i++) ++ if (p[i] == v) ++ return i; ++ /* printk a message about strange byte? */ ++ return 0; ++} ++ ++static void ++add_bits_to_ratemasks(u8* ratevec, int len, u16* brate, u16* orate) ++{ ++ while (len--) { ++ int n = 1 << find_pos(dot11ratebyte, ++ sizeof(dot11ratebyte), *ratevec & 0x7f); ++ if (*ratevec & 0x80) ++ *brate |= n; ++ *orate |= n; ++ ratevec++; ++ } ++} ++ ++static int ++acx_l_transmit_assocresp(acx_device_t *adev, const wlan_fr_assocreq_t *req) ++{ ++ struct tx *tx; ++ struct wlan_hdr_mgmt *head; ++ struct assocresp_frame_body *body; ++ u8 *p; ++ const u8 *da; ++ /* const u8 *sa; */ ++ const u8 *bssid; ++ client_t *clt; ++ ++ FN_ENTER; ++ ++ /* sa = req->hdr->a1; */ ++ da = req->hdr->a2; ++ bssid = req->hdr->a3; ++ ++ clt = acx_l_sta_list_get(adev, da); ++ if (!clt) ++ goto ok; ++ ++ /* Assoc without auth is a big no-no */ ++ /* Let's be liberal: if already assoc'ed STA sends assoc req again, ++ ** we won't be rude */ ++ if (clt->used != CLIENT_AUTHENTICATED_2 ++ && clt->used != CLIENT_ASSOCIATED_3) { ++ acx_l_transmit_deauthen(adev, da, WLAN_MGMT_REASON_CLASS2_NONAUTH); ++ goto bad; ++ } ++ ++ clt->used = CLIENT_ASSOCIATED_3; ++ ++ if (clt->aid == 0) ++ clt->aid = ++adev->aid; ++ clt->cap_info = ieee2host16(*(req->cap_info)); ++ ++ /* We cheat here a bit. We don't really care which rates are flagged ++ ** as basic by the client, so we stuff them in single ratemask */ ++ clt->rate_cap = 0; ++ if (req->supp_rates) ++ add_bits_to_ratemasks(req->supp_rates->rates, ++ req->supp_rates->len, &clt->rate_cap, &clt->rate_cap); ++ if (req->ext_rates) ++ add_bits_to_ratemasks(req->ext_rates->rates, ++ req->ext_rates->len, &clt->rate_cap, &clt->rate_cap); ++ /* We can check that client supports all basic rates, ++ ** and deny assoc if not. But let's be liberal, right? ;) */ ++ clt->rate_cfg = clt->rate_cap & adev->rate_oper; ++ if (!clt->rate_cfg) clt->rate_cfg = 1 << lowest_bit(adev->rate_oper); ++ clt->rate_cur = 1 << lowest_bit(clt->rate_cfg); ++ if (IS_ACX100(adev)) ++ clt->rate_100 = acx_bitpos2rate100[lowest_bit(clt->rate_cfg)]; ++ clt->fallback_count = clt->stepup_count = 0; ++ clt->ignore_count = 16; ++ ++ tx = acx_l_alloc_tx(adev); ++ if (!tx) ++ goto bad; ++ head = acx_l_get_txbuf(adev, tx); ++ if (!head) { ++ acx_l_dealloc_tx(adev, tx); ++ goto bad; ++ } ++ body = (void*)(head + 1); ++ ++ head->fc = WF_FSTYPE_ASSOCRESPi; ++ head->dur = req->hdr->dur; ++ MAC_COPY(head->da, da); ++ MAC_COPY(head->sa, adev->dev_addr); ++ MAC_COPY(head->bssid, bssid); ++ head->seq = req->hdr->seq; ++ ++ body->cap_info = host2ieee16(adev->capabilities); ++ body->status = host2ieee16(0); ++ body->aid = host2ieee16(clt->aid); ++ p = wlan_fill_ie_rates((u8*)&body->rates, adev->rate_supported_len, ++ adev->rate_supported); ++ p = wlan_fill_ie_rates_ext(p, adev->rate_supported_len, ++ adev->rate_supported); ++ ++ acx_l_tx_data(adev, tx, p - (u8*)head); ++ok: ++ FN_EXIT1(OK); ++ return OK; ++bad: ++ FN_EXIT1(NOT_OK); ++ return NOT_OK; ++} ++ ++ ++/*********************************************************************** ++* acx_l_transmit_reassocresp ++ ++You may be wondering, just like me, what the hell ReAuth is. ++In practice it was seen sent by STA when STA feels like losing connection. ++ ++[802.11] ++ ++5.4.2.3 Reassociation ++ ++Association is sufficient for no-transition message delivery between ++IEEE 802.11 stations. Additional functionality is needed to support ++BSS-transition mobility. The additional required functionality ++is provided by the reassociation service. Reassociation is a DSS. ++The reassociation service is invoked to 'move' a current association ++from one AP to another. This keeps the DS informed of the current ++mapping between AP and STA as the station moves from BSS to BSS within ++an ESS. Reassociation also enables changing association attributes ++of an established association while the STA remains associated with ++the same AP. Reassociation is always initiated by the mobile STA. ++ ++5.4.3.1 Authentication ++... ++A STA may be authenticated with many other STAs at any given instant. ++ ++5.4.3.1.1 Preauthentication ++ ++Because the authentication process could be time-consuming (depending ++on the authentication protocol in use), the authentication service can ++be invoked independently of the association service. Preauthentication ++is typically done by a STA while it is already associated with an AP ++(with which it previously authenticated). IEEE 802.11 does not require ++that STAs preauthenticate with APs. However, authentication is required ++before an association can be established. If the authentication is left ++until reassociation time, this may impact the speed with which a STA can ++reassociate between APs, limiting BSS-transition mobility performance. ++The use of preauthentication takes the authentication service overhead ++out of the time-critical reassociation process. ++ ++5.7.3 Reassociation ++ ++For a STA to reassociate, the reassociation service causes the following ++message to occur: ++ ++ Reassociation request ++ ++* Message type: Management ++* Message subtype: Reassociation request ++* Information items: ++ - IEEE address of the STA ++ - IEEE address of the AP with which the STA will reassociate ++ - IEEE address of the AP with which the STA is currently associated ++ - ESSID ++* Direction of message: From STA to 'new' AP ++ ++The address of the current AP is included for efficiency. The inclusion ++of the current AP address facilitates MAC reassociation to be independent ++of the DS implementation. ++ ++ Reassociation response ++* Message type: Management ++* Message subtype: Reassociation response ++* Information items: ++ - Result of the requested reassociation. (success/failure) ++ - If the reassociation is successful, the response shall include the AID. ++* Direction of message: From AP to STA ++ ++7.2.3.6 Reassociation Request frame format ++ ++The frame body of a management frame of subtype Reassociation Request ++contains the information shown in Table 9. ++ ++Table 9 Reassociation Request frame body ++Order Information ++1 Capability information ++2 Listen interval ++3 Current AP address ++4 SSID ++5 Supported rates ++ ++7.2.3.7 Reassociation Response frame format ++ ++The frame body of a management frame of subtype Reassociation Response ++contains the information shown in Table 10. ++ ++Table 10 Reassociation Response frame body ++Order Information ++1 Capability information ++2 Status code ++3 Association ID (AID) ++4 Supported rates ++ ++*/ ++static int ++acx_l_transmit_reassocresp(acx_device_t *adev, const wlan_fr_reassocreq_t *req) ++{ ++ struct tx *tx; ++ struct wlan_hdr_mgmt *head; ++ struct reassocresp_frame_body *body; ++ u8 *p; ++ const u8 *da; ++ /* const u8 *sa; */ ++ const u8 *bssid; ++ client_t *clt; ++ ++ FN_ENTER; ++ ++ /* sa = req->hdr->a1; */ ++ da = req->hdr->a2; ++ bssid = req->hdr->a3; ++ ++ /* Must be already authenticated, so it must be in the list */ ++ clt = acx_l_sta_list_get(adev, da); ++ if (!clt) ++ goto ok; ++ ++ /* Assoc without auth is a big no-no */ ++ /* Already assoc'ed STAs sending ReAssoc req are ok per 802.11 */ ++ if (clt->used != CLIENT_AUTHENTICATED_2 ++ && clt->used != CLIENT_ASSOCIATED_3) { ++ acx_l_transmit_deauthen(adev, da, WLAN_MGMT_REASON_CLASS2_NONAUTH); ++ goto bad; ++ } ++ ++ clt->used = CLIENT_ASSOCIATED_3; ++ if (clt->aid == 0) { ++ clt->aid = ++adev->aid; ++ } ++ if (req->cap_info) ++ clt->cap_info = ieee2host16(*(req->cap_info)); ++ ++ /* We cheat here a bit. We don't really care which rates are flagged ++ ** as basic by the client, so we stuff them in single ratemask */ ++ clt->rate_cap = 0; ++ if (req->supp_rates) ++ add_bits_to_ratemasks(req->supp_rates->rates, ++ req->supp_rates->len, &clt->rate_cap, &clt->rate_cap); ++ if (req->ext_rates) ++ add_bits_to_ratemasks(req->ext_rates->rates, ++ req->ext_rates->len, &clt->rate_cap, &clt->rate_cap); ++ /* We can check that client supports all basic rates, ++ ** and deny assoc if not. But let's be liberal, right? ;) */ ++ clt->rate_cfg = clt->rate_cap & adev->rate_oper; ++ if (!clt->rate_cfg) clt->rate_cfg = 1 << lowest_bit(adev->rate_oper); ++ clt->rate_cur = 1 << lowest_bit(clt->rate_cfg); ++ if (IS_ACX100(adev)) ++ clt->rate_100 = acx_bitpos2rate100[lowest_bit(clt->rate_cfg)]; ++ ++ clt->fallback_count = clt->stepup_count = 0; ++ clt->ignore_count = 16; ++ ++ tx = acx_l_alloc_tx(adev); ++ if (!tx) ++ goto ok; ++ head = acx_l_get_txbuf(adev, tx); ++ if (!head) { ++ acx_l_dealloc_tx(adev, tx); ++ goto ok; ++ } ++ body = (void*)(head + 1); ++ ++ head->fc = WF_FSTYPE_REASSOCRESPi; ++ head->dur = req->hdr->dur; ++ MAC_COPY(head->da, da); ++ MAC_COPY(head->sa, adev->dev_addr); ++ MAC_COPY(head->bssid, bssid); ++ head->seq = req->hdr->seq; ++ ++ /* IEs: 1. caps */ ++ body->cap_info = host2ieee16(adev->capabilities); ++ /* 2. status code */ ++ body->status = host2ieee16(0); ++ /* 3. AID */ ++ body->aid = host2ieee16(clt->aid); ++ /* 4. supp rates */ ++ p = wlan_fill_ie_rates((u8*)&body->rates, adev->rate_supported_len, ++ adev->rate_supported); ++ /* 5. ext supp rates */ ++ p = wlan_fill_ie_rates_ext(p, adev->rate_supported_len, ++ adev->rate_supported); ++ ++ acx_l_tx_data(adev, tx, p - (u8*)head); ++ok: ++ FN_EXIT1(OK); ++ return OK; ++bad: ++ FN_EXIT1(NOT_OK); ++ return NOT_OK; ++} ++ ++ ++/*********************************************************************** ++** acx_l_process_disassoc_from_sta ++*/ ++static void ++acx_l_process_disassoc_from_sta(acx_device_t *adev, const wlan_fr_disassoc_t *req) ++{ ++ const u8 *ta; ++ client_t *clt; ++ ++ FN_ENTER; ++ ++ ta = req->hdr->a2; ++ clt = acx_l_sta_list_get(adev, ta); ++ if (!clt) ++ goto end; ++ ++ if (clt->used != CLIENT_ASSOCIATED_3 ++ && clt->used != CLIENT_AUTHENTICATED_2) { ++ /* it's disassociating, but it's ++ ** not even authenticated! Let it know that */ ++ acxlog_mac(L_ASSOC|L_XFER, "peer ", ta, "has sent disassoc " ++ "req but it is not even auth'ed! sending deauth\n"); ++ acx_l_transmit_deauthen(adev, ta, ++ WLAN_MGMT_REASON_CLASS2_NONAUTH); ++ clt->used = CLIENT_EXIST_1; ++ } else { ++ /* mark it as auth'ed only */ ++ clt->used = CLIENT_AUTHENTICATED_2; ++ } ++end: ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acx_l_process_deauthen_from_sta ++*/ ++static void ++acx_l_process_deauth_from_sta(acx_device_t *adev, const wlan_fr_deauthen_t *req) ++{ ++ const wlan_hdr_t *hdr; ++ client_t *client; ++ ++ FN_ENTER; ++ ++ hdr = req->hdr; ++ ++ if (acx_debug & L_ASSOC) { ++ acx_print_mac("got deauth from sta:", hdr->a2, " "); ++ acx_print_mac("a1:", hdr->a1, " "); ++ acx_print_mac("a3:", hdr->a3, " "); ++ acx_print_mac("adev->addr:", adev->dev_addr, " "); ++ acx_print_mac("adev->bssid:", adev->bssid, "\n"); ++ } ++ ++ if (!mac_is_equal(adev->dev_addr, hdr->a1)) { ++ goto end; ++ } ++ ++ client = acx_l_sta_list_get(adev, hdr->a2); ++ if (!client) { ++ goto end; ++ } ++ client->used = CLIENT_EXIST_1; ++end: ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acx_l_process_disassoc_from_ap ++*/ ++static void ++acx_l_process_disassoc_from_ap(acx_device_t *adev, const wlan_fr_disassoc_t *req) ++{ ++ FN_ENTER; ++ ++ if (!adev->ap_client) { ++ /* Hrm, we aren't assoc'ed yet anyhow... */ ++ goto end; ++ } ++ ++ printk("%s: got disassoc frame with reason %d (%s)\n", ++ adev->ndev->name, *req->reason, ++ acx_wlan_reason_str(*req->reason)); ++ ++ if (mac_is_equal(adev->dev_addr, req->hdr->a1)) { ++ acx_l_transmit_deauthen(adev, adev->bssid, ++ WLAN_MGMT_REASON_DEAUTH_LEAVING); ++ SET_BIT(adev->set_mask, GETSET_RESCAN); ++ acx_schedule_task(adev, ACX_AFTER_IRQ_UPDATE_CARD_CFG); ++ } ++end: ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acx_l_process_deauth_from_ap ++*/ ++static void ++acx_l_process_deauth_from_ap(acx_device_t *adev, const wlan_fr_deauthen_t *req) ++{ ++ FN_ENTER; ++ ++ if (!adev->ap_client) { ++ /* Hrm, we aren't assoc'ed yet anyhow... */ ++ goto end; ++ } ++ ++ printk("%s: got deauth frame with reason %d (%s)\n", ++ adev->ndev->name, *req->reason, ++ acx_wlan_reason_str(*req->reason)); ++ ++ /* Chk: is ta verified to be from our AP? */ ++ if (mac_is_equal(adev->dev_addr, req->hdr->a1)) { ++ log(L_DEBUG, "AP sent us deauth packet\n"); ++ SET_BIT(adev->set_mask, GETSET_RESCAN); ++ acx_schedule_task(adev, ACX_AFTER_IRQ_UPDATE_CARD_CFG); ++ } ++end: ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acx_l_rx ++** ++** The end of the Rx path. Pulls data from a rxhostdesc into a socket ++** buffer and feeds it to the network stack via netif_rx(). ++*/ ++static void ++acx_l_rx(acx_device_t *adev, rxbuffer_t *rxbuf) ++{ ++ FN_ENTER; ++ if (likely(adev->dev_state_mask & ACX_STATE_IFACE_UP)) { ++ struct sk_buff *skb; ++ skb = acx_rxbuf_to_ether(adev, rxbuf); ++ if (likely(skb)) { ++ netif_rx(skb); ++ adev->ndev->last_rx = jiffies; ++ adev->stats.rx_packets++; ++ adev->stats.rx_bytes += skb->len; ++ } ++ } ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acx_l_process_data_frame_master ++*/ ++static int ++acx_l_process_data_frame_master(acx_device_t *adev, rxbuffer_t *rxbuf) ++{ ++ struct wlan_hdr *hdr; ++ struct tx *tx; ++ void *txbuf; ++ int len; ++ int result = NOT_OK; ++ ++ FN_ENTER; ++ ++ hdr = acx_get_wlan_hdr(adev, rxbuf); ++ ++ switch (WF_FC_FROMTODSi & hdr->fc) { ++ case 0: ++ case WF_FC_FROMDSi: ++ log(L_DEBUG, "ap->sta or adhoc->adhoc data frame ignored\n"); ++ goto done; ++ case WF_FC_TODSi: ++ break; ++ default: /* WF_FC_FROMTODSi */ ++ log(L_DEBUG, "wds data frame ignored (TODO)\n"); ++ goto done; ++ } ++ ++ /* check if it is our BSSID, if not, leave */ ++ if (!mac_is_equal(adev->bssid, hdr->a1)) { ++ goto done; ++ } ++ ++ if (mac_is_equal(adev->dev_addr, hdr->a3)) { ++ /* this one is for us */ ++ acx_l_rx(adev, rxbuf); ++ } else { ++ if (mac_is_bcast(hdr->a3)) { ++ /* this one is bcast, rx it too */ ++ acx_l_rx(adev, rxbuf); ++ } ++ tx = acx_l_alloc_tx(adev); ++ if (!tx) { ++ goto fail; ++ } ++ /* repackage, tx, and hope it someday reaches its destination */ ++ /* order is important, we do it in-place */ ++ MAC_COPY(hdr->a1, hdr->a3); ++ MAC_COPY(hdr->a3, hdr->a2); ++ MAC_COPY(hdr->a2, adev->bssid); ++ /* To_DS = 0, From_DS = 1 */ ++ hdr->fc = WF_FC_FROMDSi + WF_FTYPE_DATAi; ++ ++ txbuf = acx_l_get_txbuf(adev, tx); ++ if (txbuf) { ++ len = RXBUF_BYTES_RCVD(adev, rxbuf); ++ memcpy(txbuf, hdr, len); ++ acx_l_tx_data(adev, tx, len); ++ } else { ++ acx_l_dealloc_tx(adev, tx); ++ } ++ } ++done: ++ result = OK; ++fail: ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++** acx_l_process_data_frame_client ++*/ ++static int ++acx_l_process_data_frame_client(acx_device_t *adev, rxbuffer_t *rxbuf) ++{ ++ const u8 *da, *bssid; ++ const wlan_hdr_t *hdr; ++ struct net_device *ndev = adev->ndev; ++ int result = NOT_OK; ++ ++ FN_ENTER; ++ ++ if (ACX_STATUS_4_ASSOCIATED != adev->status) ++ goto drop; ++ ++ hdr = acx_get_wlan_hdr(adev, rxbuf); ++ ++ switch (WF_FC_FROMTODSi & hdr->fc) { ++ case 0: ++ if (adev->mode != ACX_MODE_0_ADHOC) { ++ log(L_DEBUG, "adhoc->adhoc data frame ignored\n"); ++ goto drop; ++ } ++ bssid = hdr->a3; ++ break; ++ case WF_FC_FROMDSi: ++ if (adev->mode != ACX_MODE_2_STA) { ++ log(L_DEBUG, "ap->sta data frame ignored\n"); ++ goto drop; ++ } ++ bssid = hdr->a2; ++ break; ++ case WF_FC_TODSi: ++ log(L_DEBUG, "sta->ap data frame ignored\n"); ++ goto drop; ++ default: /* WF_FC_FROMTODSi: wds->wds */ ++ log(L_DEBUG, "wds data frame ignored (todo)\n"); ++ goto drop; ++ } ++ ++ da = hdr->a1; ++ ++ if (unlikely(acx_debug & L_DEBUG)) { ++ acx_print_mac("rx: da=", da, ""); ++ acx_print_mac(" bssid=", bssid, ""); ++ acx_print_mac(" adev->bssid=", adev->bssid, ""); ++ acx_print_mac(" adev->addr=", adev->dev_addr, "\n"); ++ } ++ ++ /* promiscuous mode --> receive all packets */ ++ if (unlikely(ndev->flags & IFF_PROMISC)) ++ goto process; ++ ++ /* FIRST, check if it is our BSSID */ ++ if (!mac_is_equal(adev->bssid, bssid)) { ++ /* is not our BSSID, so bail out */ ++ goto drop; ++ } ++ ++ /* then, check if it is our address */ ++ if (mac_is_equal(adev->dev_addr, da)) { ++ goto process; ++ } ++ ++ /* then, check if it is broadcast */ ++ if (mac_is_bcast(da)) { ++ goto process; ++ } ++ ++ if (mac_is_mcast(da)) { ++ /* unconditionally receive all multicasts */ ++ if (ndev->flags & IFF_ALLMULTI) ++ goto process; ++ ++ /* FIXME: need to check against the list of ++ * multicast addresses that are configured ++ * for the interface (ifconfig) */ ++ log(L_XFER, "FIXME: multicast packet, need to check " ++ "against a list of multicast addresses " ++ "(to be created!); accepting packet for now\n"); ++ /* for now, just accept it here */ ++ goto process; ++ } ++ ++ log(L_DEBUG, "rx: foreign packet, dropping\n"); ++ goto drop; ++process: ++ /* receive packet */ ++ acx_l_rx(adev, rxbuf); ++ ++ result = OK; ++drop: ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++** acx_l_process_mgmt_frame ++** ++** Theory of operation: mgmt packet gets parsed (to make it easy ++** to access variable-sized IEs), results stored in 'parsed'. ++** Then we react to the packet. ++*/ ++typedef union parsed_mgmt_req { ++ wlan_fr_mgmt_t mgmt; ++ wlan_fr_assocreq_t assocreq; ++ wlan_fr_reassocreq_t reassocreq; ++ wlan_fr_assocresp_t assocresp; ++ wlan_fr_reassocresp_t reassocresp; ++ wlan_fr_beacon_t beacon; ++ wlan_fr_disassoc_t disassoc; ++ wlan_fr_authen_t authen; ++ wlan_fr_deauthen_t deauthen; ++ wlan_fr_proberesp_t proberesp; ++} parsed_mgmt_req_t; ++ ++void BUG_excessive_stack_usage(void); ++ ++static int ++acx_l_process_mgmt_frame(acx_device_t *adev, rxbuffer_t *rxbuf) ++{ ++ parsed_mgmt_req_t parsed; /* takes ~100 bytes of stack */ ++ wlan_hdr_t *hdr; ++ int adhoc, sta_scan, sta, ap; ++ int len; ++ ++ if (sizeof(parsed) > 256) ++ BUG_excessive_stack_usage(); ++ ++ FN_ENTER; ++ ++ hdr = acx_get_wlan_hdr(adev, rxbuf); ++ ++ /* Management frames never have these set */ ++ if (WF_FC_FROMTODSi & hdr->fc) { ++ FN_EXIT1(NOT_OK); ++ return NOT_OK; ++ } ++ ++ len = RXBUF_BYTES_RCVD(adev, rxbuf); ++ if (WF_FC_ISWEPi & hdr->fc) ++ len -= 0x10; ++ ++ adhoc = (adev->mode == ACX_MODE_0_ADHOC); ++ sta_scan = ((adev->mode == ACX_MODE_2_STA) ++ && (adev->status != ACX_STATUS_4_ASSOCIATED)); ++ sta = ((adev->mode == ACX_MODE_2_STA) ++ && (adev->status == ACX_STATUS_4_ASSOCIATED)); ++ ap = (adev->mode == ACX_MODE_3_AP); ++ ++ switch (WF_FC_FSTYPEi & hdr->fc) { ++ /* beacons first, for speed */ ++ case WF_FSTYPE_BEACONi: ++ memset(&parsed.beacon, 0, sizeof(parsed.beacon)); ++ parsed.beacon.hdr = hdr; ++ parsed.beacon.len = len; ++ if (acx_debug & L_DATA) { ++ printk("beacon len:%d fc:%04X dur:%04X seq:%04X", ++ len, hdr->fc, hdr->dur, hdr->seq); ++ acx_print_mac(" a1:", hdr->a1, ""); ++ acx_print_mac(" a2:", hdr->a2, ""); ++ acx_print_mac(" a3:", hdr->a3, "\n"); ++ } ++ wlan_mgmt_decode_beacon(&parsed.beacon); ++ /* beacon and probe response are very similar, so... */ ++ acx_l_process_probe_response(adev, &parsed.beacon, rxbuf); ++ break; ++ case WF_FSTYPE_ASSOCREQi: ++ if (!ap) ++ break; ++ memset(&parsed.assocreq, 0, sizeof(parsed.assocreq)); ++ parsed.assocreq.hdr = hdr; ++ parsed.assocreq.len = len; ++ wlan_mgmt_decode_assocreq(&parsed.assocreq); ++ if (mac_is_equal(hdr->a1, adev->bssid) ++ && mac_is_equal(hdr->a3, adev->bssid)) { ++ acx_l_transmit_assocresp(adev, &parsed.assocreq); ++ } ++ break; ++ case WF_FSTYPE_REASSOCREQi: ++ if (!ap) ++ break; ++ memset(&parsed.assocreq, 0, sizeof(parsed.assocreq)); ++ parsed.assocreq.hdr = hdr; ++ parsed.assocreq.len = len; ++ wlan_mgmt_decode_assocreq(&parsed.assocreq); ++ /* reassocreq and assocreq are equivalent */ ++ acx_l_transmit_reassocresp(adev, &parsed.reassocreq); ++ break; ++ case WF_FSTYPE_ASSOCRESPi: ++ if (!sta_scan) ++ break; ++ memset(&parsed.assocresp, 0, sizeof(parsed.assocresp)); ++ parsed.assocresp.hdr = hdr; ++ parsed.assocresp.len = len; ++ wlan_mgmt_decode_assocresp(&parsed.assocresp); ++ acx_l_process_assocresp(adev, &parsed.assocresp); ++ break; ++ case WF_FSTYPE_REASSOCRESPi: ++ if (!sta_scan) ++ break; ++ memset(&parsed.assocresp, 0, sizeof(parsed.assocresp)); ++ parsed.assocresp.hdr = hdr; ++ parsed.assocresp.len = len; ++ wlan_mgmt_decode_assocresp(&parsed.assocresp); ++ acx_l_process_reassocresp(adev, &parsed.reassocresp); ++ break; ++ case WF_FSTYPE_PROBEREQi: ++ if (ap || adhoc) { ++ /* FIXME: since we're supposed to be an AP, ++ ** we need to return a Probe Response packet. ++ ** Currently firmware is doing it for us, ++ ** but firmware is buggy! See comment elsewhere --vda */ ++ } ++ break; ++ case WF_FSTYPE_PROBERESPi: ++ memset(&parsed.proberesp, 0, sizeof(parsed.proberesp)); ++ parsed.proberesp.hdr = hdr; ++ parsed.proberesp.len = len; ++ wlan_mgmt_decode_proberesp(&parsed.proberesp); ++ acx_l_process_probe_response(adev, &parsed.proberesp, rxbuf); ++ break; ++ case 6: ++ case 7: ++ /* exit */ ++ break; ++ case WF_FSTYPE_ATIMi: ++ /* exit */ ++ break; ++ case WF_FSTYPE_DISASSOCi: ++ if (!sta && !ap) ++ break; ++ memset(&parsed.disassoc, 0, sizeof(parsed.disassoc)); ++ parsed.disassoc.hdr = hdr; ++ parsed.disassoc.len = len; ++ wlan_mgmt_decode_disassoc(&parsed.disassoc); ++ if (sta) ++ acx_l_process_disassoc_from_ap(adev, &parsed.disassoc); ++ else ++ acx_l_process_disassoc_from_sta(adev, &parsed.disassoc); ++ break; ++ case WF_FSTYPE_AUTHENi: ++ if (!sta_scan && !ap) ++ break; ++ memset(&parsed.authen, 0, sizeof(parsed.authen)); ++ parsed.authen.hdr = hdr; ++ parsed.authen.len = len; ++ wlan_mgmt_decode_authen(&parsed.authen); ++ acx_l_process_authen(adev, &parsed.authen); ++ break; ++ case WF_FSTYPE_DEAUTHENi: ++ if (!sta && !ap) ++ break; ++ memset(&parsed.deauthen, 0, sizeof(parsed.deauthen)); ++ parsed.deauthen.hdr = hdr; ++ parsed.deauthen.len = len; ++ wlan_mgmt_decode_deauthen(&parsed.deauthen); ++ if (sta) ++ acx_l_process_deauth_from_ap(adev, &parsed.deauthen); ++ else ++ acx_l_process_deauth_from_sta(adev, &parsed.deauthen); ++ break; ++ } ++ ++ FN_EXIT1(OK); ++ return OK; ++} ++ ++ ++#ifdef UNUSED ++/*********************************************************************** ++** acx_process_class_frame ++** ++** Called from IRQ context only ++*/ ++static int ++acx_process_class_frame(acx_device_t *adev, rxbuffer_t *rxbuf, int vala) ++{ ++ return OK; ++} ++#endif ++ ++ ++/*********************************************************************** ++** acx_l_process_NULL_frame ++*/ ++#ifdef BOGUS_ITS_NOT_A_NULL_FRAME_HANDLER_AT_ALL ++static int ++acx_l_process_NULL_frame(acx_device_t *adev, rxbuffer_t *rxbuf, int vala) ++{ ++ const signed char *esi; ++ const u8 *ebx; ++ const wlan_hdr_t *hdr; ++ const client_t *client; ++ int result = NOT_OK; ++ ++ hdr = acx_get_wlan_hdr(adev, rxbuf); ++ ++ switch (WF_FC_FROMTODSi & hdr->fc) { ++ case 0: ++ esi = hdr->a1; ++ ebx = hdr->a2; ++ break; ++ case WF_FC_FROMDSi: ++ esi = hdr->a1; ++ ebx = hdr->a3; ++ break; ++ case WF_FC_TODSi: ++ esi = hdr->a1; ++ ebx = hdr->a2; ++ break; ++ default: /* WF_FC_FROMTODSi */ ++ esi = hdr->a1; /* added by me! --vda */ ++ ebx = hdr->a2; ++ } ++ ++ if (esi[0x0] < 0) { ++ result = OK; ++ goto done; ++ } ++ ++ client = acx_l_sta_list_get(adev, ebx); ++ if (client) ++ result = NOT_OK; ++ else { ++#ifdef IS_IT_BROKEN ++ log(L_DEBUG|L_XFER, "<transmit_deauth 7>\n"); ++ acx_l_transmit_deauthen(adev, ebx, ++ WLAN_MGMT_REASON_CLASS2_NONAUTH); ++#else ++ log(L_DEBUG, "received NULL frame from unknown client! " ++ "We really shouldn't send deauthen here, right?\n"); ++#endif ++ result = OK; ++ } ++done: ++ return result; ++} ++#endif ++ ++ ++/*********************************************************************** ++** acx_l_process_probe_response ++*/ ++static int ++acx_l_process_probe_response(acx_device_t *adev, wlan_fr_proberesp_t *req, ++ const rxbuffer_t *rxbuf) ++{ ++ struct client *bss; ++ wlan_hdr_t *hdr; ++ ++ FN_ENTER; ++ ++ hdr = req->hdr; ++ ++ if (mac_is_equal(hdr->a3, adev->dev_addr)) { ++ log(L_ASSOC, "huh, scan found our own MAC!?\n"); ++ goto ok; /* just skip this one silently */ ++ } ++ ++ bss = acx_l_sta_list_get_or_add(adev, hdr->a2); ++ ++ /* NB: be careful modifying bss data! It may be one ++ ** of the already known clients (like our AP if we are a STA) ++ ** Thus do not blindly modify e.g. current ratemask! */ ++ ++ if (STA_LIST_ADD_CAN_FAIL && !bss) { ++ /* uh oh, we found more sites/stations than we can handle with ++ * our current setup: pull the emergency brake and stop scanning! */ ++ acx_schedule_task(adev, ACX_AFTER_IRQ_CMD_STOP_SCAN); ++ /* TODO: a nice comment what below call achieves --vda */ ++ acx_set_status(adev, ACX_STATUS_2_WAIT_AUTH); ++ goto ok; ++ } ++ /* NB: get_or_add already filled bss->address = hdr->a2 */ ++ MAC_COPY(bss->bssid, hdr->a3); ++ ++ /* copy the ESSID element */ ++ if (req->ssid && req->ssid->len <= IW_ESSID_MAX_SIZE) { ++ bss->essid_len = req->ssid->len; ++ memcpy(bss->essid, req->ssid->ssid, req->ssid->len); ++ bss->essid[req->ssid->len] = '\0'; ++ } else { ++ /* Either no ESSID IE or oversized one */ ++ printk("%s: received packet has bogus ESSID\n", ++ adev->ndev->name); ++ } ++ ++ if (req->ds_parms) ++ bss->channel = req->ds_parms->curr_ch; ++ if (req->cap_info) ++ bss->cap_info = ieee2host16(*req->cap_info); ++ ++ bss->sir = acx_signal_to_winlevel(rxbuf->phy_level); ++ bss->snr = acx_signal_to_winlevel(rxbuf->phy_snr); ++ ++ bss->rate_cap = 0; /* operational mask */ ++ bss->rate_bas = 0; /* basic mask */ ++ if (req->supp_rates) ++ add_bits_to_ratemasks(req->supp_rates->rates, ++ req->supp_rates->len, &bss->rate_bas, &bss->rate_cap); ++ if (req->ext_rates) ++ add_bits_to_ratemasks(req->ext_rates->rates, ++ req->ext_rates->len, &bss->rate_bas, &bss->rate_cap); ++ /* Fix up any possible bogosity - code elsewhere ++ * is not expecting empty masks */ ++ if (!bss->rate_cap) ++ bss->rate_cap = adev->rate_basic; ++ if (!bss->rate_bas) ++ bss->rate_bas = 1 << lowest_bit(bss->rate_cap); ++ if (!bss->rate_cur) ++ bss->rate_cur = 1 << lowest_bit(bss->rate_bas); ++ ++ /* People moan about this being too noisy at L_ASSOC */ ++ log(L_DEBUG, ++ "found %s: ESSID=\"%s\" ch=%d " ++ "BSSID="MACSTR" caps=0x%04X SIR=%d SNR=%d\n", ++ (bss->cap_info & WF_MGMT_CAP_IBSS) ? "Ad-Hoc peer" : "AP", ++ bss->essid, bss->channel, MAC(bss->bssid), bss->cap_info, ++ bss->sir, bss->snr); ++ok: ++ FN_EXIT0; ++ return OK; ++} ++ ++ ++/*********************************************************************** ++** acx_l_process_assocresp ++*/ ++static int ++acx_l_process_assocresp(acx_device_t *adev, const wlan_fr_assocresp_t *req) ++{ ++ const wlan_hdr_t *hdr; ++ int res = OK; ++ ++ FN_ENTER; ++ ++ hdr = req->hdr; ++ ++ if ((ACX_MODE_2_STA == adev->mode) ++ && mac_is_equal(adev->dev_addr, hdr->a1)) { ++ u16 st = ieee2host16(*(req->status)); ++ if (WLAN_MGMT_STATUS_SUCCESS == st) { ++ adev->aid = ieee2host16(*(req->aid)); ++ /* tell the card we are associated when ++ ** we are out of interrupt context */ ++ acx_schedule_task(adev, ACX_AFTER_IRQ_CMD_ASSOCIATE); ++ } else { ++ ++ /* TODO: we shall delete peer from sta_list, and try ++ ** other candidates... */ ++ ++ printk("%s: association FAILED: peer sent " ++ "Status Code %d (%s)\n", ++ adev->ndev->name, st, get_status_string(st)); ++ res = NOT_OK; ++ } ++ } ++ ++ FN_EXIT1(res); ++ return res; ++} ++ ++ ++/*********************************************************************** ++** acx_l_process_reassocresp ++*/ ++static int ++acx_l_process_reassocresp(acx_device_t *adev, const wlan_fr_reassocresp_t *req) ++{ ++ const wlan_hdr_t *hdr; ++ int result = NOT_OK; ++ u16 st; ++ ++ FN_ENTER; ++ ++ hdr = req->hdr; ++ ++ if (!mac_is_equal(adev->dev_addr, hdr->a1)) { ++ goto end; ++ } ++ st = ieee2host16(*(req->status)); ++ if (st == WLAN_MGMT_STATUS_SUCCESS) { ++ acx_set_status(adev, ACX_STATUS_4_ASSOCIATED); ++ result = OK; ++ } else { ++ printk("%s: reassociation FAILED: peer sent " ++ "response code %d (%s)\n", ++ adev->ndev->name, st, get_status_string(st)); ++ } ++end: ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++** acx_l_process_authen ++** ++** Called only in STA_SCAN or AP mode ++*/ ++static int ++acx_l_process_authen(acx_device_t *adev, const wlan_fr_authen_t *req) ++{ ++ const wlan_hdr_t *hdr; ++ client_t *clt; ++ wlan_ie_challenge_t *chal; ++ u16 alg, seq, status; ++ int ap, result; ++ ++ FN_ENTER; ++ ++ hdr = req->hdr; ++ ++ if (acx_debug & L_ASSOC) { ++ acx_print_mac("AUTHEN adev->addr=", adev->dev_addr, " "); ++ acx_print_mac("a1=", hdr->a1, " "); ++ acx_print_mac("a2=", hdr->a2, " "); ++ acx_print_mac("a3=", hdr->a3, " "); ++ acx_print_mac("adev->bssid=", adev->bssid, "\n"); ++ } ++ ++ if (!mac_is_equal(adev->dev_addr, hdr->a1) ++ || !mac_is_equal(adev->bssid, hdr->a3)) { ++ result = OK; ++ goto end; ++ } ++ ++ alg = ieee2host16(*(req->auth_alg)); ++ seq = ieee2host16(*(req->auth_seq)); ++ status = ieee2host16(*(req->status)); ++ ++ log(L_ASSOC, "auth algorithm %d, auth sequence %d, status %d\n", alg, seq, status); ++ ++ ap = (adev->mode == ACX_MODE_3_AP); ++ ++ if (adev->auth_alg <= 1) { ++ if (adev->auth_alg != alg) { ++ log(L_ASSOC, "auth algorithm mismatch: " ++ "our:%d peer:%d\n", adev->auth_alg, alg); ++ result = NOT_OK; ++ goto end; ++ } ++ } ++ if (ap) { ++ clt = acx_l_sta_list_get_or_add(adev, hdr->a2); ++ if (STA_LIST_ADD_CAN_FAIL && !clt) { ++ log(L_ASSOC, "could not allocate room for client\n"); ++ result = NOT_OK; ++ goto end; ++ } ++ } else { ++ clt = adev->ap_client; ++ if (!mac_is_equal(clt->address, hdr->a2)) { ++ printk("%s: malformed auth frame from AP?!\n", ++ adev->ndev->name); ++ result = NOT_OK; ++ goto end; ++ } ++ } ++ ++ /* now check which step in the authentication sequence we are ++ * currently in, and act accordingly */ ++ switch (seq) { ++ case 1: ++ if (!ap) ++ break; ++ acx_l_transmit_authen2(adev, req, clt); ++ break; ++ case 2: ++ if (ap) ++ break; ++ if (status == WLAN_MGMT_STATUS_SUCCESS) { ++ if (alg == WLAN_AUTH_ALG_OPENSYSTEM) { ++ acx_set_status(adev, ACX_STATUS_3_AUTHENTICATED); ++ acx_l_transmit_assoc_req(adev); ++ } else ++ if (alg == WLAN_AUTH_ALG_SHAREDKEY) { ++ acx_l_transmit_authen3(adev, req); ++ } ++ } else { ++ printk("%s: auth FAILED: peer sent " ++ "response code %d (%s), " ++ "still waiting for authentication\n", ++ adev->ndev->name, ++ status, get_status_string(status)); ++ acx_set_status(adev, ACX_STATUS_2_WAIT_AUTH); ++ } ++ break; ++ case 3: ++ if (!ap) ++ break; ++ if ((clt->auth_alg != WLAN_AUTH_ALG_SHAREDKEY) ++ || (alg != WLAN_AUTH_ALG_SHAREDKEY) ++ || (clt->auth_step != 2)) ++ break; ++ chal = req->challenge; ++ if (!chal ++ || memcmp(chal->challenge, clt->challenge_text, WLAN_CHALLENGE_LEN) ++ || (chal->eid != WLAN_EID_CHALLENGE) ++ || (chal->len != WLAN_CHALLENGE_LEN) ++ ) ++ break; ++ acx_l_transmit_authen4(adev, req); ++ MAC_COPY(clt->address, hdr->a2); ++ clt->used = CLIENT_AUTHENTICATED_2; ++ clt->auth_step = 4; ++ clt->seq = ieee2host16(hdr->seq); ++ break; ++ case 4: ++ if (ap) ++ break; ++ /* ok, we're through: we're authenticated. Woohoo!! */ ++ acx_set_status(adev, ACX_STATUS_3_AUTHENTICATED); ++ log(L_ASSOC, "Authenticated!\n"); ++ /* now that we're authenticated, request association */ ++ acx_l_transmit_assoc_req(adev); ++ break; ++ } ++ result = OK; ++end: ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++** acx_gen_challenge ++*/ ++static inline void ++acx_gen_challenge(wlan_ie_challenge_t* d) ++{ ++ FN_ENTER; ++ d->eid = WLAN_EID_CHALLENGE; ++ d->len = WLAN_CHALLENGE_LEN; ++ get_random_bytes(d->challenge, WLAN_CHALLENGE_LEN); ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acx_l_transmit_deauthen ++*/ ++static int ++acx_l_transmit_deauthen(acx_device_t *adev, const u8 *addr, u16 reason) ++{ ++ struct tx *tx; ++ struct wlan_hdr_mgmt *head; ++ struct deauthen_frame_body *body; ++ ++ FN_ENTER; ++ ++ tx = acx_l_alloc_tx(adev); ++ if (!tx) ++ goto bad; ++ head = acx_l_get_txbuf(adev, tx); ++ if (!head) { ++ acx_l_dealloc_tx(adev, tx); ++ goto bad; ++ } ++ body = (void*)(head + 1); ++ ++ head->fc = (WF_FTYPE_MGMTi | WF_FSTYPE_DEAUTHENi); ++ head->dur = 0; ++ MAC_COPY(head->da, addr); ++ MAC_COPY(head->sa, adev->dev_addr); ++ MAC_COPY(head->bssid, adev->bssid); ++ head->seq = 0; ++ ++ log(L_DEBUG|L_ASSOC|L_XFER, ++ "sending deauthen to "MACSTR" for %d\n", ++ MAC(addr), reason); ++ ++ body->reason = host2ieee16(reason); ++ ++ /* body is fixed size here, but beware of cutting-and-pasting this - ++ ** do not use sizeof(*body) for variable sized mgmt packets! */ ++ acx_l_tx_data(adev, tx, WLAN_HDR_A3_LEN + sizeof(*body)); ++ ++ FN_EXIT1(OK); ++ return OK; ++bad: ++ FN_EXIT1(NOT_OK); ++ return NOT_OK; ++} ++ ++ ++/*********************************************************************** ++** acx_l_transmit_authen1 ++*/ ++static int ++acx_l_transmit_authen1(acx_device_t *adev) ++{ ++ struct tx *tx; ++ struct wlan_hdr_mgmt *head; ++ struct auth_frame_body *body; ++ ++ FN_ENTER; ++ ++ log(L_ASSOC, "sending authentication1 request (auth algo %d), " ++ "awaiting response\n", adev->auth_alg); ++ ++ tx = acx_l_alloc_tx(adev); ++ if (!tx) ++ goto bad; ++ head = acx_l_get_txbuf(adev, tx); ++ if (!head) { ++ acx_l_dealloc_tx(adev, tx); ++ goto bad; ++ } ++ body = (void*)(head + 1); ++ ++ head->fc = WF_FSTYPE_AUTHENi; ++ /* duration should be 0 instead of 0x8000 to have ++ * the firmware calculate the value, right? */ ++ head->dur = 0; ++ MAC_COPY(head->da, adev->bssid); ++ MAC_COPY(head->sa, adev->dev_addr); ++ MAC_COPY(head->bssid, adev->bssid); ++ head->seq = 0; ++ ++ body->auth_alg = host2ieee16(adev->auth_alg); ++ body->auth_seq = host2ieee16(1); ++ body->status = host2ieee16(0); ++ ++ acx_l_tx_data(adev, tx, WLAN_HDR_A3_LEN + 2 + 2 + 2); ++ ++ FN_EXIT1(OK); ++ return OK; ++bad: ++ FN_EXIT1(NOT_OK); ++ return NOT_OK; ++} ++ ++ ++/*********************************************************************** ++** acx_l_transmit_authen2 ++*/ ++static int ++acx_l_transmit_authen2(acx_device_t *adev, const wlan_fr_authen_t *req, ++ client_t *clt) ++{ ++ struct tx *tx; ++ struct wlan_hdr_mgmt *head; ++ struct auth_frame_body *body; ++ unsigned int packet_len; ++ ++ FN_ENTER; ++ ++ if (!clt) ++ goto ok; ++ ++ MAC_COPY(clt->address, req->hdr->a2); ++#ifdef UNUSED ++ clt->ps = ((WF_FC_PWRMGTi & req->hdr->fc) != 0); ++#endif ++ clt->auth_alg = ieee2host16(*(req->auth_alg)); ++ clt->auth_step = 2; ++ clt->seq = ieee2host16(req->hdr->seq); ++ ++ tx = acx_l_alloc_tx(adev); ++ if (!tx) ++ goto bad; ++ head = acx_l_get_txbuf(adev, tx); ++ if (!head) { ++ acx_l_dealloc_tx(adev, tx); ++ goto bad; ++ } ++ body = (void*)(head + 1); ++ ++ head->fc = WF_FSTYPE_AUTHENi; ++ head->dur = 0 /* req->hdr->dur */; ++ MAC_COPY(head->da, req->hdr->a2); ++ MAC_COPY(head->sa, adev->dev_addr); ++ MAC_COPY(head->bssid, req->hdr->a3); ++ head->seq = 0 /* req->hdr->seq */; ++ ++ /* already in IEEE format, no endianness conversion */ ++ body->auth_alg = *(req->auth_alg); ++ body->auth_seq = host2ieee16(2); ++ body->status = host2ieee16(0); ++ ++ packet_len = WLAN_HDR_A3_LEN + 2 + 2 + 2; ++ if (ieee2host16(*(req->auth_alg)) == WLAN_AUTH_ALG_OPENSYSTEM) { ++ clt->used = CLIENT_AUTHENTICATED_2; ++ } else { /* shared key */ ++ acx_gen_challenge(&body->challenge); ++ memcpy(&clt->challenge_text, body->challenge.challenge, WLAN_CHALLENGE_LEN); ++ packet_len += 2 + 2 + 2 + 1+1+WLAN_CHALLENGE_LEN; ++ } ++ ++ acxlog_mac(L_ASSOC|L_XFER, ++ "transmit_auth2: BSSID=", head->bssid, "\n"); ++ ++ acx_l_tx_data(adev, tx, packet_len); ++ok: ++ FN_EXIT1(OK); ++ return OK; ++bad: ++ FN_EXIT1(NOT_OK); ++ return NOT_OK; ++} ++ ++ ++/*********************************************************************** ++** acx_l_transmit_authen3 ++*/ ++static int ++acx_l_transmit_authen3(acx_device_t *adev, const wlan_fr_authen_t *req) ++{ ++ struct tx *tx; ++ struct wlan_hdr_mgmt *head; ++ struct auth_frame_body *body; ++ unsigned int packet_len; ++ ++ FN_ENTER; ++ ++ tx = acx_l_alloc_tx(adev); ++ if (!tx) ++ goto ok; ++ head = acx_l_get_txbuf(adev, tx); ++ if (!head) { ++ acx_l_dealloc_tx(adev, tx); ++ goto ok; ++ } ++ body = (void*)(head + 1); ++ ++ /* add WF_FC_ISWEPi: auth step 3 needs to be encrypted */ ++ head->fc = WF_FC_ISWEPi + WF_FSTYPE_AUTHENi; ++ /* FIXME: is this needed?? authen4 does it... ++ * I think it's even wrong since we shouldn't re-use old ++ * values but instead let the firmware calculate proper ones ++ head->dur = req->hdr->dur; ++ head->seq = req->hdr->seq; ++ */ ++ MAC_COPY(head->da, adev->bssid); ++ MAC_COPY(head->sa, adev->dev_addr); ++ MAC_COPY(head->bssid, adev->bssid); ++ ++ /* already in IEEE format, no endianness conversion */ ++ body->auth_alg = *(req->auth_alg); ++ body->auth_seq = host2ieee16(3); ++ body->status = host2ieee16(0); ++ memcpy(&body->challenge, req->challenge, req->challenge->len + 2); ++ packet_len = WLAN_HDR_A3_LEN + 8 + req->challenge->len; ++ ++ log(L_ASSOC|L_XFER, "transmit_authen3!\n"); ++ ++ acx_l_tx_data(adev, tx, packet_len); ++ok: ++ FN_EXIT1(OK); ++ return OK; ++} ++ ++ ++/*********************************************************************** ++** acx_l_transmit_authen4 ++*/ ++static int ++acx_l_transmit_authen4(acx_device_t *adev, const wlan_fr_authen_t *req) ++{ ++ struct tx *tx; ++ struct wlan_hdr_mgmt *head; ++ struct auth_frame_body *body; ++ ++ FN_ENTER; ++ ++ tx = acx_l_alloc_tx(adev); ++ if (!tx) ++ goto ok; ++ head = acx_l_get_txbuf(adev, tx); ++ if (!head) { ++ acx_l_dealloc_tx(adev, tx); ++ goto ok; ++ } ++ body = (void*)(head + 1); ++ ++ head->fc = WF_FSTYPE_AUTHENi; /* 0xb0 */ ++ head->dur = 0 /* req->hdr->dur */; ++ MAC_COPY(head->da, req->hdr->a2); ++ MAC_COPY(head->sa, adev->dev_addr); ++ MAC_COPY(head->bssid, req->hdr->a3); ++ head->seq = 0 /* req->hdr->seq */; ++ ++ /* already in IEEE format, no endianness conversion */ ++ body->auth_alg = *(req->auth_alg); ++ body->auth_seq = host2ieee16(4); ++ body->status = host2ieee16(0); ++ ++ acx_l_tx_data(adev, tx, WLAN_HDR_A3_LEN + 2 + 2 + 2); ++ok: ++ FN_EXIT1(OK); ++ return OK; ++} ++ ++ ++/*********************************************************************** ++** acx_l_transmit_assoc_req ++** ++** adev->ap_client is a current candidate AP here ++*/ ++static int ++acx_l_transmit_assoc_req(acx_device_t *adev) ++{ ++ struct tx *tx; ++ struct wlan_hdr_mgmt *head; ++ u8 *body, *p, *prate; ++ unsigned int packet_len; ++ u16 cap; ++ ++ FN_ENTER; ++ ++ log(L_ASSOC, "sending association request, " ++ "awaiting response. NOT ASSOCIATED YET\n"); ++ tx = acx_l_alloc_tx(adev); ++ if (!tx) ++ goto bad; ++ head = acx_l_get_txbuf(adev, tx); ++ if (!head) { ++ acx_l_dealloc_tx(adev, tx); ++ goto bad; ++ } ++ body = (void*)(head + 1); ++ ++ head->fc = WF_FSTYPE_ASSOCREQi; ++ head->dur = host2ieee16(0x8000); ++ MAC_COPY(head->da, adev->bssid); ++ MAC_COPY(head->sa, adev->dev_addr); ++ MAC_COPY(head->bssid, adev->bssid); ++ head->seq = 0; ++ ++ p = body; ++ /* now start filling the AssocReq frame body */ ++ ++ /* since this assoc request will most likely only get ++ * sent in the STA to AP case (and not when Ad-Hoc IBSS), ++ * the cap combination indicated here will thus be ++ * WF_MGMT_CAP_ESSi *always* (no IBSS ever) ++ * The specs are more than non-obvious on all that: ++ * ++ * 802.11 7.3.1.4 Capability Information field ++ ** APs set the ESS subfield to 1 and the IBSS subfield to 0 within ++ ** Beacon or Probe Response management frames. STAs within an IBSS ++ ** set the ESS subfield to 0 and the IBSS subfield to 1 in transmitted ++ ** Beacon or Probe Response management frames ++ ** ++ ** APs set the Privacy subfield to 1 within transmitted Beacon, ++ ** Probe Response, Association Response, and Reassociation Response ++ ** if WEP is required for all data type frames within the BSS. ++ ** STAs within an IBSS set the Privacy subfield to 1 in Beacon ++ ** or Probe Response management frames if WEP is required ++ ** for all data type frames within the IBSS */ ++ ++ /* note that returning 0 will be refused by several APs... ++ * (so this indicates that you're probably supposed to ++ * "confirm" the ESS mode) */ ++ cap = WF_MGMT_CAP_ESSi; ++ ++ /* this one used to be a check on wep_restricted, ++ * but more likely it's wep_enabled instead */ ++ if (adev->wep_enabled) ++ SET_BIT(cap, WF_MGMT_CAP_PRIVACYi); ++ ++ /* Probably we can just set these always, because our hw is ++ ** capable of shortpre and PBCC --vda */ ++ /* only ask for short preamble if the peer station supports it */ ++ if (adev->ap_client->cap_info & WF_MGMT_CAP_SHORT) ++ SET_BIT(cap, WF_MGMT_CAP_SHORTi); ++ /* only ask for PBCC support if the peer station supports it */ ++ if (adev->ap_client->cap_info & WF_MGMT_CAP_PBCC) ++ SET_BIT(cap, WF_MGMT_CAP_PBCCi); ++ ++ /* IEs: 1. caps */ ++ *(u16*)p = cap; p += 2; ++ /* 2. listen interval */ ++ *(u16*)p = host2ieee16(adev->listen_interval); p += 2; ++ /* 3. ESSID */ ++ p = wlan_fill_ie_ssid(p, ++ strlen(adev->essid_for_assoc), adev->essid_for_assoc); ++ /* 4. supp rates */ ++ prate = p; ++ p = wlan_fill_ie_rates(p, ++ adev->rate_supported_len, adev->rate_supported); ++ /* 5. ext supp rates */ ++ p = wlan_fill_ie_rates_ext(p, ++ adev->rate_supported_len, adev->rate_supported); ++ ++ if (acx_debug & L_DEBUG) { ++ printk("association: rates element\n"); ++ acx_dump_bytes(prate, p - prate); ++ } ++ ++ /* calculate lengths */ ++ packet_len = WLAN_HDR_A3_LEN + (p - body); ++ ++ log(L_ASSOC, "association: requesting caps 0x%04X, ESSID \"%s\"\n", ++ cap, adev->essid_for_assoc); ++ ++ acx_l_tx_data(adev, tx, packet_len); ++ FN_EXIT1(OK); ++ return OK; ++bad: ++ FN_EXIT1(NOT_OK); ++ return NOT_OK; ++} ++ ++ ++/*********************************************************************** ++** acx_l_transmit_disassoc ++** ++** FIXME: looks like incomplete implementation of a helper: ++** acx_l_transmit_disassoc(adev, clt) - kick this client (we're an AP) ++** acx_l_transmit_disassoc(adev, NULL) - leave BSSID (we're a STA) ++*/ ++#ifdef BROKEN ++int ++acx_l_transmit_disassoc(acx_device_t *adev, client_t *clt) ++{ ++ struct tx *tx; ++ struct wlan_hdr_mgmt *head; ++ struct disassoc_frame_body *body; ++ ++ FN_ENTER; ++/* if (clt != NULL) { */ ++ tx = acx_l_alloc_tx(adev); ++ if (!tx) ++ goto bad; ++ head = acx_l_get_txbuf(adev, tx); ++ if (!head) { ++ acx_l_dealloc_tx(adev, tx); ++ goto bad; ++ } ++ body = (void*)(head + 1); ++ ++/* clt->used = CLIENT_AUTHENTICATED_2; - not (yet?) associated */ ++ ++ head->fc = WF_FSTYPE_DISASSOCi; ++ head->dur = 0; ++ /* huh? It muchly depends on whether we're STA or AP... ++ ** sta->ap: da=bssid, sa=own, bssid=bssid ++ ** ap->sta: da=sta, sa=bssid, bssid=bssid. FIXME! */ ++ MAC_COPY(head->da, adev->bssid); ++ MAC_COPY(head->sa, adev->dev_addr); ++ MAC_COPY(head->bssid, adev->dev_addr); ++ head->seq = 0; ++ ++ /* "Class 3 frame received from nonassociated station." */ ++ body->reason = host2ieee16(7); ++ ++ /* fixed size struct, ok to sizeof */ ++ acx_l_tx_data(adev, tx, WLAN_HDR_A3_LEN + sizeof(*body)); ++/* } */ ++ FN_EXIT1(OK); ++ return OK; ++bad: ++ FN_EXIT1(NOT_OK); ++ return NOT_OK; ++} ++#endif ++ ++ ++/*********************************************************************** ++** acx_s_complete_scan ++** ++** Called either from after_interrupt_task() if: ++** 1) there was Scan_Complete IRQ, or ++** 2) scanning expired in timer() ++** We need to decide which ESS or IBSS to join. ++** Iterates thru adev->sta_list: ++** if adev->ap is not bcast, will join only specified ++** ESS or IBSS with this bssid ++** checks peers' caps for ESS/IBSS bit ++** checks peers' SSID, allows exact match or hidden SSID ++** If station to join is chosen: ++** points adev->ap_client to the chosen struct client ++** sets adev->essid_for_assoc for future assoc attempt ++** Auth/assoc is not yet performed ++** Returns OK if there is no need to restart scan ++*/ ++int ++acx_s_complete_scan(acx_device_t *adev) ++{ ++ struct client *bss; ++ unsigned long flags; ++ u16 needed_cap; ++ int i; ++ int idx_found = -1; ++ int result = OK; ++ ++ FN_ENTER; ++ ++ switch (adev->mode) { ++ case ACX_MODE_0_ADHOC: ++ needed_cap = WF_MGMT_CAP_IBSS; /* 2, we require Ad-Hoc */ ++ break; ++ case ACX_MODE_2_STA: ++ needed_cap = WF_MGMT_CAP_ESS; /* 1, we require Managed */ ++ break; ++ default: ++ printk("acx: driver bug: mode=%d in complete_scan()\n", adev->mode); ++ dump_stack(); ++ goto end; ++ } ++ ++ acx_lock(adev, flags); ++ ++ /* TODO: sta_iterator hiding implementation would be nice here... */ ++ ++ for (i = 0; i < VEC_SIZE(adev->sta_list); i++) { ++ bss = &adev->sta_list[i]; ++ if (!bss->used) continue; ++ ++ ++ log(L_ASSOC, "scan table: SSID=\"%s\" CH=%d SIR=%d SNR=%d\n", ++ bss->essid, bss->channel, bss->sir, bss->snr); ++ ++ if (!mac_is_bcast(adev->ap)) ++ if (!mac_is_equal(bss->bssid, adev->ap)) ++ continue; /* keep looking */ ++ ++ /* broken peer with no mode flags set? */ ++ if (unlikely(!(bss->cap_info & (WF_MGMT_CAP_ESS | WF_MGMT_CAP_IBSS)))) { ++ printk("%s: strange peer "MACSTR" found with " ++ "neither ESS (AP) nor IBSS (Ad-Hoc) " ++ "capability - skipped\n", ++ adev->ndev->name, MAC(bss->address)); ++ continue; ++ } ++ log(L_ASSOC, "peer_cap 0x%04X, needed_cap 0x%04X\n", ++ bss->cap_info, needed_cap); ++ ++ /* does peer station support what we need? */ ++ if ((bss->cap_info & needed_cap) != needed_cap) ++ continue; /* keep looking */ ++ ++ /* strange peer with NO basic rates?! */ ++ if (unlikely(!bss->rate_bas)) { ++ printk("%s: strange peer "MACSTR" with empty rate set " ++ "- skipped\n", ++ adev->ndev->name, MAC(bss->address)); ++ continue; ++ } ++ ++ /* do we support all basic rates of this peer? */ ++ if ((bss->rate_bas & adev->rate_oper) != bss->rate_bas) { ++/* we probably need to have all rates as operational rates, ++ even in case of an 11M-only configuration */ ++#ifdef THIS_IS_TROUBLESOME ++ printk("%s: peer "MACSTR": incompatible basic rates " ++ "(AP requests 0x%04X, we have 0x%04X) " ++ "- skipped\n", ++ adev->ndev->name, MAC(bss->address), ++ bss->rate_bas, adev->rate_oper); ++ continue; ++#else ++ printk("%s: peer "MACSTR": incompatible basic rates " ++ "(AP requests 0x%04X, we have 0x%04X). " ++ "Considering anyway...\n", ++ adev->ndev->name, MAC(bss->address), ++ bss->rate_bas, adev->rate_oper); ++#endif ++ } ++ ++ if ( !(adev->reg_dom_chanmask & (1<<(bss->channel-1))) ) { ++ printk("%s: warning: peer "MACSTR" is on channel %d " ++ "outside of channel range of current " ++ "regulatory domain - couldn't join " ++ "even if other settings match. " ++ "You might want to adapt your config\n", ++ adev->ndev->name, MAC(bss->address), ++ bss->channel); ++ continue; /* keep looking */ ++ } ++ ++ if (!adev->essid_active || !strcmp(bss->essid, adev->essid)) { ++ log(L_ASSOC, ++ "found station with matching ESSID! ('%s' " ++ "station, '%s' config)\n", ++ bss->essid, ++ (adev->essid_active) ? adev->essid : "[any]"); ++ /* TODO: continue looking for peer with better SNR */ ++ bss->used = CLIENT_JOIN_CANDIDATE; ++ idx_found = i; ++ ++ /* stop searching if this station is ++ * on the current channel, otherwise ++ * keep looking for an even better match */ ++ if (bss->channel == adev->channel) ++ break; ++ } else ++ if (is_hidden_essid(bss->essid)) { ++ /* hmm, station with empty or single-space SSID: ++ * using hidden SSID broadcast? ++ */ ++ /* This behaviour is broken: which AP from zillion ++ ** of APs with hidden SSID you'd try? ++ ** We should use Probe requests to get Probe responses ++ ** and check for real SSID (are those never hidden?) */ ++ bss->used = CLIENT_JOIN_CANDIDATE; ++ if (idx_found == -1) ++ idx_found = i; ++ log(L_ASSOC, "found station with empty or " ++ "single-space (hidden) SSID, considering " ++ "for assoc attempt\n"); ++ /* ...and keep looking for better matches */ ++ } else { ++ log(L_ASSOC, "ESSID doesn't match! ('%s' " ++ "station, '%s' config)\n", ++ bss->essid, ++ (adev->essid_active) ? adev->essid : "[any]"); ++ } ++ } ++ ++ /* TODO: iterate thru join candidates instead */ ++ /* TODO: rescan if not associated within some timeout */ ++ if (idx_found != -1) { ++ char *essid_src; ++ size_t essid_len; ++ ++ bss = &adev->sta_list[idx_found]; ++ adev->ap_client = bss; ++ ++ if (is_hidden_essid(bss->essid)) { ++ /* if the ESSID of the station we found is empty ++ * (no broadcast), then use user-configured ESSID ++ * instead */ ++ essid_src = adev->essid; ++ essid_len = adev->essid_len; ++ } else { ++ essid_src = bss->essid; ++ essid_len = strlen(bss->essid); ++ } ++ ++ acx_update_capabilities(adev); ++ ++ memcpy(adev->essid_for_assoc, essid_src, essid_len); ++ adev->essid_for_assoc[essid_len] = '\0'; ++ adev->channel = bss->channel; ++ MAC_COPY(adev->bssid, bss->bssid); ++ ++ bss->rate_cfg = (bss->rate_cap & adev->rate_oper); ++ bss->rate_cur = 1 << lowest_bit(bss->rate_cfg); ++ bss->rate_100 = acx_rate111to100(bss->rate_cur); ++ ++ acxlog_mac(L_ASSOC, ++ "matching station found: ", adev->bssid, ", joining\n"); ++ ++ /* TODO: do we need to switch to the peer's channel first? */ ++ ++ if (ACX_MODE_0_ADHOC == adev->mode) { ++ acx_set_status(adev, ACX_STATUS_4_ASSOCIATED); ++ } else { ++ acx_l_transmit_authen1(adev); ++ acx_set_status(adev, ACX_STATUS_2_WAIT_AUTH); ++ } ++ } else { /* idx_found == -1 */ ++ /* uh oh, no station found in range */ ++ if (ACX_MODE_0_ADHOC == adev->mode) { ++ printk("%s: no matching station found in range, " ++ "generating our own IBSS instead\n", ++ adev->ndev->name); ++ /* we do it the HostAP way: */ ++ MAC_COPY(adev->bssid, adev->dev_addr); ++ adev->bssid[0] |= 0x02; /* 'local assigned addr' bit */ ++ /* add IBSS bit to our caps... */ ++ acx_update_capabilities(adev); ++ acx_set_status(adev, ACX_STATUS_4_ASSOCIATED); ++ /* In order to cmd_join be called below */ ++ idx_found = 0; ++ } else { ++ /* we shall scan again, AP can be ++ ** just temporarily powered off */ ++ log(L_ASSOC, ++ "no matching station found in range yet\n"); ++ acx_set_status(adev, ACX_STATUS_1_SCANNING); ++ result = NOT_OK; ++ } ++ } ++ ++ acx_unlock(adev, flags); ++ ++ if (idx_found != -1) { ++ if (ACX_MODE_0_ADHOC == adev->mode) { ++ /* need to update channel in beacon template */ ++ SET_BIT(adev->set_mask, SET_TEMPLATES); ++ if (ACX_STATE_IFACE_UP & adev->dev_state_mask) ++ acx_s_update_card_settings(adev); ++ } ++ /* Inform firmware on our decision to start or join BSS */ ++ acx_s_cmd_join_bssid(adev, adev->bssid); ++ } ++ ++end: ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++** acx_s_read_fw ++** ++** Loads a firmware image ++** ++** Returns: ++** 0 unable to load file ++** pointer to firmware success ++*/ ++firmware_image_t* ++acx_s_read_fw(struct device *dev, const char *file, u32 *size) ++{ ++ firmware_image_t *res; ++ const struct firmware *fw_entry; ++ ++ res = NULL; ++ log(L_INIT, "requesting firmware image '%s'\n", file); ++ if (!request_firmware(&fw_entry, file, dev)) { ++ *size = 8; ++ if (fw_entry->size >= 8) ++ *size = 8 + le32_to_cpu(*(u32 *)(fw_entry->data + 4)); ++ if (fw_entry->size != *size) { ++ printk("acx: firmware size does not match " ++ "firmware header: %d != %d, " ++ "aborting fw upload\n", ++ (int) fw_entry->size, (int) *size); ++ goto release_ret; ++ } ++ res = vmalloc(*size); ++ if (!res) { ++ printk("acx: no memory for firmware " ++ "(%u bytes)\n", *size); ++ goto release_ret; ++ } ++ memcpy(res, fw_entry->data, fw_entry->size); ++release_ret: ++ release_firmware(fw_entry); ++ return res; ++ } ++ printk("acx: firmware image '%s' was not provided. " ++ "Check your hotplug scripts\n", file); ++ ++ /* checksum will be verified in write_fw, so don't bother here */ ++ return res; ++} ++ ++ ++/*********************************************************************** ++** acx_s_set_wepkey ++*/ ++static void ++acx100_s_set_wepkey(acx_device_t *adev) ++{ ++ ie_dot11WEPDefaultKey_t dk; ++ int i; ++ ++ for (i = 0; i < DOT11_MAX_DEFAULT_WEP_KEYS; i++) { ++ if (adev->wep_keys[i].size != 0) { ++ log(L_INIT, "setting WEP key: %d with " ++ "total size: %d\n", i, (int) adev->wep_keys[i].size); ++ dk.action = 1; ++ dk.keySize = adev->wep_keys[i].size; ++ dk.defaultKeyNum = i; ++ memcpy(dk.key, adev->wep_keys[i].key, dk.keySize); ++ acx_s_configure(adev, &dk, ACX100_IE_DOT11_WEP_DEFAULT_KEY_WRITE); ++ } ++ } ++} ++ ++static void ++acx111_s_set_wepkey(acx_device_t *adev) ++{ ++ acx111WEPDefaultKey_t dk; ++ int i; ++ ++ for (i = 0; i < DOT11_MAX_DEFAULT_WEP_KEYS; i++) { ++ if (adev->wep_keys[i].size != 0) { ++ log(L_INIT, "setting WEP key: %d with " ++ "total size: %d\n", i, (int) adev->wep_keys[i].size); ++ memset(&dk, 0, sizeof(dk)); ++ dk.action = cpu_to_le16(1); /* "add key"; yes, that's a 16bit value */ ++ dk.keySize = adev->wep_keys[i].size; ++ ++ /* are these two lines necessary? */ ++ dk.type = 0; /* default WEP key */ ++ dk.index = 0; /* ignored when setting default key */ ++ ++ dk.defaultKeyNum = i; ++ memcpy(dk.key, adev->wep_keys[i].key, dk.keySize); ++ acx_s_issue_cmd(adev, ACX1xx_CMD_WEP_MGMT, &dk, sizeof(dk)); ++ } ++ } ++} ++ ++static void ++acx_s_set_wepkey(acx_device_t *adev) ++{ ++ if (IS_ACX111(adev)) ++ acx111_s_set_wepkey(adev); ++ else ++ acx100_s_set_wepkey(adev); ++} ++ ++ ++/*********************************************************************** ++** acx100_s_init_wep ++** ++** FIXME: this should probably be moved into the new card settings ++** management, but since we're also modifying the memory map layout here ++** due to the WEP key space we want, we should take care... ++*/ ++static int ++acx100_s_init_wep(acx_device_t *adev) ++{ ++ acx100_ie_wep_options_t options; ++ ie_dot11WEPDefaultKeyID_t dk; ++ acx_ie_memmap_t pt; ++ int res = NOT_OK; ++ ++ FN_ENTER; ++ ++ if (OK != acx_s_interrogate(adev, &pt, ACX1xx_IE_MEMORY_MAP)) { ++ goto fail; ++ } ++ ++ log(L_DEBUG, "CodeEnd:%X\n", pt.CodeEnd); ++ ++ pt.WEPCacheStart = cpu_to_le32(le32_to_cpu(pt.CodeEnd) + 0x4); ++ pt.WEPCacheEnd = cpu_to_le32(le32_to_cpu(pt.CodeEnd) + 0x4); ++ ++ if (OK != acx_s_configure(adev, &pt, ACX1xx_IE_MEMORY_MAP)) { ++ goto fail; ++ } ++ ++ /* let's choose maximum setting: 4 default keys, plus 10 other keys: */ ++ options.NumKeys = cpu_to_le16(DOT11_MAX_DEFAULT_WEP_KEYS + 10); ++ options.WEPOption = 0x00; ++ ++ log(L_ASSOC, "%s: writing WEP options\n", __func__); ++ acx_s_configure(adev, &options, ACX100_IE_WEP_OPTIONS); ++ ++ acx100_s_set_wepkey(adev); ++ ++ if (adev->wep_keys[adev->wep_current_index].size != 0) { ++ log(L_ASSOC, "setting active default WEP key number: %d\n", ++ adev->wep_current_index); ++ dk.KeyID = adev->wep_current_index; ++ acx_s_configure(adev, &dk, ACX1xx_IE_DOT11_WEP_DEFAULT_KEY_SET); /* 0x1010 */ ++ } ++ /* FIXME!!! wep_key_struct is filled nowhere! But adev ++ * is initialized to 0, and we don't REALLY need those keys either */ ++/* for (i = 0; i < 10; i++) { ++ if (adev->wep_key_struct[i].len != 0) { ++ MAC_COPY(wep_mgmt.MacAddr, adev->wep_key_struct[i].addr); ++ wep_mgmt.KeySize = cpu_to_le16(adev->wep_key_struct[i].len); ++ memcpy(&wep_mgmt.Key, adev->wep_key_struct[i].key, le16_to_cpu(wep_mgmt.KeySize)); ++ wep_mgmt.Action = cpu_to_le16(1); ++ log(L_ASSOC, "writing WEP key %d (len %d)\n", i, le16_to_cpu(wep_mgmt.KeySize)); ++ if (OK == acx_s_issue_cmd(adev, ACX1xx_CMD_WEP_MGMT, &wep_mgmt, sizeof(wep_mgmt))) { ++ adev->wep_key_struct[i].index = i; ++ } ++ } ++ } ++*/ ++ ++ /* now retrieve the updated WEPCacheEnd pointer... */ ++ if (OK != acx_s_interrogate(adev, &pt, ACX1xx_IE_MEMORY_MAP)) { ++ printk("%s: ACX1xx_IE_MEMORY_MAP read #2 FAILED\n", ++ adev->ndev->name); ++ goto fail; ++ } ++ /* ...and tell it to start allocating templates at that location */ ++ /* (no endianness conversion needed) */ ++ pt.PacketTemplateStart = pt.WEPCacheEnd; ++ ++ if (OK != acx_s_configure(adev, &pt, ACX1xx_IE_MEMORY_MAP)) { ++ printk("%s: ACX1xx_IE_MEMORY_MAP write #2 FAILED\n", ++ adev->ndev->name); ++ goto fail; ++ } ++ res = OK; ++ ++fail: ++ FN_EXIT1(res); ++ return res; ++} ++ ++ ++static int ++acx_s_init_max_template_generic(acx_device_t *adev, unsigned int len, unsigned int cmd) ++{ ++ int res; ++ union { ++ acx_template_nullframe_t null; ++ acx_template_beacon_t b; ++ acx_template_tim_t tim; ++ acx_template_probereq_t preq; ++ acx_template_proberesp_t presp; ++ } templ; ++ ++ memset(&templ, 0, len); ++ templ.null.size = cpu_to_le16(len - 2); ++ res = acx_s_issue_cmd(adev, cmd, &templ, len); ++ return res; ++} ++ ++static inline int ++acx_s_init_max_null_data_template(acx_device_t *adev) ++{ ++ return acx_s_init_max_template_generic( ++ adev, sizeof(acx_template_nullframe_t), ACX1xx_CMD_CONFIG_NULL_DATA ++ ); ++} ++ ++static inline int ++acx_s_init_max_beacon_template(acx_device_t *adev) ++{ ++ return acx_s_init_max_template_generic( ++ adev, sizeof(acx_template_beacon_t), ACX1xx_CMD_CONFIG_BEACON ++ ); ++} ++ ++static inline int ++acx_s_init_max_tim_template(acx_device_t *adev) ++{ ++ return acx_s_init_max_template_generic( ++ adev, sizeof(acx_template_tim_t), ACX1xx_CMD_CONFIG_TIM ++ ); ++} ++ ++static inline int ++acx_s_init_max_probe_response_template(acx_device_t *adev) ++{ ++ return acx_s_init_max_template_generic( ++ adev, sizeof(acx_template_proberesp_t), ACX1xx_CMD_CONFIG_PROBE_RESPONSE ++ ); ++} ++ ++static inline int ++acx_s_init_max_probe_request_template(acx_device_t *adev) ++{ ++ return acx_s_init_max_template_generic( ++ adev, sizeof(acx_template_probereq_t), ACX1xx_CMD_CONFIG_PROBE_REQUEST ++ ); ++} ++ ++/*********************************************************************** ++** acx_s_set_tim_template ++** ++** FIXME: In full blown driver we will regularly update partial virtual bitmap ++** by calling this function ++** (it can be done by irq handler on each DTIM irq or by timer...) ++ ++[802.11 7.3.2.6] TIM information element: ++- 1 EID ++- 1 Length ++1 1 DTIM Count ++ indicates how many beacons (including this) appear before next DTIM ++ (0=this one is a DTIM) ++2 1 DTIM Period ++ number of beacons between successive DTIMs ++ (0=reserved, 1=all TIMs are DTIMs, 2=every other, etc) ++3 1 Bitmap Control ++ bit0: Traffic Indicator bit associated with Assoc ID 0 (Bcast AID?) ++ set to 1 in TIM elements with a value of 0 in the DTIM Count field ++ when one or more broadcast or multicast frames are buffered at the AP. ++ bit1-7: Bitmap Offset (logically Bitmap_Offset = Bitmap_Control & 0xFE). ++4 n Partial Virtual Bitmap ++ Visible part of traffic-indication bitmap. ++ Full bitmap consists of 2008 bits (251 octets) such that bit number N ++ (0<=N<=2007) in the bitmap corresponds to bit number (N mod 8) ++ in octet number N/8 where the low-order bit of each octet is bit0, ++ and the high order bit is bit7. ++ Each set bit in virtual bitmap corresponds to traffic buffered by AP ++ for a specific station (with corresponding AID?). ++ Partial Virtual Bitmap shows a part of bitmap which has non-zero. ++ Bitmap Offset is a number of skipped zero octets (see above). ++ 'Missing' octets at the tail are also assumed to be zero. ++ Example: Length=6, Bitmap_Offset=2, Partial_Virtual_Bitmap=55 55 55 ++ This means that traffic-indication bitmap is: ++ 00000000 00000000 01010101 01010101 01010101 00000000 00000000... ++ (is bit0 in the map is always 0 and real value is in Bitmap Control bit0?) ++*/ ++static int ++acx_s_set_tim_template(acx_device_t *adev) ++{ ++/* For now, configure smallish test bitmap, all zero ("no pending data") */ ++ enum { bitmap_size = 5 }; ++ ++ acx_template_tim_t t; ++ int result; ++ ++ FN_ENTER; ++ ++ memset(&t, 0, sizeof(t)); ++ t.size = 5 + bitmap_size; /* eid+len+count+period+bmap_ctrl + bmap */ ++ t.tim_eid = WLAN_EID_TIM; ++ t.len = 3 + bitmap_size; /* count+period+bmap_ctrl + bmap */ ++ result = acx_s_issue_cmd(adev, ACX1xx_CMD_CONFIG_TIM, &t, sizeof(t)); ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++** acx_fill_beacon_or_proberesp_template ++** ++** For frame format info, please see 802.11-1999.pdf item 7.2.3.9 and below!! ++** ++** NB: we use the fact that ++** struct acx_template_proberesp and struct acx_template_beacon are the same ++** (well, almost...) ++** ++** [802.11] Beacon's body consist of these IEs: ++** 1 Timestamp ++** 2 Beacon interval ++** 3 Capability information ++** 4 SSID ++** 5 Supported rates (up to 8 rates) ++** 6 FH Parameter Set (frequency-hopping PHYs only) ++** 7 DS Parameter Set (direct sequence PHYs only) ++** 8 CF Parameter Set (only if PCF is supported) ++** 9 IBSS Parameter Set (ad-hoc only) ++** ++** Beacon only: ++** 10 TIM (AP only) (see 802.11 7.3.2.6) ++** 11 Country Information (802.11d) ++** 12 FH Parameters (802.11d) ++** 13 FH Pattern Table (802.11d) ++** ... (?!! did not yet find relevant PDF file... --vda) ++** 19 ERP Information (extended rate PHYs) ++** 20 Extended Supported Rates (if more than 8 rates) ++** ++** Proberesp only: ++** 10 Country information (802.11d) ++** 11 FH Parameters (802.11d) ++** 12 FH Pattern Table (802.11d) ++** 13-n Requested information elements (802.11d) ++** ???? ++** 18 ERP Information (extended rate PHYs) ++** 19 Extended Supported Rates (if more than 8 rates) ++*/ ++static int ++acx_fill_beacon_or_proberesp_template(acx_device_t *adev, ++ struct acx_template_beacon *templ, ++ u16 fc /* in host order! */) ++{ ++ int len; ++ u8 *p; ++ ++ FN_ENTER; ++ ++ memset(templ, 0, sizeof(*templ)); ++ MAC_BCAST(templ->da); ++ MAC_COPY(templ->sa, adev->dev_addr); ++ MAC_COPY(templ->bssid, adev->bssid); ++ ++ templ->beacon_interval = cpu_to_le16(adev->beacon_interval); ++ acx_update_capabilities(adev); ++ templ->cap = cpu_to_le16(adev->capabilities); ++ ++ p = templ->variable; ++ p = wlan_fill_ie_ssid(p, adev->essid_len, adev->essid); ++ p = wlan_fill_ie_rates(p, adev->rate_supported_len, adev->rate_supported); ++ p = wlan_fill_ie_ds_parms(p, adev->channel); ++ /* NB: should go AFTER tim, but acx seem to keep tim last always */ ++ p = wlan_fill_ie_rates_ext(p, adev->rate_supported_len, adev->rate_supported); ++ ++ switch (adev->mode) { ++ case ACX_MODE_0_ADHOC: ++ /* ATIM window */ ++ p = wlan_fill_ie_ibss_parms(p, 0); break; ++ case ACX_MODE_3_AP: ++ /* TIM IE is set up as separate template */ ++ break; ++ } ++ ++ len = p - (u8*)templ; ++ templ->fc = cpu_to_le16(WF_FTYPE_MGMT | fc); ++ /* - 2: do not count 'u16 size' field */ ++ templ->size = cpu_to_le16(len - 2); ++ ++ FN_EXIT1(len); ++ return len; ++} ++ ++ ++#if POWER_SAVE_80211 ++/*********************************************************************** ++** acx_s_set_null_data_template ++*/ ++static int ++acx_s_set_null_data_template(acx_device_t *adev) ++{ ++ struct acx_template_nullframe b; ++ int result; ++ ++ FN_ENTER; ++ ++ /* memset(&b, 0, sizeof(b)); not needed, setting all members */ ++ ++ b.size = cpu_to_le16(sizeof(b) - 2); ++ b.hdr.fc = WF_FTYPE_MGMTi | WF_FSTYPE_NULLi; ++ b.hdr.dur = 0; ++ MAC_BCAST(b.hdr.a1); ++ MAC_COPY(b.hdr.a2, adev->dev_addr); ++ MAC_COPY(b.hdr.a3, adev->bssid); ++ b.hdr.seq = 0; ++ ++ result = acx_s_issue_cmd(adev, ACX1xx_CMD_CONFIG_NULL_DATA, &b, sizeof(b)); ++ ++ FN_EXIT1(result); ++ return result; ++} ++#endif ++ ++ ++/*********************************************************************** ++** acx_s_set_beacon_template ++*/ ++static int ++acx_s_set_beacon_template(acx_device_t *adev) ++{ ++ struct acx_template_beacon bcn; ++ int len, result; ++ ++ FN_ENTER; ++ ++ len = acx_fill_beacon_or_proberesp_template(adev, &bcn, WF_FSTYPE_BEACON); ++ result = acx_s_issue_cmd(adev, ACX1xx_CMD_CONFIG_BEACON, &bcn, len); ++ ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++** acx_s_set_probe_response_template ++*/ ++static int ++acx_s_set_probe_response_template(acx_device_t *adev) ++{ ++ struct acx_template_proberesp pr; ++ int len, result; ++ ++ FN_ENTER; ++ ++ len = acx_fill_beacon_or_proberesp_template(adev, &pr, WF_FSTYPE_PROBERESP); ++ result = acx_s_issue_cmd(adev, ACX1xx_CMD_CONFIG_PROBE_RESPONSE, &pr, len); ++ ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++** acx_s_init_packet_templates() ++** ++** NOTE: order is very important here, to have a correct memory layout! ++** init templates: max Probe Request (station mode), max NULL data, ++** max Beacon, max TIM, max Probe Response. ++*/ ++static int ++acx_s_init_packet_templates(acx_device_t *adev) ++{ ++ acx_ie_memmap_t mm; /* ACX100 only */ ++ int result = NOT_OK; ++ ++ FN_ENTER; ++ ++ log(L_DEBUG|L_INIT, "initializing max packet templates\n"); ++ ++ if (OK != acx_s_init_max_probe_request_template(adev)) ++ goto failed; ++ ++ if (OK != acx_s_init_max_null_data_template(adev)) ++ goto failed; ++ ++ if (OK != acx_s_init_max_beacon_template(adev)) ++ goto failed; ++ ++ if (OK != acx_s_init_max_tim_template(adev)) ++ goto failed; ++ ++ if (OK != acx_s_init_max_probe_response_template(adev)) ++ goto failed; ++ ++ if (IS_ACX111(adev)) { ++ /* ACX111 doesn't need the memory map magic below, ++ * and the other templates will be set later (acx_start) */ ++ result = OK; ++ goto success; ++ } ++ ++ /* ACX100 will have its TIM template set, ++ * and we also need to update the memory map */ ++ ++ if (OK != acx_s_set_tim_template(adev)) ++ goto failed_acx100; ++ ++ log(L_DEBUG, "sizeof(memmap)=%d bytes\n", (int)sizeof(mm)); ++ ++ if (OK != acx_s_interrogate(adev, &mm, ACX1xx_IE_MEMORY_MAP)) ++ goto failed_acx100; ++ ++ mm.QueueStart = cpu_to_le32(le32_to_cpu(mm.PacketTemplateEnd) + 4); ++ if (OK != acx_s_configure(adev, &mm, ACX1xx_IE_MEMORY_MAP)) ++ goto failed_acx100; ++ ++ result = OK; ++ goto success; ++ ++failed_acx100: ++ log(L_DEBUG|L_INIT, ++ /* "cb=0x%X\n" */ ++ "ACXMemoryMap:\n" ++ ".CodeStart=0x%X\n" ++ ".CodeEnd=0x%X\n" ++ ".WEPCacheStart=0x%X\n" ++ ".WEPCacheEnd=0x%X\n" ++ ".PacketTemplateStart=0x%X\n" ++ ".PacketTemplateEnd=0x%X\n", ++ /* len, */ ++ le32_to_cpu(mm.CodeStart), ++ le32_to_cpu(mm.CodeEnd), ++ le32_to_cpu(mm.WEPCacheStart), ++ le32_to_cpu(mm.WEPCacheEnd), ++ le32_to_cpu(mm.PacketTemplateStart), ++ le32_to_cpu(mm.PacketTemplateEnd)); ++ ++failed: ++ printk("%s: %s() FAILED\n", adev->ndev->name, __func__); ++ ++success: ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++*/ ++static int ++acx_s_set_probe_request_template(acx_device_t *adev) ++{ ++ struct acx_template_probereq probereq; ++ char *p; ++ int res; ++ int frame_len; ++ ++ FN_ENTER; ++ ++ memset(&probereq, 0, sizeof(probereq)); ++ ++ probereq.fc = WF_FTYPE_MGMTi | WF_FSTYPE_PROBEREQi; ++ MAC_BCAST(probereq.da); ++ MAC_COPY(probereq.sa, adev->dev_addr); ++ MAC_BCAST(probereq.bssid); ++ ++ p = probereq.variable; ++ p = wlan_fill_ie_ssid(p, adev->essid_len, adev->essid); ++ p = wlan_fill_ie_rates(p, adev->rate_supported_len, adev->rate_supported); ++ p = wlan_fill_ie_rates_ext(p, adev->rate_supported_len, adev->rate_supported); ++ frame_len = p - (char*)&probereq; ++ probereq.size = cpu_to_le16(frame_len - 2); ++ ++ res = acx_s_issue_cmd(adev, ACX1xx_CMD_CONFIG_PROBE_REQUEST, &probereq, frame_len); ++ FN_EXIT0; ++ return res; ++} ++ ++ ++/*********************************************************************** ++** acx_s_init_mac ++*/ ++int ++acx_s_init_mac(acx_device_t *adev) ++{ ++ int result = NOT_OK; ++ ++ FN_ENTER; ++ ++ if (IS_ACX111(adev)) { ++ adev->ie_len = acx111_ie_len; ++ adev->ie_len_dot11 = acx111_ie_len_dot11; ++ } else { ++ adev->ie_len = acx100_ie_len; ++ adev->ie_len_dot11 = acx100_ie_len_dot11; ++ } ++ ++#if defined (ACX_MEM) ++ adev->memblocksize = 256; /* 256 is default */ ++ /* try to load radio for both ACX100 and ACX111, since both ++ * chips have at least some firmware versions making use of an ++ * external radio module */ ++ acxmem_s_upload_radio(adev); ++#else ++ if (IS_PCI(adev)) { ++ adev->memblocksize = 256; /* 256 is default */ ++ /* try to load radio for both ACX100 and ACX111, since both ++ * chips have at least some firmware versions making use of an ++ * external radio module */ ++ acxpci_s_upload_radio(adev); ++ } else { ++ adev->memblocksize = 128; ++ } ++#endif ++ ++ if (IS_ACX111(adev)) { ++ /* for ACX111, the order is different from ACX100 ++ 1. init packet templates ++ 2. create station context and create dma regions ++ 3. init wep default keys ++ */ ++ if (OK != acx_s_init_packet_templates(adev)) ++ goto fail; ++ if (OK != acx111_s_create_dma_regions(adev)) { ++ printk("%s: acx111_create_dma_regions FAILED\n", ++ adev->ndev->name); ++ goto fail; ++ } ++ } else { ++ if (OK != acx100_s_init_wep(adev)) ++ goto fail; ++ if (OK != acx_s_init_packet_templates(adev)) ++ goto fail; ++ if (OK != acx100_s_create_dma_regions(adev)) { ++ printk("%s: acx100_create_dma_regions FAILED\n", ++ adev->ndev->name); ++ goto fail; ++ } ++ } ++ ++ MAC_COPY(adev->ndev->dev_addr, adev->dev_addr); ++ result = OK; ++ ++fail: ++ if (result) ++ printk("acx: init_mac() FAILED\n"); ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++void ++acx_s_set_sane_reg_domain(acx_device_t *adev, int do_set) ++{ ++ unsigned mask; ++ ++ unsigned int i; ++ ++ for (i = 0; i < sizeof(acx_reg_domain_ids); i++) ++ if (acx_reg_domain_ids[i] == adev->reg_dom_id) ++ break; ++ ++ if (sizeof(acx_reg_domain_ids) == i) { ++ log(L_INIT, "Invalid or unsupported regulatory domain" ++ " 0x%02X specified, falling back to FCC (USA)!" ++ " Please report if this sounds fishy!\n", ++ adev->reg_dom_id); ++ i = 0; ++ adev->reg_dom_id = acx_reg_domain_ids[i]; ++ ++ /* since there was a mismatch, we need to force updating */ ++ do_set = 1; ++ } ++ ++ if (do_set) { ++ acx_ie_generic_t dom; ++ dom.m.bytes[0] = adev->reg_dom_id; ++ acx_s_configure(adev, &dom, ACX1xx_IE_DOT11_CURRENT_REG_DOMAIN); ++ } ++ ++ adev->reg_dom_chanmask = reg_domain_channel_masks[i]; ++ ++ mask = (1 << (adev->channel - 1)); ++ if (!(adev->reg_dom_chanmask & mask)) { ++ /* hmm, need to adjust our channel to reside within domain */ ++ mask = 1; ++ for (i = 1; i <= 14; i++) { ++ if (adev->reg_dom_chanmask & mask) { ++ printk("%s: adjusting selected channel from %d " ++ "to %d due to new regulatory domain\n", ++ adev->ndev->name, adev->channel, i); ++ adev->channel = i; ++ break; ++ } ++ mask <<= 1; ++ } ++ } ++} ++ ++ ++#if POWER_SAVE_80211 ++static void ++acx_s_update_80211_powersave_mode(acx_device_t *adev) ++{ ++ /* merge both structs in a union to be able to have common code */ ++ union { ++ acx111_ie_powersave_t acx111; ++ acx100_ie_powersave_t acx100; ++ } pm; ++ ++ /* change 802.11 power save mode settings */ ++ log(L_INIT, "updating 802.11 power save mode settings: " ++ "wakeup_cfg 0x%02X, listen interval %u, " ++ "options 0x%02X, hangover period %u, " ++ "enhanced_ps_transition_time %u\n", ++ adev->ps_wakeup_cfg, adev->ps_listen_interval, ++ adev->ps_options, adev->ps_hangover_period, ++ adev->ps_enhanced_transition_time); ++ acx_s_interrogate(adev, &pm, ACX1xx_IE_POWER_MGMT); ++ log(L_INIT, "Previous PS mode settings: wakeup_cfg 0x%02X, " ++ "listen interval %u, options 0x%02X, " ++ "hangover period %u, " ++ "enhanced_ps_transition_time %u, beacon_rx_time %u\n", ++ pm.acx111.wakeup_cfg, ++ pm.acx111.listen_interval, ++ pm.acx111.options, ++ pm.acx111.hangover_period, ++ IS_ACX111(adev) ? ++ pm.acx111.enhanced_ps_transition_time ++ : pm.acx100.enhanced_ps_transition_time, ++ IS_ACX111(adev) ? ++ pm.acx111.beacon_rx_time ++ : (u32)-1 ++ ); ++ pm.acx111.wakeup_cfg = adev->ps_wakeup_cfg; ++ pm.acx111.listen_interval = adev->ps_listen_interval; ++ pm.acx111.options = adev->ps_options; ++ pm.acx111.hangover_period = adev->ps_hangover_period; ++ if (IS_ACX111(adev)) { ++ pm.acx111.beacon_rx_time = cpu_to_le32(adev->ps_beacon_rx_time); ++ pm.acx111.enhanced_ps_transition_time = cpu_to_le32(adev->ps_enhanced_transition_time); ++ } else { ++ pm.acx100.enhanced_ps_transition_time = cpu_to_le16(adev->ps_enhanced_transition_time); ++ } ++ acx_s_configure(adev, &pm, ACX1xx_IE_POWER_MGMT); ++ acx_s_interrogate(adev, &pm, ACX1xx_IE_POWER_MGMT); ++ log(L_INIT, "wakeup_cfg: 0x%02X\n", pm.acx111.wakeup_cfg); ++ acx_s_msleep(40); ++ acx_s_interrogate(adev, &pm, ACX1xx_IE_POWER_MGMT); ++ log(L_INIT, "wakeup_cfg: 0x%02X\n", pm.acx111.wakeup_cfg); ++ log(L_INIT, "power save mode change %s\n", ++ (pm.acx111.wakeup_cfg & PS_CFG_PENDING) ? "FAILED" : "was successful"); ++ /* FIXME: maybe verify via PS_CFG_PENDING bit here ++ * that power save mode change was successful. */ ++ /* FIXME: we shouldn't trigger a scan immediately after ++ * fiddling with power save mode (since the firmware is sending ++ * a NULL frame then). */ ++} ++#endif ++ ++ ++/*********************************************************************** ++** acx_s_update_card_settings ++** ++** Applies accumulated changes in various adev->xxxx members ++** Called by ioctl commit handler, acx_start, acx_set_defaults, ++** acx_s_after_interrupt_task (if IRQ_CMD_UPDATE_CARD_CFG), ++*/ ++static void ++acx111_s_sens_radio_16_17(acx_device_t *adev) ++{ ++ u32 feature1, feature2; ++ ++ if ((adev->sensitivity < 1) || (adev->sensitivity > 3)) { ++ printk("%s: invalid sensitivity setting (1..3), " ++ "setting to 1\n", adev->ndev->name); ++ adev->sensitivity = 1; ++ } ++ acx111_s_get_feature_config(adev, &feature1, &feature2); ++ CLEAR_BIT(feature1, FEATURE1_LOW_RX|FEATURE1_EXTRA_LOW_RX); ++ if (adev->sensitivity > 1) ++ SET_BIT(feature1, FEATURE1_LOW_RX); ++ if (adev->sensitivity > 2) ++ SET_BIT(feature1, FEATURE1_EXTRA_LOW_RX); ++ acx111_s_feature_set(adev, feature1, feature2); ++} ++ ++ ++void ++acx_s_update_card_settings(acx_device_t *adev) ++{ ++ unsigned long flags; ++ unsigned int start_scan = 0; ++ int i; ++ ++ FN_ENTER; ++ ++ log(L_INIT, "get_mask 0x%08X, set_mask 0x%08X\n", ++ adev->get_mask, adev->set_mask); ++ ++ /* Track dependencies betweed various settings */ ++ ++ if (adev->set_mask & (GETSET_MODE|GETSET_RESCAN|GETSET_WEP)) { ++ log(L_INIT, "important setting has been changed. " ++ "Need to update packet templates, too\n"); ++ SET_BIT(adev->set_mask, SET_TEMPLATES); ++ } ++ if (adev->set_mask & GETSET_CHANNEL) { ++ /* This will actually tune RX/TX to the channel */ ++ SET_BIT(adev->set_mask, GETSET_RX|GETSET_TX); ++ switch (adev->mode) { ++ case ACX_MODE_0_ADHOC: ++ case ACX_MODE_3_AP: ++ /* Beacons contain channel# - update them */ ++ SET_BIT(adev->set_mask, SET_TEMPLATES); ++ } ++ switch (adev->mode) { ++ case ACX_MODE_0_ADHOC: ++ case ACX_MODE_2_STA: ++ start_scan = 1; ++ } ++ } ++ ++ /* Apply settings */ ++ ++#ifdef WHY_SHOULD_WE_BOTHER /* imagine we were just powered off */ ++ /* send a disassoc request in case it's required */ ++ if (adev->set_mask & (GETSET_MODE|GETSET_RESCAN|GETSET_CHANNEL|GETSET_WEP)) { ++ if (ACX_MODE_2_STA == adev->mode) { ++ if (ACX_STATUS_4_ASSOCIATED == adev->status) { ++ log(L_ASSOC, "we were ASSOCIATED - " ++ "sending disassoc request\n"); ++ acx_lock(adev, flags); ++ acx_l_transmit_disassoc(adev, NULL); ++ /* FIXME: deauth? */ ++ acx_unlock(adev, flags); ++ } ++ /* need to reset some other stuff as well */ ++ log(L_DEBUG, "resetting bssid\n"); ++ MAC_ZERO(adev->bssid); ++ SET_BIT(adev->set_mask, SET_TEMPLATES|SET_STA_LIST); ++ start_scan = 1; ++ } ++ } ++#endif ++ ++ if (adev->get_mask & GETSET_STATION_ID) { ++ u8 stationID[4 + ACX1xx_IE_DOT11_STATION_ID_LEN]; ++ const u8 *paddr; ++ ++ acx_s_interrogate(adev, &stationID, ACX1xx_IE_DOT11_STATION_ID); ++ paddr = &stationID[4]; ++ for (i = 0; i < ETH_ALEN; i++) { ++ /* we copy the MAC address (reversed in ++ * the card) to the netdevice's MAC ++ * address, and on ifup it will be ++ * copied into iwadev->dev_addr */ ++ adev->ndev->dev_addr[ETH_ALEN - 1 - i] = paddr[i]; ++ } ++ CLEAR_BIT(adev->get_mask, GETSET_STATION_ID); ++ } ++ ++ if (adev->get_mask & GETSET_SENSITIVITY) { ++ if ((RADIO_RFMD_11 == adev->radio_type) ++ || (RADIO_MAXIM_0D == adev->radio_type) ++ || (RADIO_RALINK_15 == adev->radio_type)) { ++ acx_s_read_phy_reg(adev, 0x30, &adev->sensitivity); ++ } else { ++ log(L_INIT, "don't know how to get sensitivity " ++ "for radio type 0x%02X\n", adev->radio_type); ++ adev->sensitivity = 0; ++ } ++ log(L_INIT, "got sensitivity value %u\n", adev->sensitivity); ++ ++ CLEAR_BIT(adev->get_mask, GETSET_SENSITIVITY); ++ } ++ ++ if (adev->get_mask & GETSET_ANTENNA) { ++ u8 antenna[4 + ACX1xx_IE_DOT11_CURRENT_ANTENNA_LEN]; ++ ++ memset(antenna, 0, sizeof(antenna)); ++ acx_s_interrogate(adev, antenna, ACX1xx_IE_DOT11_CURRENT_ANTENNA); ++ adev->antenna = antenna[4]; ++ log(L_INIT, "got antenna value 0x%02X\n", adev->antenna); ++ CLEAR_BIT(adev->get_mask, GETSET_ANTENNA); ++ } ++ ++ if (adev->get_mask & GETSET_ED_THRESH) { ++ if (IS_ACX100(adev)) { ++ u8 ed_threshold[4 + ACX100_IE_DOT11_ED_THRESHOLD_LEN]; ++ ++ memset(ed_threshold, 0, sizeof(ed_threshold)); ++ acx_s_interrogate(adev, ed_threshold, ACX100_IE_DOT11_ED_THRESHOLD); ++ adev->ed_threshold = ed_threshold[4]; ++ } else { ++ log(L_INIT, "acx111 doesn't support ED\n"); ++ adev->ed_threshold = 0; ++ } ++ log(L_INIT, "got Energy Detect (ED) threshold %u\n", adev->ed_threshold); ++ CLEAR_BIT(adev->get_mask, GETSET_ED_THRESH); ++ } ++ ++ if (adev->get_mask & GETSET_CCA) { ++ if (IS_ACX100(adev)) { ++ u8 cca[4 + ACX1xx_IE_DOT11_CURRENT_CCA_MODE_LEN]; ++ ++ memset(cca, 0, sizeof(adev->cca)); ++ acx_s_interrogate(adev, cca, ACX1xx_IE_DOT11_CURRENT_CCA_MODE); ++ adev->cca = cca[4]; ++ } else { ++ log(L_INIT, "acx111 doesn't support CCA\n"); ++ adev->cca = 0; ++ } ++ log(L_INIT, "got Channel Clear Assessment (CCA) value %u\n", adev->cca); ++ CLEAR_BIT(adev->get_mask, GETSET_CCA); ++ } ++ ++ if (adev->get_mask & GETSET_REG_DOMAIN) { ++ acx_ie_generic_t dom; ++ ++ acx_s_interrogate(adev, &dom, ACX1xx_IE_DOT11_CURRENT_REG_DOMAIN); ++ adev->reg_dom_id = dom.m.bytes[0]; ++ acx_s_set_sane_reg_domain(adev, 0); ++ log(L_INIT, "got regulatory domain 0x%02X\n", adev->reg_dom_id); ++ CLEAR_BIT(adev->get_mask, GETSET_REG_DOMAIN); ++ } ++ ++ if (adev->set_mask & GETSET_STATION_ID) { ++ u8 stationID[4 + ACX1xx_IE_DOT11_STATION_ID_LEN]; ++ u8 *paddr; ++ ++ paddr = &stationID[4]; ++ memcpy(adev->dev_addr, adev->ndev->dev_addr, ETH_ALEN); ++ for (i = 0; i < ETH_ALEN; i++) { ++ /* copy the MAC address we obtained when we noticed ++ * that the ethernet iface's MAC changed ++ * to the card (reversed in ++ * the card!) */ ++ paddr[i] = adev->dev_addr[ETH_ALEN - 1 - i]; ++ } ++ acx_s_configure(adev, &stationID, ACX1xx_IE_DOT11_STATION_ID); ++ CLEAR_BIT(adev->set_mask, GETSET_STATION_ID); ++ } ++ ++ if (adev->set_mask & SET_TEMPLATES) { ++ log(L_INIT, "updating packet templates\n"); ++ switch (adev->mode) { ++ case ACX_MODE_2_STA: ++ acx_s_set_probe_request_template(adev); ++#if POWER_SAVE_80211 ++ acx_s_set_null_data_template(adev); ++#endif ++ break; ++ case ACX_MODE_0_ADHOC: ++ acx_s_set_probe_request_template(adev); ++#if POWER_SAVE_80211 ++ /* maybe power save functionality is somehow possible ++ * for Ad-Hoc mode, too... FIXME: verify it somehow? firmware debug fields? */ ++ acx_s_set_null_data_template(adev); ++#endif ++ /* fall through */ ++ case ACX_MODE_3_AP: ++ acx_s_set_beacon_template(adev); ++ acx_s_set_tim_template(adev); ++ /* BTW acx111 firmware would not send probe responses ++ ** if probe request does not have all basic rates flagged ++ ** by 0x80! Thus firmware does not conform to 802.11, ++ ** it should ignore 0x80 bit in ratevector from STA. ++ ** We can 'fix' it by not using this template and ++ ** sending probe responses by hand. TODO --vda */ ++ acx_s_set_probe_response_template(adev); ++ } ++ /* Needed if generated frames are to be emitted at different tx rate now */ ++ log(L_IRQ, "redoing cmd_join_bssid() after template cfg\n"); ++ acx_s_cmd_join_bssid(adev, adev->bssid); ++ CLEAR_BIT(adev->set_mask, SET_TEMPLATES); ++ } ++ if (adev->set_mask & SET_STA_LIST) { ++ acx_lock(adev, flags); ++ acx_l_sta_list_init(adev); ++ CLEAR_BIT(adev->set_mask, SET_STA_LIST); ++ acx_unlock(adev, flags); ++ } ++ if (adev->set_mask & SET_RATE_FALLBACK) { ++ u8 rate[4 + ACX1xx_IE_RATE_FALLBACK_LEN]; ++ ++ /* configure to not do fallbacks when not in auto rate mode */ ++ rate[4] = (adev->rate_auto) ? /* adev->txrate_fallback_retries */ 1 : 0; ++ log(L_INIT, "updating Tx fallback to %u retries\n", rate[4]); ++ acx_s_configure(adev, &rate, ACX1xx_IE_RATE_FALLBACK); ++ CLEAR_BIT(adev->set_mask, SET_RATE_FALLBACK); ++ } ++ if (adev->set_mask & GETSET_TXPOWER) { ++ log(L_INIT, "updating transmit power: %u dBm\n", ++ adev->tx_level_dbm); ++ acx_s_set_tx_level(adev, adev->tx_level_dbm); ++ CLEAR_BIT(adev->set_mask, GETSET_TXPOWER); ++ } ++ ++ if (adev->set_mask & GETSET_SENSITIVITY) { ++ log(L_INIT, "updating sensitivity value: %u\n", ++ adev->sensitivity); ++ switch (adev->radio_type) { ++ case RADIO_RFMD_11: ++ case RADIO_MAXIM_0D: ++ case RADIO_RALINK_15: ++ acx_s_write_phy_reg(adev, 0x30, adev->sensitivity); ++ break; ++ case RADIO_RADIA_16: ++ case RADIO_UNKNOWN_17: ++ acx111_s_sens_radio_16_17(adev); ++ break; ++ default: ++ log(L_INIT, "don't know how to modify sensitivity " ++ "for radio type 0x%02X\n", adev->radio_type); ++ } ++ CLEAR_BIT(adev->set_mask, GETSET_SENSITIVITY); ++ } ++ ++ if (adev->set_mask & GETSET_ANTENNA) { ++ /* antenna */ ++ u8 antenna[4 + ACX1xx_IE_DOT11_CURRENT_ANTENNA_LEN]; ++ ++ memset(antenna, 0, sizeof(antenna)); ++ antenna[4] = adev->antenna; ++ log(L_INIT, "updating antenna value: 0x%02X\n", ++ adev->antenna); ++ acx_s_configure(adev, &antenna, ACX1xx_IE_DOT11_CURRENT_ANTENNA); ++ CLEAR_BIT(adev->set_mask, GETSET_ANTENNA); ++ } ++ ++ if (adev->set_mask & GETSET_ED_THRESH) { ++ /* ed_threshold */ ++ log(L_INIT, "updating Energy Detect (ED) threshold: %u\n", ++ adev->ed_threshold); ++ if (IS_ACX100(adev)) { ++ u8 ed_threshold[4 + ACX100_IE_DOT11_ED_THRESHOLD_LEN]; ++ ++ memset(ed_threshold, 0, sizeof(ed_threshold)); ++ ed_threshold[4] = adev->ed_threshold; ++ acx_s_configure(adev, &ed_threshold, ACX100_IE_DOT11_ED_THRESHOLD); ++ } ++ else ++ log(L_INIT, "acx111 doesn't support ED!\n"); ++ CLEAR_BIT(adev->set_mask, GETSET_ED_THRESH); ++ } ++ ++ if (adev->set_mask & GETSET_CCA) { ++ /* CCA value */ ++ log(L_INIT, "updating Channel Clear Assessment " ++ "(CCA) value: 0x%02X\n", adev->cca); ++ if (IS_ACX100(adev)) { ++ u8 cca[4 + ACX1xx_IE_DOT11_CURRENT_CCA_MODE_LEN]; ++ ++ memset(cca, 0, sizeof(cca)); ++ cca[4] = adev->cca; ++ acx_s_configure(adev, &cca, ACX1xx_IE_DOT11_CURRENT_CCA_MODE); ++ } ++ else ++ log(L_INIT, "acx111 doesn't support CCA!\n"); ++ CLEAR_BIT(adev->set_mask, GETSET_CCA); ++ } ++ ++ if (adev->set_mask & GETSET_LED_POWER) { ++ /* Enable Tx */ ++ log(L_INIT, "updating power LED status: %u\n", adev->led_power); ++ ++ acx_lock(adev, flags); ++#if defined (ACX_MEM) ++ acxmem_l_power_led(adev, adev->led_power); ++#else ++ if (IS_PCI(adev)) ++ acxpci_l_power_led(adev, adev->led_power); ++#endif ++ CLEAR_BIT(adev->set_mask, GETSET_LED_POWER); ++ acx_unlock(adev, flags); ++ } ++ ++ if (adev->set_mask & GETSET_POWER_80211) { ++#if POWER_SAVE_80211 ++ acx_s_update_80211_powersave_mode(adev); ++#endif ++ CLEAR_BIT(adev->set_mask, GETSET_POWER_80211); ++ } ++ ++ if (adev->set_mask & GETSET_CHANNEL) { ++ /* channel */ ++ log(L_INIT, "updating channel to: %u\n", adev->channel); ++ CLEAR_BIT(adev->set_mask, GETSET_CHANNEL); ++ } ++ ++ if (adev->set_mask & GETSET_TX) { ++ /* set Tx */ ++ log(L_INIT, "updating: %s Tx\n", ++ adev->tx_disabled ? "disable" : "enable"); ++ if (adev->tx_disabled) ++ acx_s_issue_cmd(adev, ACX1xx_CMD_DISABLE_TX, NULL, 0); ++ else ++ acx_s_issue_cmd(adev, ACX1xx_CMD_ENABLE_TX, &adev->channel, 1); ++ CLEAR_BIT(adev->set_mask, GETSET_TX); ++ } ++ ++ if (adev->set_mask & GETSET_RX) { ++ /* Enable Rx */ ++ log(L_INIT, "updating: enable Rx on channel: %u\n", ++ adev->channel); ++ acx_s_issue_cmd(adev, ACX1xx_CMD_ENABLE_RX, &adev->channel, 1); ++ CLEAR_BIT(adev->set_mask, GETSET_RX); ++ } ++ ++ if (adev->set_mask & GETSET_RETRY) { ++ u8 short_retry[4 + ACX1xx_IE_DOT11_SHORT_RETRY_LIMIT_LEN]; ++ u8 long_retry[4 + ACX1xx_IE_DOT11_LONG_RETRY_LIMIT_LEN]; ++ ++ log(L_INIT, "updating short retry limit: %u, long retry limit: %u\n", ++ adev->short_retry, adev->long_retry); ++ short_retry[0x4] = adev->short_retry; ++ long_retry[0x4] = adev->long_retry; ++ acx_s_configure(adev, &short_retry, ACX1xx_IE_DOT11_SHORT_RETRY_LIMIT); ++ acx_s_configure(adev, &long_retry, ACX1xx_IE_DOT11_LONG_RETRY_LIMIT); ++ CLEAR_BIT(adev->set_mask, GETSET_RETRY); ++ } ++ ++ if (adev->set_mask & SET_MSDU_LIFETIME) { ++ u8 xmt_msdu_lifetime[4 + ACX1xx_IE_DOT11_MAX_XMIT_MSDU_LIFETIME_LEN]; ++ ++ log(L_INIT, "updating tx MSDU lifetime: %u\n", ++ adev->msdu_lifetime); ++ *(u32 *)&xmt_msdu_lifetime[4] = cpu_to_le32((u32)adev->msdu_lifetime); ++ acx_s_configure(adev, &xmt_msdu_lifetime, ACX1xx_IE_DOT11_MAX_XMIT_MSDU_LIFETIME); ++ CLEAR_BIT(adev->set_mask, SET_MSDU_LIFETIME); ++ } ++ ++ if (adev->set_mask & GETSET_REG_DOMAIN) { ++ log(L_INIT, "updating regulatory domain: 0x%02X\n", ++ adev->reg_dom_id); ++ acx_s_set_sane_reg_domain(adev, 1); ++ CLEAR_BIT(adev->set_mask, GETSET_REG_DOMAIN); ++ } ++ ++ if (adev->set_mask & GETSET_MODE) { ++ adev->ndev->type = (adev->mode == ACX_MODE_MONITOR) ? ++ adev->monitor_type : ARPHRD_ETHER; ++ ++ switch (adev->mode) { ++ case ACX_MODE_3_AP: ++ ++ acx_lock(adev, flags); ++ acx_l_sta_list_init(adev); ++ adev->aid = 0; ++ adev->ap_client = NULL; ++ MAC_COPY(adev->bssid, adev->dev_addr); ++ /* this basically says "we're connected" */ ++ acx_set_status(adev, ACX_STATUS_4_ASSOCIATED); ++ acx_unlock(adev, flags); ++ ++ acx111_s_feature_off(adev, 0, FEATURE2_NO_TXCRYPT|FEATURE2_SNIFFER); ++ /* start sending beacons */ ++ acx_s_cmd_join_bssid(adev, adev->bssid); ++ break; ++ case ACX_MODE_MONITOR: ++ acx111_s_feature_on(adev, 0, FEATURE2_NO_TXCRYPT|FEATURE2_SNIFFER); ++ /* this stops beacons */ ++ acx_s_cmd_join_bssid(adev, adev->bssid); ++ /* this basically says "we're connected" */ ++ acx_set_status(adev, ACX_STATUS_4_ASSOCIATED); ++ SET_BIT(adev->set_mask, SET_RXCONFIG|SET_WEP_OPTIONS); ++ break; ++ case ACX_MODE_0_ADHOC: ++ case ACX_MODE_2_STA: ++ acx111_s_feature_off(adev, 0, FEATURE2_NO_TXCRYPT|FEATURE2_SNIFFER); ++ ++ acx_lock(adev, flags); ++ adev->aid = 0; ++ adev->ap_client = NULL; ++ acx_unlock(adev, flags); ++ ++ /* we want to start looking for peer or AP */ ++ start_scan = 1; ++ break; ++ case ACX_MODE_OFF: ++ /* TODO: disable RX/TX, stop any scanning activity etc: */ ++ /* adev->tx_disabled = 1; */ ++ /* SET_BIT(adev->set_mask, GETSET_RX|GETSET_TX); */ ++ ++ /* This stops beacons (invalid macmode...) */ ++ acx_s_cmd_join_bssid(adev, adev->bssid); ++ acx_set_status(adev, ACX_STATUS_0_STOPPED); ++ break; ++ } ++ CLEAR_BIT(adev->set_mask, GETSET_MODE); ++ } ++ ++ if (adev->set_mask & SET_RXCONFIG) { ++ acx_s_initialize_rx_config(adev); ++ CLEAR_BIT(adev->set_mask, SET_RXCONFIG); ++ } ++ ++ if (adev->set_mask & GETSET_RESCAN) { ++ switch (adev->mode) { ++ case ACX_MODE_0_ADHOC: ++ case ACX_MODE_2_STA: ++ start_scan = 1; ++ break; ++ } ++ CLEAR_BIT(adev->set_mask, GETSET_RESCAN); ++ } ++ ++ if (adev->set_mask & GETSET_WEP) { ++ /* encode */ ++ ++ ie_dot11WEPDefaultKeyID_t dkey; ++#ifdef DEBUG_WEP ++ struct { ++ u16 type; ++ u16 len; ++ u8 val; ++ } ACX_PACKED keyindic; ++#endif ++ log(L_INIT, "updating WEP key settings\n"); ++ ++ acx_s_set_wepkey(adev); ++ ++ dkey.KeyID = adev->wep_current_index; ++ log(L_INIT, "setting WEP key %u as default\n", dkey.KeyID); ++ acx_s_configure(adev, &dkey, ACX1xx_IE_DOT11_WEP_DEFAULT_KEY_SET); ++#ifdef DEBUG_WEP ++ keyindic.val = 3; ++ acx_s_configure(adev, &keyindic, ACX111_IE_KEY_CHOOSE); ++#endif ++ start_scan = 1; ++ CLEAR_BIT(adev->set_mask, GETSET_WEP); ++ } ++ ++ if (adev->set_mask & SET_WEP_OPTIONS) { ++ acx100_ie_wep_options_t options; ++ if (IS_ACX111(adev)) { ++ log(L_DEBUG, "setting WEP Options for acx111 is not supported\n"); ++ } else { ++ log(L_INIT, "setting WEP Options\n"); ++ acx100_s_init_wep(adev); ++#if 0 ++ /* let's choose maximum setting: 4 default keys, ++ * plus 10 other keys: */ ++ options.NumKeys = cpu_to_le16(DOT11_MAX_DEFAULT_WEP_KEYS + 10); ++ /* don't decrypt default key only, ++ * don't override decryption: */ ++ options.WEPOption = 0; ++ if (adev->mode == ACX_MODE_MONITOR) { ++ /* don't decrypt default key only, ++ * override decryption mechanism: */ ++ options.WEPOption = 2; ++ } ++ ++ acx_s_configure(adev, &options, ACX100_IE_WEP_OPTIONS); ++#endif ++ } ++ CLEAR_BIT(adev->set_mask, SET_WEP_OPTIONS); ++ } ++ ++ /* Rescan was requested */ ++ if (start_scan) { ++ switch (adev->mode) { ++ case ACX_MODE_0_ADHOC: ++ case ACX_MODE_2_STA: ++ /* We can avoid clearing list if join code ++ ** will be a bit more clever about not picking ++ ** 'bad' AP over and over again */ ++ acx_lock(adev, flags); ++ adev->ap_client = NULL; ++ acx_l_sta_list_init(adev); ++ acx_set_status(adev, ACX_STATUS_1_SCANNING); ++ acx_unlock(adev, flags); ++ ++ acx_s_cmd_start_scan(adev); ++ } ++ } ++ ++ /* debug, rate, and nick don't need any handling */ ++ /* what about sniffing mode?? */ ++ ++ log(L_INIT, "get_mask 0x%08X, set_mask 0x%08X - after update\n", ++ adev->get_mask, adev->set_mask); ++ ++/* end: */ ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acx_e_after_interrupt_task ++*/ ++static int ++acx_s_recalib_radio(acx_device_t *adev) ++{ ++ if (IS_ACX111(adev)) { ++ acx111_cmd_radiocalib_t cal; ++ ++ printk("%s: recalibrating radio\n", adev->ndev->name); ++ /* automatic recalibration, choose all methods: */ ++ cal.methods = cpu_to_le32(0x8000000f); ++ /* automatic recalibration every 60 seconds (value in TUs) ++ * I wonder what the firmware default here is? */ ++ cal.interval = cpu_to_le32(58594); ++ return acx_s_issue_cmd_timeo(adev, ACX111_CMD_RADIOCALIB, ++ &cal, sizeof(cal), CMD_TIMEOUT_MS(100)); ++ } else { ++ /* On ACX100, we need to recalibrate the radio ++ * by issuing a GETSET_TX|GETSET_RX */ ++ if (/* (OK == acx_s_issue_cmd(adev, ACX1xx_CMD_DISABLE_TX, NULL, 0)) && ++ (OK == acx_s_issue_cmd(adev, ACX1xx_CMD_DISABLE_RX, NULL, 0)) && */ ++ (OK == acx_s_issue_cmd(adev, ACX1xx_CMD_ENABLE_TX, &adev->channel, 1)) && ++ (OK == acx_s_issue_cmd(adev, ACX1xx_CMD_ENABLE_RX, &adev->channel, 1)) ) ++ return OK; ++ return NOT_OK; ++ } ++} ++ ++static void ++acx_s_after_interrupt_recalib(acx_device_t *adev) ++{ ++ int res; ++ ++ /* this helps with ACX100 at least; ++ * hopefully ACX111 also does a ++ * recalibration here */ ++ ++ /* clear flag beforehand, since we want to make sure ++ * it's cleared; then only set it again on specific circumstances */ ++ CLEAR_BIT(adev->after_interrupt_jobs, ACX_AFTER_IRQ_CMD_RADIO_RECALIB); ++ ++ /* better wait a bit between recalibrations to ++ * prevent overheating due to torturing the card ++ * into working too long despite high temperature ++ * (just a safety measure) */ ++ if (adev->recalib_time_last_success ++ && time_before(jiffies, adev->recalib_time_last_success ++ + RECALIB_PAUSE * 60 * HZ)) { ++ if (adev->recalib_msg_ratelimit <= 4) { ++ printk("%s: less than " STRING(RECALIB_PAUSE) ++ " minutes since last radio recalibration, " ++ "not recalibrating (maybe card is too hot?)\n", ++ adev->ndev->name); ++ adev->recalib_msg_ratelimit++; ++ if (adev->recalib_msg_ratelimit == 5) ++ printk("disabling above message until next recalib\n"); ++ } ++ return; ++ } ++ ++ adev->recalib_msg_ratelimit = 0; ++ ++ /* note that commands sometimes fail (card busy), ++ * so only clear flag if we were fully successful */ ++ res = acx_s_recalib_radio(adev); ++ if (res == OK) { ++ printk("%s: successfully recalibrated radio\n", ++ adev->ndev->name); ++ adev->recalib_time_last_success = jiffies; ++ adev->recalib_failure_count = 0; ++ } else { ++ /* failed: resubmit, but only limited ++ * amount of times within some time range ++ * to prevent endless loop */ ++ ++ adev->recalib_time_last_success = 0; /* we failed */ ++ ++ /* if some time passed between last ++ * attempts, then reset failure retry counter ++ * to be able to do next recalib attempt */ ++ if (time_after(jiffies, adev->recalib_time_last_attempt + 5*HZ)) ++ adev->recalib_failure_count = 0; ++ ++ if (adev->recalib_failure_count < 5) { ++ /* increment inside only, for speedup of outside path */ ++ adev->recalib_failure_count++; ++ adev->recalib_time_last_attempt = jiffies; ++ acx_schedule_task(adev, ACX_AFTER_IRQ_CMD_RADIO_RECALIB); ++ } ++ } ++} ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) ++static void ++acx_e_after_interrupt_task(struct work_struct *work) ++{ ++ acx_device_t *adev = container_of(work, acx_device_t, after_interrupt_task); ++#else ++ static void ++ acx_e_after_interrupt_task(void *data) ++ { ++ struct net_device *ndev = (struct net_device*)data; ++ acx_device_t *adev = ndev2adev(ndev); ++#endif ++ FN_ENTER; ++ ++ acx_sem_lock(adev); ++ ++ if (!adev->after_interrupt_jobs) ++ goto end; /* no jobs to do */ ++ ++#if TX_CLEANUP_IN_SOFTIRQ ++ /* can happen only on PCI */ ++ if (adev->after_interrupt_jobs & ACX_AFTER_IRQ_TX_CLEANUP) { ++ acx_lock(adev, flags); ++ acxpci_l_clean_txdesc(adev); ++ CLEAR_BIT(adev->after_interrupt_jobs, ACX_AFTER_IRQ_TX_CLEANUP); ++ acx_unlock(adev, flags); ++ } ++#endif ++ /* we see lotsa tx errors */ ++ if (adev->after_interrupt_jobs & ACX_AFTER_IRQ_CMD_RADIO_RECALIB) { ++ acx_s_after_interrupt_recalib(adev); ++ } ++ ++ /* a poor interrupt code wanted to do update_card_settings() */ ++ if (adev->after_interrupt_jobs & ACX_AFTER_IRQ_UPDATE_CARD_CFG) { ++ if (ACX_STATE_IFACE_UP & adev->dev_state_mask) ++ acx_s_update_card_settings(adev); ++ CLEAR_BIT(adev->after_interrupt_jobs, ACX_AFTER_IRQ_UPDATE_CARD_CFG); ++ } ++ ++ /* 1) we detected that no Scan_Complete IRQ came from fw, or ++ ** 2) we found too many STAs */ ++ if (adev->after_interrupt_jobs & ACX_AFTER_IRQ_CMD_STOP_SCAN) { ++ log(L_IRQ, "sending a stop scan cmd...\n"); ++ acx_s_issue_cmd(adev, ACX1xx_CMD_STOP_SCAN, NULL, 0); ++ /* HACK: set the IRQ bit, since we won't get a ++ * scan complete IRQ any more on ACX111 (works on ACX100!), ++ * since _we_, not a fw, have stopped the scan */ ++ SET_BIT(adev->irq_status, HOST_INT_SCAN_COMPLETE); ++ CLEAR_BIT(adev->after_interrupt_jobs, ACX_AFTER_IRQ_CMD_STOP_SCAN); ++ } ++ ++ /* either fw sent Scan_Complete or we detected that ++ ** no Scan_Complete IRQ came from fw. Finish scanning, ++ ** pick join partner if any */ ++ if (adev->after_interrupt_jobs & ACX_AFTER_IRQ_COMPLETE_SCAN) { ++ if (adev->status == ACX_STATUS_1_SCANNING) { ++ if (OK != acx_s_complete_scan(adev)) { ++ SET_BIT(adev->after_interrupt_jobs, ++ ACX_AFTER_IRQ_RESTART_SCAN); ++ } ++ } else { ++ /* + scan kills current join status - restore it ++ ** (do we need it for STA?) */ ++ /* + does it happen only with active scans? ++ ** active and passive scans? ALL scans including ++ ** background one? */ ++ /* + was not verified that everything is restored ++ ** (but at least we start to emit beacons again) */ ++ switch (adev->mode) { ++ case ACX_MODE_0_ADHOC: ++ case ACX_MODE_3_AP: ++ log(L_IRQ, "redoing cmd_join_bssid() after scan\n"); ++ acx_s_cmd_join_bssid(adev, adev->bssid); ++ } ++ } ++ CLEAR_BIT(adev->after_interrupt_jobs, ACX_AFTER_IRQ_COMPLETE_SCAN); ++ } ++ ++ /* STA auth or assoc timed out, start over again */ ++ if (adev->after_interrupt_jobs & ACX_AFTER_IRQ_RESTART_SCAN) { ++ log(L_IRQ, "sending a start_scan cmd...\n"); ++ acx_s_cmd_start_scan(adev); ++ CLEAR_BIT(adev->after_interrupt_jobs, ACX_AFTER_IRQ_RESTART_SCAN); ++ } ++ ++ /* whee, we got positive assoc response! 8) */ ++ if (adev->after_interrupt_jobs & ACX_AFTER_IRQ_CMD_ASSOCIATE) { ++ acx_ie_generic_t pdr; ++ /* tiny race window exists, checking that we still a STA */ ++ switch (adev->mode) { ++ case ACX_MODE_2_STA: ++ pdr.m.aid = cpu_to_le16(adev->aid); ++ acx_s_configure(adev, &pdr, ACX1xx_IE_ASSOC_ID); ++ acx_set_status(adev, ACX_STATUS_4_ASSOCIATED); ++ log(L_ASSOC|L_DEBUG, "ASSOCIATED!\n"); ++ CLEAR_BIT(adev->after_interrupt_jobs, ACX_AFTER_IRQ_CMD_ASSOCIATE); ++ } ++ } ++end: ++ acx_sem_unlock(adev); ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acx_schedule_task ++** ++** Schedule the call of the after_interrupt method after leaving ++** the interrupt context. ++*/ ++void ++acx_schedule_task(acx_device_t *adev, unsigned int set_flag) ++{ ++ SET_BIT(adev->after_interrupt_jobs, set_flag); ++ SCHEDULE_WORK(&adev->after_interrupt_task); ++} ++ ++ ++/*********************************************************************** ++*/ ++void ++acx_init_task_scheduler(acx_device_t *adev) ++{ ++ /* configure task scheduler */ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 20) ++ INIT_WORK(&adev->after_interrupt_task, acx_e_after_interrupt_task); ++#else ++ INIT_WORK(&adev->after_interrupt_task, acx_e_after_interrupt_task, ++ adev->ndev); ++#endif ++} ++ ++ ++/*********************************************************************** ++** acx_s_start ++*/ ++void ++acx_s_start(acx_device_t *adev) ++{ ++ FN_ENTER; ++ ++ /* ++ * Ok, now we do everything that can possibly be done with ioctl ++ * calls to make sure that when it was called before the card ++ * was up we get the changes asked for ++ */ ++ ++ SET_BIT(adev->set_mask, SET_TEMPLATES|SET_STA_LIST|GETSET_WEP ++ |GETSET_TXPOWER|GETSET_ANTENNA|GETSET_ED_THRESH|GETSET_CCA ++ |GETSET_REG_DOMAIN|GETSET_MODE|GETSET_CHANNEL ++ |GETSET_TX|GETSET_RX|GETSET_STATION_ID); ++ ++ log(L_INIT, "updating initial settings on iface activation\n"); ++ acx_s_update_card_settings(adev); ++ ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acx_update_capabilities ++*/ ++void ++acx_update_capabilities(acx_device_t *adev) ++{ ++ u16 cap = 0; ++ ++ switch (adev->mode) { ++ case ACX_MODE_3_AP: ++ SET_BIT(cap, WF_MGMT_CAP_ESS); break; ++ case ACX_MODE_0_ADHOC: ++ SET_BIT(cap, WF_MGMT_CAP_IBSS); break; ++ /* other types of stations do not emit beacons */ ++ } ++ ++ if (adev->wep_restricted) { ++ SET_BIT(cap, WF_MGMT_CAP_PRIVACY); ++ } ++ if (adev->cfgopt_dot11ShortPreambleOption) { ++ SET_BIT(cap, WF_MGMT_CAP_SHORT); ++ } ++ if (adev->cfgopt_dot11PBCCOption) { ++ SET_BIT(cap, WF_MGMT_CAP_PBCC); ++ } ++ if (adev->cfgopt_dot11ChannelAgility) { ++ SET_BIT(cap, WF_MGMT_CAP_AGILITY); ++ } ++ log(L_DEBUG, "caps updated from 0x%04X to 0x%04X\n", ++ adev->capabilities, cap); ++ adev->capabilities = cap; ++} ++ ++/*********************************************************************** ++** Common function to parse ALL configoption struct formats ++** (ACX100 and ACX111; FIXME: how to make it work with ACX100 USB!?!?). ++** FIXME: logging should be removed here and added to a /proc file instead ++*/ ++void ++acx_s_parse_configoption(acx_device_t *adev, const acx111_ie_configoption_t *pcfg) ++{ ++ const u8 *pEle; ++ int i; ++ int is_acx111 = IS_ACX111(adev); ++ ++ if (acx_debug & L_DEBUG) { ++ printk("configoption struct content:\n"); ++ acx_dump_bytes(pcfg, sizeof(*pcfg)); ++ } ++ ++ if (( is_acx111 && (adev->eeprom_version == 5)) ++ || (!is_acx111 && (adev->eeprom_version == 4)) ++ || (!is_acx111 && (adev->eeprom_version == 5))) { ++ /* these versions are known to be supported */ ++ } else { ++ printk("unknown chip and EEPROM version combination (%s, v%d), " ++ "don't know how to parse config options yet. " ++ "Please report\n", is_acx111 ? "ACX111" : "ACX100", ++ adev->eeprom_version); ++ return; ++ } ++ ++ /* first custom-parse the first part which has chip-specific layout */ ++ ++ pEle = (const u8 *) pcfg; ++ ++ pEle += 4; /* skip (type,len) header */ ++ ++ memcpy(adev->cfgopt_NVSv, pEle, sizeof(adev->cfgopt_NVSv)); ++ pEle += sizeof(adev->cfgopt_NVSv); ++ ++ if (is_acx111) { ++ adev->cfgopt_NVS_vendor_offs = le16_to_cpu(*(u16 *)pEle); ++ pEle += sizeof(adev->cfgopt_NVS_vendor_offs); ++ ++ adev->cfgopt_probe_delay = 200; /* good default value? */ ++ pEle += 2; /* FIXME: unknown, value 0x0001 */ ++ } else { ++ memcpy(adev->cfgopt_MAC, pEle, sizeof(adev->cfgopt_MAC)); ++ pEle += sizeof(adev->cfgopt_MAC); ++ ++ adev->cfgopt_probe_delay = le16_to_cpu(*(u16 *)pEle); ++ pEle += sizeof(adev->cfgopt_probe_delay); ++ if ((adev->cfgopt_probe_delay < 100) || (adev->cfgopt_probe_delay > 500)) { ++ printk("strange probe_delay value %d, " ++ "tweaking to 200\n", adev->cfgopt_probe_delay); ++ adev->cfgopt_probe_delay = 200; ++ } ++ } ++ ++ adev->cfgopt_eof_memory = le32_to_cpu(*(u32 *)pEle); ++ pEle += sizeof(adev->cfgopt_eof_memory); ++ ++ printk("NVS_vendor_offs:%04X probe_delay:%d eof_memory:%d\n", ++ adev->cfgopt_NVS_vendor_offs, ++ adev->cfgopt_probe_delay, ++ adev->cfgopt_eof_memory); ++ ++ adev->cfgopt_dot11CCAModes = *pEle++; ++ adev->cfgopt_dot11Diversity = *pEle++; ++ adev->cfgopt_dot11ShortPreambleOption = *pEle++; ++ adev->cfgopt_dot11PBCCOption = *pEle++; ++ adev->cfgopt_dot11ChannelAgility = *pEle++; ++ adev->cfgopt_dot11PhyType = *pEle++; ++ adev->cfgopt_dot11TempType = *pEle++; ++ printk("CCAModes:%02X Diversity:%02X ShortPreOpt:%02X " ++ "PBCC:%02X ChanAgil:%02X PHY:%02X Temp:%02X\n", ++ adev->cfgopt_dot11CCAModes, ++ adev->cfgopt_dot11Diversity, ++ adev->cfgopt_dot11ShortPreambleOption, ++ adev->cfgopt_dot11PBCCOption, ++ adev->cfgopt_dot11ChannelAgility, ++ adev->cfgopt_dot11PhyType, ++ adev->cfgopt_dot11TempType); ++ ++ /* then use common parsing for next part which has common layout */ ++ ++ pEle++; /* skip table_count (6) */ ++ ++ if (IS_MEM(adev) && IS_ACX100(adev)) ++ { ++ /* ++ * For iPaq hx4700 Generic Slave F/W 1.10.7.K. I'm not sure if these ++ * 4 extra bytes are before the dot11 things above or after, so I'm just ++ * going to guess after. If someone sees these aren't reasonable numbers, ++ * please fix this. ++ * The area from which the dot11 values above are read contains: ++ * 04 01 01 01 00 05 01 06 00 02 01 02 ++ * the 8 dot11 reads above take care of 8 of them, but which 8... ++ */ ++ pEle += 4; ++ } ++ ++ adev->cfgopt_antennas.type = pEle[0]; ++ adev->cfgopt_antennas.len = pEle[1]; ++ printk("AntennaID:%02X Len:%02X Data:", ++ adev->cfgopt_antennas.type, adev->cfgopt_antennas.len); ++ for (i = 0; i < pEle[1]; i++) { ++ adev->cfgopt_antennas.list[i] = pEle[i+2]; ++ printk("%02X ", pEle[i+2]); ++ } ++ printk("\n"); ++ ++ pEle += pEle[1] + 2; ++ adev->cfgopt_power_levels.type = pEle[0]; ++ adev->cfgopt_power_levels.len = pEle[1]; ++ printk("PowerLevelID:%02X Len:%02X Data:", ++ adev->cfgopt_power_levels.type, adev->cfgopt_power_levels.len); ++ for (i = 0; i < pEle[1]; i++) { ++ adev->cfgopt_power_levels.list[i] = le16_to_cpu(*(u16 *)&pEle[i*2+2]); ++ printk("%04X ", adev->cfgopt_power_levels.list[i]); ++ } ++ printk("\n"); ++ ++ pEle += pEle[1]*2 + 2; ++ adev->cfgopt_data_rates.type = pEle[0]; ++ adev->cfgopt_data_rates.len = pEle[1]; ++ printk("DataRatesID:%02X Len:%02X Data:", ++ adev->cfgopt_data_rates.type, adev->cfgopt_data_rates.len); ++ for (i = 0; i < pEle[1]; i++) { ++ adev->cfgopt_data_rates.list[i] = pEle[i+2]; ++ printk("%02X ", pEle[i+2]); ++ } ++ printk("\n"); ++ ++ pEle += pEle[1] + 2; ++ adev->cfgopt_domains.type = pEle[0]; ++ adev->cfgopt_domains.len = pEle[1]; ++ if (IS_MEM(adev) && IS_ACX100(adev)) ++ { ++ /* ++ * For iPaq hx4700 Generic Slave F/W 1.10.7.K. ++ * There's an extra byte between this structure and the next ++ * that is not accounted for with this structure's length. It's ++ * most likely a bug in the firmware, but we can fix it here ++ * by bumping the length of this field by 1. ++ */ ++ adev->cfgopt_domains.len++; ++ } ++ printk("DomainID:%02X Len:%02X Data:", ++ adev->cfgopt_domains.type, adev->cfgopt_domains.len); ++ for (i = 0; i < adev->cfgopt_domains.len; i++) { ++ adev->cfgopt_domains.list[i] = pEle[i+2]; ++ printk("%02X ", pEle[i+2]); ++ } ++ printk("\n"); ++ ++ pEle += adev->cfgopt_domains.len + 2; ++ ++ adev->cfgopt_product_id.type = pEle[0]; ++ adev->cfgopt_product_id.len = pEle[1]; ++ for (i = 0; i < pEle[1]; i++) { ++ adev->cfgopt_product_id.list[i] = pEle[i+2]; ++ } ++ printk("ProductID:%02X Len:%02X Data:%.*s\n", ++ adev->cfgopt_product_id.type, adev->cfgopt_product_id.len, ++ adev->cfgopt_product_id.len, (char *)adev->cfgopt_product_id.list); ++ ++ pEle += pEle[1] + 2; ++ adev->cfgopt_manufacturer.type = pEle[0]; ++ adev->cfgopt_manufacturer.len = pEle[1]; ++ for (i = 0; i < pEle[1]; i++) { ++ adev->cfgopt_manufacturer.list[i] = pEle[i+2]; ++ } ++ printk("ManufacturerID:%02X Len:%02X Data:%.*s\n", ++ adev->cfgopt_manufacturer.type, adev->cfgopt_manufacturer.len, ++ adev->cfgopt_manufacturer.len, (char *)adev->cfgopt_manufacturer.list); ++/* ++ printk("EEPROM part:\n"); ++ for (i=0; i<58; i++) { ++ printk("%02X =======> 0x%02X\n", ++ i, (u8 *)adev->cfgopt_NVSv[i-2]); ++ } ++*/ ++} ++ ++ ++/*********************************************************************** ++*/ ++static int __init ++acx_e_init_module(void) ++{ ++ int r1,r2,r3,r4; ++ ++ acx_struct_size_check(); ++ ++ printk("acx: this driver is still EXPERIMENTAL\n" ++ "acx: reading README file and/or Craig's HOWTO is " ++ "recommended, visit http://acx100.sf.net in case " ++ "of further questions/discussion\n"); ++ ++#if defined(CONFIG_ACX_PCI) ++ r1 = acxpci_e_init_module(); ++#else ++ r1 = -EINVAL; ++#endif ++#if defined(CONFIG_ACX_MEM) ++ r2 = acxmem_e_init_module(); ++#else ++ r2 = -EINVAL; ++#endif ++#if defined(CONFIG_ACX_USB) ++ r3 = acxusb_e_init_module(); ++#else ++ r3 = -EINVAL; ++#endif ++#if defined(CONFIG_ACX_CS) ++ r4 = acx_cs_init(); ++#else ++ r4 = -EINVAL; ++#endif ++ if (r2 && r1 && r3 && r4) { /* all failed! */ ++ if (r3 || r1) ++ return r3 ? r3 : r1; ++ else ++ return r2; ++ } ++ /* return success if at least one succeeded */ ++ return 0; ++ ++} ++ ++static void __exit ++acx_e_cleanup_module(void) ++{ ++#if defined(CONFIG_ACX_PCI) ++ acxpci_e_cleanup_module(); ++#endif ++#if defined(CONFIG_ACX_MEM) ++ acxmem_e_cleanup_module(); ++#endif ++#if defined(CONFIG_ACX_USB) ++ acxusb_e_cleanup_module(); ++#endif ++#if defined(CONFIG_ACX_CS) ++ acx_cs_cleanup(); ++#endif ++} ++ ++module_init(acx_e_init_module) ++module_exit(acx_e_cleanup_module) +Index: linux-2.6.23/drivers/net/wireless/acx/conv.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.23/drivers/net/wireless/acx/conv.c 2008-01-20 21:13:40.000000000 +0000 +@@ -0,0 +1,504 @@ ++/*********************************************************************** ++** Copyright (C) 2003 ACX100 Open Source Project ++** ++** The contents of this file are subject to the Mozilla Public ++** License Version 1.1 (the "License"); you may not use this file ++** except in compliance with the License. You may obtain a copy of ++** the License at http://www.mozilla.org/MPL/ ++** ++** Software distributed under the License is distributed on an "AS ++** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or ++** implied. See the License for the specific language governing ++** rights and limitations under the License. ++** ++** Alternatively, the contents of this file may be used under the ++** terms of the GNU Public License version 2 (the "GPL"), in which ++** case the provisions of the GPL are applicable instead of the ++** above. If you wish to allow the use of your version of this file ++** only under the terms of the GPL and not to allow others to use ++** your version of this file under the MPL, indicate your decision ++** by deleting the provisions above and replace them with the notice ++** and other provisions required by the GPL. If you do not delete ++** the provisions above, a recipient may use your version of this ++** file under either the MPL or the GPL. ++** --------------------------------------------------------------------- ++** Inquiries regarding the ACX100 Open Source Project can be ++** made directly to: ++** ++** acx100-users@lists.sf.net ++** http://acx100.sf.net ++** --------------------------------------------------------------------- ++*/ ++ ++#include <linux/version.h> ++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) ++#include <linux/config.h> ++#endif ++#include <linux/skbuff.h> ++#include <linux/if_arp.h> ++#include <linux/etherdevice.h> ++#include <linux/wireless.h> ++#include <net/iw_handler.h> ++ ++#include "acx.h" ++ ++ ++/*********************************************************************** ++** proto_is_stt ++** ++** Searches the 802.1h Selective Translation Table for a given ++** protocol. ++** ++** prottype - protocol number (in host order) to search for. ++** ++** Returns: ++** 1 - if the table is empty or a match is found. ++** 0 - if the table is non-empty and a match is not found. ++** ++** Based largely on p80211conv.c of the linux-wlan-ng project ++*/ ++static inline int ++proto_is_stt(unsigned int proto) ++{ ++ /* Always return found for now. This is the behavior used by the */ ++ /* Zoom Win95 driver when 802.1h mode is selected */ ++ /* TODO: If necessary, add an actual search we'll probably ++ need this to match the CMAC's way of doing things. ++ Need to do some testing to confirm. ++ */ ++ ++ if (proto == 0x80f3) /* APPLETALK */ ++ return 1; ++ ++ return 0; ++/* return ((prottype == ETH_P_AARP) || (prottype == ETH_P_IPX)); */ ++} ++ ++/* Helpers */ ++ ++static inline void ++store_llc_snap(struct wlan_llc *llc) ++{ ++ llc->dsap = 0xaa; /* SNAP, see IEEE 802 */ ++ llc->ssap = 0xaa; ++ llc->ctl = 0x03; ++} ++static inline int ++llc_is_snap(const struct wlan_llc *llc) ++{ ++ return (llc->dsap == 0xaa) ++ && (llc->ssap == 0xaa) ++ && (llc->ctl == 0x03); ++} ++static inline void ++store_oui_rfc1042(struct wlan_snap *snap) ++{ ++ snap->oui[0] = 0; ++ snap->oui[1] = 0; ++ snap->oui[2] = 0; ++} ++static inline int ++oui_is_rfc1042(const struct wlan_snap *snap) ++{ ++ return (snap->oui[0] == 0) ++ && (snap->oui[1] == 0) ++ && (snap->oui[2] == 0); ++} ++static inline void ++store_oui_8021h(struct wlan_snap *snap) ++{ ++ snap->oui[0] = 0; ++ snap->oui[1] = 0; ++ snap->oui[2] = 0xf8; ++} ++static inline int ++oui_is_8021h(const struct wlan_snap *snap) ++{ ++ return (snap->oui[0] == 0) ++ && (snap->oui[1] == 0) ++ && (snap->oui[2] == 0xf8); ++} ++ ++ ++/*********************************************************************** ++** acx_ether_to_txbuf ++** ++** Uses the contents of the ether frame to build the elements of ++** the 802.11 frame. ++** ++** We don't actually set up the frame header here. That's the ++** MAC's job. We're only handling conversion of DIXII or 802.3+LLC ++** frames to something that works with 802.11. ++** ++** Based largely on p80211conv.c of the linux-wlan-ng project ++*/ ++int ++acx_ether_to_txbuf(acx_device_t *adev, void *txbuf, const struct sk_buff *skb) ++{ ++ struct wlan_hdr_a3 *w_hdr; ++ struct wlan_ethhdr *e_hdr; ++ struct wlan_llc *e_llc; ++ struct wlan_snap *e_snap; ++ const u8 *a1, *a3; ++ int header_len, payload_len = -1; ++ /* protocol type or data length, depending on whether ++ * DIX or 802.3 ethernet format */ ++ u16 proto; ++ u16 fc; ++ ++ FN_ENTER; ++ ++ if (unlikely(!skb->len)) { ++ log(L_DEBUG, "zero-length skb!\n"); ++ goto end; ++ } ++ ++ w_hdr = (struct wlan_hdr_a3*)txbuf; ++ ++ switch (adev->mode) { ++ case ACX_MODE_MONITOR: ++ /* NB: one day we might want to play with DESC_CTL2_FCS ++ ** Will need to stop doing "- WLAN_FCS_LEN" here then */ ++ if (unlikely(skb->len >= WLAN_A4FR_MAXLEN_WEP_FCS - WLAN_FCS_LEN)) { ++ printk("%s: can't tx oversized frame (%d bytes)\n", ++ adev->ndev->name, skb->len); ++ goto end; ++ } ++ memcpy(w_hdr, skb->data, skb->len); ++ payload_len = skb->len; ++ goto end; ++ } ++ ++ /* step 1: classify ether frame, DIX or 802.3? */ ++ e_hdr = (wlan_ethhdr_t *)skb->data; ++ proto = ntohs(e_hdr->type); ++ if (proto <= 1500) { ++ log(L_DEBUG, "tx: 802.3 len: %d\n", skb->len); ++ /* codes <= 1500 reserved for 802.3 lengths */ ++ /* it's 802.3, pass ether payload unchanged, */ ++ /* trim off ethernet header and copy payload to txdesc */ ++ header_len = WLAN_HDR_A3_LEN; ++ } else { ++ /* it's DIXII, time for some conversion */ ++ /* Create 802.11 packet. Header also contains llc and snap. */ ++ ++ log(L_DEBUG, "tx: DIXII len: %d\n", skb->len); ++ ++ /* size of header is 802.11 header + llc + snap */ ++ header_len = WLAN_HDR_A3_LEN + sizeof(wlan_llc_t) + sizeof(wlan_snap_t); ++ /* llc is located behind the 802.11 header */ ++ e_llc = (wlan_llc_t*)(w_hdr + 1); ++ /* snap is located behind the llc */ ++ e_snap = (wlan_snap_t*)(e_llc + 1); ++ ++ /* setup the LLC header */ ++ store_llc_snap(e_llc); ++ ++ /* setup the SNAP header */ ++ e_snap->type = htons(proto); ++ if (proto_is_stt(proto)) { ++ store_oui_8021h(e_snap); ++ } else { ++ store_oui_rfc1042(e_snap); ++ } ++ } ++ /* trim off ethernet header and copy payload to txbuf */ ++ payload_len = skb->len - sizeof(wlan_ethhdr_t); ++ /* TODO: can we just let acx DMA payload from skb instead? */ ++ memcpy((u8*)txbuf + header_len, skb->data + sizeof(wlan_ethhdr_t), payload_len); ++ payload_len += header_len; ++ ++ /* Set up the 802.11 header */ ++ switch (adev->mode) { ++ case ACX_MODE_0_ADHOC: ++ fc = (WF_FTYPE_DATAi | WF_FSTYPE_DATAONLYi); ++ a1 = e_hdr->daddr; ++ a3 = adev->bssid; ++ break; ++ case ACX_MODE_2_STA: ++ fc = (WF_FTYPE_DATAi | WF_FSTYPE_DATAONLYi | WF_FC_TODSi); ++ a1 = adev->bssid; ++ a3 = e_hdr->daddr; ++ break; ++ case ACX_MODE_3_AP: ++ fc = (WF_FTYPE_DATAi | WF_FSTYPE_DATAONLYi | WF_FC_FROMDSi); ++ a1 = e_hdr->daddr; ++ a3 = e_hdr->saddr; ++ break; ++ default: ++ printk("%s: error - converting eth to wlan in unknown mode\n", ++ adev->ndev->name); ++ payload_len = -1; ++ goto end; ++ } ++ if (adev->wep_enabled) ++ SET_BIT(fc, WF_FC_ISWEPi); ++ ++ w_hdr->fc = fc; ++ w_hdr->dur = 0; ++ MAC_COPY(w_hdr->a1, a1); ++ MAC_COPY(w_hdr->a2, adev->dev_addr); ++ MAC_COPY(w_hdr->a3, a3); ++ w_hdr->seq = 0; ++ ++#ifdef DEBUG_CONVERT ++ if (acx_debug & L_DATA) { ++ printk("original eth frame [%d]: ", skb->len); ++ acx_dump_bytes(skb->data, skb->len); ++ printk("802.11 frame [%d]: ", payload_len); ++ acx_dump_bytes(w_hdr, payload_len); ++ } ++#endif ++ ++end: ++ FN_EXIT1(payload_len); ++ return payload_len; ++} ++ ++ ++/*********************************************************************** ++** acx_rxbuf_to_ether ++** ++** Uses the contents of a received 802.11 frame to build an ether ++** frame. ++** ++** This function extracts the src and dest address from the 802.11 ++** frame to use in the construction of the eth frame. ++** ++** Based largely on p80211conv.c of the linux-wlan-ng project ++*/ ++struct sk_buff* ++acx_rxbuf_to_ether(acx_device_t *adev, rxbuffer_t *rxbuf) ++{ ++ struct wlan_hdr *w_hdr; ++ struct wlan_ethhdr *e_hdr; ++ struct wlan_llc *e_llc; ++ struct wlan_snap *e_snap; ++ struct sk_buff *skb; ++ const u8 *daddr; ++ const u8 *saddr; ++ const u8 *e_payload; ++ int buflen, payload_length; ++ unsigned int payload_offset, mtu; ++ u16 fc; ++ ++ FN_ENTER; ++ ++ /* This looks complex because it must handle possible ++ ** phy header in rxbuff */ ++ w_hdr = acx_get_wlan_hdr(adev, rxbuf); ++ payload_offset = WLAN_HDR_A3_LEN; /* it is relative to w_hdr */ ++ payload_length = RXBUF_BYTES_USED(rxbuf) /* entire rxbuff... */ ++ - ((u8*)w_hdr - (u8*)rxbuf) /* minus space before 802.11 frame */ ++ - WLAN_HDR_A3_LEN; /* minus 802.11 header */ ++ ++ /* setup some vars for convenience */ ++ fc = w_hdr->fc; ++ switch (WF_FC_FROMTODSi & fc) { ++ case 0: ++ daddr = w_hdr->a1; ++ saddr = w_hdr->a2; ++ break; ++ case WF_FC_FROMDSi: ++ daddr = w_hdr->a1; ++ saddr = w_hdr->a3; ++ break; ++ case WF_FC_TODSi: ++ daddr = w_hdr->a3; ++ saddr = w_hdr->a2; ++ break; ++ default: /* WF_FC_FROMTODSi */ ++ payload_offset += (WLAN_HDR_A4_LEN - WLAN_HDR_A3_LEN); ++ payload_length -= (WLAN_HDR_A4_LEN - WLAN_HDR_A3_LEN); ++ daddr = w_hdr->a3; ++ saddr = w_hdr->a4; ++ } ++ ++ if ((WF_FC_ISWEPi & fc) && IS_ACX100(adev)) { ++ /* chop off the IV+ICV WEP header and footer */ ++ log(L_DATA|L_DEBUG, "rx: WEP packet, " ++ "chopping off IV and ICV\n"); ++ payload_offset += WLAN_WEP_IV_LEN; ++ payload_length -= WLAN_WEP_IV_LEN + WLAN_WEP_ICV_LEN; ++ } ++ ++ if (unlikely(payload_length < 0)) { ++ printk("%s: rx frame too short, ignored\n", adev->ndev->name); ++ goto ret_null; ++ } ++ ++ e_hdr = (wlan_ethhdr_t*) ((u8*) w_hdr + payload_offset); ++ e_llc = (wlan_llc_t*) e_hdr; ++ e_snap = (wlan_snap_t*) (e_llc + 1); ++ mtu = adev->ndev->mtu; ++ e_payload = (u8*) (e_snap + 1); ++ ++ log(L_DATA, "rx: payload_offset %d, payload_length %d\n", ++ payload_offset, payload_length); ++ log(L_XFER|L_DATA, ++ "rx: frame info: llc=%02X%02X%02X " ++ "snap.oui=%02X%02X%02X snap.type=%04X\n", ++ e_llc->dsap, e_llc->ssap, e_llc->ctl, ++ e_snap->oui[0], e_snap->oui[1], e_snap->oui[2], ++ ntohs(e_snap->type)); ++ ++ /* Test for the various encodings */ ++ if ((payload_length >= sizeof(wlan_ethhdr_t)) ++ && ((e_llc->dsap != 0xaa) || (e_llc->ssap != 0xaa)) ++ && ( (mac_is_equal(daddr, e_hdr->daddr)) ++ || (mac_is_equal(saddr, e_hdr->saddr)) ++ ) ++ ) { ++ /* 802.3 Encapsulated: */ ++ /* wlan frame body contains complete eth frame (header+body) */ ++ log(L_DEBUG|L_DATA, "rx: 802.3 ENCAP len=%d\n", payload_length); ++ ++ if (unlikely(payload_length > (mtu + ETH_HLEN))) { ++ printk("%s: rx: ENCAP frame too large (%d > %d)\n", ++ adev->ndev->name, ++ payload_length, mtu + ETH_HLEN); ++ goto ret_null; ++ } ++ ++ /* allocate space and setup host buffer */ ++ buflen = payload_length; ++ /* Attempt to align IP header (14 bytes eth header + 2 = 16) */ ++ skb = dev_alloc_skb(buflen + 2); ++ if (unlikely(!skb)) ++ goto no_skb; ++ skb_reserve(skb, 2); ++ skb_put(skb, buflen); /* make room */ ++ ++ /* now copy the data from the 80211 frame */ ++ memcpy(skb->data, e_hdr, payload_length); ++ ++ } else if ( (payload_length >= sizeof(wlan_llc_t)+sizeof(wlan_snap_t)) ++ && llc_is_snap(e_llc) ) { ++ /* wlan frame body contains: AA AA 03 ... (it's a SNAP) */ ++ ++ if ( !oui_is_rfc1042(e_snap) ++ || (proto_is_stt(ieee2host16(e_snap->type)) /* && (ethconv == WLAN_ETHCONV_8021h) */)) { ++ log(L_DEBUG|L_DATA, "rx: SNAP+RFC1042 len=%d\n", payload_length); ++ /* wlan frame body contains: AA AA 03 !(00 00 00) ... -or- */ ++ /* wlan frame body contains: AA AA 03 00 00 00 0x80f3 ... */ ++ /* build eth hdr, type = len, copy AA AA 03... as eth body */ ++ /* it's a SNAP + RFC1042 frame && protocol is in STT */ ++ ++ if (unlikely(payload_length > mtu)) { ++ printk("%s: rx: SNAP frame too large (%d > %d)\n", ++ adev->ndev->name, ++ payload_length, mtu); ++ goto ret_null; ++ } ++ ++ /* allocate space and setup host buffer */ ++ buflen = payload_length + ETH_HLEN; ++ skb = dev_alloc_skb(buflen + 2); ++ if (unlikely(!skb)) ++ goto no_skb; ++ skb_reserve(skb, 2); ++ skb_put(skb, buflen); /* make room */ ++ ++ /* create 802.3 header */ ++ e_hdr = (wlan_ethhdr_t*) skb->data; ++ MAC_COPY(e_hdr->daddr, daddr); ++ MAC_COPY(e_hdr->saddr, saddr); ++ e_hdr->type = htons(payload_length); ++ ++ /* Now copy the data from the 80211 frame. ++ Make room in front for the eth header, and keep the ++ llc and snap from the 802.11 payload */ ++ memcpy(skb->data + ETH_HLEN, ++ e_llc, payload_length); ++ ++ } else { ++ /* wlan frame body contains: AA AA 03 00 00 00 [type] [tail] */ ++ /* build eth hdr, type=[type], copy [tail] as eth body */ ++ log(L_DEBUG|L_DATA, "rx: 802.1h/RFC1042 len=%d\n", ++ payload_length); ++ /* it's an 802.1h frame (an RFC1042 && protocol is not in STT) */ ++ /* build a DIXII + RFC894 */ ++ ++ payload_length -= sizeof(wlan_llc_t) + sizeof(wlan_snap_t); ++ if (unlikely(payload_length > mtu)) { ++ printk("%s: rx: DIXII frame too large (%d > %d)\n", ++ adev->ndev->name, ++ payload_length, mtu); ++ goto ret_null; ++ } ++ ++ /* allocate space and setup host buffer */ ++ buflen = payload_length + ETH_HLEN; ++ skb = dev_alloc_skb(buflen + 2); ++ if (unlikely(!skb)) ++ goto no_skb; ++ skb_reserve(skb, 2); ++ skb_put(skb, buflen); /* make room */ ++ ++ /* create 802.3 header */ ++ e_hdr = (wlan_ethhdr_t *) skb->data; ++ MAC_COPY(e_hdr->daddr, daddr); ++ MAC_COPY(e_hdr->saddr, saddr); ++ e_hdr->type = e_snap->type; ++ ++ /* Now copy the data from the 80211 frame. ++ Make room in front for the eth header, and cut off the ++ llc and snap from the 802.11 payload */ ++ memcpy(skb->data + ETH_HLEN, ++ e_payload, payload_length); ++ } ++ ++ } else { ++ log(L_DEBUG|L_DATA, "rx: NON-ENCAP len=%d\n", payload_length); ++ /* build eth hdr, type=len, copy wlan body as eth body */ ++ /* any NON-ENCAP */ ++ /* it's a generic 80211+LLC or IPX 'Raw 802.3' */ ++ /* build an 802.3 frame */ ++ ++ if (unlikely(payload_length > mtu)) { ++ printk("%s: rx: OTHER frame too large (%d > %d)\n", ++ adev->ndev->name, payload_length, mtu); ++ goto ret_null; ++ } ++ ++ /* allocate space and setup host buffer */ ++ buflen = payload_length + ETH_HLEN; ++ skb = dev_alloc_skb(buflen + 2); ++ if (unlikely(!skb)) ++ goto no_skb; ++ skb_reserve(skb, 2); ++ skb_put(skb, buflen); /* make room */ ++ ++ /* set up the 802.3 header */ ++ e_hdr = (wlan_ethhdr_t *) skb->data; ++ MAC_COPY(e_hdr->daddr, daddr); ++ MAC_COPY(e_hdr->saddr, saddr); ++ e_hdr->type = htons(payload_length); ++ ++ /* now copy the data from the 80211 frame */ ++ memcpy(skb->data + ETH_HLEN, e_llc, payload_length); ++ } ++ ++ skb->dev = adev->ndev; ++ skb->protocol = eth_type_trans(skb, adev->ndev); ++ ++#ifdef DEBUG_CONVERT ++ if (acx_debug & L_DATA) { ++ int len = RXBUF_BYTES_RCVD(adev, rxbuf); ++ printk("p802.11 frame [%d]: ", len); ++ acx_dump_bytes(w_hdr, len); ++ printk("eth frame [%d]: ", skb->len); ++ acx_dump_bytes(skb->data, skb->len); ++ } ++#endif ++ ++ FN_EXIT0; ++ return skb; ++ ++no_skb: ++ printk("%s: rx: no memory for skb (%d bytes)\n", ++ adev->ndev->name, buflen + 2); ++ret_null: ++ FN_EXIT1((int)NULL); ++ return NULL; ++} +Index: linux-2.6.23/drivers/net/wireless/acx/cs.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.23/drivers/net/wireless/acx/cs.c 2008-01-20 21:13:40.000000000 +0000 +@@ -0,0 +1,5703 @@ ++/*********************************************************************** ++** Copyright (C) 2003 ACX100 Open Source Project ++** ++** The contents of this file are subject to the Mozilla Public ++** License Version 1.1 (the "License"); you may not use this file ++** except in compliance with the License. You may obtain a copy of ++** the License at http://www.mozilla.org/MPL/ ++** ++** Software distributed under the License is distributed on an "AS ++** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or ++** implied. See the License for the specific language governing ++** rights and limitations under the License. ++** ++** Alternatively, the contents of this file may be used under the ++** terms of the GNU Public License version 2 (the "GPL"), in which ++** case the provisions of the GPL are applicable instead of the ++** above. If you wish to allow the use of your version of this file ++** only under the terms of the GPL and not to allow others to use ++** your version of this file under the MPL, indicate your decision ++** by deleting the provisions above and replace them with the notice ++** and other provisions required by the GPL. If you do not delete ++** the provisions above, a recipient may use your version of this ++** file under either the MPL or the GPL. ++** --------------------------------------------------------------------- ++** Inquiries regarding the ACX100 Open Source Project can be ++** made directly to: ++** ++** acx100-users@lists.sf.net ++** http://acx100.sf.net ++** --------------------------------------------------------------------- ++** ++** Slave memory interface support: ++** ++** Todd Blumer - SDG Systems ++** Bill Reese - HP ++** Eric McCorkle - Shadowsun ++** ++** CF support, (c) Fabrice Crohas, Paul Sokolovsky ++*/ ++#define ACX_MEM 1 ++ ++/* ++ * non-zero makes it dump the ACX memory to the console then ++ * panic when you cat /proc/driver/acx_wlan0_diag ++ */ ++#define DUMP_MEM_DEFINED 1 ++ ++#define DUMP_MEM_DURING_DIAG 0 ++#define DUMP_IF_SLOW 0 ++ ++#define PATCH_AROUND_BAD_SPOTS 1 ++#define HX4700_FIRMWARE_CHECKSUM 0x0036862e ++#define HX4700_ALTERNATE_FIRMWARE_CHECKSUM 0x00368a75 ++ ++#include <linux/version.h> ++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) ++#include <linux/config.h> ++#endif ++ ++/* Linux 2.6.18+ uses <linux/utsrelease.h> */ ++#ifndef UTS_RELEASE ++#include <linux/utsrelease.h> ++#endif ++ ++#include <linux/compiler.h> /* required for Lx 2.6.8 ?? */ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/sched.h> ++#include <linux/types.h> ++#include <linux/skbuff.h> ++#include <linux/slab.h> ++#include <linux/if_arp.h> ++#include <linux/irq.h> ++#include <linux/rtnetlink.h> ++#include <linux/wireless.h> ++#include <net/iw_handler.h> ++#include <linux/netdevice.h> ++#include <linux/ioport.h> ++#include <linux/pci.h> ++#include <linux/platform_device.h> ++#include <linux/pm.h> ++#include <linux/vmalloc.h> ++#include <linux/delay.h> ++#include <linux/workqueue.h> ++#include <linux/inetdevice.h> ++ ++#define PCMCIA_DEBUG 1 ++ ++/* ++ All the PCMCIA modules use PCMCIA_DEBUG to control debugging. If ++ you do not define PCMCIA_DEBUG at all, all the debug code will be ++ left out. If you compile with PCMCIA_DEBUG=0, the debug code will ++ be present but disabled -- but it can then be enabled for specific ++ modules at load time with a 'pc_debug=#' option to insmod. ++ ++*/ ++#include <pcmcia/cs_types.h> ++#include <pcmcia/cs.h> ++#include <pcmcia/cistpl.h> ++#include <pcmcia/cisreg.h> ++#include <pcmcia/ds.h> ++#include "acx.h" ++#include "acx_hw.h" ++ ++#ifdef PCMCIA_DEBUG ++static int pc_debug = PCMCIA_DEBUG; ++module_param(pc_debug, int, 0); ++static char *version = "$Revision: 1.10 $"; ++#define DEBUG(n, args...) if (pc_debug>(n)) printk(KERN_DEBUG args); ++#else ++#define DEBUG(n, args...) ++#endif ++ ++ ++static win_req_t memwin; ++ ++typedef struct local_info_t { ++ dev_node_t node; ++ struct net_device *ndev; ++} local_info_t; ++ ++static struct net_device *resume_ndev; ++ ++ ++/*********************************************************************** ++*/ ++ ++#define CARD_EEPROM_ID_SIZE 6 ++ ++#include <asm/io.h> ++ ++#define REG_ACX_VENDOR_ID 0x900 ++/* ++ * This is the vendor id on the HX4700, anyway ++ */ ++#define ACX_VENDOR_ID 0x8400104c ++ ++typedef enum { ++ ACX_SOFT_RESET = 0, ++ ++ ACX_SLV_REG_ADDR, ++ ACX_SLV_REG_DATA, ++ ACX_SLV_REG_ADATA, ++ ++ ACX_SLV_MEM_CP, ++ ACX_SLV_MEM_ADDR, ++ ACX_SLV_MEM_DATA, ++ ACX_SLV_MEM_CTL, ++} acxreg_t; ++ ++/*********************************************************************** ++*/ ++static void acxmem_i_tx_timeout(struct net_device *ndev); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) ++static irqreturn_t acxmem_i_interrupt(int irq, void *dev_id); ++#else ++static irqreturn_t acxmem_i_interrupt(int irq, void *dev_id, struct pt_regs *regs); ++#endif ++static void acxmem_i_set_multicast_list(struct net_device *ndev); ++ ++static int acxmem_e_open(struct net_device *ndev); ++static int acxmem_e_close(struct net_device *ndev); ++static void acxmem_s_up(struct net_device *ndev); ++static void acxmem_s_down(struct net_device *ndev); ++ ++static void dump_acxmem (acx_device_t *adev, u32 start, int length); ++static int acxmem_complete_hw_reset (acx_device_t *adev); ++static void acxmem_s_delete_dma_regions(acx_device_t *adev); ++ ++static int ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) ++acxmem_e_suspend( struct net_device *ndev, pm_message_t state); ++#else ++acxmem_e_suspend( struct net_device *ndev, u32 state); ++#endif ++static void ++fw_resumer(struct work_struct *notused); ++//fw_resumer( void *data ); ++ ++static int acx_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) ++{ ++ struct net_device *ndev = ptr; ++ acx_device_t *adev = ndev2adev(ndev); ++ ++ /* ++ * Upper level ioctl() handlers send a NETDEV_CHANGEADDR if the MAC address changes. ++ */ ++ ++ if (NETDEV_CHANGEADDR == event) { ++ /* ++ * the upper layers put the new MAC address in ndev->dev_addr; we just copy ++ * it over and update the ACX with it. ++ */ ++ MAC_COPY(adev->dev_addr, adev->ndev->dev_addr); ++ adev->set_mask |= GETSET_STATION_ID; ++ acx_s_update_card_settings (adev); ++ } ++ ++ return 0; ++} ++ ++static struct notifier_block acx_netdev_notifier = { ++ .notifier_call = acx_netdev_event, ++}; ++ ++/*********************************************************************** ++** Register access ++*/ ++ ++/* Pick one */ ++/* #define INLINE_IO static */ ++#define INLINE_IO static inline ++ ++INLINE_IO u32 ++read_id_register (acx_device_t *adev) ++{ ++ writel (0x24, &adev->iobase[ACX_SLV_REG_ADDR]); ++ return readl (&adev->iobase[ACX_SLV_REG_DATA]); ++} ++ ++INLINE_IO u32 ++read_reg32(acx_device_t *adev, unsigned int offset) ++{ ++ u32 val; ++ u32 addr; ++ ++ if (offset > IO_ACX_ECPU_CTRL) ++ addr = offset; ++ else ++ addr = adev->io[offset]; ++ ++ if (addr < 0x20) { ++ return readl(((u8*)adev->iobase) + addr); ++ } ++ ++ writel( addr, &adev->iobase[ACX_SLV_REG_ADDR] ); ++ val = readl( &adev->iobase[ACX_SLV_REG_DATA] ); ++ ++ return val; ++} ++ ++INLINE_IO u16 ++read_reg16(acx_device_t *adev, unsigned int offset) ++{ ++ u16 lo; ++ u32 addr; ++ ++ if (offset > IO_ACX_ECPU_CTRL) ++ addr = offset; ++ else ++ addr = adev->io[offset]; ++ ++ if (addr < 0x20) { ++ return readw(((u8 *) adev->iobase) + addr); ++ } ++ ++ writel( addr, &adev->iobase[ACX_SLV_REG_ADDR] ); ++ lo = readw( (u16 *)&adev->iobase[ACX_SLV_REG_DATA] ); ++ ++ return lo; ++} ++ ++INLINE_IO u8 ++read_reg8(acx_device_t *adev, unsigned int offset) ++{ ++ u8 lo; ++ u32 addr; ++ ++ if (offset > IO_ACX_ECPU_CTRL) ++ addr = offset; ++ else ++ addr = adev->io[offset]; ++ ++ if (addr < 0x20) ++ return readb(((u8 *)adev->iobase) + addr); ++ ++ writel( addr, &adev->iobase[ACX_SLV_REG_ADDR] ); ++ lo = readw( (u8 *)&adev->iobase[ACX_SLV_REG_DATA] ); ++ ++ return (u8)lo; ++} ++ ++INLINE_IO void ++write_reg32(acx_device_t *adev, unsigned int offset, u32 val) ++{ ++ u32 addr; ++ ++ if (offset > IO_ACX_ECPU_CTRL) ++ addr = offset; ++ else ++ addr = adev->io[offset]; ++ ++ if (addr < 0x20) { ++ writel(val, ((u8*)adev->iobase) + addr); ++ return; ++ } ++ ++ writel( addr, &adev->iobase[ACX_SLV_REG_ADDR] ); ++ writel( val, &adev->iobase[ACX_SLV_REG_DATA] ); ++} ++ ++INLINE_IO void ++write_reg16(acx_device_t *adev, unsigned int offset, u16 val) ++{ ++ u32 addr; ++ ++ if (offset > IO_ACX_ECPU_CTRL) ++ addr = offset; ++ else ++ addr = adev->io[offset]; ++ ++ if (addr < 0x20) { ++ writew(val, ((u8 *)adev->iobase) + addr); ++ return; ++ } ++ writel( addr, &adev->iobase[ACX_SLV_REG_ADDR] ); ++ writew( val, (u16 *) &adev->iobase[ACX_SLV_REG_DATA] ); ++} ++ ++INLINE_IO void ++write_reg8(acx_device_t *adev, unsigned int offset, u8 val) ++{ ++ u32 addr; ++ ++ if (offset > IO_ACX_ECPU_CTRL) ++ addr = offset; ++ else ++ addr = adev->io[offset]; ++ ++ if (addr < 0x20) { ++ writeb(val, ((u8 *) adev->iobase) + addr); ++ return; ++ } ++ writel( addr, &adev->iobase[ACX_SLV_REG_ADDR] ); ++ writeb( val, (u8 *)&adev->iobase[ACX_SLV_REG_DATA] ); ++} ++ ++/* Handle PCI posting properly: ++ * Make sure that writes reach the adapter in case they require to be executed ++ * *before* the next write, by reading a random (and safely accessible) register. ++ * This call has to be made if there is no read following (which would flush the data ++ * to the adapter), yet the written data has to reach the adapter immediately. */ ++INLINE_IO void ++write_flush(acx_device_t *adev) ++{ ++ /* readb(adev->iobase + adev->io[IO_ACX_INFO_MAILBOX_OFFS]); */ ++ /* faster version (accesses the first register, IO_ACX_SOFT_RESET, ++ * which should also be safe): */ ++ (void) readl(adev->iobase); ++} ++ ++INLINE_IO void ++set_regbits (acx_device_t *adev, unsigned int offset, u32 bits) { ++ u32 tmp; ++ ++ tmp = read_reg32 (adev, offset); ++ tmp = tmp | bits; ++ write_reg32 (adev, offset, tmp); ++ write_flush (adev); ++} ++ ++INLINE_IO void ++clear_regbits (acx_device_t *adev, unsigned int offset, u32 bits) { ++ u32 tmp; ++ ++ tmp = read_reg32 (adev, offset); ++ tmp = tmp & ~bits; ++ write_reg32 (adev, offset, tmp); ++ write_flush (adev); ++} ++ ++/* ++ * Copy from PXA memory to the ACX memory. This assumes both the PXA and ACX ++ * addresses are 32 bit aligned. Count is in bytes. ++ */ ++INLINE_IO void ++write_slavemem32 (acx_device_t *adev, u32 slave_address, u32 val) ++{ ++ write_reg32 (adev, IO_ACX_SLV_MEM_CTL, 0x0); ++ write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, slave_address); ++ udelay (10); ++ write_reg32 (adev, IO_ACX_SLV_MEM_DATA, val); ++} ++ ++INLINE_IO u32 ++read_slavemem32 (acx_device_t *adev, u32 slave_address) ++{ ++ u32 val; ++ ++ write_reg32 (adev, IO_ACX_SLV_MEM_CTL, 0x0); ++ write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, slave_address); ++ udelay (10); ++ val = read_reg32 (adev, IO_ACX_SLV_MEM_DATA); ++ ++ return val; ++} ++ ++INLINE_IO void ++write_slavemem8 (acx_device_t *adev, u32 slave_address, u8 val) ++{ ++ u32 data; ++ u32 base; ++ int offset; ++ ++ /* ++ * Get the word containing the target address and the byte offset in that word. ++ */ ++ base = slave_address & ~3; ++ offset = (slave_address & 3) * 8; ++ ++ data = read_slavemem32 (adev, base); ++ data &= ~(0xff << offset); ++ data |= val << offset; ++ write_slavemem32 (adev, base, data); ++} ++ ++INLINE_IO u8 ++read_slavemem8 (acx_device_t *adev, u32 slave_address) ++{ ++ u8 val; ++ u32 base; ++ u32 data; ++ int offset; ++ ++ base = slave_address & ~3; ++ offset = (slave_address & 3) * 8; ++ ++ data = read_slavemem32 (adev, base); ++ ++ val = (data >> offset) & 0xff; ++ ++ return val; ++} ++ ++/* ++ * doesn't split across word boundaries ++ */ ++INLINE_IO void ++write_slavemem16 (acx_device_t *adev, u32 slave_address, u16 val) ++{ ++ u32 data; ++ u32 base; ++ int offset; ++ ++ /* ++ * Get the word containing the target address and the byte offset in that word. ++ */ ++ base = slave_address & ~3; ++ offset = (slave_address & 3) * 8; ++ ++ data = read_slavemem32 (adev, base); ++ data &= ~(0xffff << offset); ++ data |= val << offset; ++ write_slavemem32 (adev, base, data); ++} ++ ++/* ++ * doesn't split across word boundaries ++ */ ++INLINE_IO u16 ++read_slavemem16 (acx_device_t *adev, u32 slave_address) ++{ ++ u16 val; ++ u32 base; ++ u32 data; ++ int offset; ++ ++ base = slave_address & ~3; ++ offset = (slave_address & 3) * 8; ++ ++ data = read_slavemem32 (adev, base); ++ ++ val = (data >> offset) & 0xffff; ++ ++ return val; ++} ++ ++/* ++ * Copy from slave memory ++ * ++ * TODO - rewrite using address autoincrement, handle partial words ++ */ ++void ++copy_from_slavemem (acx_device_t *adev, u8 *destination, u32 source, int count) { ++ u32 tmp = 0; ++ u8 *ptmp = (u8 *) &tmp; ++ ++ /* ++ * Right now I'm making the assumption that the destination is aligned, but ++ * I'd better check. ++ */ ++ if ((u32) destination & 3) { ++ printk ("acx copy_from_slavemem: warning! destination not word-aligned!\n"); ++ } ++ ++ while (count >= 4) { ++ write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, source); ++ udelay (10); ++ *((u32 *) destination) = read_reg32 (adev, IO_ACX_SLV_MEM_DATA); ++ count -= 4; ++ source += 4; ++ destination += 4; ++ } ++ ++ /* ++ * If the word reads above didn't satisfy the count, read one more word ++ * and transfer a byte at a time until the request is satisfied. ++ */ ++ if (count) { ++ write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, source); ++ udelay (10); ++ tmp = read_reg32 (adev, IO_ACX_SLV_MEM_DATA); ++ while (count--) { ++ *destination++ = *ptmp++; ++ } ++ } ++} ++ ++/* ++ * Copy to slave memory ++ * ++ * TODO - rewrite using autoincrement, handle partial words ++ */ ++void ++copy_to_slavemem (acx_device_t *adev, u32 destination, u8 *source, int count) ++{ ++ u32 tmp = 0; ++ u8* ptmp = (u8 *) &tmp; ++ static u8 src[512]; /* make static to avoid huge stack objects */ ++ ++ /* ++ * For now, make sure the source is word-aligned by copying it to a word-aligned ++ * buffer. Someday rewrite to avoid the extra copy. ++ */ ++ if (count > sizeof (src)) { ++ printk ("acx copy_to_slavemem: Warning! buffer overflow!\n"); ++ count = sizeof (src); ++ } ++ memcpy (src, source, count); ++ source = src; ++ ++ while (count >= 4) { ++ write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, destination); ++ udelay (10); ++ write_reg32 (adev, IO_ACX_SLV_MEM_DATA, *((u32 *) source)); ++ count -= 4; ++ source += 4; ++ destination += 4; ++ } ++ ++ /* ++ * If there are leftovers read the next word from the acx and merge in ++ * what they want to write. ++ */ ++ if (count) { ++ write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, destination); ++ udelay (10); ++ tmp = read_reg32 (adev, IO_ACX_SLV_MEM_DATA); ++ while (count--) { ++ *ptmp++ = *source++; ++ } ++ /* ++ * reset address in case we're currently in auto-increment mode ++ */ ++ write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, destination); ++ udelay (10); ++ write_reg32 (adev, IO_ACX_SLV_MEM_DATA, tmp); ++ udelay (10); ++ } ++ ++} ++ ++/* ++ * Block copy to slave buffers using memory block chain mode. Copies to the ACX ++ * transmit buffer structure with minimal intervention on our part. ++ * Interrupts should be disabled when calling this. ++ */ ++void ++chaincopy_to_slavemem (acx_device_t *adev, u32 destination, u8 *source, int count) ++{ ++ u32 val; ++ u32 *data = (u32 *) source; ++ static u8 aligned_source[WLAN_A4FR_MAXLEN_WEP_FCS]; ++ ++ /* ++ * Warn if the pointers don't look right. Destination must fit in [23:5] with ++ * zero elsewhere and source should be 32 bit aligned. ++ * This should never happen since we're in control of both, but I want to know about ++ * it if it does. ++ */ ++ if ((destination & 0x00ffffe0) != destination) { ++ printk ("acx chaincopy: destination block 0x%04x not aligned!\n", destination); ++ } ++ if (count > sizeof aligned_source) { ++ printk( KERN_ERR "chaincopy_to_slavemem overflow!\n" ); ++ count = sizeof aligned_source; ++ } ++ if ((u32) source & 3) { ++ memcpy (aligned_source, source, count); ++ data = (u32 *) aligned_source; ++ } ++ ++ /* ++ * SLV_MEM_CTL[17:16] = memory block chain mode with auto-increment ++ * SLV_MEM_CTL[5:2] = offset to data portion = 1 word ++ */ ++ val = 2 << 16 | 1 << 2; ++ writel (val, &adev->iobase[ACX_SLV_MEM_CTL]); ++ ++ /* ++ * SLV_MEM_CP[23:5] = start of 1st block ++ * SLV_MEM_CP[3:2] = offset to memblkptr = 0 ++ */ ++ val = destination & 0x00ffffe0; ++ writel (val, &adev->iobase[ACX_SLV_MEM_CP]); ++ ++ /* ++ * SLV_MEM_ADDR[23:2] = SLV_MEM_CTL[5:2] + SLV_MEM_CP[23:5] ++ */ ++ val = (destination & 0x00ffffe0) + (1<<2); ++ writel (val, &adev->iobase[ACX_SLV_MEM_ADDR]); ++ ++ /* ++ * Write the data to the slave data register, rounding up to the end ++ * of the word containing the last byte (hence the > 0) ++ */ ++ while (count > 0) { ++ writel (*data++, &adev->iobase[ACX_SLV_MEM_DATA]); ++ count -= 4; ++ } ++} ++ ++ ++/* ++ * Block copy from slave buffers using memory block chain mode. Copies from the ACX ++ * receive buffer structures with minimal intervention on our part. ++ * Interrupts should be disabled when calling this. ++ */ ++void ++chaincopy_from_slavemem (acx_device_t *adev, u8 *destination, u32 source, int count) ++{ ++ u32 val; ++ u32 *data = (u32 *) destination; ++ static u8 aligned_destination[WLAN_A4FR_MAXLEN_WEP_FCS]; ++ int saved_count = count; ++ ++ /* ++ * Warn if the pointers don't look right. Destination must fit in [23:5] with ++ * zero elsewhere and source should be 32 bit aligned. ++ * Turns out the network stack sends unaligned things, so fix them before ++ * copying to the ACX. ++ */ ++ if ((source & 0x00ffffe0) != source) { ++ printk ("acx chaincopy: source block 0x%04x not aligned!\n", source); ++ dump_acxmem (adev, 0, 0x10000); ++ } ++ if ((u32) destination & 3) { ++ //printk ("acx chaincopy: data destination not word aligned!\n"); ++ data = (u32 *) aligned_destination; ++ if (count > sizeof aligned_destination) { ++ printk( KERN_ERR "chaincopy_from_slavemem overflow!\n" ); ++ count = sizeof aligned_destination; ++ } ++ } ++ ++ /* ++ * SLV_MEM_CTL[17:16] = memory block chain mode with auto-increment ++ * SLV_MEM_CTL[5:2] = offset to data portion = 1 word ++ */ ++ val = (2 << 16) | (1 << 2); ++ writel (val, &adev->iobase[ACX_SLV_MEM_CTL]); ++ ++ /* ++ * SLV_MEM_CP[23:5] = start of 1st block ++ * SLV_MEM_CP[3:2] = offset to memblkptr = 0 ++ */ ++ val = source & 0x00ffffe0; ++ writel (val, &adev->iobase[ACX_SLV_MEM_CP]); ++ ++ /* ++ * SLV_MEM_ADDR[23:2] = SLV_MEM_CTL[5:2] + SLV_MEM_CP[23:5] ++ */ ++ val = (source & 0x00ffffe0) + (1<<2); ++ writel (val, &adev->iobase[ACX_SLV_MEM_ADDR]); ++ ++ /* ++ * Read the data from the slave data register, rounding up to the end ++ * of the word containing the last byte (hence the > 0) ++ */ ++ while (count > 0) { ++ *data++ = readl (&adev->iobase[ACX_SLV_MEM_DATA]); ++ count -= 4; ++ } ++ ++ /* ++ * If the destination wasn't aligned, we would have saved it in ++ * the aligned buffer, so copy it where it should go. ++ */ ++ if ((u32) destination & 3) { ++ memcpy (destination, aligned_destination, saved_count); ++ } ++} ++ ++char ++printable (char c) ++{ ++ return ((c >= 20) && (c < 127)) ? c : '.'; ++} ++ ++#if DUMP_MEM_DEFINED > 0 ++static void ++dump_acxmem (acx_device_t *adev, u32 start, int length) ++{ ++ int i; ++ u8 buf[16]; ++ ++ while (length > 0) { ++ printk ("%04x ", start); ++ copy_from_slavemem (adev, buf, start, 16); ++ for (i = 0; (i < 16) && (i < length); i++) { ++ printk ("%02x ", buf[i]); ++ } ++ for (i = 0; (i < 16) && (i < length); i++) { ++ printk ("%c", printable (buf[i])); ++ } ++ printk ("\n"); ++ start += 16; ++ length -= 16; ++ } ++} ++#endif ++ ++static void ++enable_acx_irq(acx_device_t *adev); ++static void ++disable_acx_irq(acx_device_t *adev); ++ ++/* ++ * Return an acx pointer to the next transmit data block. ++ */ ++u32 ++allocate_acx_txbuf_space (acx_device_t *adev, int count) { ++ u32 block, next, last_block; ++ int blocks_needed; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&adev->txbuf_lock, flags); ++ /* ++ * Take 4 off the memory block size to account for the reserved word at the start of ++ * the block. ++ */ ++ blocks_needed = count / (adev->memblocksize - 4); ++ if (count % (adev->memblocksize - 4)) ++ blocks_needed++; ++ ++ if (blocks_needed <= adev->acx_txbuf_blocks_free) { ++ /* ++ * Take blocks at the head of the free list. ++ */ ++ last_block = block = adev->acx_txbuf_free; ++ ++ /* ++ * Follow block pointers through the requested number of blocks both to ++ * find the new head of the free list and to set the flags for the blocks ++ * appropriately. ++ */ ++ while (blocks_needed--) { ++ /* ++ * Keep track of the last block of the allocation ++ */ ++ last_block = adev->acx_txbuf_free; ++ ++ /* ++ * Make sure the end control flag is not set. ++ */ ++ next = read_slavemem32 (adev, adev->acx_txbuf_free) & 0x7ffff; ++ write_slavemem32 (adev, adev->acx_txbuf_free, next); ++ ++ /* ++ * Update the new head of the free list ++ */ ++ adev->acx_txbuf_free = next << 5; ++ adev->acx_txbuf_blocks_free--; ++ ++ } ++ ++ /* ++ * Flag the last block both by clearing out the next pointer ++ * and marking the control field. ++ */ ++ write_slavemem32 (adev, last_block, 0x02000000); ++ ++ /* ++ * If we're out of buffers make sure the free list pointer is NULL ++ */ ++ if (!adev->acx_txbuf_blocks_free) { ++ adev->acx_txbuf_free = 0; ++ } ++ } ++ else { ++ block = 0; ++ } ++ spin_unlock_irqrestore (&adev->txbuf_lock, flags); ++ return block; ++} ++ ++/* ++ * Return buffer space back to the pool by following the next pointers until we find ++ * the block marked as the end. Point the last block to the head of the free list, ++ * then update the head of the free list to point to the newly freed memory. ++ * This routine gets called in interrupt context, so it shouldn't block to protect ++ * the integrity of the linked list. The ISR already holds the lock. ++ */ ++void ++reclaim_acx_txbuf_space (acx_device_t *adev, u32 blockptr) { ++ u32 cur, last, next; ++ unsigned long flags; ++ ++ spin_lock_irqsave (&adev->txbuf_lock, flags); ++ if ((blockptr >= adev->acx_txbuf_start) && ++ (blockptr <= adev->acx_txbuf_start + ++ (adev->acx_txbuf_numblocks - 1) * adev->memblocksize)) { ++ cur = blockptr; ++ do { ++ last = cur; ++ next = read_slavemem32 (adev, cur); ++ ++ /* ++ * Advance to the next block in this allocation ++ */ ++ cur = (next & 0x7ffff) << 5; ++ ++ /* ++ * This block now counts as free. ++ */ ++ adev->acx_txbuf_blocks_free++; ++ } while (!(next & 0x02000000)); ++ ++ /* ++ * last now points to the last block of that allocation. Update the pointer ++ * in that block to point to the free list and reset the free list to the ++ * first block of the free call. If there were no free blocks, make sure ++ * the new end of the list marks itself as truly the end. ++ */ ++ if (adev->acx_txbuf_free) { ++ write_slavemem32 (adev, last, adev->acx_txbuf_free >> 5); ++ } ++ else { ++ write_slavemem32 (adev, last, 0x02000000); ++ } ++ adev->acx_txbuf_free = blockptr; ++ } ++ spin_unlock_irqrestore(&adev->txbuf_lock, flags); ++} ++ ++/* ++ * Initialize the pieces managing the transmit buffer pool on the ACX. The transmit ++ * buffer is a circular queue with one 32 bit word reserved at the beginning of each ++ * block. The upper 13 bits are a control field, of which only 0x02000000 has any ++ * meaning. The lower 19 bits are the address of the next block divided by 32. ++ */ ++void ++init_acx_txbuf (acx_device_t *adev) { ++ ++ /* ++ * acx100_s_init_memory_pools set up txbuf_start and txbuf_numblocks for us. ++ * All we need to do is reset the rest of the bookeeping. ++ */ ++ ++ adev->acx_txbuf_free = adev->acx_txbuf_start; ++ adev->acx_txbuf_blocks_free = adev->acx_txbuf_numblocks; ++ ++ /* ++ * Initialization leaves the last transmit pool block without a pointer back to ++ * the head of the list, but marked as the end of the list. That's how we want ++ * to see it, too, so leave it alone. This is only ever called after a firmware ++ * reset, so the ACX memory is in the state we want. ++ */ ++ ++} ++ ++INLINE_IO int ++adev_present(acx_device_t *adev) ++{ ++ /* fast version (accesses the first register, IO_ACX_SOFT_RESET, ++ * which should be safe): */ ++ return readl(adev->iobase) != 0xffffffff; ++} ++ ++/*********************************************************************** ++*/ ++static inline txdesc_t* ++get_txdesc(acx_device_t *adev, int index) ++{ ++ return (txdesc_t*) (((u8*)adev->txdesc_start) + index * adev->txdesc_size); ++} ++ ++static inline txdesc_t* ++advance_txdesc(acx_device_t *adev, txdesc_t* txdesc, int inc) ++{ ++ return (txdesc_t*) (((u8*)txdesc) + inc * adev->txdesc_size); ++} ++ ++static txhostdesc_t* ++get_txhostdesc(acx_device_t *adev, txdesc_t* txdesc) ++{ ++ int index = (u8*)txdesc - (u8*)adev->txdesc_start; ++ if (unlikely(ACX_DEBUG && (index % adev->txdesc_size))) { ++ printk("bad txdesc ptr %p\n", txdesc); ++ return NULL; ++ } ++ index /= adev->txdesc_size; ++ if (unlikely(ACX_DEBUG && (index >= TX_CNT))) { ++ printk("bad txdesc ptr %p\n", txdesc); ++ return NULL; ++ } ++ return &adev->txhostdesc_start[index*2]; ++} ++ ++static inline client_t* ++get_txc(acx_device_t *adev, txdesc_t* txdesc) ++{ ++ int index = (u8*)txdesc - (u8*)adev->txdesc_start; ++ if (unlikely(ACX_DEBUG && (index % adev->txdesc_size))) { ++ printk("bad txdesc ptr %p\n", txdesc); ++ return NULL; ++ } ++ index /= adev->txdesc_size; ++ if (unlikely(ACX_DEBUG && (index >= TX_CNT))) { ++ printk("bad txdesc ptr %p\n", txdesc); ++ return NULL; ++ } ++ return adev->txc[index]; ++} ++ ++static inline u16 ++get_txr(acx_device_t *adev, txdesc_t* txdesc) ++{ ++ int index = (u8*)txdesc - (u8*)adev->txdesc_start; ++ index /= adev->txdesc_size; ++ return adev->txr[index]; ++} ++ ++static inline void ++put_txcr(acx_device_t *adev, txdesc_t* txdesc, client_t* c, u16 r111) ++{ ++ int index = (u8*)txdesc - (u8*)adev->txdesc_start; ++ if (unlikely(ACX_DEBUG && (index % adev->txdesc_size))) { ++ printk("bad txdesc ptr %p\n", txdesc); ++ return; ++ } ++ index /= adev->txdesc_size; ++ if (unlikely(ACX_DEBUG && (index >= TX_CNT))) { ++ printk("bad txdesc ptr %p\n", txdesc); ++ return; ++ } ++ adev->txc[index] = c; ++ adev->txr[index] = r111; ++} ++ ++ ++/*********************************************************************** ++** EEPROM and PHY read/write helpers ++*/ ++/*********************************************************************** ++** acxmem_read_eeprom_byte ++** ++** Function called to read an octet in the EEPROM. ++** ++** This function is used by acxmem_e_probe to check if the ++** connected card is a legal one or not. ++** ++** Arguments: ++** adev ptr to acx_device structure ++** addr address to read in the EEPROM ++** charbuf ptr to a char. This is where the read octet ++** will be stored ++*/ ++int ++acxmem_read_eeprom_byte(acx_device_t *adev, u32 addr, u8 *charbuf) ++{ ++ int result; ++ int count; ++ ++ write_reg32(adev, IO_ACX_EEPROM_CFG, 0); ++ write_reg32(adev, IO_ACX_EEPROM_ADDR, addr); ++ write_flush(adev); ++ write_reg32(adev, IO_ACX_EEPROM_CTL, 2); ++ ++ count = 0xffff; ++ while (read_reg16(adev, IO_ACX_EEPROM_CTL)) { ++ /* scheduling away instead of CPU burning loop ++ * doesn't seem to work here at all: ++ * awful delay, sometimes also failure. ++ * Doesn't matter anyway (only small delay). */ ++ if (unlikely(!--count)) { ++ printk("%s: timeout waiting for EEPROM read\n", ++ adev->ndev->name); ++ result = NOT_OK; ++ goto fail; ++ } ++ cpu_relax(); ++ } ++ ++ *charbuf = read_reg8(adev, IO_ACX_EEPROM_DATA); ++ log(L_DEBUG, "EEPROM at 0x%04X = 0x%02X\n", addr, *charbuf); ++ result = OK; ++ ++fail: ++ return result; ++} ++ ++ ++/*********************************************************************** ++** We don't lock hw accesses here since we never r/w eeprom in IRQ ++** Note: this function sleeps only because of GFP_KERNEL alloc ++*/ ++#ifdef UNUSED ++int ++acxmem_s_write_eeprom(acx_device_t *adev, u32 addr, u32 len, const u8 *charbuf) ++{ ++ u8 *data_verify = NULL; ++ unsigned long flags; ++ int count, i; ++ int result = NOT_OK; ++ u16 gpio_orig; ++ ++ printk("acx: WARNING! I would write to EEPROM now. " ++ "Since I really DON'T want to unless you know " ++ "what you're doing (THIS CODE WILL PROBABLY " ++ "NOT WORK YET!), I will abort that now. And " ++ "definitely make sure to make a " ++ "/proc/driver/acx_wlan0_eeprom backup copy first!!! " ++ "(the EEPROM content includes the PCI config header!! " ++ "If you kill important stuff, then you WILL " ++ "get in trouble and people DID get in trouble already)\n"); ++ return OK; ++ ++ FN_ENTER; ++ ++ data_verify = kmalloc(len, GFP_KERNEL); ++ if (!data_verify) { ++ goto end; ++ } ++ ++ /* first we need to enable the OE (EEPROM Output Enable) GPIO line ++ * to be able to write to the EEPROM. ++ * NOTE: an EEPROM writing success has been reported, ++ * but you probably have to modify GPIO_OUT, too, ++ * and you probably need to activate a different GPIO ++ * line instead! */ ++ gpio_orig = read_reg16(adev, IO_ACX_GPIO_OE); ++ write_reg16(adev, IO_ACX_GPIO_OE, gpio_orig & ~1); ++ write_flush(adev); ++ ++ /* ok, now start writing the data out */ ++ for (i = 0; i < len; i++) { ++ write_reg32(adev, IO_ACX_EEPROM_CFG, 0); ++ write_reg32(adev, IO_ACX_EEPROM_ADDR, addr + i); ++ write_reg32(adev, IO_ACX_EEPROM_DATA, *(charbuf + i)); ++ write_flush(adev); ++ write_reg32(adev, IO_ACX_EEPROM_CTL, 1); ++ ++ count = 0xffff; ++ while (read_reg16(adev, IO_ACX_EEPROM_CTL)) { ++ if (unlikely(!--count)) { ++ printk("WARNING, DANGER!!! " ++ "Timeout waiting for EEPROM write\n"); ++ goto end; ++ } ++ cpu_relax(); ++ } ++ } ++ ++ /* disable EEPROM writing */ ++ write_reg16(adev, IO_ACX_GPIO_OE, gpio_orig); ++ write_flush(adev); ++ ++ /* now start a verification run */ ++ for (i = 0; i < len; i++) { ++ write_reg32(adev, IO_ACX_EEPROM_CFG, 0); ++ write_reg32(adev, IO_ACX_EEPROM_ADDR, addr + i); ++ write_flush(adev); ++ write_reg32(adev, IO_ACX_EEPROM_CTL, 2); ++ ++ count = 0xffff; ++ while (read_reg16(adev, IO_ACX_EEPROM_CTL)) { ++ if (unlikely(!--count)) { ++ printk("timeout waiting for EEPROM read\n"); ++ goto end; ++ } ++ cpu_relax(); ++ } ++ ++ data_verify[i] = read_reg16(adev, IO_ACX_EEPROM_DATA); ++ } ++ ++ if (0 == memcmp(charbuf, data_verify, len)) ++ result = OK; /* read data matches, success */ ++ ++end: ++ kfree(data_verify); ++ FN_EXIT1(result); ++ return result; ++} ++#endif /* UNUSED */ ++ ++ ++/*********************************************************************** ++** acxmem_s_read_phy_reg ++** ++** Messing with rx/tx disabling and enabling here ++** (write_reg32(adev, IO_ACX_ENABLE, 0b000000xx)) kills traffic ++*/ ++int ++acxmem_s_read_phy_reg(acx_device_t *adev, u32 reg, u8 *charbuf) ++{ ++ int result = NOT_OK; ++ int count; ++ ++ FN_ENTER; ++ ++ write_reg32(adev, IO_ACX_PHY_ADDR, reg); ++ write_flush(adev); ++ write_reg32(adev, IO_ACX_PHY_CTL, 2); ++ ++ count = 0xffff; ++ while (read_reg32(adev, IO_ACX_PHY_CTL)) { ++ /* scheduling away instead of CPU burning loop ++ * doesn't seem to work here at all: ++ * awful delay, sometimes also failure. ++ * Doesn't matter anyway (only small delay). */ ++ if (unlikely(!--count)) { ++ printk("%s: timeout waiting for phy read\n", ++ adev->ndev->name); ++ *charbuf = 0; ++ goto fail; ++ } ++ cpu_relax(); ++ } ++ ++ log(L_DEBUG, "count was %u\n", count); ++ *charbuf = read_reg8(adev, IO_ACX_PHY_DATA); ++ ++ log(L_DEBUG, "radio PHY at 0x%04X = 0x%02X\n", *charbuf, reg); ++ result = OK; ++ goto fail; /* silence compiler warning */ ++fail: ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++*/ ++int ++acxmem_s_write_phy_reg(acx_device_t *adev, u32 reg, u8 value) ++{ ++ int count; ++ FN_ENTER; ++ ++ /* mprusko said that 32bit accesses result in distorted sensitivity ++ * on his card. Unconfirmed, looks like it's not true (most likely since we ++ * now properly flush writes). */ ++ write_reg32(adev, IO_ACX_PHY_DATA, value); ++ write_reg32(adev, IO_ACX_PHY_ADDR, reg); ++ write_flush(adev); ++ write_reg32(adev, IO_ACX_PHY_CTL, 1); ++ write_flush(adev); ++ ++ count = 0xffff; ++ while (read_reg32(adev, IO_ACX_PHY_CTL)) { ++ /* scheduling away instead of CPU burning loop ++ * doesn't seem to work here at all: ++ * awful delay, sometimes also failure. ++ * Doesn't matter anyway (only small delay). */ ++ if (unlikely(!--count)) { ++ printk("%s: timeout waiting for phy read\n", ++ adev->ndev->name); ++ goto fail; ++ } ++ cpu_relax(); ++ } ++ ++ log(L_DEBUG, "radio PHY write 0x%02X at 0x%04X\n", value, reg); ++ fail: ++ FN_EXIT1(OK); ++ return OK; ++} ++ ++ ++#define NO_AUTO_INCREMENT 1 ++ ++/*********************************************************************** ++** acxmem_s_write_fw ++** ++** Write the firmware image into the card. ++** ++** Arguments: ++** adev wlan device structure ++** fw_image firmware image. ++** ++** Returns: ++** 1 firmware image corrupted ++** 0 success ++*/ ++static int ++acxmem_s_write_fw(acx_device_t *adev, const firmware_image_t *fw_image, u32 offset) ++{ ++ int len, size, checkMismatch = -1; ++ u32 sum, v32, tmp, id; ++ /* we skip the first four bytes which contain the control sum */ ++ const u8 *p = (u8*)fw_image + 4; ++ ++ /* start the image checksum by adding the image size value */ ++ sum = p[0]+p[1]+p[2]+p[3]; ++ p += 4; ++ ++#ifdef NOPE ++#if NO_AUTO_INCREMENT ++ write_reg32(adev, IO_ACX_SLV_MEM_CTL, 0); /* use basic mode */ ++#else ++ write_reg32(adev, IO_ACX_SLV_MEM_CTL, 1); /* use autoincrement mode */ ++ write_reg32(adev, IO_ACX_SLV_MEM_ADDR, offset); /* configure start address */ ++ write_flush(adev); ++#endif ++#endif ++ len = 0; ++ size = le32_to_cpu(fw_image->size) & (~3); ++ ++ while (likely(len < size)) { ++ v32 = be32_to_cpu(*(u32*)p); ++ sum += p[0]+p[1]+p[2]+p[3]; ++ p += 4; ++ len += 4; ++ ++#ifdef NOPE ++#if NO_AUTO_INCREMENT ++ write_reg32(adev, IO_ACX_SLV_MEM_ADDR, offset + len - 4); ++ write_flush(adev); ++#endif ++ write_reg32(adev, IO_ACX_SLV_MEM_DATA, v32); ++ write_flush(adev); ++#endif ++ write_slavemem32 (adev, offset + len - 4, v32); ++ ++ id = read_id_register (adev); ++ ++ /* ++ * check the data written ++ */ ++ tmp = read_slavemem32 (adev, offset + len - 4); ++ if (checkMismatch && (tmp != v32)) { ++ printk ("first data mismatch at 0x%08x good 0x%08x bad 0x%08x id 0x%08x\n", ++ offset + len - 4, v32, tmp, id); ++ checkMismatch = 0; ++ } ++ } ++ log(L_DEBUG, "firmware written, size:%d sum1:%x sum2:%x\n", ++ size, sum, le32_to_cpu(fw_image->chksum)); ++ ++ /* compare our checksum with the stored image checksum */ ++ return (sum != le32_to_cpu(fw_image->chksum)); ++} ++ ++ ++/*********************************************************************** ++** acxmem_s_validate_fw ++** ++** Compare the firmware image given with ++** the firmware image written into the card. ++** ++** Arguments: ++** adev wlan device structure ++** fw_image firmware image. ++** ++** Returns: ++** NOT_OK firmware image corrupted or not correctly written ++** OK success ++*/ ++static int ++acxmem_s_validate_fw(acx_device_t *adev, const firmware_image_t *fw_image, ++ u32 offset) ++{ ++ u32 sum, v32, w32; ++ int len, size; ++ int result = OK; ++ /* we skip the first four bytes which contain the control sum */ ++ const u8 *p = (u8*)fw_image + 4; ++ ++ /* start the image checksum by adding the image size value */ ++ sum = p[0]+p[1]+p[2]+p[3]; ++ p += 4; ++ ++ write_reg32(adev, IO_ACX_SLV_END_CTL, 0); ++ ++#if NO_AUTO_INCREMENT ++ write_reg32(adev, IO_ACX_SLV_MEM_CTL, 0); /* use basic mode */ ++#else ++ write_reg32(adev, IO_ACX_SLV_MEM_CTL, 1); /* use autoincrement mode */ ++ write_reg32(adev, IO_ACX_SLV_MEM_ADDR, offset); /* configure start address */ ++#endif ++ ++ len = 0; ++ size = le32_to_cpu(fw_image->size) & (~3); ++ ++ while (likely(len < size)) { ++ v32 = be32_to_cpu(*(u32*)p); ++ p += 4; ++ len += 4; ++ ++#ifdef NOPE ++#if NO_AUTO_INCREMENT ++ write_reg32(adev, IO_ACX_SLV_MEM_ADDR, offset + len - 4); ++#endif ++ udelay(10); ++ w32 = read_reg32(adev, IO_ACX_SLV_MEM_DATA); ++#endif ++ w32 = read_slavemem32 (adev, offset + len - 4); ++ ++ if (unlikely(w32 != v32)) { ++ printk("acx: FATAL: firmware upload: " ++ "data parts at offset %d don't match\n(0x%08X vs. 0x%08X)!\n" ++ "I/O timing issues or defective memory, with DWL-xx0+? " ++ "ACX_IO_WIDTH=16 may help. Please report\n", ++ len, v32, w32); ++ result = NOT_OK; ++ break; ++ } ++ ++ sum += (u8)w32 + (u8)(w32>>8) + (u8)(w32>>16) + (u8)(w32>>24); ++ } ++ ++ /* sum control verification */ ++ if (result != NOT_OK) { ++ if (sum != le32_to_cpu(fw_image->chksum)) { ++ printk("acx: FATAL: firmware upload: " ++ "checksums don't match!\n"); ++ result = NOT_OK; ++ } ++ } ++ ++ return result; ++} ++ ++ ++/*********************************************************************** ++** acxmem_s_upload_fw ++** ++** Called from acx_reset_dev ++*/ ++static int ++acxmem_s_upload_fw(acx_device_t *adev) ++{ ++ firmware_image_t *fw_image = NULL; ++ int res = NOT_OK; ++ int try; ++ u32 file_size; ++ char *filename = "WLANGEN.BIN"; ++#ifdef PATCH_AROUND_BAD_SPOTS ++ u32 offset; ++ int i; ++ /* ++ * arm-linux-objdump -d patch.bin, or ++ * od -Ax -t x4 patch.bin after finding the bounds ++ * of the .text section with arm-linux-objdump -s patch.bin ++ */ ++ u32 patch[] = { ++ 0xe584c030, 0xe59fc008, ++ 0xe92d1000, 0xe59fc004, 0xe8bd8000, 0x0000080c, ++ 0x0000aa68, 0x605a2200, 0x2c0a689c, 0x2414d80a, ++ 0x2f00689f, 0x1c27d007, 0x06241e7c, 0x2f000e24, ++ 0xe000d1f6, 0x602e6018, 0x23036468, 0x480203db, ++ 0x60ca6003, 0xbdf0750a, 0xffff0808 ++ }; ++#endif ++ ++ FN_ENTER; ++ /* No combined image; tell common we need the radio firmware, too */ ++ adev->need_radio_fw = 1; ++ ++ fw_image = acx_s_read_fw(adev->dev, filename, &file_size); ++ if (!fw_image) { ++ FN_EXIT1(NOT_OK); ++ return NOT_OK; ++ } ++ ++ for (try = 1; try <= 5; try++) { ++ res = acxmem_s_write_fw(adev, fw_image, 0); ++ log(L_DEBUG|L_INIT, "acx_write_fw (main): %d\n", res); ++ if (OK == res) { ++ res = acxmem_s_validate_fw(adev, fw_image, 0); ++ log(L_DEBUG|L_INIT, "acx_validate_fw " ++ "(main): %d\n", res); ++ } ++ ++ if (OK == res) { ++ SET_BIT(adev->dev_state_mask, ACX_STATE_FW_LOADED); ++ break; ++ } ++ printk("acx: firmware upload attempt #%d FAILED, " ++ "retrying...\n", try); ++ acx_s_msleep(1000); /* better wait for a while... */ ++ } ++ ++#ifdef PATCH_AROUND_BAD_SPOTS ++ /* ++ * Only want to do this if the firmware is exactly what we expect for an ++ * iPaq 4700; otherwise, bad things would ensue. ++ */ ++ if ((HX4700_FIRMWARE_CHECKSUM == fw_image->chksum) || ++ (HX4700_ALTERNATE_FIRMWARE_CHECKSUM == fw_image->chksum)) { ++ /* ++ * Put the patch after the main firmware image. 0x950c contains ++ * the ACX's idea of the end of the firmware. Use that location to ++ * load ours (which depends on that location being 0xab58) then ++ * update that location to point to after ours. ++ */ ++ ++ offset = read_slavemem32 (adev, 0x950c); ++ ++ log (L_DEBUG, "acx: patching in at 0x%04x\n", offset); ++ ++ for (i = 0; i < sizeof(patch) / sizeof(patch[0]); i++) { ++ write_slavemem32 (adev, offset, patch[i]); ++ offset += sizeof(u32); ++ } ++ ++ /* ++ * Patch the instruction at 0x0804 to branch to our ARM patch at 0xab58 ++ */ ++ write_slavemem32 (adev, 0x0804, 0xea000000 + (0xab58-0x0804-8)/4); ++ ++ /* ++ * Patch the instructions at 0x1f40 to branch to our Thumb patch at 0xab74 ++ * ++ * 4a00 ldr r2, [pc, #0] ++ * 4710 bx r2 ++ * .data 0xab74+1 ++ */ ++ write_slavemem32 (adev, 0x1f40, 0x47104a00); ++ write_slavemem32 (adev, 0x1f44, 0x0000ab74+1); ++ ++ /* ++ * Bump the end of the firmware up to beyond our patch. ++ */ ++ write_slavemem32 (adev, 0x950c, offset); ++ ++ } ++#endif ++ ++ vfree(fw_image); ++ ++ FN_EXIT1(res); ++ return res; ++} ++ ++ ++/*********************************************************************** ++** acxmem_s_upload_radio ++** ++** Uploads the appropriate radio module firmware into the card. ++*/ ++int ++acxmem_s_upload_radio(acx_device_t *adev) ++{ ++ acx_ie_memmap_t mm; ++ firmware_image_t *radio_image; ++ acx_cmd_radioinit_t radioinit; ++ int res = NOT_OK; ++ int try; ++ u32 offset; ++ u32 size; ++ char filename[sizeof("RADIONN.BIN")]; ++ ++ if (!adev->need_radio_fw) return OK; ++ ++ FN_ENTER; ++ ++ acx_s_interrogate(adev, &mm, ACX1xx_IE_MEMORY_MAP); ++ offset = le32_to_cpu(mm.CodeEnd); ++ ++ snprintf(filename, sizeof(filename), "RADIO%02x.BIN", ++ adev->radio_type); ++ radio_image = acx_s_read_fw(adev->dev, filename, &size); ++ if (!radio_image) { ++ printk("acx: can't load radio module '%s'\n", filename); ++ goto fail; ++ } ++ ++ acx_s_issue_cmd(adev, ACX1xx_CMD_SLEEP, NULL, 0); ++ ++ for (try = 1; try <= 5; try++) { ++ res = acxmem_s_write_fw(adev, radio_image, offset); ++ log(L_DEBUG|L_INIT, "acx_write_fw (radio): %d\n", res); ++ if (OK == res) { ++ res = acxmem_s_validate_fw(adev, radio_image, offset); ++ log(L_DEBUG|L_INIT, "acx_validate_fw (radio): %d\n", res); ++ } ++ ++ if (OK == res) ++ break; ++ printk("acx: radio firmware upload attempt #%d FAILED, " ++ "retrying...\n", try); ++ acx_s_msleep(1000); /* better wait for a while... */ ++ } ++ ++ acx_s_issue_cmd(adev, ACX1xx_CMD_WAKE, NULL, 0); ++ radioinit.offset = cpu_to_le32(offset); ++ ++ /* no endian conversion needed, remains in card CPU area: */ ++ radioinit.len = radio_image->size; ++ ++ vfree(radio_image); ++ ++ if (OK != res) ++ goto fail; ++ ++ /* will take a moment so let's have a big timeout */ ++ acx_s_issue_cmd_timeo(adev, ACX1xx_CMD_RADIOINIT, ++ &radioinit, sizeof(radioinit), CMD_TIMEOUT_MS(1000)); ++ ++ res = acx_s_interrogate(adev, &mm, ACX1xx_IE_MEMORY_MAP); ++ ++fail: ++ FN_EXIT1(res); ++ return res; ++} ++ ++/*********************************************************************** ++** acxmem_l_reset_mac ++** ++** MAC will be reset ++** Call context: reset_dev ++*/ ++static void ++acxmem_l_reset_mac(acx_device_t *adev) ++{ ++ int count; ++ FN_ENTER; ++ ++ /* halt eCPU */ ++ set_regbits (adev, IO_ACX_ECPU_CTRL, 0x1); ++ ++ /* now do soft reset of eCPU, set bit */ ++ set_regbits (adev, IO_ACX_SOFT_RESET, 0x1); ++ log(L_DEBUG, "%s: enable soft reset...\n", __func__); ++ ++ /* Windows driver sleeps here for a while with this sequence */ ++ for (count = 0; count < 200; count++) { ++ udelay (50); ++ } ++ ++ /* now clear bit again: deassert eCPU reset */ ++ log(L_DEBUG, "%s: disable soft reset and go to init mode...\n", __func__); ++ clear_regbits (adev, IO_ACX_SOFT_RESET, 0x1); ++ ++ /* now start a burst read from initial EEPROM */ ++ set_regbits (adev, IO_ACX_EE_START, 0x1); ++ ++ /* ++ * Windows driver sleeps here for a while with this sequence ++ */ ++ for (count = 0; count < 200; count++) { ++ udelay (50); ++ } ++ ++ /* Windows driver writes 0x10000 to register 0x808 here */ ++ ++ write_reg32 (adev, 0x808, 0x10000); ++ ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acxmem_s_verify_init ++*/ ++static int ++acxmem_s_verify_init(acx_device_t *adev) ++{ ++ int result = NOT_OK; ++ unsigned long timeout; ++ ++ FN_ENTER; ++ ++ timeout = jiffies + 2*HZ; ++ for (;;) { ++ u32 irqstat = read_reg32(adev, IO_ACX_IRQ_STATUS_NON_DES); ++ if ((irqstat != 0xFFFFFFFF) && (irqstat & HOST_INT_FCS_THRESHOLD)) { ++ result = OK; ++ write_reg32(adev, IO_ACX_IRQ_ACK, HOST_INT_FCS_THRESHOLD); ++ break; ++ } ++ if (time_after(jiffies, timeout)) ++ break; ++ /* Init may take up to ~0.5 sec total */ ++ acx_s_msleep(50); ++ } ++ ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++** A few low-level helpers ++** ++** Note: these functions are not protected by lock ++** and thus are never allowed to be called from IRQ. ++** Also they must not race with fw upload which uses same hw regs ++*/ ++ ++/*********************************************************************** ++** acxmem_write_cmd_type_status ++*/ ++ ++static inline void ++acxmem_write_cmd_type_status(acx_device_t *adev, u16 type, u16 status) ++{ ++ write_slavemem32 (adev, (u32) adev->cmd_area, type | (status << 16)); ++ write_flush(adev); ++} ++ ++ ++/*********************************************************************** ++** acxmem_read_cmd_type_status ++*/ ++static u32 ++acxmem_read_cmd_type_status(acx_device_t *adev) ++{ ++ u32 cmd_type, cmd_status; ++ ++ cmd_type = read_slavemem32 (adev, (u32) adev->cmd_area); ++ ++ cmd_status = (cmd_type >> 16); ++ cmd_type = (u16)cmd_type; ++ ++ log(L_CTL, "cmd_type:%04X cmd_status:%04X [%s]\n", ++ cmd_type, cmd_status, ++ acx_cmd_status_str(cmd_status)); ++ ++ return cmd_status; ++} ++ ++ ++/*********************************************************************** ++** acxmem_s_reset_dev ++** ++** Arguments: ++** netdevice that contains the adev variable ++** Returns: ++** NOT_OK on fail ++** OK on success ++** Side effects: ++** device is hard reset ++** Call context: ++** acxmem_e_probe ++** Comment: ++** This resets the device using low level hardware calls ++** as well as uploads and verifies the firmware to the card ++*/ ++ ++static inline void ++init_mboxes(acx_device_t *adev) ++{ ++ u32 cmd_offs, info_offs; ++ ++ cmd_offs = read_reg32(adev, IO_ACX_CMD_MAILBOX_OFFS); ++ info_offs = read_reg32(adev, IO_ACX_INFO_MAILBOX_OFFS); ++ adev->cmd_area = (u8*) cmd_offs; ++ adev->info_area = (u8*) info_offs; ++ /* ++ log(L_DEBUG, "iobase2=%p\n" ++ */ ++ log( L_DEBUG, "cmd_mbox_offset=%X cmd_area=%p\n" ++ "info_mbox_offset=%X info_area=%p\n", ++ cmd_offs, adev->cmd_area, ++ info_offs, adev->info_area); ++} ++ ++ ++static inline void ++read_eeprom_area(acx_device_t *adev) ++{ ++#if ACX_DEBUG > 1 ++ int offs; ++ u8 tmp; ++ ++ for (offs = 0x8c; offs < 0xb9; offs++) ++ acxmem_read_eeprom_byte(adev, offs, &tmp); ++#endif ++} ++ ++static int ++acxmem_s_reset_dev(acx_device_t *adev) ++{ ++ const char* msg = ""; ++ unsigned long flags; ++ int result = NOT_OK; ++ u16 hardware_info; ++ u16 ecpu_ctrl; ++ int count; ++ u32 tmp; ++ ++ FN_ENTER; ++ /* ++ write_reg32 (adev, IO_ACX_SLV_MEM_CP, 0); ++ */ ++ /* reset the device to make sure the eCPU is stopped ++ * to upload the firmware correctly */ ++ ++ acx_lock(adev, flags); ++ ++ /* Windows driver does some funny things here */ ++ /* ++ * clear bit 0x200 in register 0x2A0 ++ */ ++ clear_regbits (adev, 0x2A0, 0x200); ++ ++ /* ++ * Set bit 0x200 in ACX_GPIO_OUT ++ */ ++ set_regbits (adev, IO_ACX_GPIO_OUT, 0x200); ++ ++ /* ++ * read register 0x900 until its value is 0x8400104C, sleeping ++ * in between reads if it's not immediate ++ */ ++ tmp = read_reg32 (adev, REG_ACX_VENDOR_ID); ++ count = 500; ++ while (count-- && (tmp != ACX_VENDOR_ID)) { ++ mdelay (10); ++ tmp = read_reg32 (adev, REG_ACX_VENDOR_ID); ++ } ++ ++ /* end what Windows driver does */ ++ ++ acxmem_l_reset_mac(adev); ++ ++ ecpu_ctrl = read_reg32(adev, IO_ACX_ECPU_CTRL) & 1; ++ if (!ecpu_ctrl) { ++ msg = "eCPU is already running. "; ++ goto end_unlock; ++ } ++ ++#ifdef WE_DONT_NEED_THAT_DO_WE ++ if (read_reg16(adev, IO_ACX_SOR_CFG) & 2) { ++ /* eCPU most likely means "embedded CPU" */ ++ msg = "eCPU did not start after boot from flash. "; ++ goto end_unlock; ++ } ++ ++ /* check sense on reset flags */ ++ if (read_reg16(adev, IO_ACX_SOR_CFG) & 0x10) { ++ printk("%s: eCPU did not start after boot (SOR), " ++ "is this fatal?\n", adev->ndev->name); ++ } ++#endif ++ /* scan, if any, is stopped now, setting corresponding IRQ bit */ ++ adev->irq_status |= HOST_INT_SCAN_COMPLETE; ++ ++ acx_unlock(adev, flags); ++ ++ /* need to know radio type before fw load */ ++ /* Need to wait for arrival of this information in a loop, ++ * most probably since eCPU runs some init code from EEPROM ++ * (started burst read in reset_mac()) which also ++ * sets the radio type ID */ ++ ++ count = 0xffff; ++ do { ++ hardware_info = read_reg16(adev, IO_ACX_EEPROM_INFORMATION); ++ if (!--count) { ++ msg = "eCPU didn't indicate radio type"; ++ goto end_fail; ++ } ++ cpu_relax(); ++ } while (!(hardware_info & 0xff00)); /* radio type still zero? */ ++ printk("ACX radio type 0x%02x\n", (hardware_info >> 8) & 0xff); ++ /* printk("DEBUG: count %d\n", count); */ ++ adev->form_factor = hardware_info & 0xff; ++ adev->radio_type = hardware_info >> 8; ++ ++ /* load the firmware */ ++ if (OK != acxmem_s_upload_fw(adev)) ++ goto end_fail; ++ ++ /* acx_s_msleep(10); this one really shouldn't be required */ ++ ++ /* now start eCPU by clearing bit */ ++ clear_regbits (adev, IO_ACX_ECPU_CTRL, 0x1); ++ log(L_DEBUG, "booted eCPU up and waiting for completion...\n"); ++ ++ /* Windows driver clears bit 0x200 in register 0x2A0 here */ ++ clear_regbits (adev, 0x2A0, 0x200); ++ ++ /* Windows driver sets bit 0x200 in ACX_GPIO_OUT here */ ++ set_regbits (adev, IO_ACX_GPIO_OUT, 0x200); ++ /* wait for eCPU bootup */ ++ if (OK != acxmem_s_verify_init(adev)) { ++ msg = "timeout waiting for eCPU. "; ++ goto end_fail; ++ } ++ log(L_DEBUG, "eCPU has woken up, card is ready to be configured\n"); ++ init_mboxes(adev); ++ acxmem_write_cmd_type_status(adev, ACX1xx_CMD_RESET, 0); ++ ++ /* test that EEPROM is readable */ ++ read_eeprom_area(adev); ++ ++ result = OK; ++ goto end; ++ ++/* Finish error message. Indicate which function failed */ ++end_unlock: ++ acx_unlock(adev, flags); ++end_fail: ++ printk("acx: %sreset_dev() FAILED\n", msg); ++end: ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++** acxmem_s_issue_cmd_timeo ++** ++** Sends command to fw, extract result ++** ++** NB: we do _not_ take lock inside, so be sure to not touch anything ++** which may interfere with IRQ handler operation ++** ++** TODO: busy wait is a bit silly, so: ++** 1) stop doing many iters - go to sleep after first ++** 2) go to waitqueue based approach: wait, not poll! ++*/ ++#undef FUNC ++#define FUNC "issue_cmd" ++ ++#if !ACX_DEBUG ++int ++acxmem_s_issue_cmd_timeo( ++ acx_device_t *adev, ++ unsigned int cmd, ++ void *buffer, ++ unsigned buflen, ++ unsigned cmd_timeout) ++{ ++#else ++int ++acxmem_s_issue_cmd_timeo_debug( ++ acx_device_t *adev, ++ unsigned cmd, ++ void *buffer, ++ unsigned buflen, ++ unsigned cmd_timeout, ++ const char* cmdstr) ++{ ++ unsigned long start = jiffies; ++#endif ++ const char *devname; ++ unsigned counter; ++ u16 irqtype; ++ int i, j; ++ u8 *p; ++ u16 cmd_status; ++ unsigned long timeout; ++ ++ FN_ENTER; ++ ++ devname = adev->ndev->name; ++ if (!devname || !devname[0] || devname[4]=='%') ++ devname = "acx"; ++ ++ log(L_CTL, FUNC"(cmd:%s,buflen:%u,timeout:%ums,type:0x%04X)\n", ++ cmdstr, buflen, cmd_timeout, ++ buffer ? le16_to_cpu(((acx_ie_generic_t *)buffer)->type) : -1); ++ ++ if (!(adev->dev_state_mask & ACX_STATE_FW_LOADED)) { ++ printk("%s: "FUNC"(): firmware is not loaded yet, " ++ "cannot execute commands!\n", devname); ++ goto bad; ++ } ++ ++ if ((acx_debug & L_DEBUG) && (cmd != ACX1xx_CMD_INTERROGATE)) { ++ printk("input buffer (len=%u):\n", buflen); ++ acx_dump_bytes(buffer, buflen); ++ } ++ ++ /* wait for firmware to become idle for our command submission */ ++ timeout = HZ/5; ++ counter = (timeout * 1000 / HZ) - 1; /* in ms */ ++ timeout += jiffies; ++ do { ++ cmd_status = acxmem_read_cmd_type_status(adev); ++ /* Test for IDLE state */ ++ if (!cmd_status) ++ break; ++ if (counter % 8 == 0) { ++ if (time_after(jiffies, timeout)) { ++ counter = 0; ++ break; ++ } ++ /* we waited 8 iterations, no luck. Sleep 8 ms */ ++ acx_s_msleep(8); ++ } ++ } while (likely(--counter)); ++ ++ if (!counter) { ++ /* the card doesn't get idle, we're in trouble */ ++ printk("%s: "FUNC"(): cmd_status is not IDLE: 0x%04X!=0\n", ++ devname, cmd_status); ++#if DUMP_IF_SLOW > 0 ++ dump_acxmem (adev, 0, 0x10000); ++ panic ("not idle"); ++#endif ++ goto bad; ++ } else if (counter < 190) { /* if waited >10ms... */ ++ log(L_CTL|L_DEBUG, FUNC"(): waited for IDLE %dms. " ++ "Please report\n", 199 - counter); ++ } ++ ++ /* now write the parameters of the command if needed */ ++ if (buffer && buflen) { ++ /* if it's an INTERROGATE command, just pass the length ++ * of parameters to read, as data */ ++#if CMD_DISCOVERY ++ if (cmd == ACX1xx_CMD_INTERROGATE) ++ memset_io(adev->cmd_area + 4, 0xAA, buflen); ++#endif ++ /* ++ * slave memory version ++ */ ++ copy_to_slavemem (adev, (u32) (adev->cmd_area + 4), buffer, ++ (cmd == ACX1xx_CMD_INTERROGATE) ? 4 : buflen); ++ } ++ /* now write the actual command type */ ++ acxmem_write_cmd_type_status(adev, cmd, 0); ++ ++ /* clear CMD_COMPLETE bit. can be set only by IRQ handler: */ ++ adev->irq_status &= ~HOST_INT_CMD_COMPLETE; ++ ++ /* execute command */ ++ write_reg16(adev, IO_ACX_INT_TRIG, INT_TRIG_CMD); ++ write_flush(adev); ++ ++ /* wait for firmware to process command */ ++ ++ /* Ensure nonzero and not too large timeout. ++ ** Also converts e.g. 100->99, 200->199 ++ ** which is nice but not essential */ ++ cmd_timeout = (cmd_timeout-1) | 1; ++ if (unlikely(cmd_timeout > 1199)) ++ cmd_timeout = 1199; ++ ++ /* we schedule away sometimes (timeout can be large) */ ++ counter = cmd_timeout; ++ timeout = jiffies + cmd_timeout * HZ / 1000; ++ do { ++ if (!adev->irqs_active) { /* IRQ disabled: poll */ ++ irqtype = read_reg16(adev, IO_ACX_IRQ_STATUS_NON_DES); ++ if (irqtype & HOST_INT_CMD_COMPLETE) { ++ write_reg16(adev, IO_ACX_IRQ_ACK, ++ HOST_INT_CMD_COMPLETE); ++ break; ++ } ++ } else { /* Wait when IRQ will set the bit */ ++ irqtype = adev->irq_status; ++ if (irqtype & HOST_INT_CMD_COMPLETE) ++ break; ++ } ++ ++ if (counter % 8 == 0) { ++ if (time_after(jiffies, timeout)) { ++ counter = 0; ++ break; ++ } ++ /* we waited 8 iterations, no luck. Sleep 8 ms */ ++ acx_s_msleep(8); ++ } ++ } while (likely(--counter)); ++ ++ /* save state for debugging */ ++ cmd_status = acxmem_read_cmd_type_status(adev); ++ ++ /* put the card in IDLE state */ ++ acxmem_write_cmd_type_status(adev, ACX1xx_CMD_RESET, 0); ++ ++ if (!counter) { /* timed out! */ ++ printk("%s: "FUNC"(): timed out %s for CMD_COMPLETE. " ++ "irq bits:0x%04X irq_status:0x%04X timeout:%dms " ++ "cmd_status:%d (%s)\n", ++ devname, (adev->irqs_active) ? "waiting" : "polling", ++ irqtype, adev->irq_status, cmd_timeout, ++ cmd_status, acx_cmd_status_str(cmd_status)); ++ printk("%s: "FUNC"(): device irq status 0x%04x\n", ++ devname, read_reg16(adev, IO_ACX_IRQ_STATUS_NON_DES)); ++ printk("%s: "FUNC"(): IO_ACX_IRQ_MASK 0x%04x IO_ACX_FEMR 0x%04x\n", ++ devname, ++ read_reg16 (adev, IO_ACX_IRQ_MASK), ++ read_reg16 (adev, IO_ACX_FEMR)); ++ if (read_reg16 (adev, IO_ACX_IRQ_MASK) == 0xffff) { ++ printk ("acxmem: firmware probably hosed - reloading\n"); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) ++ { ++ pm_message_t state; ++ /* acxmem_e_suspend (resume_pdev, state); */ ++ acxmem_e_suspend (adev->ndev , state); ++ } ++#else ++ acxmem_e_suspend (adev, 0); ++#endif ++ { ++ resume_ndev = adev->ndev; ++ fw_resumer (NULL); ++ } ++ } ++ ++ goto bad; ++ } else if (cmd_timeout - counter > 30) { /* if waited >30ms... */ ++ log(L_CTL|L_DEBUG, FUNC"(): %s for CMD_COMPLETE %dms. " ++ "count:%d. Please report\n", ++ (adev->irqs_active) ? "waited" : "polled", ++ cmd_timeout - counter, counter); ++ } ++ ++ if (1 != cmd_status) { /* it is not a 'Success' */ ++ printk("%s: "FUNC"(): cmd_status is not SUCCESS: %d (%s). " ++ "Took %dms of %d\n", ++ devname, cmd_status, acx_cmd_status_str(cmd_status), ++ cmd_timeout - counter, cmd_timeout); ++ /* zero out result buffer ++ * WARNING: this will trash stack in case of illegally large input ++ * length! */ ++ if (buflen > 388) { ++ /* ++ * 388 is maximum command length ++ */ ++ printk ("invalid length 0x%08x\n", buflen); ++ buflen = 388; ++ } ++ p = (u8 *) buffer; ++ for (i = 0; i < buflen; i+= 16) { ++ printk ("%04x:", i); ++ for (j = 0; (j < 16) && (i+j < buflen); j++) { ++ printk (" %02x", *p++); ++ } ++ printk ("\n"); ++ } ++ if (buffer && buflen) ++ memset(buffer, 0, buflen); ++ goto bad; ++ } ++ ++ /* read in result parameters if needed */ ++ if (buffer && buflen && (cmd == ACX1xx_CMD_INTERROGATE)) { ++ copy_from_slavemem (adev, buffer, (u32) (adev->cmd_area + 4), buflen); ++ if (acx_debug & L_DEBUG) { ++ printk("output buffer (len=%u): ", buflen); ++ acx_dump_bytes(buffer, buflen); ++ } ++ } ++ ++/* ok: */ ++ log(L_CTL, FUNC"(%s): took %ld jiffies to complete\n", ++ cmdstr, jiffies - start); ++ FN_EXIT1(OK); ++ return OK; ++ ++bad: ++ /* Give enough info so that callers can avoid ++ ** printing their own diagnostic messages */ ++#if ACX_DEBUG ++ printk("%s: "FUNC"(cmd:%s) FAILED\n", devname, cmdstr); ++#else ++ printk("%s: "FUNC"(cmd:0x%04X) FAILED\n", devname, cmd); ++#endif ++ dump_stack(); ++ FN_EXIT1(NOT_OK); ++ return NOT_OK; ++} ++ ++ ++/*********************************************************************** ++*/ ++#if defined(NONESSENTIAL_FEATURES) ++typedef struct device_id { ++ unsigned char id[6]; ++ char *descr; ++ char *type; ++} device_id_t; ++ ++static const device_id_t ++device_ids[] = ++{ ++ { ++ {'G', 'l', 'o', 'b', 'a', 'l'}, ++ NULL, ++ NULL, ++ }, ++ { ++ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, ++ "uninitialized", ++ "SpeedStream SS1021 or Gigafast WF721-AEX" ++ }, ++ { ++ {0x80, 0x81, 0x82, 0x83, 0x84, 0x85}, ++ "non-standard", ++ "DrayTek Vigor 520" ++ }, ++ { ++ {'?', '?', '?', '?', '?', '?'}, ++ "non-standard", ++ "Level One WPC-0200" ++ }, ++ { ++ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ++ "empty", ++ "DWL-650+ variant" ++ } ++}; ++ ++static void ++acx_show_card_eeprom_id(acx_device_t *adev) ++{ ++ unsigned char buffer[CARD_EEPROM_ID_SIZE]; ++ int i; ++ ++ memset(&buffer, 0, CARD_EEPROM_ID_SIZE); ++ /* use direct EEPROM access */ ++ for (i = 0; i < CARD_EEPROM_ID_SIZE; i++) { ++ if (OK != acxmem_read_eeprom_byte(adev, ++ ACX100_EEPROM_ID_OFFSET + i, ++ &buffer[i])) { ++ printk("acx: reading EEPROM FAILED\n"); ++ break; ++ } ++ } ++ ++ for (i = 0; i < VEC_SIZE(device_ids); i++) { ++ if (!memcmp(&buffer, device_ids[i].id, CARD_EEPROM_ID_SIZE)) { ++ if (device_ids[i].descr) { ++ printk("acx: EEPROM card ID string check " ++ "found %s card ID: is this %s?\n", ++ device_ids[i].descr, device_ids[i].type); ++ } ++ break; ++ } ++ } ++ if (i == VEC_SIZE(device_ids)) { ++ printk("acx: EEPROM card ID string check found " ++ "unknown card: expected 'Global', got '%.*s\'. " ++ "Please report\n", CARD_EEPROM_ID_SIZE, buffer); ++ } ++} ++#endif /* NONESSENTIAL_FEATURES */ ++ ++/*********************************************************************** ++** acxmem_free_desc_queues ++** ++** Releases the queues that have been allocated, the ++** others have been initialised to NULL so this ++** function can be used if only part of the queues were allocated. ++*/ ++ ++void ++acxmem_free_desc_queues(acx_device_t *adev) ++{ ++#define ACX_FREE_QUEUE(size, ptr, phyaddr) \ ++ if (ptr) { \ ++ kfree(ptr); \ ++ ptr = NULL; \ ++ size = 0; \ ++ } ++ ++ FN_ENTER; ++ ++ ACX_FREE_QUEUE(adev->txhostdesc_area_size, adev->txhostdesc_start, adev->txhostdesc_startphy); ++ ACX_FREE_QUEUE(adev->txbuf_area_size, adev->txbuf_start, adev->txbuf_startphy); ++ ++ adev->txdesc_start = NULL; ++ ++ ACX_FREE_QUEUE(adev->rxhostdesc_area_size, adev->rxhostdesc_start, adev->rxhostdesc_startphy); ++ ACX_FREE_QUEUE(adev->rxbuf_area_size, adev->rxbuf_start, adev->rxbuf_startphy); ++ ++ adev->rxdesc_start = NULL; ++ ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acxmem_s_delete_dma_regions ++*/ ++static void ++acxmem_s_delete_dma_regions(acx_device_t *adev) ++{ ++ unsigned long flags; ++ ++ FN_ENTER; ++ /* disable radio Tx/Rx. Shouldn't we use the firmware commands ++ * here instead? Or are we that much down the road that it's no ++ * longer possible here? */ ++ /* ++ * slave memory interface really doesn't like this. ++ */ ++ /* ++ write_reg16(adev, IO_ACX_ENABLE, 0); ++ */ ++ ++ acx_s_msleep(100); ++ ++ acx_lock(adev, flags); ++ acxmem_free_desc_queues(adev); ++ acx_unlock(adev, flags); ++ ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acxmem_e_probe ++** ++** Probe routine called when a PCI device w/ matching ID is found. ++** Here's the sequence: ++** - Allocate the PCI resources. ++** - Read the PCMCIA attribute memory to make sure we have a WLAN card ++** - Reset the MAC ++** - Initialize the dev and wlan data ++** - Initialize the MAC ++** ++** pdev - ptr to pci device structure containing info about pci configuration ++** id - ptr to the device id entry that matched this device ++*/ ++static const u16 ++IO_ACX100[] = ++{ ++ 0x0000, /* IO_ACX_SOFT_RESET */ ++ ++ 0x0014, /* IO_ACX_SLV_MEM_ADDR */ ++ 0x0018, /* IO_ACX_SLV_MEM_DATA */ ++ 0x001c, /* IO_ACX_SLV_MEM_CTL */ ++ 0x0020, /* IO_ACX_SLV_END_CTL */ ++ ++ 0x0034, /* IO_ACX_FEMR */ ++ ++ 0x007c, /* IO_ACX_INT_TRIG */ ++ 0x0098, /* IO_ACX_IRQ_MASK */ ++ 0x00a4, /* IO_ACX_IRQ_STATUS_NON_DES */ ++ 0x00a8, /* IO_ACX_IRQ_STATUS_CLEAR */ ++ 0x00ac, /* IO_ACX_IRQ_ACK */ ++ 0x00b0, /* IO_ACX_HINT_TRIG */ ++ ++ 0x0104, /* IO_ACX_ENABLE */ ++ ++ 0x0250, /* IO_ACX_EEPROM_CTL */ ++ 0x0254, /* IO_ACX_EEPROM_ADDR */ ++ 0x0258, /* IO_ACX_EEPROM_DATA */ ++ 0x025c, /* IO_ACX_EEPROM_CFG */ ++ ++ 0x0268, /* IO_ACX_PHY_ADDR */ ++ 0x026c, /* IO_ACX_PHY_DATA */ ++ 0x0270, /* IO_ACX_PHY_CTL */ ++ ++ 0x0290, /* IO_ACX_GPIO_OE */ ++ ++ 0x0298, /* IO_ACX_GPIO_OUT */ ++ ++ 0x02a4, /* IO_ACX_CMD_MAILBOX_OFFS */ ++ 0x02a8, /* IO_ACX_INFO_MAILBOX_OFFS */ ++ 0x02ac, /* IO_ACX_EEPROM_INFORMATION */ ++ ++ 0x02d0, /* IO_ACX_EE_START */ ++ 0x02d4, /* IO_ACX_SOR_CFG */ ++ 0x02d8 /* IO_ACX_ECPU_CTRL */ ++}; ++ ++static const u16 ++IO_ACX111[] = ++{ ++ 0x0000, /* IO_ACX_SOFT_RESET */ ++ ++ 0x0014, /* IO_ACX_SLV_MEM_ADDR */ ++ 0x0018, /* IO_ACX_SLV_MEM_DATA */ ++ 0x001c, /* IO_ACX_SLV_MEM_CTL */ ++ 0x0020, /* IO_ACX_SLV_MEM_CP */ ++ ++ 0x0034, /* IO_ACX_FEMR */ ++ ++ 0x00b4, /* IO_ACX_INT_TRIG */ ++ 0x00d4, /* IO_ACX_IRQ_MASK */ ++ /* we do mean NON_DES (0xf0), not NON_DES_MASK which is at 0xe0: */ ++ 0x00f0, /* IO_ACX_IRQ_STATUS_NON_DES */ ++ 0x00e4, /* IO_ACX_IRQ_STATUS_CLEAR */ ++ 0x00e8, /* IO_ACX_IRQ_ACK */ ++ 0x00ec, /* IO_ACX_HINT_TRIG */ ++ ++ 0x01d0, /* IO_ACX_ENABLE */ ++ ++ 0x0338, /* IO_ACX_EEPROM_CTL */ ++ 0x033c, /* IO_ACX_EEPROM_ADDR */ ++ 0x0340, /* IO_ACX_EEPROM_DATA */ ++ 0x0344, /* IO_ACX_EEPROM_CFG */ ++ ++ 0x0350, /* IO_ACX_PHY_ADDR */ ++ 0x0354, /* IO_ACX_PHY_DATA */ ++ 0x0358, /* IO_ACX_PHY_CTL */ ++ ++ 0x0374, /* IO_ACX_GPIO_OE */ ++ ++ 0x037c, /* IO_ACX_GPIO_OUT */ ++ ++ 0x0388, /* IO_ACX_CMD_MAILBOX_OFFS */ ++ 0x038c, /* IO_ACX_INFO_MAILBOX_OFFS */ ++ 0x0390, /* IO_ACX_EEPROM_INFORMATION */ ++ ++ 0x0100, /* IO_ACX_EE_START */ ++ 0x0104, /* IO_ACX_SOR_CFG */ ++ 0x0108, /* IO_ACX_ECPU_CTRL */ ++}; ++ ++static void ++dummy_netdev_init(struct net_device *ndev) {} ++ ++/* ++ * Most of the acx specific pieces of hardware reset. ++ */ ++static int ++acxmem_complete_hw_reset (acx_device_t *adev) ++{ ++ acx111_ie_configoption_t co; ++ ++ /* NB: read_reg() reads may return bogus data before reset_dev(), ++ * since the firmware which directly controls large parts of the I/O ++ * registers isn't initialized yet. ++ * acx100 seems to be more affected than acx111 */ ++ if (OK != acxmem_s_reset_dev (adev)) ++ return -1; ++ ++ if (IS_ACX100(adev)) { ++ /* ACX100: configopt struct in cmd mailbox - directly after reset */ ++ copy_from_slavemem (adev, (u8*) &co, (u32) adev->cmd_area, sizeof (co)); ++ } ++ ++ if (OK != acx_s_init_mac(adev)) ++ return -3; ++ ++ if (IS_ACX111(adev)) { ++ /* ACX111: configopt struct needs to be queried after full init */ ++ acx_s_interrogate(adev, &co, ACX111_IE_CONFIG_OPTIONS); ++ } ++ ++ /* ++ * Set up transmit buffer administration ++ */ ++ init_acx_txbuf (adev); ++ ++ /* ++ * Windows driver writes 0x01000000 to register 0x288, RADIO_CTL, if the form factor ++ * is 3. It also write protects the EEPROM by writing 1<<9 to GPIO_OUT ++ */ ++ if (adev->form_factor == 3) { ++ set_regbits (adev, 0x288, 0x01000000); ++ set_regbits (adev, 0x298, 1<<9); ++ } ++ ++/* TODO: merge them into one function, they are called just once and are the same for pci & usb */ ++ if (OK != acxmem_read_eeprom_byte(adev, 0x05, &adev->eeprom_version)) ++ return -2; ++ ++ acx_s_parse_configoption(adev, &co); ++ acx_s_get_firmware_version(adev); /* needs to be after acx_s_init_mac() */ ++ acx_display_hardware_details(adev); ++ ++ return 0; ++} ++ ++static int acx_init_netdev(struct net_device *ndev, struct device *dev, int base_addr, int addr_size, int irq) ++{ ++ const char *chip_name; ++ int result = -EIO; ++ int err; ++ u8 chip_type; ++ acx_device_t *adev = NULL; ++ ++ FN_ENTER; ++ ++ /* FIXME: prism54 calls pci_set_mwi() here, ++ * should we do/support the same? */ ++ ++ /* chiptype is u8 but id->driver_data is ulong ++ ** Works for now (possible values are 1 and 2) */ ++ chip_type = CHIPTYPE_ACX100; ++ /* acx100 and acx111 have different PCI memory regions */ ++ if (chip_type == CHIPTYPE_ACX100) { ++ chip_name = "ACX100"; ++ } else if (chip_type == CHIPTYPE_ACX111) { ++ chip_name = "ACX111"; ++ } else { ++ printk("acx: unknown chip type 0x%04X\n", chip_type); ++ goto fail_unknown_chiptype; ++ } ++ ++ printk("acx: found %s-based wireless network card\n", chip_name); ++ log(L_ANY, "initial debug setting is 0x%04X\n", acx_debug); ++ ++ ++ dev_set_drvdata(dev, ndev); ++ ++ ether_setup(ndev); ++ ++ ndev->irq = irq; ++ ++ ndev->base_addr = base_addr; ++printk (KERN_INFO "memwinbase=%lx memwinsize=%u\n",memwin.Base,memwin.Size); ++ if (addr_size == 0 || ndev->irq == 0) ++ goto fail_hw_params; ++ ndev->open = &acxmem_e_open; ++ ndev->stop = &acxmem_e_close; ++ //pdev->dev.release = &acxmem_e_release; ++ ndev->hard_start_xmit = &acx_i_start_xmit; ++ ndev->get_stats = &acx_e_get_stats; ++#if IW_HANDLER_VERSION <= 5 ++ ndev->get_wireless_stats = &acx_e_get_wireless_stats; ++#endif ++ ndev->wireless_handlers = (struct iw_handler_def *)&acx_ioctl_handler_def; ++ ndev->set_multicast_list = &acxmem_i_set_multicast_list; ++ ndev->tx_timeout = &acxmem_i_tx_timeout; ++ ndev->change_mtu = &acx_e_change_mtu; ++ ndev->watchdog_timeo = 4 * HZ; ++ ++ adev = ndev2adev(ndev); ++ spin_lock_init(&adev->lock); /* initial state: unlocked */ ++ spin_lock_init(&adev->txbuf_lock); ++ /* We do not start with downed sem: we want PARANOID_LOCKING to work */ ++ sema_init(&adev->sem, 1); /* initial state: 1 (upped) */ ++ /* since nobody can see new netdev yet, we can as well ++ ** just _presume_ that we're under sem (instead of actually taking it): */ ++ /* acx_sem_lock(adev); */ ++ adev->dev = dev; ++ adev->ndev = ndev; ++ adev->dev_type = DEVTYPE_MEM; ++ adev->chip_type = chip_type; ++ adev->chip_name = chip_name; ++ adev->io = (CHIPTYPE_ACX100 == chip_type) ? IO_ACX100 : IO_ACX111; ++ adev->membase = (volatile u32 *) ndev->base_addr; ++ adev->iobase = (volatile u32 *) ioremap_nocache (ndev->base_addr, addr_size); ++ /* to find crashes due to weird driver access ++ * to unconfigured interface (ifup) */ ++ adev->mgmt_timer.function = (void (*)(unsigned long))0x0000dead; ++ ++#if defined(NONESSENTIAL_FEATURES) ++ acx_show_card_eeprom_id(adev); ++#endif /* NONESSENTIAL_FEATURES */ ++ ++#ifdef SET_MODULE_OWNER ++ SET_MODULE_OWNER(ndev); ++#endif ++ // need to fix that @@ ++ SET_NETDEV_DEV(ndev, dev); ++ ++ log(L_IRQ|L_INIT, "using IRQ %d\n", ndev->irq); ++ ++ /* ok, pci setup is finished, now start initializing the card */ ++ ++ if (OK != acxmem_complete_hw_reset (adev)) ++ goto fail_reset; ++ ++ /* ++ * Set up default things for most of the card settings. ++ */ ++ acx_s_set_defaults(adev); ++ ++ /* Register the card, AFTER everything else has been set up, ++ * since otherwise an ioctl could step on our feet due to ++ * firmware operations happening in parallel or uninitialized data */ ++ err = register_netdev(ndev); ++ if (OK != err) { ++ printk("acx: register_netdev() FAILED: %d\n", err); ++ goto fail_register_netdev; ++ } ++ ++ acx_proc_register_entries(ndev); ++ ++ /* Now we have our device, so make sure the kernel doesn't try ++ * to send packets even though we're not associated to a network yet */ ++ acx_stop_queue(ndev, "on probe"); ++ acx_carrier_off(ndev, "on probe"); ++ ++ /* ++ * Set up a default monitor type so that poor combinations of initialization ++ * sequences in monitor mode don't end up destroying the hardware type. ++ */ ++ adev->monitor_type = ARPHRD_ETHER; ++ ++ /* ++ * Register to receive inetaddr notifier changes. This will allow us to ++ * catch if the user changes the MAC address of the interface. ++ */ ++ register_netdevice_notifier(&acx_netdev_notifier); ++ ++ /* after register_netdev() userspace may start working with dev ++ * (in particular, on other CPUs), we only need to up the sem */ ++ /* acx_sem_unlock(adev); */ ++ ++ printk("acx "ACX_RELEASE": net device %s, driver compiled " ++ "against wireless extensions %d and Linux %s\n", ++ ndev->name, WIRELESS_EXT, UTS_RELEASE); ++ ++#if CMD_DISCOVERY ++ great_inquisitor(adev); ++#endif ++ ++ result = OK; ++ goto done; ++ ++ /* error paths: undo everything in reverse order... */ ++ ++fail_register_netdev: ++ ++ acxmem_s_delete_dma_regions(adev); ++ ++fail_reset: ++fail_hw_params: ++ free_netdev(ndev); ++fail_unknown_chiptype: ++ ++ ++done: ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++** acxmem_e_remove ++** ++** Shut device down (if not hot unplugged) ++** and deallocate PCI resources for the acx chip. ++** ++** pdev - ptr to PCI device structure containing info about pci configuration ++*/ ++static int __devexit ++acxmem_e_remove(struct pcmcia_device *link) ++{ ++ struct net_device *ndev; ++ acx_device_t *adev; ++ unsigned long flags; ++ ++ FN_ENTER; ++ ++ ndev = ((local_info_t*)link->priv)->ndev; ++ if (!ndev) { ++ log(L_DEBUG, "%s: card is unused. Skipping any release code\n", ++ __func__); ++ goto end; ++ } ++ ++ adev = ndev2adev(ndev); ++ ++ /* If device wasn't hot unplugged... */ ++ if (adev_present(adev)) { ++ ++ acx_sem_lock(adev); ++ ++ /* disable both Tx and Rx to shut radio down properly */ ++ acx_s_issue_cmd(adev, ACX1xx_CMD_DISABLE_TX, NULL, 0); ++ acx_s_issue_cmd(adev, ACX1xx_CMD_DISABLE_RX, NULL, 0); ++ ++#ifdef REDUNDANT ++ /* put the eCPU to sleep to save power ++ * Halting is not possible currently, ++ * since not supported by all firmware versions */ ++ acx_s_issue_cmd(adev, ACX100_CMD_SLEEP, NULL, 0); ++#endif ++ acx_lock(adev, flags); ++ ++ /* disable power LED to save power :-) */ ++ log(L_INIT, "switching off power LED to save power\n"); ++ acxmem_l_power_led(adev, 0); ++ ++ /* stop our eCPU */ ++ if (IS_ACX111(adev)) { ++ /* FIXME: does this actually keep halting the eCPU? ++ * I don't think so... ++ */ ++ acxmem_l_reset_mac(adev); ++ } else { ++ u16 temp; ++ ++ /* halt eCPU */ ++ temp = read_reg16(adev, IO_ACX_ECPU_CTRL) | 0x1; ++ write_reg16(adev, IO_ACX_ECPU_CTRL, temp); ++ write_flush(adev); ++ } ++ ++ acx_unlock(adev, flags); ++ ++ acx_sem_unlock(adev); ++ } ++ ++ ++ /* ++ * Unregister the notifier chain ++ */ ++ unregister_netdevice_notifier(&acx_netdev_notifier); ++ ++ /* unregister the device to not let the kernel ++ * (e.g. ioctls) access a half-deconfigured device ++ * NB: this will cause acxmem_e_close() to be called, ++ * thus we shouldn't call it under sem! */ ++ log(L_INIT, "removing device %s\n", ndev->name); ++ unregister_netdev(ndev); ++ ++ /* unregister_netdev ensures that no references to us left. ++ * For paranoid reasons we continue to follow the rules */ ++ acx_sem_lock(adev); ++ ++ if (adev->dev_state_mask & ACX_STATE_IFACE_UP) { ++ acxmem_s_down(ndev); ++ CLEAR_BIT(adev->dev_state_mask, ACX_STATE_IFACE_UP); ++ } ++ ++ acx_proc_unregister_entries(ndev); ++ ++ acxmem_s_delete_dma_regions(adev); ++ ++ /* finally, clean up PCI bus state */ ++ if (adev->iobase) iounmap((void *)adev->iobase); ++ ++ acx_sem_unlock(adev); ++ ++ /* Free netdev (quite late, ++ * since otherwise we might get caught off-guard ++ * by a netdev timeout handler execution ++ * expecting to see a working dev...) */ ++ free_netdev(ndev); ++ ++ printk ("e_remove done\n"); ++end: ++ FN_EXIT0; ++ ++ return 0; ++} ++ ++ ++/*********************************************************************** ++** TODO: PM code needs to be fixed / debugged / tested. ++*/ ++#ifdef CONFIG_PM ++static int ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) ++acxmem_e_suspend( struct net_device *ndev, pm_message_t state) ++#else ++acxmem_e_suspend( struct net_device *ndev, u32 state) ++#endif ++{ ++ FN_ENTER; ++ acx_device_t *adev; ++ printk("acx: suspend handler is experimental!\n"); ++ printk("sus: dev %p\n", ndev); ++ ++ if (!netif_running(ndev)) ++ goto end; ++ // @@ need to get it from link or something like that ++ adev = ndev2adev(ndev); ++ printk("sus: adev %p\n", adev); ++ ++ acx_sem_lock(adev); ++ ++ netif_device_detach(adev->ndev); /* this one cannot sleep */ ++ acxmem_s_down(adev->ndev); ++ /* down() does not set it to 0xffff, but here we really want that */ ++ write_reg16(adev, IO_ACX_IRQ_MASK, 0xffff); ++ write_reg16(adev, IO_ACX_FEMR, 0x0); ++ acxmem_s_delete_dma_regions(adev); ++ ++ /* ++ * Turn the ACX chip off. ++ */ ++ ++ acx_sem_unlock(adev); ++end: ++ FN_EXIT0; ++ return OK; ++} ++ ++ ++static void ++fw_resumer(struct work_struct *notused) ++{ ++ acx_device_t *adev; ++ struct net_device *ndev = resume_ndev; ++ ++ printk("acx: resume handler is experimental!\n"); ++ printk("rsm: got dev %p\n", ndev); ++ ++ if (!netif_running(ndev)) ++ return; ++ ++ adev = ndev2adev(ndev); ++ printk("rsm: got adev %p\n", adev); ++ ++ acx_sem_lock(adev); ++ ++ /* ++ * Turn on the ACX. ++ */ ++ ++ acxmem_complete_hw_reset (adev); ++ ++ /* ++ * done by acx_s_set_defaults for initial startup ++ */ ++ acxmem_set_interrupt_mask(adev); ++ ++ printk ("rsm: bringing up interface\n"); ++ SET_BIT (adev->set_mask, GETSET_ALL); ++ acxmem_s_up(ndev); ++ printk("rsm: acx up done\n"); ++ ++ /* now even reload all card parameters as they were before suspend, ++ * and possibly be back in the network again already :-) ++ */ ++ /* - most settings updated in acxmem_s_up() ++ if (ACX_STATE_IFACE_UP & adev->dev_state_mask) { ++ adev->set_mask = GETSET_ALL; ++ acx_s_update_card_settings(adev); ++ printk("rsm: settings updated\n"); ++ } ++ */ ++ netif_device_attach(ndev); ++ printk("rsm: device attached\n"); ++ ++ acx_sem_unlock(adev); ++} ++ ++DECLARE_WORK( fw_resume_work, fw_resumer ); ++ ++static int ++acxmem_e_resume(struct pcmcia_device *link) ++{ ++ FN_ENTER; ++ ++ //resume_pdev = pdev; ++ schedule_work( &fw_resume_work ); ++ ++ FN_EXIT0; ++ return OK; ++} ++#endif /* CONFIG_PM */ ++ ++ ++/*********************************************************************** ++** acxmem_s_up ++** ++** This function is called by acxmem_e_open (when ifconfig sets the device as up) ++** ++** Side effects: ++** - Enables on-card interrupt requests ++** - calls acx_s_start ++*/ ++ ++static void ++enable_acx_irq(acx_device_t *adev) ++{ ++ FN_ENTER; ++ write_reg16(adev, IO_ACX_IRQ_MASK, adev->irq_mask); ++ write_reg16(adev, IO_ACX_FEMR, 0x8000); ++ adev->irqs_active = 1; ++ FN_EXIT0; ++} ++ ++static void ++acxmem_s_up(struct net_device *ndev) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ unsigned long flags; ++ ++ FN_ENTER; ++ ++ acx_lock(adev, flags); ++ enable_acx_irq(adev); ++ acx_unlock(adev, flags); ++ ++ /* acx fw < 1.9.3.e has a hardware timer, and older drivers ++ ** used to use it. But we don't do that anymore, our OS ++ ** has reliable software timers */ ++ init_timer(&adev->mgmt_timer); ++ adev->mgmt_timer.function = acx_i_timer; ++ adev->mgmt_timer.data = (unsigned long)adev; ++ ++ /* Need to set ACX_STATE_IFACE_UP first, or else ++ ** timer won't be started by acx_set_status() */ ++ SET_BIT(adev->dev_state_mask, ACX_STATE_IFACE_UP); ++ switch (adev->mode) { ++ case ACX_MODE_0_ADHOC: ++ case ACX_MODE_2_STA: ++ /* actual scan cmd will happen in start() */ ++ acx_set_status(adev, ACX_STATUS_1_SCANNING); break; ++ case ACX_MODE_3_AP: ++ case ACX_MODE_MONITOR: ++ acx_set_status(adev, ACX_STATUS_4_ASSOCIATED); break; ++ } ++ ++ acx_s_start(adev); ++ ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acxmem_s_down ++** ++** This disables the netdevice ++** ++** Side effects: ++** - disables on-card interrupt request ++*/ ++ ++static void ++disable_acx_irq(acx_device_t *adev) ++{ ++ FN_ENTER; ++ ++ /* I guess mask is not 0xffff because acx100 won't signal ++ ** cmd completion then (needed for ifup). ++ ** Someone with acx100 please confirm */ ++ write_reg16(adev, IO_ACX_IRQ_MASK, adev->irq_mask_off); ++ write_reg16(adev, IO_ACX_FEMR, 0x0); ++ adev->irqs_active = 0; ++ FN_EXIT0; ++} ++ ++static void ++acxmem_s_down(struct net_device *ndev) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ unsigned long flags; ++ ++ FN_ENTER; ++ ++ /* Disable IRQs first, so that IRQs cannot race with us */ ++ /* then wait until interrupts have finished executing on other CPUs */ ++ acx_lock(adev, flags); ++ disable_acx_irq(adev); ++ synchronize_irq(adev->pdev->irq); ++ acx_unlock(adev, flags); ++ ++ /* we really don't want to have an asynchronous tasklet disturb us ++ ** after something vital for its job has been shut down, so ++ ** end all remaining work now. ++ ** ++ ** NB: carrier_off (done by set_status below) would lead to ++ ** not yet fully understood deadlock in FLUSH_SCHEDULED_WORK(). ++ ** That's why we do FLUSH first. ++ ** ++ ** NB2: we have a bad locking bug here: FLUSH_SCHEDULED_WORK() ++ ** waits for acx_e_after_interrupt_task to complete if it is running ++ ** on another CPU, but acx_e_after_interrupt_task ++ ** will sleep on sem forever, because it is taken by us! ++ ** Work around that by temporary sem unlock. ++ ** This will fail miserably if we'll be hit by concurrent ++ ** iwconfig or something in between. TODO! */ ++ acx_sem_unlock(adev); ++ FLUSH_SCHEDULED_WORK(); ++ acx_sem_lock(adev); ++ ++ /* This is possible: ++ ** FLUSH_SCHEDULED_WORK -> acx_e_after_interrupt_task -> ++ ** -> set_status(ASSOCIATED) -> wake_queue() ++ ** That's why we stop queue _after_ FLUSH_SCHEDULED_WORK ++ ** lock/unlock is just paranoia, maybe not needed */ ++ acx_lock(adev, flags); ++ acx_stop_queue(ndev, "on ifdown"); ++ acx_set_status(adev, ACX_STATUS_0_STOPPED); ++ acx_unlock(adev, flags); ++ ++ /* kernel/timer.c says it's illegal to del_timer_sync() ++ ** a timer which restarts itself. We guarantee this cannot ++ ** ever happen because acx_i_timer() never does this if ++ ** status is ACX_STATUS_0_STOPPED */ ++ del_timer_sync(&adev->mgmt_timer); ++ ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acxmem_e_open ++** ++** Called as a result of SIOCSIFFLAGS ioctl changing the flags bit IFF_UP ++** from clear to set. In other words: ifconfig up. ++** ++** Returns: ++** 0 success ++** >0 f/w reported error ++** <0 driver reported error ++*/ ++static int ++acxmem_e_open(struct net_device *ndev) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ int result = OK; ++ ++ FN_ENTER; ++ ++ acx_sem_lock(adev); ++ ++ acx_init_task_scheduler(adev); ++ ++/* TODO: pci_set_power_state(pdev, PCI_D0); ? */ ++ ++#if 0 ++ /* request shared IRQ handler */ ++ if (request_irq(ndev->irq, acxmem_i_interrupt, SA_INTERRUPT, ndev->name, ndev)) { ++ printk("%s: request_irq FAILED\n", ndev->name); ++ result = -EAGAIN; ++ goto done; ++ } ++ set_irq_type (ndev->irq, IRQT_FALLING); ++ log(L_DEBUG|L_IRQ, "request_irq %d successful\n", ndev->irq); ++#endif ++ ++ /* ifup device */ ++ acxmem_s_up(ndev); ++ ++ /* We don't currently have to do anything else. ++ * The setup of the MAC should be subsequently completed via ++ * the mlme commands. ++ * Higher layers know we're ready from dev->start==1 and ++ * dev->tbusy==0. Our rx path knows to pass up received/ ++ * frames because of dev->flags&IFF_UP is true. ++ */ ++done: ++ acx_sem_unlock(adev); ++ ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++** acxmem_e_close ++** ++** Called as a result of SIOCSIIFFLAGS ioctl changing the flags bit IFF_UP ++** from set to clear. I.e. called by "ifconfig DEV down" ++** ++** Returns: ++** 0 success ++** >0 f/w reported error ++** <0 driver reported error ++*/ ++static int ++acxmem_e_close(struct net_device *ndev) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ ++ FN_ENTER; ++ ++ acx_sem_lock(adev); ++ ++ /* ifdown device */ ++ CLEAR_BIT(adev->dev_state_mask, ACX_STATE_IFACE_UP); ++ if (netif_device_present(ndev)) { ++ acxmem_s_down(ndev); ++ } ++ ++ /* disable all IRQs, release shared IRQ handler */ ++ write_reg16(adev, IO_ACX_IRQ_MASK, 0xffff); ++ write_reg16(adev, IO_ACX_FEMR, 0x0); ++ free_irq(ndev->irq, ndev); ++ ++/* TODO: pci_set_power_state(pdev, PCI_D3hot); ? */ ++ ++ /* We currently don't have to do anything else. ++ * Higher layers know we're not ready from dev->start==0 and ++ * dev->tbusy==1. Our rx path knows to not pass up received ++ * frames because of dev->flags&IFF_UP is false. ++ */ ++ acx_sem_unlock(adev); ++ ++ log(L_INIT, "closed device\n"); ++ FN_EXIT0; ++ return OK; ++} ++ ++ ++/*********************************************************************** ++** acxmem_i_tx_timeout ++** ++** Called from network core. Must not sleep! ++*/ ++static void ++acxmem_i_tx_timeout(struct net_device *ndev) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ unsigned long flags; ++ unsigned int tx_num_cleaned; ++ ++ FN_ENTER; ++ ++ acx_lock(adev, flags); ++ ++ /* clean processed tx descs, they may have been completely full */ ++ tx_num_cleaned = acxmem_l_clean_txdesc(adev); ++ ++ /* nothing cleaned, yet (almost) no free buffers available? ++ * --> clean all tx descs, no matter which status!! ++ * Note that I strongly suspect that doing emergency cleaning ++ * may confuse the firmware. This is a last ditch effort to get ++ * ANYTHING to work again... ++ * ++ * TODO: it's best to simply reset & reinit hw from scratch... ++ */ ++ if ((adev->tx_free <= TX_EMERG_CLEAN) && (tx_num_cleaned == 0)) { ++ printk("%s: FAILED to free any of the many full tx buffers. " ++ "Switching to emergency freeing. " ++ "Please report!\n", ndev->name); ++ acxmem_l_clean_txdesc_emergency(adev); ++ } ++ ++ if (acx_queue_stopped(ndev) && (ACX_STATUS_4_ASSOCIATED == adev->status)) ++ acx_wake_queue(ndev, "after tx timeout"); ++ ++ /* stall may have happened due to radio drift, so recalib radio */ ++ acx_schedule_task(adev, ACX_AFTER_IRQ_CMD_RADIO_RECALIB); ++ ++ /* do unimportant work last */ ++ printk("%s: tx timeout!\n", ndev->name); ++ adev->stats.tx_errors++; ++ ++ acx_unlock(adev, flags); ++ ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acxmem_i_set_multicast_list ++** FIXME: most likely needs refinement ++*/ ++static void ++acxmem_i_set_multicast_list(struct net_device *ndev) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ unsigned long flags; ++ ++ FN_ENTER; ++ ++ acx_lock(adev, flags); ++ ++ /* firmwares don't have allmulti capability, ++ * so just use promiscuous mode instead in this case. */ ++ if (ndev->flags & (IFF_PROMISC|IFF_ALLMULTI)) { ++ SET_BIT(adev->rx_config_1, RX_CFG1_RCV_PROMISCUOUS); ++ CLEAR_BIT(adev->rx_config_1, RX_CFG1_FILTER_ALL_MULTI); ++ SET_BIT(adev->set_mask, SET_RXCONFIG); ++ /* let kernel know in case *we* needed to set promiscuous */ ++ ndev->flags |= (IFF_PROMISC|IFF_ALLMULTI); ++ } else { ++ CLEAR_BIT(adev->rx_config_1, RX_CFG1_RCV_PROMISCUOUS); ++ SET_BIT(adev->rx_config_1, RX_CFG1_FILTER_ALL_MULTI); ++ SET_BIT(adev->set_mask, SET_RXCONFIG); ++ ndev->flags &= ~(IFF_PROMISC|IFF_ALLMULTI); ++ } ++ ++ /* cannot update card settings directly here, atomic context */ ++ acx_schedule_task(adev, ACX_AFTER_IRQ_UPDATE_CARD_CFG); ++ ++ acx_unlock(adev, flags); ++ ++ FN_EXIT0; ++} ++ ++ ++/*************************************************************** ++** acxmem_l_process_rxdesc ++** ++** Called directly and only from the IRQ handler ++*/ ++ ++#if !ACX_DEBUG ++static inline void log_rxbuffer(const acx_device_t *adev) {} ++#else ++static void ++log_rxbuffer(const acx_device_t *adev) ++{ ++ register const struct rxhostdesc *rxhostdesc; ++ int i; ++ /* no FN_ENTER here, we don't want that */ ++ ++ rxhostdesc = adev->rxhostdesc_start; ++ if (unlikely(!rxhostdesc)) return; ++ for (i = 0; i < RX_CNT; i++) { ++ if ((rxhostdesc->Ctl_16 & cpu_to_le16(DESC_CTL_HOSTOWN)) ++ && (rxhostdesc->Status & cpu_to_le32(DESC_STATUS_FULL))) ++ printk("rx: buf %d full\n", i); ++ rxhostdesc++; ++ } ++} ++#endif ++ ++static void ++acxmem_l_process_rxdesc(acx_device_t *adev) ++{ ++ register rxhostdesc_t *hostdesc; ++ register rxdesc_t *rxdesc; ++ unsigned count, tail; ++ u32 addr; ++ u8 Ctl_8; ++ ++ FN_ENTER; ++ ++ if (unlikely(acx_debug & L_BUFR)) ++ log_rxbuffer(adev); ++ ++ /* First, have a loop to determine the first descriptor that's ++ * full, just in case there's a mismatch between our current ++ * rx_tail and the full descriptor we're supposed to handle. */ ++ tail = adev->rx_tail; ++ count = RX_CNT; ++ while (1) { ++ hostdesc = &adev->rxhostdesc_start[tail]; ++ rxdesc = &adev->rxdesc_start[tail]; ++ /* advance tail regardless of outcome of the below test */ ++ tail = (tail + 1) % RX_CNT; ++ ++ /* ++ * Unlike the PCI interface, where the ACX can write directly to ++ * the host descriptors, on the slave memory interface we have to ++ * pull these. All we really need to do is check the Ctl_8 field ++ * in the rx descriptor on the ACX, which should be 0x11000000 if ++ * we should process it. ++ */ ++ Ctl_8 = hostdesc->Ctl_16 = read_slavemem8 (adev, (u32) &(rxdesc->Ctl_8)); ++ if ((Ctl_8 & DESC_CTL_HOSTOWN) && ++ (Ctl_8 & DESC_CTL_ACXDONE)) ++ break; /* found it! */ ++ ++ if (unlikely(!--count)) /* hmm, no luck: all descs empty, bail out */ ++ goto end; ++ } ++ ++ /* now process descriptors, starting with the first we figured out */ ++ while (1) { ++ log(L_BUFR, "rx: tail=%u Ctl_8=%02X\n", tail, Ctl_8); ++ /* ++ * If the ACX has CTL_RECLAIM set on this descriptor there ++ * is no buffer associated; it just wants us to tell it to ++ * reclaim the memory. ++ */ ++ if (!(Ctl_8 & DESC_CTL_RECLAIM)) { ++ ++ /* ++ * slave interface - pull data now ++ */ ++ hostdesc->length = read_slavemem16 (adev, (u32) &(rxdesc->total_length)); ++ ++ /* ++ * hostdesc->data is an rxbuffer_t, which includes header information, ++ * but the length in the data packet doesn't. The header information ++ * takes up an additional 12 bytes, so add that to the length we copy. ++ */ ++ addr = read_slavemem32 (adev, (u32) &(rxdesc->ACXMemPtr)); ++ if (addr) { ++ /* ++ * How can &(rxdesc->ACXMemPtr) above ever be zero? Looks like we ++ * get that now and then - try to trap it for debug. ++ */ ++ if (addr & 0xffff0000) { ++ printk("rxdesc 0x%08x\n", (u32) rxdesc); ++ dump_acxmem (adev, 0, 0x10000); ++ panic ("Bad access!"); ++ } ++ chaincopy_from_slavemem (adev, (u8 *) hostdesc->data, addr, ++ hostdesc->length + ++ (u32) &((rxbuffer_t *)0)->hdr_a3); ++ acx_l_process_rxbuf(adev, hostdesc->data); ++ } ++ } ++ else { ++ printk ("rx reclaim only!\n"); ++ } ++ ++ hostdesc->Status = 0; ++ ++ /* ++ * Let the ACX know we're done. ++ */ ++ CLEAR_BIT (Ctl_8, DESC_CTL_HOSTOWN); ++ SET_BIT (Ctl_8, DESC_CTL_HOSTDONE); ++ SET_BIT (Ctl_8, DESC_CTL_RECLAIM); ++ write_slavemem8 (adev, (u32) &rxdesc->Ctl_8, Ctl_8); ++ ++ /* ++ * Now tell the ACX we've finished with the receive buffer so ++ * it can finish the reclaim. ++ */ ++ write_reg16 (adev, IO_ACX_INT_TRIG, INT_TRIG_RXPRC); ++ ++ /* ok, descriptor is handled, now check the next descriptor */ ++ hostdesc = &adev->rxhostdesc_start[tail]; ++ rxdesc = &adev->rxdesc_start[tail]; ++ ++ Ctl_8 = hostdesc->Ctl_16 = read_slavemem8 (adev, (u32) &(rxdesc->Ctl_8)); ++ ++ /* if next descriptor is empty, then bail out */ ++ if (!(Ctl_8 & DESC_CTL_HOSTOWN) || !(Ctl_8 & DESC_CTL_ACXDONE)) ++ break; ++ ++ tail = (tail + 1) % RX_CNT; ++ } ++end: ++ adev->rx_tail = tail; ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acxmem_i_interrupt ++** ++** IRQ handler (atomic context, must not sleep, blah, blah) ++*/ ++ ++/* scan is complete. all frames now on the receive queue are valid */ ++#define INFO_SCAN_COMPLETE 0x0001 ++#define INFO_WEP_KEY_NOT_FOUND 0x0002 ++/* hw has been reset as the result of a watchdog timer timeout */ ++#define INFO_WATCH_DOG_RESET 0x0003 ++/* failed to send out NULL frame from PS mode notification to AP */ ++/* recommended action: try entering 802.11 PS mode again */ ++#define INFO_PS_FAIL 0x0004 ++/* encryption/decryption process on a packet failed */ ++#define INFO_IV_ICV_FAILURE 0x0005 ++ ++/* Info mailbox format: ++2 bytes: type ++2 bytes: status ++more bytes may follow ++ rumors say about status: ++ 0x0000 info available (set by hw) ++ 0x0001 information received (must be set by host) ++ 0x1000 info available, mailbox overflowed (messages lost) (set by hw) ++ but in practice we've seen: ++ 0x9000 when we did not set status to 0x0001 on prev message ++ 0x1001 when we did set it ++ 0x0000 was never seen ++ conclusion: this is really a bitfield: ++ 0x1000 is 'info available' bit ++ 'mailbox overflowed' bit is 0x8000, not 0x1000 ++ value of 0x0000 probably means that there are no messages at all ++ P.S. I dunno how in hell hw is supposed to notice that messages are lost - ++ it does NOT clear bit 0x0001, and this bit will probably stay forever set ++ after we set it once. Let's hope this will be fixed in firmware someday ++*/ ++ ++static void ++handle_info_irq(acx_device_t *adev) ++{ ++#if ACX_DEBUG ++ static const char * const info_type_msg[] = { ++ "(unknown)", ++ "scan complete", ++ "WEP key not found", ++ "internal watchdog reset was done", ++ "failed to send powersave (NULL frame) notification to AP", ++ "encrypt/decrypt on a packet has failed", ++ "TKIP tx keys disabled", ++ "TKIP rx keys disabled", ++ "TKIP rx: key ID not found", ++ "???", ++ "???", ++ "???", ++ "???", ++ "???", ++ "???", ++ "???", ++ "TKIP IV value exceeds thresh" ++ }; ++#endif ++ u32 info_type, info_status; ++ ++ info_type = read_slavemem32 (adev, (u32) adev->info_area); ++ ++ info_status = (info_type >> 16); ++ info_type = (u16)info_type; ++ ++ /* inform fw that we have read this info message */ ++ write_slavemem32(adev, (u32) adev->info_area, info_type | 0x00010000); ++ write_reg16(adev, IO_ACX_INT_TRIG, INT_TRIG_INFOACK); ++ write_flush(adev); ++ ++ log(L_CTL, "info_type:%04X info_status:%04X\n", ++ info_type, info_status); ++ ++ log(L_IRQ, "got Info IRQ: status %04X type %04X: %s\n", ++ info_status, info_type, ++ info_type_msg[(info_type >= VEC_SIZE(info_type_msg)) ? ++ 0 : info_type] ++ ); ++} ++ ++ ++static void ++log_unusual_irq(u16 irqtype) { ++ /* ++ if (!printk_ratelimit()) ++ return; ++ */ ++ ++ printk("acx: got"); ++ if (irqtype & HOST_INT_TX_XFER) { ++ printk(" Tx_Xfer"); ++ } ++ if (irqtype & HOST_INT_RX_COMPLETE) { ++ printk(" Rx_Complete"); ++ } ++ if (irqtype & HOST_INT_DTIM) { ++ printk(" DTIM"); ++ } ++ if (irqtype & HOST_INT_BEACON) { ++ printk(" Beacon"); ++ } ++ if (irqtype & HOST_INT_TIMER) { ++ log(L_IRQ, " Timer"); ++ } ++ if (irqtype & HOST_INT_KEY_NOT_FOUND) { ++ printk(" Key_Not_Found"); ++ } ++ if (irqtype & HOST_INT_IV_ICV_FAILURE) { ++ printk(" IV_ICV_Failure (crypto)"); ++ } ++ /* HOST_INT_CMD_COMPLETE */ ++ /* HOST_INT_INFO */ ++ if (irqtype & HOST_INT_OVERFLOW) { ++ printk(" Overflow"); ++ } ++ if (irqtype & HOST_INT_PROCESS_ERROR) { ++ printk(" Process_Error"); ++ } ++ /* HOST_INT_SCAN_COMPLETE */ ++ if (irqtype & HOST_INT_FCS_THRESHOLD) { ++ printk(" FCS_Threshold"); ++ } ++ if (irqtype & HOST_INT_UNKNOWN) { ++ printk(" Unknown"); ++ } ++ printk(" IRQ(s)\n"); ++} ++ ++ ++static void ++update_link_quality_led(acx_device_t *adev) ++{ ++ int qual; ++ ++ qual = acx_signal_determine_quality(adev->wstats.qual.level, adev->wstats.qual.noise); ++ if (qual > adev->brange_max_quality) ++ qual = adev->brange_max_quality; ++ ++ if (time_after(jiffies, adev->brange_time_last_state_change + ++ (HZ/2 - HZ/2 * (unsigned long)qual / adev->brange_max_quality ) )) { ++ acxmem_l_power_led(adev, (adev->brange_last_state == 0)); ++ adev->brange_last_state ^= 1; /* toggle */ ++ adev->brange_time_last_state_change = jiffies; ++ } ++} ++ ++ ++#define MAX_IRQLOOPS_PER_JIFFY (20000/HZ) /* a la orinoco.c */ ++ ++static irqreturn_t ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) ++acxmem_i_interrupt(int irq, void *dev_id) ++#else ++acxmwm_i_interrupt(int irq, void *dev_id, struct pt_regs *regs) ++#endif ++{ ++ acx_device_t *adev; ++ unsigned long flags; ++ unsigned int irqcount = MAX_IRQLOOPS_PER_JIFFY; ++ register u16 irqtype; ++ u16 unmasked; ++ ++ adev = ndev2adev((struct net_device*)dev_id); ++ ++ /* LOCKING: can just spin_lock() since IRQs are disabled anyway. ++ * I am paranoid */ ++ acx_lock(adev, flags); ++ ++ unmasked = read_reg16(adev, IO_ACX_IRQ_STATUS_CLEAR); ++ if (unlikely(0xffff == unmasked)) { ++ /* 0xffff value hints at missing hardware, ++ * so don't do anything. ++ * Not very clean, but other drivers do the same... */ ++ log(L_IRQ, "IRQ type:FFFF - device removed? IRQ_NONE\n"); ++ goto none; ++ } ++ ++ /* We will check only "interesting" IRQ types */ ++ irqtype = unmasked & ~adev->irq_mask; ++ if (!irqtype) { ++ /* We are on a shared IRQ line and it wasn't our IRQ */ ++ log(L_IRQ, "IRQ type:%04X, mask:%04X - all are masked, IRQ_NONE\n", ++ unmasked, adev->irq_mask); ++ goto none; ++ } ++ ++ /* Done here because IRQ_NONEs taking three lines of log ++ ** drive me crazy */ ++ FN_ENTER; ++ ++#define IRQ_ITERATE 1 ++#if IRQ_ITERATE ++if (jiffies != adev->irq_last_jiffies) { ++ adev->irq_loops_this_jiffy = 0; ++ adev->irq_last_jiffies = jiffies; ++} ++ ++/* safety condition; we'll normally abort loop below ++ * in case no IRQ type occurred */ ++while (likely(--irqcount)) { ++#endif ++ /* ACK all IRQs ASAP */ ++ write_reg16(adev, IO_ACX_IRQ_ACK, 0xffff); ++ ++ log(L_IRQ, "IRQ type:%04X, mask:%04X, type & ~mask:%04X\n", ++ unmasked, adev->irq_mask, irqtype); ++ ++ /* Handle most important IRQ types first */ ++ if (irqtype & HOST_INT_RX_DATA) { ++ log(L_IRQ, "got Rx_Data IRQ\n"); ++ acxmem_l_process_rxdesc(adev); ++ } ++ if (irqtype & HOST_INT_TX_COMPLETE) { ++ log(L_IRQ, "got Tx_Complete IRQ\n"); ++ /* don't clean up on each Tx complete, wait a bit ++ * unless we're going towards full, in which case ++ * we do it immediately, too (otherwise we might lockup ++ * with a full Tx buffer if we go into ++ * acxmem_l_clean_txdesc() at a time when we won't wakeup ++ * the net queue in there for some reason...) */ ++ if (adev->tx_free <= TX_START_CLEAN) { ++#if TX_CLEANUP_IN_SOFTIRQ ++ acx_schedule_task(adev, ACX_AFTER_IRQ_TX_CLEANUP); ++#else ++ acxmem_l_clean_txdesc(adev); ++#endif ++ } ++ } ++ ++ /* Less frequent ones */ ++ if (irqtype & (0 ++ | HOST_INT_CMD_COMPLETE ++ | HOST_INT_INFO ++ | HOST_INT_SCAN_COMPLETE ++ )) { ++ if (irqtype & HOST_INT_CMD_COMPLETE) { ++ log(L_IRQ, "got Command_Complete IRQ\n"); ++ /* save the state for the running issue_cmd() */ ++ SET_BIT(adev->irq_status, HOST_INT_CMD_COMPLETE); ++ } ++ if (irqtype & HOST_INT_INFO) { ++ handle_info_irq(adev); ++ } ++ if (irqtype & HOST_INT_SCAN_COMPLETE) { ++ log(L_IRQ, "got Scan_Complete IRQ\n"); ++ /* need to do that in process context */ ++ acx_schedule_task(adev, ACX_AFTER_IRQ_COMPLETE_SCAN); ++ /* remember that fw is not scanning anymore */ ++ SET_BIT(adev->irq_status, HOST_INT_SCAN_COMPLETE); ++ } ++ } ++ ++ /* These we just log, but either they happen rarely ++ * or we keep them masked out */ ++ if (irqtype & (0 ++ /* | HOST_INT_RX_DATA */ ++ /* | HOST_INT_TX_COMPLETE */ ++ | HOST_INT_TX_XFER ++ | HOST_INT_RX_COMPLETE ++ | HOST_INT_DTIM ++ | HOST_INT_BEACON ++ | HOST_INT_TIMER ++ | HOST_INT_KEY_NOT_FOUND ++ | HOST_INT_IV_ICV_FAILURE ++ /* | HOST_INT_CMD_COMPLETE */ ++ /* | HOST_INT_INFO */ ++ | HOST_INT_OVERFLOW ++ | HOST_INT_PROCESS_ERROR ++ /* | HOST_INT_SCAN_COMPLETE */ ++ | HOST_INT_FCS_THRESHOLD ++ | HOST_INT_UNKNOWN ++ )) { ++ log_unusual_irq(irqtype); ++ } ++ ++#if IRQ_ITERATE ++ unmasked = read_reg16(adev, IO_ACX_IRQ_STATUS_CLEAR); ++ irqtype = unmasked & ~adev->irq_mask; ++ /* Bail out if no new IRQ bits or if all are masked out */ ++ if (!irqtype) ++ break; ++ ++ if (unlikely(++adev->irq_loops_this_jiffy > MAX_IRQLOOPS_PER_JIFFY)) { ++ printk(KERN_ERR "acx: too many interrupts per jiffy!\n"); ++ /* Looks like card floods us with IRQs! Try to stop that */ ++ write_reg16(adev, IO_ACX_IRQ_MASK, 0xffff); ++ /* This will short-circuit all future attempts to handle IRQ. ++ * We cant do much more... */ ++ adev->irq_mask = 0; ++ break; ++ } ++} ++#endif ++ /* Routine to perform blink with range */ ++ if (unlikely(adev->led_power == 2)) ++ update_link_quality_led(adev); ++ ++/* handled: */ ++ /* write_flush(adev); - not needed, last op was read anyway */ ++ acx_unlock(adev, flags); ++ FN_EXIT0; ++ return IRQ_HANDLED; ++ ++none: ++ acx_unlock(adev, flags); ++ return IRQ_NONE; ++} ++ ++ ++/*********************************************************************** ++** acxmem_l_power_led ++*/ ++void ++acxmem_l_power_led(acx_device_t *adev, int enable) ++{ ++ u16 gpio_pled = IS_ACX111(adev) ? 0x0040 : 0x0800; ++ ++ /* A hack. Not moving message rate limiting to adev->xxx ++ * (it's only a debug message after all) */ ++ static int rate_limit = 0; ++ ++ if (rate_limit++ < 3) ++ log(L_IOCTL, "Please report in case toggling the power " ++ "LED doesn't work for your card!\n"); ++ if (enable) ++ write_reg16(adev, IO_ACX_GPIO_OUT, ++ read_reg16(adev, IO_ACX_GPIO_OUT) & ~gpio_pled); ++ else ++ write_reg16(adev, IO_ACX_GPIO_OUT, ++ read_reg16(adev, IO_ACX_GPIO_OUT) | gpio_pled); ++} ++ ++ ++/*********************************************************************** ++** Ioctls ++*/ ++ ++/*********************************************************************** ++*/ ++int ++acx111pci_ioctl_info( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ struct iw_param *vwrq, ++ char *extra) ++{ ++#if ACX_DEBUG > 1 ++ acx_device_t *adev = ndev2adev(ndev); ++ rxdesc_t *rxdesc; ++ txdesc_t *txdesc; ++ rxhostdesc_t *rxhostdesc; ++ txhostdesc_t *txhostdesc; ++ struct acx111_ie_memoryconfig memconf; ++ struct acx111_ie_queueconfig queueconf; ++ unsigned long flags; ++ int i; ++ char memmap[0x34]; ++ char rxconfig[0x8]; ++ char fcserror[0x8]; ++ char ratefallback[0x5]; ++ ++ if ( !(acx_debug & (L_IOCTL|L_DEBUG)) ) ++ return OK; ++ /* using printk() since we checked debug flag already */ ++ ++ acx_sem_lock(adev); ++ ++ if (!IS_ACX111(adev)) { ++ printk("acx111-specific function called " ++ "with non-acx111 chip, aborting\n"); ++ goto end_ok; ++ } ++ ++ /* get Acx111 Memory Configuration */ ++ memset(&memconf, 0, sizeof(memconf)); ++ /* BTW, fails with 12 (Write only) error code. ++ ** Retained for easy testing of issue_cmd error handling :) */ ++ printk ("Interrogating queue config\n"); ++ acx_s_interrogate(adev, &memconf, ACX1xx_IE_QUEUE_CONFIG); ++ printk ("done with queue config\n"); ++ ++ /* get Acx111 Queue Configuration */ ++ memset(&queueconf, 0, sizeof(queueconf)); ++ printk ("Interrogating mem config options\n"); ++ acx_s_interrogate(adev, &queueconf, ACX1xx_IE_MEMORY_CONFIG_OPTIONS); ++ printk ("done with mem config options\n"); ++ ++ /* get Acx111 Memory Map */ ++ memset(memmap, 0, sizeof(memmap)); ++ printk ("Interrogating mem map\n"); ++ acx_s_interrogate(adev, &memmap, ACX1xx_IE_MEMORY_MAP); ++ printk ("done with mem map\n"); ++ ++ /* get Acx111 Rx Config */ ++ memset(rxconfig, 0, sizeof(rxconfig)); ++ printk ("Interrogating rxconfig\n"); ++ acx_s_interrogate(adev, &rxconfig, ACX1xx_IE_RXCONFIG); ++ printk ("done with queue rxconfig\n"); ++ ++ /* get Acx111 fcs error count */ ++ memset(fcserror, 0, sizeof(fcserror)); ++ printk ("Interrogating fcs err count\n"); ++ acx_s_interrogate(adev, &fcserror, ACX1xx_IE_FCS_ERROR_COUNT); ++ printk ("done with err count\n"); ++ ++ /* get Acx111 rate fallback */ ++ memset(ratefallback, 0, sizeof(ratefallback)); ++ printk ("Interrogating rate fallback\n"); ++ acx_s_interrogate(adev, &ratefallback, ACX1xx_IE_RATE_FALLBACK); ++ printk ("done with rate fallback\n"); ++ ++ /* force occurrence of a beacon interrupt */ ++ /* TODO: comment why is this necessary */ ++ write_reg16(adev, IO_ACX_HINT_TRIG, HOST_INT_BEACON); ++ ++ /* dump Acx111 Mem Configuration */ ++ printk("dump mem config:\n" ++ "data read: %d, struct size: %d\n" ++ "Number of stations: %1X\n" ++ "Memory block size: %1X\n" ++ "tx/rx memory block allocation: %1X\n" ++ "count rx: %X / tx: %X queues\n" ++ "options %1X\n" ++ "fragmentation %1X\n" ++ "Rx Queue 1 Count Descriptors: %X\n" ++ "Rx Queue 1 Host Memory Start: %X\n" ++ "Tx Queue 1 Count Descriptors: %X\n" ++ "Tx Queue 1 Attributes: %X\n", ++ memconf.len, (int) sizeof(memconf), ++ memconf.no_of_stations, ++ memconf.memory_block_size, ++ memconf.tx_rx_memory_block_allocation, ++ memconf.count_rx_queues, memconf.count_tx_queues, ++ memconf.options, ++ memconf.fragmentation, ++ memconf.rx_queue1_count_descs, ++ acx2cpu(memconf.rx_queue1_host_rx_start), ++ memconf.tx_queue1_count_descs, ++ memconf.tx_queue1_attributes); ++ ++ /* dump Acx111 Queue Configuration */ ++ printk("dump queue head:\n" ++ "data read: %d, struct size: %d\n" ++ "tx_memory_block_address (from card): %X\n" ++ "rx_memory_block_address (from card): %X\n" ++ "rx1_queue address (from card): %X\n" ++ "tx1_queue address (from card): %X\n" ++ "tx1_queue attributes (from card): %X\n", ++ queueconf.len, (int) sizeof(queueconf), ++ queueconf.tx_memory_block_address, ++ queueconf.rx_memory_block_address, ++ queueconf.rx1_queue_address, ++ queueconf.tx1_queue_address, ++ queueconf.tx1_attributes); ++ ++ /* dump Acx111 Mem Map */ ++ printk("dump mem map:\n" ++ "data read: %d, struct size: %d\n" ++ "Code start: %X\n" ++ "Code end: %X\n" ++ "WEP default key start: %X\n" ++ "WEP default key end: %X\n" ++ "STA table start: %X\n" ++ "STA table end: %X\n" ++ "Packet template start: %X\n" ++ "Packet template end: %X\n" ++ "Queue memory start: %X\n" ++ "Queue memory end: %X\n" ++ "Packet memory pool start: %X\n" ++ "Packet memory pool end: %X\n" ++ "iobase: %p\n" ++ "iobase2: %p\n", ++ *((u16 *)&memmap[0x02]), (int) sizeof(memmap), ++ *((u32 *)&memmap[0x04]), ++ *((u32 *)&memmap[0x08]), ++ *((u32 *)&memmap[0x0C]), ++ *((u32 *)&memmap[0x10]), ++ *((u32 *)&memmap[0x14]), ++ *((u32 *)&memmap[0x18]), ++ *((u32 *)&memmap[0x1C]), ++ *((u32 *)&memmap[0x20]), ++ *((u32 *)&memmap[0x24]), ++ *((u32 *)&memmap[0x28]), ++ *((u32 *)&memmap[0x2C]), ++ *((u32 *)&memmap[0x30]), ++ adev->iobase, ++ adev->iobase2); ++ ++ /* dump Acx111 Rx Config */ ++ printk("dump rx config:\n" ++ "data read: %d, struct size: %d\n" ++ "rx config: %X\n" ++ "rx filter config: %X\n", ++ *((u16 *)&rxconfig[0x02]), (int) sizeof(rxconfig), ++ *((u16 *)&rxconfig[0x04]), ++ *((u16 *)&rxconfig[0x06])); ++ ++ /* dump Acx111 fcs error */ ++ printk("dump fcserror:\n" ++ "data read: %d, struct size: %d\n" ++ "fcserrors: %X\n", ++ *((u16 *)&fcserror[0x02]), (int) sizeof(fcserror), ++ *((u32 *)&fcserror[0x04])); ++ ++ /* dump Acx111 rate fallback */ ++ printk("dump rate fallback:\n" ++ "data read: %d, struct size: %d\n" ++ "ratefallback: %X\n", ++ *((u16 *)&ratefallback[0x02]), (int) sizeof(ratefallback), ++ *((u8 *)&ratefallback[0x04])); ++ ++ /* protect against IRQ */ ++ acx_lock(adev, flags); ++ ++ /* dump acx111 internal rx descriptor ring buffer */ ++ rxdesc = adev->rxdesc_start; ++ ++ /* loop over complete receive pool */ ++ if (rxdesc) for (i = 0; i < RX_CNT; i++) { ++ printk("\ndump internal rxdesc %d:\n" ++ "mem pos %p\n" ++ "next 0x%X\n" ++ "acx mem pointer (dynamic) 0x%X\n" ++ "CTL (dynamic) 0x%X\n" ++ "Rate (dynamic) 0x%X\n" ++ "RxStatus (dynamic) 0x%X\n" ++ "Mod/Pre (dynamic) 0x%X\n", ++ i, ++ rxdesc, ++ acx2cpu(rxdesc->pNextDesc), ++ acx2cpu(rxdesc->ACXMemPtr), ++ rxdesc->Ctl_8, ++ rxdesc->rate, ++ rxdesc->error, ++ rxdesc->SNR); ++ rxdesc++; ++ } ++ ++ /* dump host rx descriptor ring buffer */ ++ ++ rxhostdesc = adev->rxhostdesc_start; ++ ++ /* loop over complete receive pool */ ++ if (rxhostdesc) for (i = 0; i < RX_CNT; i++) { ++ printk("\ndump host rxdesc %d:\n" ++ "mem pos %p\n" ++ "buffer mem pos 0x%X\n" ++ "buffer mem offset 0x%X\n" ++ "CTL 0x%X\n" ++ "Length 0x%X\n" ++ "next 0x%X\n" ++ "Status 0x%X\n", ++ i, ++ rxhostdesc, ++ acx2cpu(rxhostdesc->data_phy), ++ rxhostdesc->data_offset, ++ le16_to_cpu(rxhostdesc->Ctl_16), ++ le16_to_cpu(rxhostdesc->length), ++ acx2cpu(rxhostdesc->desc_phy_next), ++ rxhostdesc->Status); ++ rxhostdesc++; ++ } ++ ++ /* dump acx111 internal tx descriptor ring buffer */ ++ txdesc = adev->txdesc_start; ++ ++ /* loop over complete transmit pool */ ++ if (txdesc) for (i = 0; i < TX_CNT; i++) { ++ printk("\ndump internal txdesc %d:\n" ++ "size 0x%X\n" ++ "mem pos %p\n" ++ "next 0x%X\n" ++ "acx mem pointer (dynamic) 0x%X\n" ++ "host mem pointer (dynamic) 0x%X\n" ++ "length (dynamic) 0x%X\n" ++ "CTL (dynamic) 0x%X\n" ++ "CTL2 (dynamic) 0x%X\n" ++ "Status (dynamic) 0x%X\n" ++ "Rate (dynamic) 0x%X\n", ++ i, ++ (int) sizeof(struct txdesc), ++ txdesc, ++ acx2cpu(txdesc->pNextDesc), ++ acx2cpu(txdesc->AcxMemPtr), ++ acx2cpu(txdesc->HostMemPtr), ++ le16_to_cpu(txdesc->total_length), ++ txdesc->Ctl_8, ++ txdesc->Ctl2_8, txdesc->error, ++ txdesc->u.r1.rate); ++ txdesc = advance_txdesc(adev, txdesc, 1); ++ } ++ ++ /* dump host tx descriptor ring buffer */ ++ ++ txhostdesc = adev->txhostdesc_start; ++ ++ /* loop over complete host send pool */ ++ if (txhostdesc) for (i = 0; i < TX_CNT * 2; i++) { ++ printk("\ndump host txdesc %d:\n" ++ "mem pos %p\n" ++ "buffer mem pos 0x%X\n" ++ "buffer mem offset 0x%X\n" ++ "CTL 0x%X\n" ++ "Length 0x%X\n" ++ "next 0x%X\n" ++ "Status 0x%X\n", ++ i, ++ txhostdesc, ++ acx2cpu(txhostdesc->data_phy), ++ txhostdesc->data_offset, ++ le16_to_cpu(txhostdesc->Ctl_16), ++ le16_to_cpu(txhostdesc->length), ++ acx2cpu(txhostdesc->desc_phy_next), ++ le32_to_cpu(txhostdesc->Status)); ++ txhostdesc++; ++ } ++ ++ /* write_reg16(adev, 0xb4, 0x4); */ ++ ++ acx_unlock(adev, flags); ++end_ok: ++ ++ acx_sem_unlock(adev); ++#endif /* ACX_DEBUG */ ++ return OK; ++} ++ ++ ++/*********************************************************************** ++*/ ++int ++acx100mem_ioctl_set_phy_amp_bias( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ struct iw_param *vwrq, ++ char *extra) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ unsigned long flags; ++ u16 gpio_old; ++ ++ if (!IS_ACX100(adev)) { ++ /* WARNING!!! ++ * Removing this check *might* damage ++ * hardware, since we're tweaking GPIOs here after all!!! ++ * You've been warned... ++ * WARNING!!! */ ++ printk("acx: sorry, setting bias level for non-acx100 " ++ "is not supported yet\n"); ++ return OK; ++ } ++ ++ if (*extra > 7) { ++ printk("acx: invalid bias parameter, range is 0-7\n"); ++ return -EINVAL; ++ } ++ ++ acx_sem_lock(adev); ++ ++ /* Need to lock accesses to [IO_ACX_GPIO_OUT]: ++ * IRQ handler uses it to update LED */ ++ acx_lock(adev, flags); ++ gpio_old = read_reg16(adev, IO_ACX_GPIO_OUT); ++ write_reg16(adev, IO_ACX_GPIO_OUT, (gpio_old & 0xf8ff) | ((u16)*extra << 8)); ++ acx_unlock(adev, flags); ++ ++ log(L_DEBUG, "gpio_old: 0x%04X\n", gpio_old); ++ printk("%s: PHY power amplifier bias: old:%d, new:%d\n", ++ ndev->name, ++ (gpio_old & 0x0700) >> 8, (unsigned char)*extra); ++ ++ acx_sem_unlock(adev); ++ ++ return OK; ++} ++ ++/*************************************************************** ++** acxmem_l_alloc_tx ++** Actually returns a txdesc_t* ptr ++** ++** FIXME: in case of fragments, should allocate multiple descrs ++** after figuring out how many we need and whether we still have ++** sufficiently many. ++*/ ++tx_t* ++acxmem_l_alloc_tx(acx_device_t *adev) ++{ ++ struct txdesc *txdesc; ++ unsigned head; ++ u8 ctl8; ++ static int txattempts = 0; ++ ++ FN_ENTER; ++ ++ if (unlikely(!adev->tx_free)) { ++ printk("acx: BUG: no free txdesc left\n"); ++ /* ++ * Probably the ACX ignored a transmit attempt and now there's a packet ++ * sitting in the queue we think should be transmitting but the ACX doesn't ++ * know about. ++ * On the first pass, send the ACX a TxProc interrupt to try moving ++ * things along, and if that doesn't work (ie, we get called again) completely ++ * flush the transmit queue. ++ */ ++ if (txattempts < 10) { ++ txattempts++; ++ printk ("acx: trying to wake up ACX\n"); ++ write_reg16(adev, IO_ACX_INT_TRIG, INT_TRIG_TXPRC); ++ write_flush(adev); } ++ else { ++ txattempts = 0; ++ printk ("acx: flushing transmit queue.\n"); ++ acxmem_l_clean_txdesc_emergency (adev); ++ } ++ txdesc = NULL; ++ goto end; ++ } ++ ++ /* ++ * Make a quick check to see if there is transmit buffer space on ++ * the ACX. This can't guarantee there is enough space for the packet ++ * since we don't yet know how big it is, but it will prevent at least some ++ * annoyances. ++ */ ++ if (!adev->acx_txbuf_blocks_free) { ++ txdesc = NULL; ++ goto end; ++ } ++ ++ head = adev->tx_head; ++ /* ++ * txdesc points to ACX memory ++ */ ++ txdesc = get_txdesc(adev, head); ++ ctl8 = read_slavemem8 (adev, (u32) &(txdesc->Ctl_8)); ++ ++ /* ++ * If we don't own the buffer (HOSTOWN) it is certainly not free; however, ++ * we may have previously thought we had enough memory to send ++ * a packet, allocated the buffer then gave up when we found not enough ++ * transmit buffer space on the ACX. In that case, HOSTOWN and ++ * ACXDONE will both be set. ++ */ ++ if (unlikely(DESC_CTL_HOSTOWN != (ctl8 & DESC_CTL_HOSTOWN))) { ++ /* whoops, descr at current index is not free, so probably ++ * ring buffer already full */ ++ printk("acx: BUG: tx_head:%d Ctl8:0x%02X - failed to find " ++ "free txdesc\n", head, ctl8); ++ txdesc = NULL; ++ goto end; ++ } ++ ++ /* Needed in case txdesc won't be eventually submitted for tx */ ++ write_slavemem8 (adev, (u32) &(txdesc->Ctl_8), DESC_CTL_ACXDONE_HOSTOWN); ++ ++ adev->tx_free--; ++ log(L_BUFT, "tx: got desc %u, %u remain\n", ++ head, adev->tx_free); ++ /* Keep a few free descs between head and tail of tx ring. ++ ** It is not absolutely needed, just feels safer */ ++ if (adev->tx_free < TX_STOP_QUEUE) { ++ log(L_BUF, "stop queue (%u tx desc left)\n", ++ adev->tx_free); ++ acx_stop_queue(adev->ndev, NULL); ++ } ++ ++ /* returning current descriptor, so advance to next free one */ ++ adev->tx_head = (head + 1) % TX_CNT; ++end: ++ FN_EXIT0; ++ ++ return (tx_t*)txdesc; ++} ++ ++ ++/*************************************************************** ++** acxmem_l_dealloc_tx ++** Clears out a previously allocatedvoid acxmem_l_dealloc_tx(tx_t *tx_opaque); ++ transmit descriptor. The ACX ++** can get confused if we skip transmit descriptors in the queue, ++** so when we don't need a descriptor return it to its original ++** state and move the queue head pointer back. ++** ++*/ ++void ++acxmem_l_dealloc_tx(acx_device_t *adev, tx_t *tx_opaque) ++{ ++ /* ++ * txdesc is the address of the descriptor on the ACX. ++ */ ++ txdesc_t *txdesc = (txdesc_t*)tx_opaque; ++ txdesc_t tmptxdesc; ++ int index; ++ ++ memset (&tmptxdesc, 0, sizeof(tmptxdesc)); ++ tmptxdesc.Ctl_8 = DESC_CTL_HOSTOWN | DESC_CTL_FIRSTFRAG; ++ tmptxdesc.u.r1.rate = 0x0a; ++ ++ /* ++ * Clear out all of the transmit descriptor except for the next pointer ++ */ ++ copy_to_slavemem (adev, (u32) &(txdesc->HostMemPtr), ++ (u8 *) &(tmptxdesc.HostMemPtr), ++ sizeof (tmptxdesc) - sizeof(tmptxdesc.pNextDesc)); ++ ++ /* ++ * This is only called immediately after we've allocated, so we should ++ * be able to set the head back to this descriptor. ++ */ ++ index = ((u8*) txdesc - (u8*)adev->txdesc_start) / adev->txdesc_size; ++ printk ("acx_dealloc: moving head from %d to %d\n", adev->tx_head, index); ++ adev->tx_head = index; ++} ++ ++ ++/*********************************************************************** ++*/ ++void* ++acxmem_l_get_txbuf(acx_device_t *adev, tx_t* tx_opaque) ++{ ++ return get_txhostdesc(adev, (txdesc_t*)tx_opaque)->data; ++} ++ ++ ++/*********************************************************************** ++** acxmem_l_tx_data ++** ++** Can be called from IRQ (rx -> (AP bridging or mgmt response) -> tx). ++** Can be called from acx_i_start_xmit (data frames from net core). ++** ++** FIXME: in case of fragments, should loop over the number of ++** pre-allocated tx descrs, properly setting up transfer data and ++** CTL_xxx flags according to fragment number. ++*/ ++void ++acxmem_update_queue_indicator (acx_device_t *adev, int txqueue) ++{ ++#ifdef USING_MORE_THAN_ONE_TRANSMIT_QUEUE ++ u32 indicator; ++ unsigned long flags; ++ int count; ++ ++ /* ++ * Can't handle an interrupt while we're fiddling with the ACX's lock, ++ * according to TI. The ACX is supposed to hold fw_lock for at most ++ * 500ns. ++ */ ++ local_irq_save (flags); ++ ++ /* ++ * Wait for ACX to release the lock (at most 500ns). ++ */ ++ count = 0; ++ while (read_slavemem16 (adev, (u32) &(adev->acx_queue_indicator->fw_lock)) ++ && (count++ < 50)) { ++ ndelay (10); ++ } ++ if (count < 50) { ++ ++ /* ++ * Take out the host lock - anything non-zero will work, so don't worry about ++ * be/le ++ */ ++ write_slavemem16 (adev, (u32) &(adev->acx_queue_indicator->host_lock), 1); ++ ++ /* ++ * Avoid a race condition ++ */ ++ count = 0; ++ while (read_slavemem16 (adev, (u32) &(adev->acx_queue_indicator->fw_lock)) ++ && (count++ < 50)) { ++ ndelay (10); ++ } ++ ++ if (count < 50) { ++ /* ++ * Mark the queue active ++ */ ++ indicator = read_slavemem32 (adev, (u32) &(adev->acx_queue_indicator->indicator)); ++ indicator |= cpu_to_le32 (1 << txqueue); ++ write_slavemem32 (adev, (u32) &(adev->acx_queue_indicator->indicator), indicator); ++ } ++ ++ /* ++ * Release the host lock ++ */ ++ write_slavemem16 (adev, (u32) &(adev->acx_queue_indicator->host_lock), 0); ++ ++ } ++ ++ /* ++ * Restore interrupts ++ */ ++ local_irq_restore (flags); ++#endif ++} ++ ++void ++acxmem_l_tx_data(acx_device_t *adev, tx_t* tx_opaque, int len) ++{ ++ /* ++ * txdesc is the address on the ACX ++ */ ++ txdesc_t *txdesc = (txdesc_t*)tx_opaque; ++ txhostdesc_t *hostdesc1, *hostdesc2; ++ client_t *clt; ++ u16 rate_cur; ++ u8 Ctl_8, Ctl2_8; ++ u32 addr; ++ ++ FN_ENTER; ++ /* fw doesn't tx such packets anyhow */ ++ if (unlikely(len < WLAN_HDR_A3_LEN)) ++ goto end; ++ ++ hostdesc1 = get_txhostdesc(adev, txdesc); ++ /* modify flag status in separate variable to be able to write it back ++ * in one big swoop later (also in order to have less device memory ++ * accesses) */ ++ Ctl_8 = read_slavemem8 (adev, (u32) &(txdesc->Ctl_8)); ++ Ctl2_8 = 0; /* really need to init it to 0, not txdesc->Ctl2_8, it seems */ ++ ++ hostdesc2 = hostdesc1 + 1; ++ ++ /* DON'T simply set Ctl field to 0 here globally, ++ * it needs to maintain a consistent flag status (those are state flags!!), ++ * otherwise it may lead to severe disruption. Only set or reset particular ++ * flags at the exact moment this is needed... */ ++ ++ /* let chip do RTS/CTS handshaking before sending ++ * in case packet size exceeds threshold */ ++ if (len > adev->rts_threshold) ++ SET_BIT(Ctl2_8, DESC_CTL2_RTS); ++ else ++ CLEAR_BIT(Ctl2_8, DESC_CTL2_RTS); ++ ++ switch (adev->mode) { ++ case ACX_MODE_0_ADHOC: ++ case ACX_MODE_3_AP: ++ clt = acx_l_sta_list_get(adev, ((wlan_hdr_t*)hostdesc1->data)->a1); ++ break; ++ case ACX_MODE_2_STA: ++ clt = adev->ap_client; ++ break; ++#if 0 ++/* testing was done on acx111: */ ++ case ACX_MODE_MONITOR: ++ SET_BIT(Ctl2_8, 0 ++/* sends CTS to self before packet */ ++ + DESC_CTL2_SEQ /* don't increase sequence field */ ++/* not working (looks like good fcs is still added) */ ++ + DESC_CTL2_FCS /* don't add the FCS */ ++/* not tested */ ++ + DESC_CTL2_MORE_FRAG ++/* not tested */ ++ + DESC_CTL2_RETRY /* don't increase retry field */ ++/* not tested */ ++ + DESC_CTL2_POWER /* don't increase power mgmt. field */ ++/* no effect */ ++ + DESC_CTL2_WEP /* encrypt this frame */ ++/* not tested */ ++ + DESC_CTL2_DUR /* don't increase duration field */ ++ ); ++ /* fallthrough */ ++#endif ++ default: /* ACX_MODE_OFF, ACX_MODE_MONITOR */ ++ clt = NULL; ++ break; ++ } ++ ++ rate_cur = clt ? clt->rate_cur : adev->rate_bcast; ++ if (unlikely(!rate_cur)) { ++ printk("acx: driver bug! bad ratemask\n"); ++ goto end; ++ } ++ ++ /* used in tx cleanup routine for auto rate and accounting: */ ++ put_txcr(adev, txdesc, clt, rate_cur); ++ ++ write_slavemem16 (adev, (u32) &(txdesc->total_length), cpu_to_le16(len)); ++ hostdesc2->length = cpu_to_le16(len - WLAN_HDR_A3_LEN); ++ if (IS_ACX111(adev)) { ++ /* note that if !txdesc->do_auto, txrate->cur ++ ** has only one nonzero bit */ ++ txdesc->u.r2.rate111 = cpu_to_le16( ++ rate_cur ++ /* WARNING: I was never able to make it work with prism54 AP. ++ ** It was falling down to 1Mbit where shortpre is not applicable, ++ ** and not working at all at "5,11 basic rates only" setting. ++ ** I even didn't see tx packets in radio packet capture. ++ ** Disabled for now --vda */ ++ /*| ((clt->shortpre && clt->cur!=RATE111_1) ? RATE111_SHORTPRE : 0) */ ++ ); ++#ifdef TODO_FIGURE_OUT_WHEN_TO_SET_THIS ++ /* should add this to rate111 above as necessary */ ++ | (clt->pbcc511 ? RATE111_PBCC511 : 0) ++#endif ++ hostdesc1->length = cpu_to_le16(len); ++ } else { /* ACX100 */ ++ u8 rate_100 = clt ? clt->rate_100 : adev->rate_bcast100; ++ write_slavemem8 (adev, (u32) &(txdesc->u.r1.rate), rate_100); ++#ifdef TODO_FIGURE_OUT_WHEN_TO_SET_THIS ++ if (clt->pbcc511) { ++ if (n == RATE100_5 || n == RATE100_11) ++ n |= RATE100_PBCC511; ++ } ++ ++ if (clt->shortpre && (clt->cur != RATE111_1)) ++ SET_BIT(Ctl_8, DESC_CTL_SHORT_PREAMBLE); /* set Short Preamble */ ++#endif ++ /* set autodma and reclaim and 1st mpdu */ ++ SET_BIT(Ctl_8, DESC_CTL_FIRSTFRAG); ++ ++#if ACX_FRAGMENTATION ++ /* SET_BIT(Ctl2_8, DESC_CTL2_MORE_FRAG); cannot set it unconditionally, needs to be set for all non-last fragments */ ++#endif ++ hostdesc1->length = cpu_to_le16(WLAN_HDR_A3_LEN); ++ ++ /* ++ * Since we're not using autodma copy the packet data to the acx now. ++ * Even host descriptors point to the packet header, and the odd indexed ++ * descriptor following points to the packet data. ++ * ++ * The first step is to find free memory in the ACX transmit buffers. ++ * They don't necessarily map one to one with the transmit queue entries, ++ * so search through them starting just after the last one used. ++ */ ++ addr = allocate_acx_txbuf_space (adev, len); ++ if (addr) { ++ chaincopy_to_slavemem (adev, addr, hostdesc1->data, len); ++ } ++ else { ++ /* ++ * Bummer. We thought we might have enough room in the transmit ++ * buffers to send this packet, but it turns out we don't. alloc_tx ++ * has already marked this transmit descriptor as HOSTOWN and ACXDONE, ++ * which means the ACX will hang when it gets to this descriptor unless ++ * we do something about it. Having a bubble in the transmit queue just ++ * doesn't seem to work, so we have to reset this transmit queue entry's ++ * state to its original value and back up our head pointer to point ++ * back to this entry. ++ */ ++ hostdesc1->length = 0; ++ hostdesc2->length = 0; ++ write_slavemem16 (adev, (u32) &(txdesc->total_length), 0); ++ write_slavemem8 (adev, (u32) &(txdesc->Ctl_8), DESC_CTL_HOSTOWN | DESC_CTL_FIRSTFRAG); ++ adev->tx_head = ((u8*) txdesc - (u8*) adev->txdesc_start) / adev->txdesc_size; ++ goto end; ++ } ++ /* ++ * Tell the ACX where the packet is. ++ */ ++ write_slavemem32 (adev, (u32) &(txdesc->AcxMemPtr), addr); ++ ++ } ++ /* don't need to clean ack/rts statistics here, already ++ * done on descr cleanup */ ++ ++ /* clears HOSTOWN and ACXDONE bits, thus telling that the descriptors ++ * are now owned by the acx100; do this as LAST operation */ ++ CLEAR_BIT(Ctl_8, DESC_CTL_ACXDONE_HOSTOWN); ++ /* flush writes before we release hostdesc to the adapter here */ ++ //wmb(); ++ ++ /* write back modified flags */ ++ /* ++ * At this point Ctl_8 should just be FIRSTFRAG ++ */ ++ write_slavemem8 (adev, (u32) &(txdesc->Ctl2_8),Ctl2_8); ++ write_slavemem8 (adev, (u32) &(txdesc->Ctl_8), Ctl_8); ++ /* unused: txdesc->tx_time = cpu_to_le32(jiffies); */ ++ ++ /* ++ * Update the queue indicator to say there's data on the first queue. ++ */ ++ acxmem_update_queue_indicator (adev, 0); ++ ++ /* flush writes before we tell the adapter that it's its turn now */ ++ mmiowb(); ++ write_reg16(adev, IO_ACX_INT_TRIG, INT_TRIG_TXPRC); ++ write_flush(adev); ++ ++ /* log the packet content AFTER sending it, ++ * in order to not delay sending any further than absolutely needed ++ * Do separate logs for acx100/111 to have human-readable rates */ ++ if (unlikely(acx_debug & (L_XFER|L_DATA))) { ++ u16 fc = ((wlan_hdr_t*)hostdesc1->data)->fc; ++ if (IS_ACX111(adev)) ++ printk("tx: pkt (%s): len %d " ++ "rate %04X%s status %u\n", ++ acx_get_packet_type_string(le16_to_cpu(fc)), len, ++ le16_to_cpu(txdesc->u.r2.rate111), ++ (le16_to_cpu(txdesc->u.r2.rate111) & RATE111_SHORTPRE) ? "(SPr)" : "", ++ adev->status); ++ else ++ printk("tx: pkt (%s): len %d rate %03u%s status %u\n", ++ acx_get_packet_type_string(fc), len, ++ read_slavemem8 (adev, (u32) &(txdesc->u.r1.rate)), ++ (Ctl_8 & DESC_CTL_SHORT_PREAMBLE) ? "(SPr)" : "", ++ adev->status); ++ ++ if (acx_debug & L_DATA) { ++ printk("tx: 802.11 [%d]: ", len); ++ acx_dump_bytes(hostdesc1->data, len); ++ } ++ } ++end: ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acxmem_l_clean_txdesc ++** ++** This function resets the txdescs' status when the ACX100 ++** signals the TX done IRQ (txdescs have been processed), starting with ++** the pool index of the descriptor which we would use next, ++** in order to make sure that we can be as fast as possible ++** in filling new txdescs. ++** Everytime we get called we know where the next packet to be cleaned is. ++*/ ++ ++#if !ACX_DEBUG ++static inline void log_txbuffer(const acx_device_t *adev) {} ++#else ++static void ++log_txbuffer(acx_device_t *adev) ++{ ++ txdesc_t *txdesc; ++ int i; ++ u8 Ctl_8; ++ ++ /* no FN_ENTER here, we don't want that */ ++ /* no locks here, since it's entirely non-critical code */ ++ txdesc = adev->txdesc_start; ++ if (unlikely(!txdesc)) return; ++ printk("tx: desc->Ctl8's:"); ++ for (i = 0; i < TX_CNT; i++) { ++ Ctl_8 = read_slavemem8 (adev, (u32) &(txdesc->Ctl_8)); ++ printk(" %02X", Ctl_8); ++ txdesc = advance_txdesc(adev, txdesc, 1); ++ } ++ printk("\n"); ++} ++#endif ++ ++ ++static void ++handle_tx_error(acx_device_t *adev, u8 error, unsigned int finger) ++{ ++ const char *err = "unknown error"; ++ ++ /* hmm, should we handle this as a mask ++ * of *several* bits? ++ * For now I think only caring about ++ * individual bits is ok... */ ++ switch (error) { ++ case 0x01: ++ err = "no Tx due to error in other fragment"; ++ adev->wstats.discard.fragment++; ++ break; ++ case 0x02: ++ err = "Tx aborted"; ++ adev->stats.tx_aborted_errors++; ++ break; ++ case 0x04: ++ err = "Tx desc wrong parameters"; ++ adev->wstats.discard.misc++; ++ break; ++ case 0x08: ++ err = "WEP key not found"; ++ adev->wstats.discard.misc++; ++ break; ++ case 0x10: ++ err = "MSDU lifetime timeout? - try changing " ++ "'iwconfig retry lifetime XXX'"; ++ adev->wstats.discard.misc++; ++ break; ++ case 0x20: ++ err = "excessive Tx retries due to either distance " ++ "too high or unable to Tx or Tx frame error - " ++ "try changing 'iwconfig txpower XXX' or " ++ "'sens'itivity or 'retry'"; ++ adev->wstats.discard.retries++; ++ /* Tx error 0x20 also seems to occur on ++ * overheating, so I'm not sure whether we ++ * actually want to do aggressive radio recalibration, ++ * since people maybe won't notice then that their hardware ++ * is slowly getting cooked... ++ * Or is it still a safe long distance from utter ++ * radio non-functionality despite many radio recalibs ++ * to final destructive overheating of the hardware? ++ * In this case we really should do recalib here... ++ * I guess the only way to find out is to do a ++ * potentially fatal self-experiment :-\ ++ * Or maybe only recalib in case we're using Tx ++ * rate auto (on errors switching to lower speed ++ * --> less heat?) or 802.11 power save mode? ++ * ++ * ok, just do it. */ ++ if (++adev->retry_errors_msg_ratelimit % 4 == 0) { ++ if (adev->retry_errors_msg_ratelimit <= 20) { ++ printk("%s: several excessive Tx " ++ "retry errors occurred, attempting " ++ "to recalibrate radio. Radio " ++ "drift might be caused by increasing " ++ "card temperature, please check the card " ++ "before it's too late!\n", ++ adev->ndev->name); ++ if (adev->retry_errors_msg_ratelimit == 20) ++ printk("disabling above message\n"); ++ } ++ ++ acx_schedule_task(adev, ACX_AFTER_IRQ_CMD_RADIO_RECALIB); ++ } ++ break; ++ case 0x40: ++ err = "Tx buffer overflow"; ++ adev->stats.tx_fifo_errors++; ++ break; ++ case 0x80: ++ err = "DMA error"; ++ adev->wstats.discard.misc++; ++ break; ++ } ++ adev->stats.tx_errors++; ++ if (adev->stats.tx_errors <= 20) ++ printk("%s: tx error 0x%02X, buf %02u! (%s)\n", ++ adev->ndev->name, error, finger, err); ++ else ++ printk("%s: tx error 0x%02X, buf %02u!\n", ++ adev->ndev->name, error, finger); ++} ++ ++ ++unsigned int ++acxmem_l_clean_txdesc(acx_device_t *adev) ++{ ++ txdesc_t *txdesc; ++ unsigned finger; ++ int num_cleaned; ++ u16 r111; ++ u8 error, ack_failures, rts_failures, rts_ok, r100, Ctl_8; ++ u32 acxmem; ++ txdesc_t tmptxdesc; ++ ++ FN_ENTER; ++ ++ /* ++ * Set up a template descriptor for re-initialization. The only ++ * things that get set are Ctl_8 and the rate, and the rate defaults ++ * to 1Mbps. ++ */ ++ memset (&tmptxdesc, 0, sizeof (tmptxdesc)); ++ tmptxdesc.Ctl_8 = DESC_CTL_HOSTOWN | DESC_CTL_FIRSTFRAG; ++ tmptxdesc.u.r1.rate = 0x0a; ++ ++ if (unlikely(acx_debug & L_DEBUG)) ++ log_txbuffer(adev); ++ ++ log(L_BUFT, "tx: cleaning up bufs from %u\n", adev->tx_tail); ++ ++ /* We know first descr which is not free yet. We advance it as far ++ ** as we see correct bits set in following descs (if next desc ++ ** is NOT free, we shouldn't advance at all). We know that in ++ ** front of tx_tail may be "holes" with isolated free descs. ++ ** We will catch up when all intermediate descs will be freed also */ ++ ++ finger = adev->tx_tail; ++ num_cleaned = 0; ++ while (likely(finger != adev->tx_head)) { ++ txdesc = get_txdesc(adev, finger); ++ ++ /* If we allocated txdesc on tx path but then decided ++ ** to NOT use it, then it will be left as a free "bubble" ++ ** in the "allocated for tx" part of the ring. ++ ** We may meet it on the next ring pass here. */ ++ ++ /* stop if not marked as "tx finished" and "host owned" */ ++ Ctl_8 = read_slavemem8 (adev, (u32) &(txdesc->Ctl_8)); ++ if ((Ctl_8 & DESC_CTL_ACXDONE_HOSTOWN) ++ != DESC_CTL_ACXDONE_HOSTOWN) { ++ if (unlikely(!num_cleaned)) { /* maybe remove completely */ ++ log(L_BUFT, "clean_txdesc: tail isn't free. " ++ "tail:%d head:%d\n", ++ adev->tx_tail, adev->tx_head); ++ } ++ break; ++ } ++ ++ /* remember desc values... */ ++ error = read_slavemem8 (adev, (u32) &(txdesc->error)); ++ ack_failures = read_slavemem8 (adev, (u32) &(txdesc->ack_failures)); ++ rts_failures = read_slavemem8 (adev, (u32) &(txdesc->u.rts.rts_failures)); ++ rts_ok = read_slavemem8 (adev, (u32) &(txdesc->u.rts.rts_ok)); ++ r100 = read_slavemem8 (adev, (u32) &(txdesc->u.r1.rate)); ++ r111 = le16_to_cpu(read_slavemem16 (adev, (u32) &(txdesc->u.r2.rate111))); ++ ++ /* need to check for certain error conditions before we ++ * clean the descriptor: we still need valid descr data here */ ++ if (unlikely(0x30 & error)) { ++ /* only send IWEVTXDROP in case of retry or lifetime exceeded; ++ * all other errors mean we screwed up locally */ ++ union iwreq_data wrqu; ++ wlan_hdr_t *hdr; ++ txhostdesc_t *hostdesc; ++ ++ hostdesc = get_txhostdesc(adev, txdesc); ++ hdr = (wlan_hdr_t *)hostdesc->data; ++ MAC_COPY(wrqu.addr.sa_data, hdr->a1); ++ wireless_send_event(adev->ndev, IWEVTXDROP, &wrqu, NULL); ++ } ++ ++ /* ++ * Free up the transmit data buffers ++ */ ++ acxmem = read_slavemem32 (adev, (u32) &(txdesc->AcxMemPtr)); ++ if (acxmem) { ++ reclaim_acx_txbuf_space (adev, acxmem); ++ } ++ ++ /* ...and free the desc by clearing all the fields ++ except the next pointer */ ++ copy_to_slavemem (adev, ++ (u32) &(txdesc->HostMemPtr), ++ (u8 *) &(tmptxdesc.HostMemPtr), ++ sizeof (tmptxdesc) - sizeof(tmptxdesc.pNextDesc) ++ ); ++ ++ adev->tx_free++; ++ num_cleaned++; ++ ++ if ((adev->tx_free >= TX_START_QUEUE) ++ && (adev->status == ACX_STATUS_4_ASSOCIATED) ++ && (acx_queue_stopped(adev->ndev)) ++ ) { ++ log(L_BUF, "tx: wake queue (avail. Tx desc %u)\n", ++ adev->tx_free); ++ acx_wake_queue(adev->ndev, NULL); ++ } ++ ++ /* do error checking, rate handling and logging ++ * AFTER having done the work, it's faster */ ++ ++ /* do rate handling */ ++ if (adev->rate_auto) { ++ struct client *clt = get_txc(adev, txdesc); ++ if (clt) { ++ u16 cur = get_txr(adev, txdesc); ++ if (clt->rate_cur == cur) { ++ acx_l_handle_txrate_auto(adev, clt, ++ cur, /* intended rate */ ++ r100, r111, /* actually used rate */ ++ (error & 0x30), /* was there an error? */ ++ TX_CNT + TX_CLEAN_BACKLOG - adev->tx_free); ++ } ++ } ++ } ++ ++ if (unlikely(error)) ++ handle_tx_error(adev, error, finger); ++ ++ if (IS_ACX111(adev)) ++ log(L_BUFT, "tx: cleaned %u: !ACK=%u !RTS=%u RTS=%u r111=%04X\n", ++ finger, ack_failures, rts_failures, rts_ok, r111); ++ else ++ log(L_BUFT, "tx: cleaned %u: !ACK=%u !RTS=%u RTS=%u rate=%u\n", ++ finger, ack_failures, rts_failures, rts_ok, r100); ++ ++ /* update pointer for descr to be cleaned next */ ++ finger = (finger + 1) % TX_CNT; ++ } ++ ++ /* remember last position */ ++ adev->tx_tail = finger; ++/* end: */ ++ FN_EXIT1(num_cleaned); ++ return num_cleaned; ++} ++ ++/* clean *all* Tx descriptors, and regardless of their previous state. ++ * Used for brute-force reset handling. */ ++void ++acxmem_l_clean_txdesc_emergency(acx_device_t *adev) ++{ ++ txdesc_t *txdesc; ++ int i; ++ u32 acxmem; ++ ++ FN_ENTER; ++ ++ for (i = 0; i < TX_CNT; i++) { ++ txdesc = get_txdesc(adev, i); ++ ++ /* free it */ ++ write_slavemem8 (adev, (u32) &(txdesc->ack_failures), 0); ++ write_slavemem8 (adev, (u32) &(txdesc->u.rts.rts_failures), 0); ++ write_slavemem8 (adev, (u32) &(txdesc->u.rts.rts_ok), 0); ++ write_slavemem8 (adev, (u32) &(txdesc->error), 0); ++ write_slavemem8 (adev, (u32) &(txdesc->Ctl_8), DESC_CTL_HOSTOWN); ++ ++ /* ++ * Clean up the memory allocated on the ACX for this transmit descriptor. ++ */ ++ acxmem = read_slavemem32 (adev, (u32) &(txdesc->AcxMemPtr)); ++ if (acxmem) { ++ reclaim_acx_txbuf_space (adev, acxmem); ++ } ++ ++ write_slavemem32 (adev, (u32) &(txdesc->AcxMemPtr), 0); ++ } ++ ++ adev->tx_free = TX_CNT; ++ ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acxmem_s_create_tx_host_desc_queue ++*/ ++ ++static void* ++allocate(acx_device_t *adev, size_t size, dma_addr_t *phy, const char *msg) ++{ ++ void *ptr; ++ ptr = kmalloc (size, GFP_KERNEL); ++ /* ++ * The ACX can't use the physical address, so we'll have to fake it ++ * later and it might be handy to have the virtual address. ++ */ ++ *phy = (dma_addr_t) NULL; ++ ++ if (ptr) { ++ log(L_DEBUG, "%s sz=%d adr=0x%p phy=0x%08llx\n", ++ msg, (int)size, ptr, (unsigned long long)*phy); ++ memset(ptr, 0, size); ++ return ptr; ++ } ++ printk(KERN_ERR "acx: %s allocation FAILED (%d bytes)\n", ++ msg, (int)size); ++ return NULL; ++} ++ ++ ++/* ++ * In the generic slave memory access mode, most of the stuff in ++ * the txhostdesc_t is unused. It's only here because the rest of ++ * the ACX driver expects it to be since the PCI version uses indirect ++ * host memory organization with DMA. Since we're not using DMA the ++ * only use we have for the host descriptors is to store the packets ++ * on the way out. ++ */ ++static int ++acxmem_s_create_tx_host_desc_queue(acx_device_t *adev) ++{ ++ txhostdesc_t *hostdesc; ++ u8 *txbuf; ++ int i; ++ ++ FN_ENTER; ++ ++ /* allocate TX buffer */ ++ adev->txbuf_area_size = TX_CNT * WLAN_A4FR_MAXLEN_WEP_FCS; ++ ++ adev->txbuf_start = allocate(adev, adev->txbuf_area_size, ++ &adev->txbuf_startphy, "txbuf_start"); ++ if (!adev->txbuf_start) ++ goto fail; ++ ++ /* allocate the TX host descriptor queue pool */ ++ adev->txhostdesc_area_size = TX_CNT * 2*sizeof(*hostdesc); ++ ++ adev->txhostdesc_start = allocate(adev, adev->txhostdesc_area_size, ++ &adev->txhostdesc_startphy, "txhostdesc_start"); ++ if (!adev->txhostdesc_start) ++ goto fail; ++ ++ /* check for proper alignment of TX host descriptor pool */ ++ if ((long) adev->txhostdesc_start & 3) { ++ printk("acx: driver bug: dma alloc returns unaligned address\n"); ++ goto fail; ++ } ++ ++ hostdesc = adev->txhostdesc_start; ++ txbuf = adev->txbuf_start; ++ ++#if 0 ++/* Each tx buffer is accessed by hardware via ++** txdesc -> txhostdesc(s) -> txbuffer(s). ++** We use only one txhostdesc per txdesc, but it looks like ++** acx111 is buggy: it accesses second txhostdesc ++** (via hostdesc.desc_phy_next field) even if ++** txdesc->length == hostdesc->length and thus ++** entire packet was placed into first txhostdesc. ++** Due to this bug acx111 hangs unless second txhostdesc ++** has le16_to_cpu(hostdesc.length) = 3 (or larger) ++** Storing NULL into hostdesc.desc_phy_next ++** doesn't seem to help. ++** ++** Update: although it worked on Xterasys XN-2522g ++** with len=3 trick, WG311v2 is even more bogus, doesn't work. ++** Keeping this code (#ifdef'ed out) for documentational purposes. ++*/ ++ for (i = 0; i < TX_CNT*2; i++) { ++ hostdesc_phy += sizeof(*hostdesc); ++ if (!(i & 1)) { ++ hostdesc->data_phy = cpu2acx(txbuf_phy); ++ /* hostdesc->data_offset = ... */ ++ /* hostdesc->reserved = ... */ ++ hostdesc->Ctl_16 = cpu_to_le16(DESC_CTL_HOSTOWN); ++ /* hostdesc->length = ... */ ++ hostdesc->desc_phy_next = cpu2acx(hostdesc_phy); ++ hostdesc->pNext = ptr2acx(NULL); ++ /* hostdesc->Status = ... */ ++ /* below: non-hardware fields */ ++ hostdesc->data = txbuf; ++ ++ txbuf += WLAN_A4FR_MAXLEN_WEP_FCS; ++ txbuf_phy += WLAN_A4FR_MAXLEN_WEP_FCS; ++ } else { ++ /* hostdesc->data_phy = ... */ ++ /* hostdesc->data_offset = ... */ ++ /* hostdesc->reserved = ... */ ++ /* hostdesc->Ctl_16 = ... */ ++ hostdesc->length = cpu_to_le16(3); /* bug workaround */ ++ /* hostdesc->desc_phy_next = ... */ ++ /* hostdesc->pNext = ... */ ++ /* hostdesc->Status = ... */ ++ /* below: non-hardware fields */ ++ /* hostdesc->data = ... */ ++ } ++ hostdesc++; ++ } ++#endif ++/* We initialize two hostdescs so that they point to adjacent ++** memory areas. Thus txbuf is really just a contiguous memory area */ ++ for (i = 0; i < TX_CNT*2; i++) { ++ /* ->data is a non-hardware field: */ ++ hostdesc->data = txbuf; ++ ++ if (!(i & 1)) { ++ txbuf += WLAN_HDR_A3_LEN; ++ } else { ++ txbuf += WLAN_A4FR_MAXLEN_WEP_FCS - WLAN_HDR_A3_LEN; ++ } ++ hostdesc++; ++ } ++ hostdesc--; ++ ++ FN_EXIT1(OK); ++ return OK; ++fail: ++ printk("acx: create_tx_host_desc_queue FAILED\n"); ++ /* dealloc will be done by free function on error case */ ++ FN_EXIT1(NOT_OK); ++ return NOT_OK; ++} ++ ++ ++/*************************************************************** ++** acxmem_s_create_rx_host_desc_queue ++*/ ++/* the whole size of a data buffer (header plus data body) ++ * plus 32 bytes safety offset at the end */ ++#define RX_BUFFER_SIZE (sizeof(rxbuffer_t) + 32) ++ ++static int ++acxmem_s_create_rx_host_desc_queue(acx_device_t *adev) ++{ ++ rxhostdesc_t *hostdesc; ++ rxbuffer_t *rxbuf; ++ int i; ++ ++ FN_ENTER; ++ ++ /* allocate the RX host descriptor queue pool */ ++ adev->rxhostdesc_area_size = RX_CNT * sizeof(*hostdesc); ++ ++ adev->rxhostdesc_start = allocate(adev, adev->rxhostdesc_area_size, ++ &adev->rxhostdesc_startphy, "rxhostdesc_start"); ++ if (!adev->rxhostdesc_start) ++ goto fail; ++ ++ /* check for proper alignment of RX host descriptor pool */ ++ if ((long) adev->rxhostdesc_start & 3) { ++ printk("acx: driver bug: dma alloc returns unaligned address\n"); ++ goto fail; ++ } ++ ++ /* allocate Rx buffer pool which will be used by the acx ++ * to store the whole content of the received frames in it */ ++ adev->rxbuf_area_size = RX_CNT * RX_BUFFER_SIZE; ++ ++ adev->rxbuf_start = allocate(adev, adev->rxbuf_area_size, ++ &adev->rxbuf_startphy, "rxbuf_start"); ++ if (!adev->rxbuf_start) ++ goto fail; ++ ++ rxbuf = adev->rxbuf_start; ++ hostdesc = adev->rxhostdesc_start; ++ ++ /* don't make any popular C programming pointer arithmetic mistakes ++ * here, otherwise I'll kill you... ++ * (and don't dare asking me why I'm warning you about that...) */ ++ for (i = 0; i < RX_CNT; i++) { ++ hostdesc->data = rxbuf; ++ hostdesc->length = cpu_to_le16(RX_BUFFER_SIZE); ++ rxbuf++; ++ hostdesc++; ++ } ++ hostdesc--; ++ FN_EXIT1(OK); ++ return OK; ++fail: ++ printk("acx: create_rx_host_desc_queue FAILED\n"); ++ /* dealloc will be done by free function on error case */ ++ FN_EXIT1(NOT_OK); ++ return NOT_OK; ++} ++ ++ ++/*************************************************************** ++** acxmem_s_create_hostdesc_queues ++*/ ++int ++acxmem_s_create_hostdesc_queues(acx_device_t *adev) ++{ ++ int result; ++ result = acxmem_s_create_tx_host_desc_queue(adev); ++ if (OK != result) return result; ++ result = acxmem_s_create_rx_host_desc_queue(adev); ++ return result; ++} ++ ++ ++/*************************************************************** ++** acxmem_create_tx_desc_queue ++*/ ++static void ++acxmem_create_tx_desc_queue(acx_device_t *adev, u32 tx_queue_start) ++{ ++ txdesc_t *txdesc; ++ u32 clr; ++ int i; ++ ++ FN_ENTER; ++ ++ if (IS_ACX100(adev)) ++ adev->txdesc_size = sizeof(*txdesc); ++ else ++ /* the acx111 txdesc is 4 bytes larger */ ++ adev->txdesc_size = sizeof(*txdesc) + 4; ++ ++ /* ++ * This refers to an ACX address, not one of ours ++ */ ++ adev->txdesc_start = (txdesc_t *) tx_queue_start; ++ ++ log(L_DEBUG, "adev->txdesc_start=%p\n", ++ adev->txdesc_start); ++ ++ adev->tx_free = TX_CNT; ++ /* done by memset: adev->tx_head = 0; */ ++ /* done by memset: adev->tx_tail = 0; */ ++ txdesc = adev->txdesc_start; ++ ++ if (IS_ACX111(adev)) { ++ /* ACX111 has a preinitialized Tx buffer! */ ++ /* loop over whole send pool */ ++ /* FIXME: do we have to do the hostmemptr stuff here?? */ ++ for (i = 0; i < TX_CNT; i++) { ++ txdesc->Ctl_8 = DESC_CTL_HOSTOWN; ++ /* reserve two (hdr desc and payload desc) */ ++ txdesc = advance_txdesc(adev, txdesc, 1); ++ } ++ } else { ++ /* ACX100 Tx buffer needs to be initialized by us */ ++ /* clear whole send pool. sizeof is safe here (we are acx100) */ ++ ++ /* ++ * adev->txdesc_start refers to device memory, so we can't write ++ * directly to it. ++ */ ++ clr = (u32) adev->txdesc_start; ++ while (clr < (u32) adev->txdesc_start + (TX_CNT * sizeof(*txdesc))) { ++ write_slavemem32 (adev, clr, 0); ++ clr += 4; ++ } ++ ++ /* loop over whole send pool */ ++ for (i = 0; i < TX_CNT; i++) { ++ log(L_DEBUG, "configure card tx descriptor: 0x%p, " ++ "size: 0x%X\n", txdesc, adev->txdesc_size); ++ ++ /* initialise ctl */ ++ /* ++ * No auto DMA here ++ */ ++ write_slavemem8 (adev, (u32) &(txdesc->Ctl_8), ++ (u8) (DESC_CTL_HOSTOWN | DESC_CTL_FIRSTFRAG)); ++ /* done by memset(0): txdesc->Ctl2_8 = 0; */ ++ ++ /* point to next txdesc */ ++ write_slavemem32 (adev, (u32) &(txdesc->pNextDesc), ++ (u32) cpu_to_le32 ((u8 *) txdesc + adev->txdesc_size)); ++ ++ /* go to the next one */ ++ /* ++ is safe here (we are acx100) */ ++ txdesc++; ++ } ++ /* go back to the last one */ ++ txdesc--; ++ /* and point to the first making it a ring buffer */ ++ write_slavemem32 (adev, (u32) &(txdesc->pNextDesc), ++ (u32) cpu_to_le32 (tx_queue_start)); ++ } ++ FN_EXIT0; ++} ++ ++ ++/*************************************************************** ++** acxmem_create_rx_desc_queue ++*/ ++static void ++acxmem_create_rx_desc_queue(acx_device_t *adev, u32 rx_queue_start) ++{ ++ rxdesc_t *rxdesc; ++ u32 mem_offs; ++ int i; ++ ++ FN_ENTER; ++ ++ /* done by memset: adev->rx_tail = 0; */ ++ ++ /* ACX111 doesn't need any further config: preconfigures itself. ++ * Simply print ring buffer for debugging */ ++ if (IS_ACX111(adev)) { ++ /* rxdesc_start already set here */ ++ ++ adev->rxdesc_start = (rxdesc_t *) rx_queue_start; ++ ++ rxdesc = adev->rxdesc_start; ++ for (i = 0; i < RX_CNT; i++) { ++ log(L_DEBUG, "rx descriptor %d @ 0x%p\n", i, rxdesc); ++ rxdesc = adev->rxdesc_start = (rxdesc_t *) ++ acx2cpu(rxdesc->pNextDesc); ++ } ++ } else { ++ /* we didn't pre-calculate rxdesc_start in case of ACX100 */ ++ /* rxdesc_start should be right AFTER Tx pool */ ++ adev->rxdesc_start = (rxdesc_t *) ++ ((u8 *) adev->txdesc_start + (TX_CNT * sizeof(txdesc_t))); ++ /* NB: sizeof(txdesc_t) above is valid because we know ++ ** we are in if (acx100) block. Beware of cut-n-pasting elsewhere! ++ ** acx111's txdesc is larger! */ ++ ++ mem_offs = (u32) adev->rxdesc_start; ++ while (mem_offs < (u32) adev->rxdesc_start + (RX_CNT * sizeof (*rxdesc))) { ++ write_slavemem32 (adev, mem_offs, 0); ++ mem_offs += 4; ++ } ++ ++ /* loop over whole receive pool */ ++ rxdesc = adev->rxdesc_start; ++ for (i = 0; i < RX_CNT; i++) { ++ log(L_DEBUG, "rx descriptor @ 0x%p\n", rxdesc); ++ /* point to next rxdesc */ ++ write_slavemem32 (adev, (u32) &(rxdesc->pNextDesc), ++ (u32) cpu_to_le32 ((u8 *) rxdesc + sizeof(*rxdesc))); ++ /* go to the next one */ ++ rxdesc++; ++ } ++ /* go to the last one */ ++ rxdesc--; ++ ++ /* and point to the first making it a ring buffer */ ++ write_slavemem32 (adev, (u32) &(rxdesc->pNextDesc), ++ (u32) cpu_to_le32 (rx_queue_start)); ++ } ++ FN_EXIT0; ++} ++ ++ ++/*************************************************************** ++** acxmem_create_desc_queues ++*/ ++void ++acxmem_create_desc_queues(acx_device_t *adev, u32 tx_queue_start, u32 rx_queue_start) ++{ ++ u32 *p; ++ int i; ++ ++ acxmem_create_tx_desc_queue(adev, tx_queue_start); ++ acxmem_create_rx_desc_queue(adev, rx_queue_start); ++ p = (u32 *) adev->acx_queue_indicator; ++ for (i = 0; i < 4; i++) { ++ write_slavemem32 (adev, (u32) p, 0); ++ p++; ++ } ++} ++ ++ ++/*************************************************************** ++** acxmem_s_proc_diag_output ++*/ ++char* ++acxmem_s_proc_diag_output(char *p, acx_device_t *adev) ++{ ++ const char *rtl, *thd, *ttl; ++ txdesc_t *txdesc; ++ u8 Ctl_8; ++ rxdesc_t *rxdesc; ++ int i; ++ u32 tmp; ++ txdesc_t txd; ++ u8 buf[0x200]; ++ int j, k; ++ ++ FN_ENTER; ++ ++#if DUMP_MEM_DURING_DIAG > 0 ++ dump_acxmem (adev, 0, 0x10000); ++ panic ("dump finished"); ++#endif ++ ++ p += sprintf(p, "** Rx buf **\n"); ++ rxdesc = adev->rxdesc_start; ++ if (rxdesc) for (i = 0; i < RX_CNT; i++) { ++ rtl = (i == adev->rx_tail) ? " [tail]" : ""; ++ Ctl_8 = read_slavemem8 (adev, (u32) &(rxdesc->Ctl_8)); ++ if (Ctl_8 & DESC_CTL_HOSTOWN) ++ p += sprintf(p, "%02u (%02x) FULL%s\n", i, Ctl_8, rtl); ++ else ++ p += sprintf(p, "%02u (%02x) empty%s\n", i, Ctl_8, rtl); ++ rxdesc++; ++ } ++ p += sprintf(p, "** Tx buf (free %d, Linux netqueue %s) **\n", adev->tx_free, ++ acx_queue_stopped(adev->ndev) ? "STOPPED" : "running"); ++ ++ p += sprintf(p, "** Tx buf %d blocks total, %d available, free list head %04x\n", ++ adev->acx_txbuf_numblocks, adev->acx_txbuf_blocks_free, adev->acx_txbuf_free); ++ txdesc = adev->txdesc_start; ++ if (txdesc) { ++ for (i = 0; i < TX_CNT; i++) { ++ thd = (i == adev->tx_head) ? " [head]" : ""; ++ ttl = (i == adev->tx_tail) ? " [tail]" : ""; ++ copy_from_slavemem (adev, (u8 *) &txd, (u32) txdesc, sizeof (txd)); ++ Ctl_8 = read_slavemem8 (adev, (u32) &(txdesc->Ctl_8)); ++ if (Ctl_8 & DESC_CTL_ACXDONE) ++ p += sprintf(p, "%02u ready to free (%02X)%s%s", i, Ctl_8, thd, ttl); ++ else if (Ctl_8 & DESC_CTL_HOSTOWN) ++ p += sprintf(p, "%02u available (%02X)%s%s", i, Ctl_8, thd, ttl); ++ else ++ p += sprintf(p, "%02u busy (%02X)%s%s", i, Ctl_8, thd, ttl); ++ tmp = read_slavemem32 (adev, (u32) &(txdesc->AcxMemPtr)); ++ if (tmp) { ++ p += sprintf (p, " %04x", tmp); ++ while ((tmp = read_slavemem32 (adev, (u32) tmp)) != 0x02000000) { ++ tmp <<= 5; ++ p += sprintf (p, " %04x", tmp); ++ } ++ } ++ p += sprintf (p, "\n"); ++ p += sprintf (p, " %04x: %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %02x %02x %02x %02x\n" ++ "%02x %02x %02x %02x %04x\n", ++ (u32) txdesc, ++ txd.pNextDesc.v, txd.HostMemPtr.v, txd.AcxMemPtr.v, txd.tx_time, ++ txd.total_length, txd.Reserved, ++ txd.dummy[0], txd.dummy[1], txd.dummy[2], txd.dummy[3], ++ txd.Ctl_8, txd.Ctl2_8, txd.error, txd.ack_failures, ++ txd.u.rts.rts_failures, txd.u.rts.rts_ok, txd.u.r1.rate, txd.u.r1.queue_ctrl, ++ txd.queue_info ++ ); ++ if (txd.AcxMemPtr.v) { ++ copy_from_slavemem (adev, buf, txd.AcxMemPtr.v, sizeof (buf)); ++ for (j = 0; (j < txd.total_length) && (j<(sizeof(buf)-4)); j+=16) { ++ p += sprintf (p, " "); ++ for (k = 0; (k < 16) && (j+k < txd.total_length); k++) { ++ p += sprintf (p, " %02x", buf[j+k+4]); ++ } ++ p += sprintf (p, "\n"); ++ } ++ } ++ txdesc = advance_txdesc(adev, txdesc, 1); ++ } ++ } ++ ++ p += sprintf(p, ++ "\n" ++ "** Generic slave data **\n" ++ "irq_mask 0x%04x irq_status 0x%04x irq on acx 0x%04x\n" ++ "txbuf_start 0x%p, txbuf_area_size %u\n" ++ "txdesc_size %u, txdesc_start 0x%p\n" ++ "txhostdesc_start 0x%p, txhostdesc_area_size %u\n" ++ "txbuf start 0x%04x, txbuf size %d\n" ++ "rxdesc_start 0x%p\n" ++ "rxhostdesc_start 0x%p, rxhostdesc_area_size %u\n" ++ "rxbuf_start 0x%p, rxbuf_area_size %u\n", ++ adev->irq_mask, adev->irq_status, read_reg32(adev, IO_ACX_IRQ_STATUS_NON_DES), ++ adev->txbuf_start, adev->txbuf_area_size, ++ adev->txdesc_size, adev->txdesc_start, ++ adev->txhostdesc_start, adev->txhostdesc_area_size, ++ adev->acx_txbuf_start, adev->acx_txbuf_numblocks * adev->memblocksize, ++ adev->rxdesc_start, ++ adev->rxhostdesc_start, adev->rxhostdesc_area_size, ++ adev->rxbuf_start, adev->rxbuf_area_size); ++ FN_EXIT0; ++ return p; ++} ++ ++ ++/*********************************************************************** ++*/ ++int ++acxmem_proc_eeprom_output(char *buf, acx_device_t *adev) ++{ ++ char *p = buf; ++ int i; ++ ++ FN_ENTER; ++ ++ for (i = 0; i < 0x400; i++) { ++ acxmem_read_eeprom_byte(adev, i, p++); ++ } ++ ++ FN_EXIT1(p - buf); ++ return p - buf; ++} ++ ++ ++/*********************************************************************** ++*/ ++void ++acxmem_set_interrupt_mask(acx_device_t *adev) ++{ ++ if (IS_ACX111(adev)) { ++ adev->irq_mask = (u16) ~(0 ++ | HOST_INT_RX_DATA ++ | HOST_INT_TX_COMPLETE ++ /* | HOST_INT_TX_XFER */ ++ /* | HOST_INT_RX_COMPLETE */ ++ /* | HOST_INT_DTIM */ ++ /* | HOST_INT_BEACON */ ++ /* | HOST_INT_TIMER */ ++ /* | HOST_INT_KEY_NOT_FOUND */ ++ | HOST_INT_IV_ICV_FAILURE ++ | HOST_INT_CMD_COMPLETE ++ | HOST_INT_INFO ++ | HOST_INT_OVERFLOW ++ /* | HOST_INT_PROCESS_ERROR */ ++ | HOST_INT_SCAN_COMPLETE ++ | HOST_INT_FCS_THRESHOLD ++ | HOST_INT_UNKNOWN ++ ); ++ /* Or else acx100 won't signal cmd completion, right? */ ++ adev->irq_mask_off = (u16)~( HOST_INT_CMD_COMPLETE ); /* 0xfdff */ ++ } else { ++ adev->irq_mask = (u16) ~(0 ++ | HOST_INT_RX_DATA ++ | HOST_INT_TX_COMPLETE ++ /* | HOST_INT_TX_XFER */ ++ /* | HOST_INT_RX_COMPLETE */ ++ /* | HOST_INT_DTIM */ ++ /* | HOST_INT_BEACON */ ++ /* | HOST_INT_TIMER */ ++ /* | HOST_INT_KEY_NOT_FOUND */ ++ /* | HOST_INT_IV_ICV_FAILURE */ ++ | HOST_INT_CMD_COMPLETE ++ | HOST_INT_INFO ++ /* | HOST_INT_OVERFLOW */ ++ /* | HOST_INT_PROCESS_ERROR */ ++ | HOST_INT_SCAN_COMPLETE ++ /* | HOST_INT_FCS_THRESHOLD */ ++ /* | HOST_INT_BEACON_MISSED */ ++ ); ++ adev->irq_mask_off = (u16)~( HOST_INT_UNKNOWN ); /* 0x7fff */ ++ } ++} ++ ++ ++/*********************************************************************** ++*/ ++int ++acx100mem_s_set_tx_level(acx_device_t *adev, u8 level_dbm) ++{ ++ struct acx111_ie_tx_level tx_level; ++ ++ /* since it can be assumed that at least the Maxim radio has a ++ * maximum power output of 20dBm and since it also can be ++ * assumed that these values drive the DAC responsible for ++ * setting the linear Tx level, I'd guess that these values ++ * should be the corresponding linear values for a dBm value, ++ * in other words: calculate the values from that formula: ++ * Y [dBm] = 10 * log (X [mW]) ++ * then scale the 0..63 value range onto the 1..100mW range (0..20 dBm) ++ * and you're done... ++ * Hopefully that's ok, but you never know if we're actually ++ * right... (especially since Windows XP doesn't seem to show ++ * actual Tx dBm values :-P) */ ++ ++ /* NOTE: on Maxim, value 30 IS 30mW, and value 10 IS 10mW - so the ++ * values are EXACTLY mW!!! Not sure about RFMD and others, ++ * though... */ ++ static const u8 dbm2val_maxim[21] = { ++ 63, 63, 63, 62, ++ 61, 61, 60, 60, ++ 59, 58, 57, 55, ++ 53, 50, 47, 43, ++ 38, 31, 23, 13, ++ 0 ++ }; ++ static const u8 dbm2val_rfmd[21] = { ++ 0, 0, 0, 1, ++ 2, 2, 3, 3, ++ 4, 5, 6, 8, ++ 10, 13, 16, 20, ++ 25, 32, 41, 50, ++ 63 ++ }; ++ const u8 *table; ++ ++ switch (adev->radio_type) { ++ case RADIO_MAXIM_0D: ++ table = &dbm2val_maxim[0]; ++ break; ++ case RADIO_RFMD_11: ++ case RADIO_RALINK_15: ++ table = &dbm2val_rfmd[0]; ++ break; ++ default: ++ printk("%s: unknown/unsupported radio type, " ++ "cannot modify tx power level yet!\n", ++ adev->ndev->name); ++ return NOT_OK; ++ } ++ /* ++ * The hx4700 EEPROM, at least, only supports 1 power setting. The configure ++ * routine matches the PA bias with the gain, so just use its default value. ++ * The values are: 0x2b for the gain and 0x03 for the PA bias. The firmware ++ * writes the gain level to the Tx gain control DAC and the PA bias to the Maxim ++ * radio's PA bias register. The firmware limits itself to 0 - 64 when writing to the ++ * gain control DAC. ++ * ++ * Physically between the ACX and the radio, higher Tx gain control DAC values result ++ * in less power output; 0 volts to the Maxim radio results in the highest output power ++ * level, which I'm assuming matches up with 0 in the Tx Gain DAC register. ++ * ++ * Although there is only the 1 power setting, one of the radio firmware functions adjusts ++ * the transmit power level up and down. That function is called by the ACX FIQ handler ++ * under certain conditions. ++ */ ++ tx_level.level = 1; ++ //return acx_s_configure(adev, &tx_level, ACX1xx_IE_DOT11_TX_POWER_LEVEL); ++ ++ printk("%s: changing radio power level to %u dBm (%u)\n", ++ adev->ndev->name, level_dbm, table[level_dbm]); ++ acxmem_s_write_phy_reg(adev, 0x11, table[level_dbm]); ++ ++ return 0; ++} ++ ++void acxmem_e_release(struct device *dev) { ++} ++ ++/*********************************************************************** ++** acx_cs part ++** ++** called by pcmcia card service ++*/ ++ ++/* ++ The event() function is this driver's Card Services event handler. ++ It will be called by Card Services when an appropriate card status ++ event is received. The config() and release() entry points are ++ used to configure or release a socket, in response to card ++ insertion and ejection events. They are invoked from the acx_cs ++ event handler. ++*/ ++ ++static int acx_cs_config(struct pcmcia_device *link); ++static void acx_cs_release(struct pcmcia_device *link); ++ ++/* ++ The attach() and detach() entry points are used to create and destroy ++ "instances" of the driver, where each instance represents everything ++ needed to manage one actual PCMCIA card. ++*/ ++ ++static void acx_cs_detach(struct pcmcia_device *p_dev); ++ ++/* ++ You'll also need to prototype all the functions that will actually ++ be used to talk to your device. See 'pcmem_cs' for a good example ++ of a fully self-sufficient driver; the other drivers rely more or ++ less on other parts of the kernel. ++*/ ++ ++/* ++ A linked list of "instances" of the acxnet device. Each actual ++ PCMCIA card corresponds to one device instance, and is described ++ by one struct pcmcia_device structure (defined in ds.h). ++ ++ You may not want to use a linked list for this -- for example, the ++ memory card driver uses an array of struct pcmcia_device pointers, where minor ++ device numbers are used to derive the corresponding array index. ++*/ ++ ++/* ++ A driver needs to provide a dev_node_t structure for each device ++ on a card. In some cases, there is only one device per card (for ++ example, ethernet cards, modems). In other cases, there may be ++ many actual or logical devices (SCSI adapters, memory cards with ++ multiple partitions). The dev_node_t structures need to be kept ++ in a linked list starting at the 'dev' field of a struct pcmcia_device ++ structure. We allocate them in the card's private data structure, ++ because they generally shouldn't be allocated dynamically. ++ ++ In this case, we also provide a flag to indicate if a device is ++ "stopped" due to a power management event, or card ejection. The ++ device IO routines can use a flag like this to throttle IO to a ++ card that is not ready to accept it. ++*/ ++ ++ ++/*====================================================================== ++ ++ acx_attach() creates an "instance" of the driver, allocating ++ local data structures for one device. The device is registered ++ with Card Services. ++ ++ The dev_link structure is initialized, but we don't actually ++ configure the card at this point -- we wait until we receive a ++ card insertion event. ++ ++ ======================================================================*/ ++ ++static int acx_cs_probe(struct pcmcia_device *link) ++{ ++ local_info_t *local; ++ struct net_device *ndev; ++ ++ DEBUG(0, "acx_attach()\n"); ++ ++ ndev = alloc_netdev(sizeof(acx_device_t), "wlan%d", dummy_netdev_init); ++ if (!ndev) { ++ printk("acx: no memory for netdevice struct\n"); ++ return -ENOMEM; ++ } ++ ++ /* Interrupt setup */ ++ link->irq.Attributes = IRQ_TYPE_EXCLUSIVE | IRQ_HANDLE_PRESENT; ++ link->irq.IRQInfo1 = IRQ_LEVEL_ID; ++ link->irq.Handler = acxmem_i_interrupt; ++ link->irq.Instance = ndev; ++ ++ /* ++ General socket configuration defaults can go here. In this ++ client, we assume very little, and rely on the CIS for almost ++ everything. In most clients, many details (i.e., number, sizes, ++ and attributes of IO windows) are fixed by the nature of the ++ device, and can be hard-wired here. ++ */ ++ link->conf.Attributes = CONF_ENABLE_IRQ; ++ link->conf.IntType = INT_MEMORY_AND_IO; ++ link->conf.Present = PRESENT_OPTION | PRESENT_COPY; ++ ++ /* Allocate space for private device-specific data */ ++ local = kzalloc(sizeof(local_info_t), GFP_KERNEL); ++ if (!local) { ++ printk(KERN_ERR "acx_cs: no memory for new device\n"); ++ return -ENOMEM; ++ } ++ local->ndev = ndev; ++ ++ link->priv = local; ++ ++ return acx_cs_config(link); ++} /* acx_attach */ ++ ++/*====================================================================== ++ ++ This deletes a driver "instance". The device is de-registered ++ with Card Services. If it has been released, all local data ++ structures are freed. Otherwise, the structures will be freed ++ when the device is released. ++ ++ ======================================================================*/ ++ ++static void acx_cs_detach(struct pcmcia_device *link) ++{ ++ DEBUG(0, "acx_detach(0x%p)\n", link); ++ ++ ++ if ( ((local_info_t*)link->priv)->ndev ) { ++ acxmem_e_close( ((local_info_t*)link->priv)->ndev ); ++ } ++ ++ acx_cs_release(link); ++ ++ ((local_info_t*)link->priv)->ndev = NULL; ++ ++ kfree(link->priv); ++} /* acx_detach */ ++ ++/*====================================================================== ++ ++ acx_config() is scheduled to run after a CARD_INSERTION event ++ is received, to configure the PCMCIA socket, and to make the ++ device available to the system. ++ ++ ======================================================================*/ ++ ++#define CS_CHECK(fn, ret) \ ++do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0) ++ ++static int acx_cs_config(struct pcmcia_device *link) ++{ ++ tuple_t tuple; ++ cisparse_t parse; ++ local_info_t *local = link->priv; ++ int last_fn, last_ret; ++ u_char buf[64]; ++ win_req_t req; ++ memreq_t map; ++// int i; ++// acx_device_t *adev; ++ ++// adev = (acx_device_t *)link->priv; ++ ++ DEBUG(0, "acx_cs_config(0x%p)\n", link); ++ ++ /* ++ In this loop, we scan the CIS for configuration table entries, ++ each of which describes a valid card configuration, including ++ voltage, IO window, memory window, and interrupt settings. ++ ++ We make no assumptions about the card to be configured: we use ++ just the information available in the CIS. In an ideal world, ++ this would work for any PCMCIA card, but it requires a complete ++ and accurate CIS. In practice, a driver usually "knows" most of ++ these things without consulting the CIS, and most client drivers ++ will only use the CIS to fill in implementation-defined details. ++ */ ++ tuple.Attributes = 0; ++ tuple.TupleData = (cisdata_t *)buf; ++ tuple.TupleDataMax = sizeof(buf); ++ tuple.TupleOffset = 0; ++ tuple.DesiredTuple = CISTPL_CFTABLE_ENTRY; ++ ++ /* don't trust the CIS on this; Linksys got it wrong */ ++ //link->conf.Present = 0x63; ++ ++ CS_CHECK(GetFirstTuple, pcmcia_get_first_tuple(link, &tuple)); ++ while (1) { ++ cistpl_cftable_entry_t dflt = { 0 }; ++ cistpl_cftable_entry_t *cfg = &(parse.cftable_entry); ++ if (pcmcia_get_tuple_data(link, &tuple) != 0 || ++ pcmcia_parse_tuple(link, &tuple, &parse) != 0) ++ goto next_entry; ++ ++ if (cfg->flags & CISTPL_CFTABLE_DEFAULT) dflt = *cfg; ++ if (cfg->index == 0) goto next_entry; ++ link->conf.ConfigIndex = cfg->index; ++ ++ /* Does this card need audio output? */ ++ if (cfg->flags & CISTPL_CFTABLE_AUDIO) { ++ link->conf.Attributes |= CONF_ENABLE_SPKR; ++ link->conf.Status = CCSR_AUDIO_ENA; ++ } ++ ++ /* Use power settings for Vcc and Vpp if present */ ++ /* Note that the CIS values need to be rescaled */ ++ if (cfg->vpp1.present & (1<<CISTPL_POWER_VNOM)) ++ link->conf.Vpp = ++ cfg->vpp1.param[CISTPL_POWER_VNOM]/10000; ++ else if (dflt.vpp1.present & (1<<CISTPL_POWER_VNOM)) ++ link->conf.Vpp = ++ dflt.vpp1.param[CISTPL_POWER_VNOM]/10000; ++ ++ /* Do we need to allocate an interrupt? */ ++ if (cfg->irq.IRQInfo1 || dflt.irq.IRQInfo1) ++ link->conf.Attributes |= CONF_ENABLE_IRQ; ++ if ((cfg->mem.nwin > 0) || (dflt.mem.nwin > 0)) { ++ cistpl_mem_t *mem = ++ (cfg->mem.nwin) ? &cfg->mem : &dflt.mem; ++// req.Attributes = WIN_DATA_WIDTH_16|WIN_MEMORY_TYPE_AM|WIN_ENABLE|WIN_USE_WAIT; ++ req.Attributes = WIN_DATA_WIDTH_16|WIN_MEMORY_TYPE_CM|WIN_ENABLE|WIN_USE_WAIT; ++ req.Base = mem->win[0].host_addr; ++ req.Size = mem->win[0].len; ++ req.Size=0x1000; ++ req.AccessSpeed = 0; ++ if (pcmcia_request_window(&link, &req, &link->win) != 0) ++ goto next_entry; ++ map.Page = 0; map.CardOffset = mem->win[0].card_addr; ++ if (pcmcia_map_mem_page(link->win, &map) != 0) ++ goto next_entry; ++ else ++ printk(KERN_INFO "MEMORY WINDOW FOUND!!!\n"); ++ } ++ /* If we got this far, we're cool! */ ++ break; ++ ++ next_entry: ++ CS_CHECK(GetNextTuple, pcmcia_get_next_tuple(link, &tuple)); ++ } ++ ++ if (link->conf.Attributes & CONF_ENABLE_IRQ) { ++ printk(KERN_INFO "requesting Irq...\n"); ++ CS_CHECK(RequestIRQ, pcmcia_request_irq(link, &link->irq)); ++ } ++ ++ /* ++ This actually configures the PCMCIA socket -- setting up ++ the I/O windows and the interrupt mapping, and putting the ++ card and host interface into "Memory and IO" mode. ++ */ ++ CS_CHECK(RequestConfiguration, pcmcia_request_configuration(link, &link->conf)); ++ DEBUG(0,"RequestConfiguration OK\n"); ++ ++ ++ memwin.Base=req.Base; ++ memwin.Size=req.Size; ++ ++ acx_init_netdev(local->ndev, &link->dev, memwin.Base, memwin.Size, link->irq.AssignedIRQ); ++ ++#if 1 ++ /* ++ At this point, the dev_node_t structure(s) need to be ++ initialized and arranged in a linked list at link->dev_node. ++ */ ++ strcpy(local->node.dev_name, local->ndev->name ); ++ local->node.major = local->node.minor = 0; ++ link->dev_node = &local->node; ++ ++ /* Finally, report what we've done */ ++ printk(KERN_INFO "%s: index 0x%02x: ", ++ local->ndev->name, link->conf.ConfigIndex); ++#endif ++ if (link->conf.Attributes & CONF_ENABLE_IRQ) ++ printk("irq %d", link->irq.AssignedIRQ); ++ if (link->io.NumPorts1) ++ printk(", io 0x%04x-0x%04x", link->io.BasePort1, ++ link->io.BasePort1+link->io.NumPorts1-1); ++ if (link->io.NumPorts2) ++ printk(" & 0x%04x-0x%04x", link->io.BasePort2, ++ link->io.BasePort2+link->io.NumPorts2-1); ++ if (link->win) ++ printk(", mem 0x%06lx-0x%06lx\n", req.Base, ++ req.Base+req.Size-1); ++ return 0; ++ ++ cs_failed: ++ cs_error(link, last_fn, last_ret); ++ acx_cs_release(link); ++ return -ENODEV; ++} /* acx_config */ ++ ++/*====================================================================== ++ ++ After a card is removed, acx_release() will unregister the ++ device, and release the PCMCIA configuration. If the device is ++ still open, this will be postponed until it is closed. ++ ++ ======================================================================*/ ++ ++static void acx_cs_release(struct pcmcia_device *link) ++{ ++ DEBUG(0, "acx_release(0x%p)\n", link); ++ acxmem_e_remove(link); ++ pcmcia_disable_device(link); ++} ++ ++static int acx_cs_suspend(struct pcmcia_device *link) ++{ ++ local_info_t *local = link->priv; ++ ++ pm_message_t state; ++ acxmem_e_suspend ( local->ndev, state); ++ /* Already done in suspend ++ * netif_device_detach(local->ndev); */ ++ ++ return 0; ++} ++ ++static int acx_cs_resume(struct pcmcia_device *link) ++{ ++ local_info_t *local = link->priv; ++ ++ FN_ENTER; ++ resume_ndev = local->ndev; ++ ++ schedule_work( &fw_resume_work ); ++ ++ /* Already done in suspend ++ if (link->open) { ++ // do we need reset for ACX, if so what function nane is ? ++ //reset_acx_card(local->eth_dev); ++ netif_device_attach(local->ndev); ++ } */ ++ ++ FN_EXIT0; ++ return 0; ++} ++ ++static struct pcmcia_device_id acx_ids[] = { ++ PCMCIA_DEVICE_MANF_CARD(0x0097, 0x8402), ++ PCMCIA_DEVICE_MANF_CARD(0x0250, 0xb001), ++ PCMCIA_DEVICE_NULL, ++}; ++MODULE_DEVICE_TABLE(pcmcia, acx_ids); ++ ++static struct pcmcia_driver acx_driver = { ++ .owner = THIS_MODULE, ++ .drv = { ++ .name = "acx_cs", ++ }, ++ .probe = acx_cs_probe, ++ .remove = acx_cs_detach, ++ .id_table = acx_ids, ++ .suspend = acx_cs_suspend, ++ .resume = acx_cs_resume, ++}; ++ ++int acx_cs_init(void) ++{ ++ /* return success if at least one succeeded */ ++ DEBUG(0, "acxcs_init()\n"); ++ return pcmcia_register_driver(&acx_driver); ++} ++ ++void acx_cs_cleanup(void) ++{ ++ pcmcia_unregister_driver(&acx_driver); ++} ++ ++/* ++ This program is free software; you can redistribute it and/or ++ modify it under the terms of the GNU General Public License ++ as published by the Free Software Foundation; either version 2 ++ of the License, or (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ In addition: ++ ++ Redistribution and use in source and binary forms, with or without ++ modification, are permitted provided that the following conditions ++ are met: ++ ++ 1. Redistributions of source code must retain the above copyright ++ notice, this list of conditions and the following disclaimer. ++ 2. Redistributions in binary form must reproduce the above copyright ++ notice, this list of conditions and the following disclaimer in the ++ documentation and/or other materials provided with the distribution. ++ 3. The name of the author may not be used to endorse or promote ++ products derived from this software without specific prior written ++ permission. ++ ++ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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. ++*/ ++ ++MODULE_DESCRIPTION( "ACX Cardbus Driver" ); ++MODULE_LICENSE( "GPL" ); ++ +Index: linux-2.6.23/drivers/net/wireless/acx/htcsable_acx.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.23/drivers/net/wireless/acx/htcsable_acx.c 2008-01-20 21:13:40.000000000 +0000 +@@ -0,0 +1,118 @@ ++/* ++ * WLAN (TI TNETW1100B) support in the HTC Sable ++ * ++ * Copyright (c) 2006 SDG Systems, LLC ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive for ++ * more details. ++ * ++ * 28-March-2006 Todd Blumer <todd@sdgsystems.com> ++ */ ++ ++ ++#include <linux/kernel.h> ++#include <linux/platform_device.h> ++#include <linux/delay.h> ++ ++#include <asm/hardware.h> ++ ++#include <asm/arch/pxa-regs.h> ++#include <linux/mfd/asic3_base.h> ++#include <asm/arch/htcsable-gpio.h> ++#include <asm/arch/htcsable-asic.h> ++#include <asm/io.h> ++ ++#include "acx_hw.h" ++ ++#define WLAN_BASE PXA_CS2_PHYS ++ ++/* ++off: b15 c8 d3 ++on: d3 c8 b5 b5- ++*/ ++ ++#define GPIO_NR_HTCSABLE_ACX111 111 ++ ++static int ++htcsable_wlan_stop( void ); ++ ++static int ++htcsable_wlan_start( void ) ++{ ++ printk( "htcsable_wlan_start\n" ); ++ ++ /*asic3_set_gpio_out_c(&htcsable_asic3.dev, 1<<GPIOC_ACX_RESET, 0);*/ ++ asic3_set_gpio_out_c(&htcsable_asic3.dev, 1<<GPIOC_ACX_PWR_3, 1<<GPIOC_ACX_PWR_3); /* related to acx */ ++ SET_HTCSABLE_GPIO(ACX111, 1); ++ asic3_set_gpio_out_b(&htcsable_asic3.dev, 1<<GPIOB_ACX_PWR_1, 1<<GPIOB_ACX_PWR_1); ++ asic3_set_gpio_out_d(&htcsable_asic3.dev, 1<<GPIOD_ACX_PWR_2, 1<<GPIOD_ACX_PWR_2); ++ mdelay(260); ++ asic3_set_gpio_out_c(&htcsable_asic3.dev, 1<<GPIOC_ACX_RESET, 1<<GPIOC_ACX_RESET); ++ ++ return 0; ++} ++ ++static int ++htcsable_wlan_stop( void ) ++{ ++ printk( "htcsable_wlan_stop\n" ); ++ asic3_set_gpio_out_b(&htcsable_asic3.dev, 1<<GPIOB_ACX_PWR_1, 0); ++ asic3_set_gpio_out_c(&htcsable_asic3.dev, 1<<GPIOC_ACX_RESET, 0); ++ asic3_set_gpio_out_d(&htcsable_asic3.dev, 1<<GPIOD_ACX_PWR_2, 0); ++ SET_HTCSABLE_GPIO(ACX111, 0); /* not necessary to power down this one? */ ++ asic3_set_gpio_out_c(&htcsable_asic3.dev, 1<<GPIOC_ACX_PWR_3, 0); /* not necessary to power down this one? */ ++ ++ return 0; ++} ++ ++static struct resource acx_resources[] = { ++ [0] = { ++ .start = WLAN_BASE, ++ .end = WLAN_BASE + 0x20, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++// .start = asic3_irq_base(&htcsable_asic3.dev) + ASIC3_GPIOC_IRQ_BASE+GPIOC_WIFI_IRQ_N, ++// .end = asic3_irq_base(&htcsable_asic3.dev) + ASIC3_GPIOC_IRQ_BASE+GPIOC_WIFI_IRQ_N, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static struct acx_hardware_data acx_data = { ++ .start_hw = htcsable_wlan_start, ++ .stop_hw = htcsable_wlan_stop, ++}; ++ ++static struct platform_device acx_device = { ++ .name = "acx-mem", ++ .dev = { ++ .platform_data = &acx_data, ++ }, ++ .num_resources = ARRAY_SIZE( acx_resources ), ++ .resource = acx_resources, ++}; ++ ++static int __init ++htcsable_wlan_init( void ) ++{ ++ printk( "htcsable_wlan_init: acx-mem platform_device_register\n" ); ++ acx_device.resource[1].start = asic3_irq_base(&htcsable_asic3.dev) + ASIC3_GPIOB_IRQ_BASE+GPIOB_ACX_IRQ_N; ++ acx_device.resource[1].end = asic3_irq_base(&htcsable_asic3.dev) + ASIC3_GPIOB_IRQ_BASE+GPIOB_ACX_IRQ_N; ++ return platform_device_register( &acx_device ); ++} ++ ++ ++static void __exit ++htcsable_wlan_exit( void ) ++{ ++ platform_device_unregister( &acx_device ); ++} ++ ++module_init( htcsable_wlan_init ); ++module_exit( htcsable_wlan_exit ); ++ ++MODULE_AUTHOR( "Todd Blumer <todd@sdgsystems.com>" ); ++MODULE_DESCRIPTION( "WLAN driver for HTC Sable" ); ++MODULE_LICENSE( "GPL" ); ++ +Index: linux-2.6.23/drivers/net/wireless/acx/htcuniversal_acx.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.23/drivers/net/wireless/acx/htcuniversal_acx.c 2008-01-20 21:13:40.000000000 +0000 +@@ -0,0 +1,108 @@ ++/* ++ * WLAN (TI TNETW1100B) support in the HTC Universal ++ * ++ * Copyright (c) 2006 SDG Systems, LLC ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive for ++ * more details. ++ * ++ * 28-March-2006 Todd Blumer <todd@sdgsystems.com> ++ */ ++ ++ ++#include <linux/kernel.h> ++#include <linux/platform_device.h> ++#include <linux/delay.h> ++ ++#include <asm/hardware.h> ++ ++#include <asm/arch/pxa-regs.h> ++#include <linux/soc/asic3_base.h> ++#include <asm/arch/htcuniversal-gpio.h> ++#include <asm/arch/htcuniversal-asic.h> ++#include <asm/io.h> ++ ++#include "acx_hw.h" ++ ++#define WLAN_BASE PXA_CS2_PHYS ++ ++ ++static int ++htcuniversal_wlan_start( void ) ++{ ++ htcuniversal_egpio_enable(1<<EGPIO6_WIFI_ON); ++ asic3_set_gpio_out_c(&htcuniversal_asic3.dev, 1<<GPIOC_WIFI_PWR1_ON, 1<<GPIOC_WIFI_PWR1_ON); ++ asic3_set_gpio_out_d(&htcuniversal_asic3.dev, 1<<GPIOD_WIFI_PWR3_ON, 1<<GPIOD_WIFI_PWR3_ON); ++ asic3_set_gpio_out_d(&htcuniversal_asic3.dev, 1<<GPIOD_WIFI_PWR2_ON, 1<<GPIOD_WIFI_PWR2_ON); ++ mdelay(100); ++ ++ asic3_set_gpio_out_c(&htcuniversal_asic3.dev, 1<<GPIOC_WIFI_RESET, 0); ++ mdelay(100); ++ asic3_set_gpio_out_c(&htcuniversal_asic3.dev, 1<<GPIOC_WIFI_RESET, 1<<GPIOC_WIFI_RESET); ++ mdelay(100); ++ return 0; ++} ++ ++static int ++htcuniversal_wlan_stop( void ) ++{ ++ asic3_set_gpio_out_c(&htcuniversal_asic3.dev, 1<<GPIOC_WIFI_RESET, 0); ++ ++ htcuniversal_egpio_disable(1<<EGPIO6_WIFI_ON); ++ asic3_set_gpio_out_c(&htcuniversal_asic3.dev, 1<<GPIOC_WIFI_PWR1_ON, 0); ++ asic3_set_gpio_out_d(&htcuniversal_asic3.dev, 1<<GPIOD_WIFI_PWR2_ON, 0); ++ asic3_set_gpio_out_d(&htcuniversal_asic3.dev, 1<<GPIOD_WIFI_PWR3_ON, 0); ++ return 0; ++} ++ ++static struct resource acx_resources[] = { ++ [0] = { ++ .start = WLAN_BASE, ++ .end = WLAN_BASE + 0x20, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++// .start = asic3_irq_base(&htcuniversal_asic3.dev) + ASIC3_GPIOC_IRQ_BASE+GPIOC_WIFI_IRQ_N, ++// .end = asic3_irq_base(&htcuniversal_asic3.dev) + ASIC3_GPIOC_IRQ_BASE+GPIOC_WIFI_IRQ_N, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static struct acx_hardware_data acx_data = { ++ .start_hw = htcuniversal_wlan_start, ++ .stop_hw = htcuniversal_wlan_stop, ++}; ++ ++static struct platform_device acx_device = { ++ .name = "acx-mem", ++ .dev = { ++ .platform_data = &acx_data, ++ }, ++ .num_resources = ARRAY_SIZE( acx_resources ), ++ .resource = acx_resources, ++}; ++ ++static int __init ++htcuniversal_wlan_init( void ) ++{ ++ printk( "htcuniversal_wlan_init: acx-mem platform_device_register\n" ); ++ acx_device.resource[1].start = asic3_irq_base(&htcuniversal_asic3.dev) + ASIC3_GPIOC_IRQ_BASE+GPIOC_WIFI_IRQ_N; ++ acx_device.resource[1].end = asic3_irq_base(&htcuniversal_asic3.dev) + ASIC3_GPIOC_IRQ_BASE+GPIOC_WIFI_IRQ_N; ++ return platform_device_register( &acx_device ); ++} ++ ++ ++static void __exit ++htcuniversal_wlan_exit( void ) ++{ ++ platform_device_unregister( &acx_device ); ++} ++ ++module_init( htcuniversal_wlan_init ); ++module_exit( htcuniversal_wlan_exit ); ++ ++MODULE_AUTHOR( "Todd Blumer <todd@sdgsystems.com>" ); ++MODULE_DESCRIPTION( "WLAN driver for HTC Universal" ); ++MODULE_LICENSE( "GPL" ); ++ +Index: linux-2.6.23/drivers/net/wireless/acx/hx4700_acx.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.23/drivers/net/wireless/acx/hx4700_acx.c 2008-01-20 21:13:40.000000000 +0000 +@@ -0,0 +1,108 @@ ++/* ++ * WLAN (TI TNETW1100B) support in the hx470x. ++ * ++ * Copyright (c) 2006 SDG Systems, LLC ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive for ++ * more details. ++ * ++ * 28-March-2006 Todd Blumer <todd@sdgsystems.com> ++ */ ++ ++ ++#include <linux/kernel.h> ++#include <linux/platform_device.h> ++#include <linux/delay.h> ++#include <linux/leds.h> ++ ++#include <asm/hardware.h> ++ ++#include <asm/arch/pxa-regs.h> ++#include <asm/arch/hx4700-gpio.h> ++#include <asm/arch/hx4700-core.h> ++#include <asm/io.h> ++ ++#include "acx_hw.h" ++ ++#define WLAN_OFFSET 0x1000000 ++#define WLAN_BASE (PXA_CS5_PHYS+WLAN_OFFSET) ++ ++ ++static int ++hx4700_wlan_start( void ) ++{ ++ SET_HX4700_GPIO( WLAN_RESET_N, 0 ); ++ mdelay(5); ++ hx4700_egpio_enable( EGPIO0_VCC_3V3_EN ); ++ mdelay(100); ++ hx4700_egpio_enable( EGPIO7_VCC_3V3_WL_EN ); ++ mdelay(150); ++ hx4700_egpio_enable( EGPIO1_WL_VREG_EN | EGPIO2_VCC_2V1_WL_EN | ++ EGPIO6_WL1V8_EN ); ++ mdelay(10); ++ SET_HX4700_GPIO( WLAN_RESET_N, 1 ); ++ mdelay(50); ++ led_trigger_event_shared(hx4700_radio_trig, LED_FULL); ++ return 0; ++} ++ ++static int ++hx4700_wlan_stop( void ) ++{ ++ hx4700_egpio_disable( EGPIO0_VCC_3V3_EN | EGPIO1_WL_VREG_EN | ++ EGPIO7_VCC_3V3_WL_EN | EGPIO2_VCC_2V1_WL_EN | ++ EGPIO6_WL1V8_EN ); ++ SET_HX4700_GPIO( WLAN_RESET_N, 0 ); ++ led_trigger_event_shared(hx4700_radio_trig, LED_OFF); ++ return 0; ++} ++ ++static struct resource acx_resources[] = { ++ [0] = { ++ .start = WLAN_BASE, ++ .end = WLAN_BASE + 0x20, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = HX4700_IRQ(WLAN_IRQ_N), ++ .end = HX4700_IRQ(WLAN_IRQ_N), ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static struct acx_hardware_data acx_data = { ++ .start_hw = hx4700_wlan_start, ++ .stop_hw = hx4700_wlan_stop, ++}; ++ ++static struct platform_device acx_device = { ++ .name = "acx-mem", ++ .dev = { ++ .platform_data = &acx_data, ++ }, ++ .num_resources = ARRAY_SIZE( acx_resources ), ++ .resource = acx_resources, ++}; ++ ++static int __init ++hx4700_wlan_init( void ) ++{ ++ printk( "hx4700_wlan_init: acx-mem platform_device_register\n" ); ++ return platform_device_register( &acx_device ); ++} ++ ++ ++static void __exit ++hx4700_wlan_exit( void ) ++{ ++ platform_device_unregister( &acx_device ); ++} ++ ++module_init( hx4700_wlan_init ); ++module_exit( hx4700_wlan_exit ); ++ ++MODULE_AUTHOR( "Todd Blumer <todd@sdgsystems.com>" ); ++MODULE_DESCRIPTION( "WLAN driver for iPAQ hx4700" ); ++MODULE_LICENSE( "GPL" ); ++ +Index: linux-2.6.23/drivers/net/wireless/acx/ioctl.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.23/drivers/net/wireless/acx/ioctl.c 2008-01-20 21:13:40.000000000 +0000 +@@ -0,0 +1,2748 @@ ++/*********************************************************************** ++** Copyright (C) 2003 ACX100 Open Source Project ++** ++** The contents of this file are subject to the Mozilla Public ++** License Version 1.1 (the "License"); you may not use this file ++** except in compliance with the License. You may obtain a copy of ++** the License at http://www.mozilla.org/MPL/ ++** ++** Software distributed under the License is distributed on an "AS ++** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or ++** implied. See the License for the specific language governing ++** rights and limitations under the License. ++** ++** Alternatively, the contents of this file may be used under the ++** terms of the GNU Public License version 2 (the "GPL"), in which ++** case the provisions of the GPL are applicable instead of the ++** above. If you wish to allow the use of your version of this file ++** only under the terms of the GPL and not to allow others to use ++** your version of this file under the MPL, indicate your decision ++** by deleting the provisions above and replace them with the notice ++** and other provisions required by the GPL. If you do not delete ++** the provisions above, a recipient may use your version of this ++** file under either the MPL or the GPL. ++** --------------------------------------------------------------------- ++** Inquiries regarding the ACX100 Open Source Project can be ++** made directly to: ++** ++** acx100-users@lists.sf.net ++** http://acx100.sf.net ++** --------------------------------------------------------------------- ++*/ ++ ++#include <linux/version.h> ++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) ++#include <linux/config.h> ++#endif ++#include <linux/kernel.h> ++#include <linux/types.h> ++#include <asm/io.h> ++/* #include <asm/uaccess.h> */ /* required for 2.4.x kernels; verify_write() */ ++#include <linux/if_arp.h> ++#include <linux/wireless.h> ++#include <net/iw_handler.h> ++ ++#include "acx.h" ++ ++ ++/*********************************************************************** ++*/ ++ ++/* channel frequencies ++ * TODO: Currently, every other 802.11 driver keeps its own copy of this. In ++ * the long run this should be integrated into ieee802_11.h or wireless.h or ++ * whatever IEEE802.11x framework evolves */ ++static const u16 acx_channel_freq[] = { ++ 2412, 2417, 2422, 2427, 2432, 2437, 2442, ++ 2447, 2452, 2457, 2462, 2467, 2472, 2484, ++}; ++ ++ ++/*********************************************************************** ++** acx_ioctl_commit ++*/ ++static int ++acx_ioctl_commit(struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ ++ FN_ENTER; ++ ++ acx_sem_lock(adev); ++ if (ACX_STATE_IFACE_UP & adev->dev_state_mask) ++ acx_s_update_card_settings(adev); ++ acx_sem_unlock(adev); ++ ++ FN_EXIT0; ++ return OK; ++} ++ ++ ++/*********************************************************************** ++*/ ++static int ++acx_ioctl_get_name( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ static const char * const names[] = { "IEEE 802.11b+/g+", "IEEE 802.11b+" }; ++ ++ strcpy(wrqu->name, names[IS_ACX111(adev) ? 0 : 1]); ++ ++ return OK; ++} ++ ++ ++/*********************************************************************** ++** acx_ioctl_set_freq ++*/ ++static int ++acx_ioctl_set_freq( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ int channel = -1; ++ unsigned int mult = 1; ++ int result; ++ ++ FN_ENTER; ++ ++ if (wrqu->freq.e == 0 && wrqu->freq.m <= 1000) { ++ /* Setting by channel number */ ++ channel = wrqu->freq.m; ++ } else { ++ /* If setting by frequency, convert to a channel */ ++ int i; ++ ++ for (i = 0; i < (6 - wrqu->freq.e); i++) ++ mult *= 10; ++ ++ for (i = 1; i <= 14; i++) ++ if (wrqu->freq.m == acx_channel_freq[i - 1] * mult) ++ channel = i; ++ } ++ ++ if (channel > 14) { ++ result = -EINVAL; ++ goto end; ++ } ++ ++ acx_sem_lock(adev); ++ ++ adev->channel = channel; ++ /* hmm, the following code part is strange, but this is how ++ * it was being done before... */ ++ log(L_IOCTL, "Changing to channel %d\n", channel); ++ SET_BIT(adev->set_mask, GETSET_CHANNEL); ++ ++ result = -EINPROGRESS; /* need to call commit handler */ ++ ++ acx_sem_unlock(adev); ++end: ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++*/ ++static inline int ++acx_ioctl_get_freq( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ wrqu->freq.e = 0; ++ wrqu->freq.m = adev->channel; ++ return OK; ++} ++ ++ ++/*********************************************************************** ++** acx_ioctl_set_mode ++*/ ++static int ++acx_ioctl_set_mode( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ int result; ++ ++ FN_ENTER; ++ ++ acx_sem_lock(adev); ++ ++ switch (wrqu->mode) { ++ case IW_MODE_AUTO: ++ adev->mode = ACX_MODE_OFF; ++ break; ++ case IW_MODE_MONITOR: ++ adev->mode = ACX_MODE_MONITOR; ++ break; ++ case IW_MODE_ADHOC: ++ adev->mode = ACX_MODE_0_ADHOC; ++ break; ++ case IW_MODE_INFRA: ++ adev->mode = ACX_MODE_2_STA; ++ break; ++ case IW_MODE_MASTER: ++ printk("acx: master mode (HostAP) is very, very " ++ "experimental! It might work partially, but " ++ "better get prepared for nasty surprises " ++ "at any time\n"); ++ adev->mode = ACX_MODE_3_AP; ++ break; ++ case IW_MODE_REPEAT: ++ case IW_MODE_SECOND: ++ default: ++ result = -EOPNOTSUPP; ++ goto end_unlock; ++ } ++ ++ log(L_ASSOC, "new adev->mode=%d\n", adev->mode); ++ SET_BIT(adev->set_mask, GETSET_MODE); ++ result = -EINPROGRESS; ++ ++end_unlock: ++ acx_sem_unlock(adev); ++ ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++*/ ++static int ++acx_ioctl_get_mode( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ int result = 0; ++ ++ switch (adev->mode) { ++ case ACX_MODE_OFF: ++ wrqu->mode = IW_MODE_AUTO; break; ++ case ACX_MODE_MONITOR: ++ wrqu->mode = IW_MODE_MONITOR; break; ++ case ACX_MODE_0_ADHOC: ++ wrqu->mode = IW_MODE_ADHOC; break; ++ case ACX_MODE_2_STA: ++ wrqu->mode = IW_MODE_INFRA; break; ++ case ACX_MODE_3_AP: ++ wrqu->mode = IW_MODE_MASTER; break; ++ default: ++ result = -EOPNOTSUPP; ++ } ++ return result; ++} ++ ++ ++/*********************************************************************** ++*/ ++static int ++acx_ioctl_set_sens( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ struct iw_param *vwrq = &wrqu->sens; ++ acx_device_t *adev = ndev2adev(ndev); ++ ++ acx_sem_lock(adev); ++ ++ adev->sensitivity = (1 == vwrq->disabled) ? 0 : vwrq->value; ++ SET_BIT(adev->set_mask, GETSET_SENSITIVITY); ++ ++ acx_sem_unlock(adev); ++ ++ return -EINPROGRESS; ++} ++ ++ ++/*********************************************************************** ++*/ ++static int ++acx_ioctl_get_sens( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ struct iw_param *vwrq = &wrqu->sens; ++ acx_device_t *adev = ndev2adev(ndev); ++ ++ if (IS_USB(adev)) ++ /* setting the PHY reg via fw cmd doesn't work yet */ ++ return -EOPNOTSUPP; ++ ++ /* acx_sem_lock(adev); */ ++ ++ vwrq->value = adev->sensitivity; ++ vwrq->disabled = (vwrq->value == 0); ++ vwrq->fixed = 1; ++ ++ /* acx_sem_unlock(adev); */ ++ ++ return OK; ++} ++ ++ ++/*********************************************************************** ++** acx_ioctl_set_ap ++** ++** Sets the MAC address of the AP to associate with ++*/ ++static int ++acx_ioctl_set_ap( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ struct sockaddr *awrq = &wrqu->ap_addr; ++ acx_device_t *adev = ndev2adev(ndev); ++ int result = 0; ++ const u8 *ap; ++ ++ FN_ENTER; ++ if (NULL == awrq) { ++ result = -EFAULT; ++ goto end; ++ } ++ if (ARPHRD_ETHER != awrq->sa_family) { ++ result = -EINVAL; ++ goto end; ++ } ++ ++ ap = awrq->sa_data; ++ acxlog_mac(L_IOCTL, "set AP=", ap, "\n"); ++ ++ MAC_COPY(adev->ap, ap); ++ ++ /* We want to start rescan in managed or ad-hoc mode, ++ ** otherwise just set adev->ap. ++ ** "iwconfig <if> ap <mac> mode managed": we must be able ++ ** to set ap _first_ and _then_ set mode */ ++ switch (adev->mode) { ++ case ACX_MODE_0_ADHOC: ++ case ACX_MODE_2_STA: ++ /* FIXME: if there is a convention on what zero AP means, ++ ** please add a comment about that. I don't know of any --vda */ ++ if (mac_is_zero(ap)) { ++ /* "off" == 00:00:00:00:00:00 */ ++ MAC_BCAST(adev->ap); ++ log(L_IOCTL, "Not reassociating\n"); ++ } else { ++ log(L_IOCTL, "Forcing reassociation\n"); ++ SET_BIT(adev->set_mask, GETSET_RESCAN); ++ } ++ break; ++ } ++ result = -EINPROGRESS; ++end: ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++*/ ++static int ++acx_ioctl_get_ap( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ struct sockaddr *awrq = &wrqu->ap_addr; ++ acx_device_t *adev = ndev2adev(ndev); ++ ++ if (ACX_STATUS_4_ASSOCIATED == adev->status) { ++ /* as seen in Aironet driver, airo.c */ ++ MAC_COPY(awrq->sa_data, adev->bssid); ++ } else { ++ MAC_ZERO(awrq->sa_data); ++ } ++ awrq->sa_family = ARPHRD_ETHER; ++ return OK; ++} ++ ++ ++/*********************************************************************** ++** acx_ioctl_get_aplist ++** ++** Deprecated in favor of iwscan. ++** We simply return the list of currently available stations in range, ++** don't do a new scan. ++*/ ++static int ++acx_ioctl_get_aplist( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ struct iw_point *dwrq = &wrqu->data; ++ acx_device_t *adev = ndev2adev(ndev); ++ struct sockaddr *address = (struct sockaddr *) extra; ++ struct iw_quality qual[IW_MAX_AP]; ++ int i, cur; ++ int result = OK; ++ ++ FN_ENTER; ++ ++ /* we have AP list only in STA mode */ ++ if (ACX_MODE_2_STA != adev->mode) { ++ result = -EOPNOTSUPP; ++ goto end; ++ } ++ ++ cur = 0; ++ for (i = 0; i < VEC_SIZE(adev->sta_list); i++) { ++ struct client *bss = &adev->sta_list[i]; ++ if (!bss->used) continue; ++ MAC_COPY(address[cur].sa_data, bss->bssid); ++ address[cur].sa_family = ARPHRD_ETHER; ++ qual[cur].level = bss->sir; ++ qual[cur].noise = bss->snr; ++#ifndef OLD_QUALITY ++ qual[cur].qual = acx_signal_determine_quality(qual[cur].level, ++ qual[cur].noise); ++#else ++ qual[cur].qual = (qual[cur].noise <= 100) ? ++ 100 - qual[cur].noise : 0; ++#endif ++ /* no scan: level/noise/qual not updated: */ ++ qual[cur].updated = 0; ++ cur++; ++ } ++ if (cur) { ++ dwrq->flags = 1; ++ memcpy(extra + sizeof(struct sockaddr)*cur, &qual, ++ sizeof(struct iw_quality)*cur); ++ } ++ dwrq->length = cur; ++end: ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++*/ ++static int ++acx_ioctl_set_scan( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ int result; ++ ++ FN_ENTER; ++ ++ acx_sem_lock(adev); ++ ++ /* don't start scan if device is not up yet */ ++ if (!(adev->dev_state_mask & ACX_STATE_IFACE_UP)) { ++ result = -EAGAIN; ++ goto end_unlock; ++ } ++ ++ /* This is NOT a rescan for new AP! ++ ** Do not use SET_BIT(GETSET_RESCAN); */ ++ acx_s_cmd_start_scan(adev); ++ result = OK; ++ ++end_unlock: ++ acx_sem_unlock(adev); ++/* end: */ ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++** acx_s_scan_add_station ++*/ ++/* helper. not sure whether it's really a _s_leeping fn */ ++static char* ++acx_s_scan_add_station( ++ acx_device_t *adev, ++ char *ptr, ++ char *end_buf, ++ struct client *bss) ++{ ++ struct iw_event iwe; ++ char *ptr_rate; ++ ++ FN_ENTER; ++ ++ /* MAC address has to be added first */ ++ iwe.cmd = SIOCGIWAP; ++ iwe.u.ap_addr.sa_family = ARPHRD_ETHER; ++ MAC_COPY(iwe.u.ap_addr.sa_data, bss->bssid); ++ acxlog_mac(L_IOCTL, "scan, station address: ", bss->bssid, "\n"); ++ ptr = iwe_stream_add_event(ptr, end_buf, &iwe, IW_EV_ADDR_LEN); ++ ++ /* Add ESSID */ ++ iwe.cmd = SIOCGIWESSID; ++ iwe.u.data.length = bss->essid_len; ++ iwe.u.data.flags = 1; ++ log(L_IOCTL, "scan, essid: %s\n", bss->essid); ++ ptr = iwe_stream_add_point(ptr, end_buf, &iwe, bss->essid); ++ ++ /* Add mode */ ++ iwe.cmd = SIOCGIWMODE; ++ if (bss->cap_info & (WF_MGMT_CAP_ESS | WF_MGMT_CAP_IBSS)) { ++ if (bss->cap_info & WF_MGMT_CAP_ESS) ++ iwe.u.mode = IW_MODE_MASTER; ++ else ++ iwe.u.mode = IW_MODE_ADHOC; ++ log(L_IOCTL, "scan, mode: %d\n", iwe.u.mode); ++ ptr = iwe_stream_add_event(ptr, end_buf, &iwe, IW_EV_UINT_LEN); ++ } ++ ++ /* Add frequency */ ++ iwe.cmd = SIOCGIWFREQ; ++ iwe.u.freq.m = acx_channel_freq[bss->channel - 1] * 100000; ++ iwe.u.freq.e = 1; ++ log(L_IOCTL, "scan, frequency: %d\n", iwe.u.freq.m); ++ ptr = iwe_stream_add_event(ptr, end_buf, &iwe, IW_EV_FREQ_LEN); ++ ++ /* Add link quality */ ++ iwe.cmd = IWEVQUAL; ++ /* FIXME: these values should be expressed in dBm, but we don't know ++ * how to calibrate it yet */ ++ iwe.u.qual.level = bss->sir; ++ iwe.u.qual.noise = bss->snr; ++#ifndef OLD_QUALITY ++ iwe.u.qual.qual = acx_signal_determine_quality(iwe.u.qual.level, ++ iwe.u.qual.noise); ++#else ++ iwe.u.qual.qual = (iwe.u.qual.noise <= 100) ? ++ 100 - iwe.u.qual.noise : 0; ++#endif ++ iwe.u.qual.updated = 7; ++ log(L_IOCTL, "scan, link quality: %d/%d/%d\n", ++ iwe.u.qual.level, iwe.u.qual.noise, iwe.u.qual.qual); ++ ptr = iwe_stream_add_event(ptr, end_buf, &iwe, IW_EV_QUAL_LEN); ++ ++ /* Add encryption */ ++ iwe.cmd = SIOCGIWENCODE; ++ if (bss->cap_info & WF_MGMT_CAP_PRIVACY) ++ iwe.u.data.flags = IW_ENCODE_ENABLED | IW_ENCODE_NOKEY; ++ else ++ iwe.u.data.flags = IW_ENCODE_DISABLED; ++ iwe.u.data.length = 0; ++ log(L_IOCTL, "scan, encryption flags: %X\n", iwe.u.data.flags); ++ ptr = iwe_stream_add_point(ptr, end_buf, &iwe, bss->essid); ++ ++ /* add rates */ ++ iwe.cmd = SIOCGIWRATE; ++ iwe.u.bitrate.fixed = iwe.u.bitrate.disabled = 0; ++ ptr_rate = ptr + IW_EV_LCP_LEN; ++ ++ { ++ u16 rate = bss->rate_cap; ++ const u8* p = acx_bitpos2ratebyte; ++ while (rate) { ++ if (rate & 1) { ++ iwe.u.bitrate.value = *p * 500000; /* units of 500kb/s */ ++ log(L_IOCTL, "scan, rate: %d\n", iwe.u.bitrate.value); ++ ptr_rate = iwe_stream_add_value(ptr, ptr_rate, end_buf, ++ &iwe, IW_EV_PARAM_LEN); ++ } ++ rate >>= 1; ++ p++; ++ }} ++ ++ if ((ptr_rate - ptr) > (ptrdiff_t)IW_EV_LCP_LEN) ++ ptr = ptr_rate; ++ ++ /* drop remaining station data items for now */ ++ ++ FN_EXIT0; ++ return ptr; ++} ++ ++ ++/*********************************************************************** ++ * acx_ioctl_get_scan ++ */ ++static int ++acx_ioctl_get_scan( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ struct iw_point *dwrq = &wrqu->data; ++ acx_device_t *adev = ndev2adev(ndev); ++ char *ptr = extra; ++ int i; ++ int result = OK; ++ ++ FN_ENTER; ++ ++ acx_sem_lock(adev); ++ ++ /* no scan available if device is not up yet */ ++ if (!(adev->dev_state_mask & ACX_STATE_IFACE_UP)) { ++ log(L_IOCTL, "iface not up yet\n"); ++ result = -EAGAIN; ++ goto end_unlock; ++ } ++ ++#ifdef ENODATA_TO_BE_USED_AFTER_SCAN_ERROR_ONLY ++ if (adev->bss_table_count == 0) { ++ /* no stations found */ ++ result = -ENODATA; ++ goto end_unlock; ++ } ++#endif ++ ++ for (i = 0; i < VEC_SIZE(adev->sta_list); i++) { ++ struct client *bss = &adev->sta_list[i]; ++ if (!bss->used) continue; ++ ptr = acx_s_scan_add_station(adev, ptr, ++ extra + IW_SCAN_MAX_DATA, bss); ++ } ++ dwrq->length = ptr - extra; ++ dwrq->flags = 0; ++ ++end_unlock: ++ acx_sem_unlock(adev); ++/* end: */ ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++** acx_ioctl_set_essid ++*/ ++static int ++acx_ioctl_set_essid( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ struct iw_point *dwrq = &wrqu->essid; ++ acx_device_t *adev = ndev2adev(ndev); ++ int len = dwrq->length; ++ int result; ++ ++ FN_ENTER; ++ ++ if (len < 0) { ++ result = -EINVAL; ++ goto end; ++ } ++ ++ log(L_IOCTL, "set ESSID '%*s', length %d, flags 0x%04X\n", ++ len, extra, len, dwrq->flags); ++ ++#if WIRELESS_EXT >= 21 ++ /* WE 21 gives real ESSID strlen, not +1 (trailing zero): ++ * see LKML "[patch] drivers/net/wireless: correct reported ssid lengths" */ ++ len += 1; ++#endif ++ ++ acx_sem_lock(adev); ++ ++ /* ESSID disabled? */ ++ if (0 == dwrq->flags) { ++ adev->essid_active = 0; ++ ++ } else { ++ if (len > IW_ESSID_MAX_SIZE) { ++ result = -E2BIG; ++ goto end_unlock; ++ } ++ ++ if (len >= sizeof(adev->essid)) ++ len = sizeof(adev->essid) - 1; ++ memcpy(adev->essid, extra, len); ++ adev->essid[len] = '\0'; ++ /* Paranoia: just in case there is a '\0'... */ ++ adev->essid_len = strlen(adev->essid); ++ adev->essid_active = 1; ++ } ++ ++ SET_BIT(adev->set_mask, GETSET_RESCAN); ++ ++ result = -EINPROGRESS; ++ ++end_unlock: ++ acx_sem_unlock(adev); ++end: ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++*/ ++static int ++acx_ioctl_get_essid( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ struct iw_point *dwrq = &wrqu->essid; ++ acx_device_t *adev = ndev2adev(ndev); ++ ++ dwrq->flags = adev->essid_active; ++ if (adev->essid_active) { ++ memcpy(extra, adev->essid, adev->essid_len); ++ extra[adev->essid_len] = '\0'; ++ dwrq->length = adev->essid_len + 1; ++ dwrq->flags = 1; ++ } ++ return OK; ++} ++ ++ ++/*********************************************************************** ++** acx_l_update_client_rates ++*/ ++static void ++acx_l_update_client_rates(acx_device_t *adev, u16 rate) ++{ ++ int i; ++ for (i = 0; i < VEC_SIZE(adev->sta_list); i++) { ++ client_t *clt = &adev->sta_list[i]; ++ if (!clt->used) continue; ++ clt->rate_cfg = (clt->rate_cap & rate); ++ if (!clt->rate_cfg) { ++ /* no compatible rates left: kick client */ ++ acxlog_mac(L_ASSOC, "client ",clt->address," kicked: " ++ "rates are not compatible anymore\n"); ++ acx_l_sta_list_del(adev, clt); ++ continue; ++ } ++ clt->rate_cur &= clt->rate_cfg; ++ if (!clt->rate_cur) { ++ /* current rate become invalid, choose a valid one */ ++ clt->rate_cur = 1 << lowest_bit(clt->rate_cfg); ++ } ++ if (IS_ACX100(adev)) ++ clt->rate_100 = acx_bitpos2rate100[highest_bit(clt->rate_cur)]; ++ clt->fallback_count = clt->stepup_count = 0; ++ clt->ignore_count = 16; ++ } ++ switch (adev->mode) { ++ case ACX_MODE_2_STA: ++ if (adev->ap_client && !adev->ap_client->used) { ++ /* Owwww... we kicked our AP!! :) */ ++ SET_BIT(adev->set_mask, GETSET_RESCAN); ++ } ++ } ++} ++ ++ ++/*********************************************************************** ++*/ ++/* maps bits from acx111 rate to rate in Mbits */ ++static const unsigned int ++acx111_rate_tbl[] = { ++ 1000000, /* 0 */ ++ 2000000, /* 1 */ ++ 5500000, /* 2 */ ++ 6000000, /* 3 */ ++ 9000000, /* 4 */ ++ 11000000, /* 5 */ ++ 12000000, /* 6 */ ++ 18000000, /* 7 */ ++ 22000000, /* 8 */ ++ 24000000, /* 9 */ ++ 36000000, /* 10 */ ++ 48000000, /* 11 */ ++ 54000000, /* 12 */ ++ 500000, /* 13, should not happen */ ++ 500000, /* 14, should not happen */ ++ 500000, /* 15, should not happen */ ++}; ++ ++/*********************************************************************** ++ * acx_ioctl_set_rate ++ */ ++static int ++acx_ioctl_set_rate( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ struct iw_param *vwrq = &wrqu->param; ++ acx_device_t *adev = ndev2adev(ndev); ++ u16 txrate_cfg = 1; ++ unsigned long flags; ++ int autorate; ++ int result = -EINVAL; ++ ++ FN_ENTER; ++ log(L_IOCTL, "rate %d fixed 0x%X disabled 0x%X flags 0x%X\n", ++ vwrq->value, vwrq->fixed, vwrq->disabled, vwrq->flags); ++ ++ if ((0 == vwrq->fixed) || (1 == vwrq->fixed)) { ++ int i = VEC_SIZE(acx111_rate_tbl)-1; ++ if (vwrq->value == -1) ++ /* "iwconfig rate auto" --> choose highest */ ++ vwrq->value = IS_ACX100(adev) ? 22000000 : 54000000; ++ while (i >= 0) { ++ if (vwrq->value == acx111_rate_tbl[i]) { ++ txrate_cfg <<= i; ++ i = 0; ++ break; ++ } ++ i--; ++ } ++ if (i == -1) { /* no matching rate */ ++ result = -EINVAL; ++ goto end; ++ } ++ } else { /* rate N, N<1000 (driver specific): we don't use this */ ++ result = -EOPNOTSUPP; ++ goto end; ++ } ++ /* now: only one bit is set in txrate_cfg, corresponding to ++ ** indicated rate */ ++ ++ autorate = (vwrq->fixed == 0) && (RATE111_1 != txrate_cfg); ++ if (autorate) { ++ /* convert 00100000 -> 00111111 */ ++ txrate_cfg = (txrate_cfg<<1)-1; ++ } ++ ++ if (IS_ACX100(adev)) { ++ txrate_cfg &= RATE111_ACX100_COMPAT; ++ if (!txrate_cfg) { ++ result = -ENOTSUPP; /* rate is not supported by acx100 */ ++ goto end; ++ } ++ } ++ ++ acx_sem_lock(adev); ++ acx_lock(adev, flags); ++ ++ adev->rate_auto = autorate; ++ adev->rate_oper = txrate_cfg; ++ adev->rate_basic = txrate_cfg; ++ /* only do that in auto mode, non-auto will be able to use ++ * one specific Tx rate only anyway */ ++ if (autorate) { ++ /* only use 802.11b base rates, for standard 802.11b H/W ++ * compatibility */ ++ adev->rate_basic &= RATE111_80211B_COMPAT; ++ } ++ adev->rate_bcast = 1 << lowest_bit(txrate_cfg); ++ if (IS_ACX100(adev)) ++ adev->rate_bcast100 = acx_rate111to100(adev->rate_bcast); ++ acx_l_update_ratevector(adev); ++ acx_l_update_client_rates(adev, txrate_cfg); ++ ++ /* Do/don't do tx rate fallback; beacon contents and rate */ ++ SET_BIT(adev->set_mask, SET_RATE_FALLBACK|SET_TEMPLATES); ++ result = -EINPROGRESS; ++ ++ acx_unlock(adev, flags); ++ acx_sem_unlock(adev); ++end: ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++** acx_ioctl_get_rate ++*/ ++static int ++acx_ioctl_get_rate( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ struct iw_param *vwrq = &wrqu->param; ++ acx_device_t *adev = ndev2adev(ndev); ++ unsigned long flags; ++ u16 rate; ++ ++ acx_lock(adev, flags); ++ rate = adev->rate_oper; ++ if (adev->ap_client) ++ rate = adev->ap_client->rate_cur; ++ vwrq->value = acx111_rate_tbl[highest_bit(rate)]; ++ vwrq->fixed = !adev->rate_auto; ++ vwrq->disabled = 0; ++ acx_unlock(adev, flags); ++ ++ return OK; ++} ++ ++static int ++acx_ioctl_set_rts( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ struct iw_param *vwrq = &wrqu->rts; ++ acx_device_t *adev = ndev2adev(ndev); ++ int val = vwrq->value; ++ ++ if (vwrq->disabled) ++ val = 2312; ++ if ((val < 0) || (val > 2312)) ++ return -EINVAL; ++ ++ adev->rts_threshold = val; ++ return OK; ++} ++ ++static inline int ++acx_ioctl_get_rts( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ struct iw_param *vwrq = &wrqu->rts; ++ acx_device_t *adev = ndev2adev(ndev); ++ ++ vwrq->value = adev->rts_threshold; ++ vwrq->disabled = (vwrq->value >= 2312); ++ vwrq->fixed = 1; ++ return OK; ++} ++ ++ ++#if ACX_FRAGMENTATION ++static int ++acx_ioctl_set_frag( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ struct iw_param *vwrq, ++ char *extra) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ int val = vwrq->value; ++ ++ if (vwrq->disabled) ++ val = 32767; ++ else ++ if ((val < 256) || (val > 2347)) ++ return -EINVAL; ++ ++ adev->frag_threshold = val; ++ return OK; ++} ++ ++static inline int ++acx_ioctl_get_frag( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ struct iw_param *vwrq = &wrqu->frag; ++ acx_device_t *adev = ndev2adev(ndev); ++ ++ vwrq->value = adev->frag_threshold; ++ vwrq->disabled = (vwrq->value >= 2347); ++ vwrq->fixed = 1; ++ return OK; ++} ++#endif ++ ++ ++/*********************************************************************** ++** acx_ioctl_set_encode ++*/ ++static int ++acx_ioctl_set_encode( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ struct iw_point *dwrq = &wrqu->encoding; ++ acx_device_t *adev = ndev2adev(ndev); ++ int index; ++ int result; ++ ++ FN_ENTER; ++ ++ log(L_IOCTL, "set encoding flags=0x%04X, size=%d, key: %s\n", ++ dwrq->flags, dwrq->length, extra ? "set" : "No key"); ++ ++ acx_sem_lock(adev); ++ ++ index = (dwrq->flags & IW_ENCODE_INDEX) - 1; ++ ++ if (dwrq->length > 0) { ++ /* if index is 0 or invalid, use default key */ ++ if ((index < 0) || (index > 3)) ++ index = (int)adev->wep_current_index; ++ ++ if (0 == (dwrq->flags & IW_ENCODE_NOKEY)) { ++ if (dwrq->length > 29) ++ dwrq->length = 29; /* restrict it */ ++ ++ if (dwrq->length > 13) { ++ /* 29*8 == 232, WEP256 */ ++ adev->wep_keys[index].size = 29; ++ } else if (dwrq->length > 5) { ++ /* 13*8 == 104bit, WEP128 */ ++ adev->wep_keys[index].size = 13; ++ } else if (dwrq->length > 0) { ++ /* 5*8 == 40bit, WEP64 */ ++ adev->wep_keys[index].size = 5; ++ } else { ++ /* disable key */ ++ adev->wep_keys[index].size = 0; ++ } ++ ++ memset(adev->wep_keys[index].key, 0, ++ sizeof(adev->wep_keys[index].key)); ++ memcpy(adev->wep_keys[index].key, extra, dwrq->length); ++ } ++ } else { ++ /* set transmit key */ ++ if ((index >= 0) && (index <= 3)) ++ adev->wep_current_index = index; ++ else if (0 == (dwrq->flags & IW_ENCODE_MODE)) { ++ /* complain if we were not just setting ++ * the key mode */ ++ result = -EINVAL; ++ goto end_unlock; ++ } ++ } ++ ++ adev->wep_enabled = !(dwrq->flags & IW_ENCODE_DISABLED); ++ ++ if (dwrq->flags & IW_ENCODE_OPEN) { ++ adev->auth_alg = WLAN_AUTH_ALG_OPENSYSTEM; ++ adev->wep_restricted = 0; ++ ++ } else if (dwrq->flags & IW_ENCODE_RESTRICTED) { ++ adev->auth_alg = WLAN_AUTH_ALG_SHAREDKEY; ++ adev->wep_restricted = 1; ++ } ++ ++ /* set flag to make sure the card WEP settings get updated */ ++ SET_BIT(adev->set_mask, GETSET_WEP); ++ ++ log(L_IOCTL, "len=%d, key at 0x%p, flags=0x%X\n", ++ dwrq->length, extra, dwrq->flags); ++ ++ for (index = 0; index <= 3; index++) { ++ if (adev->wep_keys[index].size) { ++ log(L_IOCTL, "index=%d, size=%d, key at 0x%p\n", ++ adev->wep_keys[index].index, ++ (int) adev->wep_keys[index].size, ++ adev->wep_keys[index].key); ++ } ++ } ++ result = -EINPROGRESS; ++ ++end_unlock: ++ acx_sem_unlock(adev); ++ ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++** acx_ioctl_get_encode ++*/ ++static int ++acx_ioctl_get_encode( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ struct iw_point *dwrq = &wrqu->encoding; ++ acx_device_t *adev = ndev2adev(ndev); ++ int index = (dwrq->flags & IW_ENCODE_INDEX) - 1; ++ ++ FN_ENTER; ++ ++ if (adev->wep_enabled == 0) { ++ dwrq->flags = IW_ENCODE_DISABLED; ++ } else { ++ if ((index < 0) || (index > 3)) ++ index = (int)adev->wep_current_index; ++ ++ dwrq->flags = (adev->wep_restricted == 1) ? ++ IW_ENCODE_RESTRICTED : IW_ENCODE_OPEN; ++ dwrq->length = adev->wep_keys[index].size; ++ ++ memcpy(extra, adev->wep_keys[index].key, ++ adev->wep_keys[index].size); ++ } ++ ++ /* set the current index */ ++ SET_BIT(dwrq->flags, index + 1); ++ ++ log(L_IOCTL, "len=%d, key=%p, flags=0x%X\n", ++ dwrq->length, dwrq->pointer, ++ dwrq->flags); ++ ++ FN_EXIT1(OK); ++ return OK; ++} ++ ++ ++/*********************************************************************** ++*/ ++static int ++acx_ioctl_set_power( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ struct iw_param *vwrq = &wrqu->power; ++ acx_device_t *adev = ndev2adev(ndev); ++ int result = -EINPROGRESS; ++ ++ FN_ENTER; ++ ++ log(L_IOCTL, "set 802.11 powersave flags=0x%04X\n", vwrq->flags); ++ ++ acx_sem_lock(adev); ++ ++ if (vwrq->disabled) { ++ CLEAR_BIT(adev->ps_wakeup_cfg, PS_CFG_ENABLE); ++ SET_BIT(adev->set_mask, GETSET_POWER_80211); ++ goto end; ++ } ++ if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { ++ u16 ps_timeout = (vwrq->value * 1024) / 1000; ++ ++ if (ps_timeout > 255) ++ ps_timeout = 255; ++ log(L_IOCTL, "setting PS timeout value to %d time units " ++ "due to %dus\n", ps_timeout, vwrq->value); ++ adev->ps_hangover_period = ps_timeout; ++ } else if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_PERIOD) { ++ u16 ps_periods = vwrq->value / 1000000; ++ ++ if (ps_periods > 255) ++ ps_periods = 255; ++ log(L_IOCTL, "setting PS period value to %d periods " ++ "due to %dus\n", ps_periods, vwrq->value); ++ adev->ps_listen_interval = ps_periods; ++ CLEAR_BIT(adev->ps_wakeup_cfg, PS_CFG_WAKEUP_MODE_MASK); ++ SET_BIT(adev->ps_wakeup_cfg, PS_CFG_WAKEUP_EACH_ITVL); ++ } ++ ++ switch (vwrq->flags & IW_POWER_MODE) { ++ /* FIXME: are we doing the right thing here? */ ++ case IW_POWER_UNICAST_R: ++ CLEAR_BIT(adev->ps_options, PS_OPT_STILL_RCV_BCASTS); ++ break; ++ case IW_POWER_MULTICAST_R: ++ SET_BIT(adev->ps_options, PS_OPT_STILL_RCV_BCASTS); ++ break; ++ case IW_POWER_ALL_R: ++ SET_BIT(adev->ps_options, PS_OPT_STILL_RCV_BCASTS); ++ break; ++ case IW_POWER_ON: ++ break; ++ default: ++ log(L_IOCTL, "unknown PS mode\n"); ++ result = -EINVAL; ++ goto end; ++ } ++ ++ SET_BIT(adev->ps_wakeup_cfg, PS_CFG_ENABLE); ++ SET_BIT(adev->set_mask, GETSET_POWER_80211); ++end: ++ acx_sem_unlock(adev); ++ ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++*/ ++static int ++acx_ioctl_get_power( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ struct iw_param *vwrq = &wrqu->power; ++ acx_device_t *adev = ndev2adev(ndev); ++ ++ FN_ENTER; ++ ++ log(L_IOCTL, "Get 802.11 Power Save flags = 0x%04X\n", vwrq->flags); ++ vwrq->disabled = ((adev->ps_wakeup_cfg & PS_CFG_ENABLE) == 0); ++ if (vwrq->disabled) ++ goto end; ++ ++ if ((vwrq->flags & IW_POWER_TYPE) == IW_POWER_TIMEOUT) { ++ vwrq->value = adev->ps_hangover_period * 1000 / 1024; ++ vwrq->flags = IW_POWER_TIMEOUT; ++ } else { ++ vwrq->value = adev->ps_listen_interval * 1000000; ++ vwrq->flags = IW_POWER_PERIOD|IW_POWER_RELATIVE; ++ } ++ if (adev->ps_options & PS_OPT_STILL_RCV_BCASTS) ++ SET_BIT(vwrq->flags, IW_POWER_ALL_R); ++ else ++ SET_BIT(vwrq->flags, IW_POWER_UNICAST_R); ++end: ++ FN_EXIT1(OK); ++ return OK; ++} ++ ++ ++/*********************************************************************** ++** acx_ioctl_get_txpow ++*/ ++static inline int ++acx_ioctl_get_txpow( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ struct iw_param *vwrq = &wrqu->power; ++ acx_device_t *adev = ndev2adev(ndev); ++ ++ FN_ENTER; ++ ++ vwrq->flags = IW_TXPOW_DBM; ++ vwrq->disabled = 0; ++ vwrq->fixed = 1; ++ vwrq->value = adev->tx_level_dbm; ++ ++ log(L_IOCTL, "get txpower:%d dBm\n", adev->tx_level_dbm); ++ ++ FN_EXIT1(OK); ++ return OK; ++} ++ ++ ++/*********************************************************************** ++** acx_ioctl_set_txpow ++*/ ++static int ++acx_ioctl_set_txpow( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ struct iw_param *vwrq = &wrqu->power; ++ acx_device_t *adev = ndev2adev(ndev); ++ int result; ++ ++ FN_ENTER; ++ ++ log(L_IOCTL, "set txpower:%d, disabled:%d, flags:0x%04X\n", ++ vwrq->value, vwrq->disabled, vwrq->flags); ++ ++ acx_sem_lock(adev); ++ ++ if (vwrq->disabled != adev->tx_disabled) { ++ SET_BIT(adev->set_mask, GETSET_TX); ++ } ++ ++ adev->tx_disabled = vwrq->disabled; ++ if (vwrq->value == -1) { ++ if (vwrq->disabled) { ++ adev->tx_level_dbm = 0; ++ log(L_IOCTL, "disable radio tx\n"); ++ } else { ++ /* adev->tx_level_auto = 1; */ ++ log(L_IOCTL, "set tx power auto (NIY)\n"); ++ } ++ } else { ++ adev->tx_level_dbm = vwrq->value <= 20 ? vwrq->value : 20; ++ /* adev->tx_level_auto = 0; */ ++ log(L_IOCTL, "set txpower=%d dBm\n", adev->tx_level_dbm); ++ } ++ SET_BIT(adev->set_mask, GETSET_TXPOWER); ++ ++ result = -EINPROGRESS; ++ ++ acx_sem_unlock(adev); ++ ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++** acx_ioctl_get_range ++*/ ++static int ++acx_ioctl_get_range( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ struct iw_point *dwrq = &wrqu->data; ++ struct iw_range *range = (struct iw_range *)extra; ++ acx_device_t *adev = ndev2adev(ndev); ++ int i,n; ++ ++ FN_ENTER; ++ ++ if (!dwrq->pointer) ++ goto end; ++ ++ dwrq->length = sizeof(struct iw_range); ++ memset(range, 0, sizeof(struct iw_range)); ++ n = 0; ++ for (i = 1; i <= 14; i++) { ++ if (adev->reg_dom_chanmask & (1 << (i - 1))) { ++ range->freq[n].i = i; ++ range->freq[n].m = acx_channel_freq[i - 1] * 100000; ++ range->freq[n].e = 1; /* units are MHz */ ++ n++; ++ } ++ } ++ range->num_channels = n; ++ range->num_frequency = n; ++ ++ range->min_rts = 0; ++ range->max_rts = 2312; ++ ++#if ACX_FRAGMENTATION ++ range->min_frag = 256; ++ range->max_frag = 2312; ++#endif ++ ++ range->encoding_size[0] = 5; ++ range->encoding_size[1] = 13; ++ range->encoding_size[2] = 29; ++ range->num_encoding_sizes = 3; ++ range->max_encoding_tokens = 4; ++ ++ range->min_pmp = 0; ++ range->max_pmp = 5000000; ++ range->min_pmt = 0; ++ range->max_pmt = 65535 * 1000; ++ range->pmp_flags = IW_POWER_PERIOD; ++ range->pmt_flags = IW_POWER_TIMEOUT; ++ range->pm_capa = IW_POWER_PERIOD | IW_POWER_TIMEOUT | IW_POWER_ALL_R; ++ ++ if (IS_ACX100(adev)) { /* ACX100 has direct radio programming - arbitrary levels, so offer a lot */ ++ for (i = 0; i <= IW_MAX_TXPOWER - 1; i++) ++ range->txpower[i] = 20 * i / (IW_MAX_TXPOWER - 1); ++ range->num_txpower = IW_MAX_TXPOWER; ++ range->txpower_capa = IW_TXPOW_DBM; ++ } ++ else { ++ int count = min(IW_MAX_TXPOWER, (int)adev->cfgopt_power_levels.len); ++ for (i = 0; i <= count; i++) ++ range->txpower[i] = adev->cfgopt_power_levels.list[i]; ++ range->num_txpower = count; ++ /* this list is given in mW */ ++ range->txpower_capa = IW_TXPOW_MWATT; ++ } ++ ++ range->we_version_compiled = WIRELESS_EXT; ++ range->we_version_source = 0x9; ++ ++ range->retry_capa = IW_RETRY_LIMIT; ++ range->retry_flags = IW_RETRY_LIMIT; ++ range->min_retry = 1; ++ range->max_retry = 255; ++ ++ range->r_time_flags = IW_RETRY_LIFETIME; ++ range->min_r_time = 0; ++ /* FIXME: lifetime ranges and orders of magnitude are strange?? */ ++ range->max_r_time = 65535; ++ ++ if (IS_USB(adev)) ++ range->sensitivity = 0; ++ else if (IS_ACX111(adev)) ++ range->sensitivity = 3; ++ else ++ range->sensitivity = 255; ++ ++ for (i=0; i < adev->rate_supported_len; i++) { ++ range->bitrate[i] = (adev->rate_supported[i] & ~0x80) * 500000; ++ /* never happens, but keep it, to be safe: */ ++ if (range->bitrate[i] == 0) ++ break; ++ } ++ range->num_bitrates = i; ++ ++ range->max_qual.qual = 100; ++ range->max_qual.level = 100; ++ range->max_qual.noise = 100; ++ /* TODO: better values */ ++ range->avg_qual.qual = 90; ++ range->avg_qual.level = 80; ++ range->avg_qual.noise = 2; ++ ++end: ++ FN_EXIT1(OK); ++ return OK; ++} ++ ++ ++/*********************************************************************** ++** Private functions ++*/ ++ ++/*********************************************************************** ++** acx_ioctl_get_nick ++*/ ++static inline int ++acx_ioctl_get_nick( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ struct iw_point *dwrq = &wrqu->data; ++ acx_device_t *adev = ndev2adev(ndev); ++ ++ strcpy(extra, adev->nick); ++ dwrq->length = strlen(extra) + 1; ++ ++ return OK; ++} ++ ++ ++/*********************************************************************** ++** acx_ioctl_set_nick ++*/ ++static int ++acx_ioctl_set_nick( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ struct iw_point *dwrq = &wrqu->data; ++ acx_device_t *adev = ndev2adev(ndev); ++ int result; ++ ++ FN_ENTER; ++ ++ acx_sem_lock(adev); ++ ++ if (dwrq->length > IW_ESSID_MAX_SIZE + 1) { ++ result = -E2BIG; ++ goto end_unlock; ++ } ++ ++ /* extra includes trailing \0, so it's ok */ ++ strcpy(adev->nick, extra); ++ result = OK; ++ ++end_unlock: ++ acx_sem_unlock(adev); ++ ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++** acx_ioctl_get_retry ++*/ ++static int ++acx_ioctl_get_retry( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ struct iw_param *vwrq = &wrqu->retry; ++ acx_device_t *adev = ndev2adev(ndev); ++ unsigned int type = vwrq->flags & IW_RETRY_TYPE; ++ unsigned int modifier = vwrq->flags & IW_RETRY_MODIFIER; ++ int result; ++ ++ FN_ENTER; ++ ++ acx_sem_lock(adev); ++ ++ /* return the short retry number by default */ ++ if (type == IW_RETRY_LIFETIME) { ++ vwrq->flags = IW_RETRY_LIFETIME; ++ vwrq->value = adev->msdu_lifetime; ++ } else if (modifier == IW_RETRY_MAX) { ++ vwrq->flags = IW_RETRY_LIMIT | IW_RETRY_MAX; ++ vwrq->value = adev->long_retry; ++ } else { ++ vwrq->flags = IW_RETRY_LIMIT; ++ if (adev->long_retry != adev->short_retry) ++ SET_BIT(vwrq->flags, IW_RETRY_MIN); ++ vwrq->value = adev->short_retry; ++ } ++ ++ /* can't be disabled */ ++ vwrq->disabled = (u8)0; ++ result = OK; ++ ++ acx_sem_unlock(adev); ++ ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++** acx_ioctl_set_retry ++*/ ++static int ++acx_ioctl_set_retry( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ struct iw_param *vwrq = &wrqu->retry; ++ acx_device_t *adev = ndev2adev(ndev); ++ int result; ++ ++ FN_ENTER; ++ ++ if (!vwrq) { ++ result = -EFAULT; ++ goto end; ++ } ++ if (vwrq->disabled) { ++ result = -EINVAL; ++ goto end; ++ } ++ ++ acx_sem_lock(adev); ++ ++ result = -EINVAL; ++ if (IW_RETRY_LIMIT == (vwrq->flags & IW_RETRY_TYPE)) { ++ printk("old retry limits: short %d long %d\n", ++ adev->short_retry, adev->long_retry); ++ if (vwrq->flags & IW_RETRY_MAX) { ++ adev->long_retry = vwrq->value; ++ } else if (vwrq->flags & IW_RETRY_MIN) { ++ adev->short_retry = vwrq->value; ++ } else { ++ /* no modifier: set both */ ++ adev->long_retry = vwrq->value; ++ adev->short_retry = vwrq->value; ++ } ++ printk("new retry limits: short %d long %d\n", ++ adev->short_retry, adev->long_retry); ++ SET_BIT(adev->set_mask, GETSET_RETRY); ++ result = -EINPROGRESS; ++ } ++ else if (vwrq->flags & IW_RETRY_LIFETIME) { ++ adev->msdu_lifetime = vwrq->value; ++ printk("new MSDU lifetime: %d\n", adev->msdu_lifetime); ++ SET_BIT(adev->set_mask, SET_MSDU_LIFETIME); ++ result = -EINPROGRESS; ++ } ++ ++ acx_sem_unlock(adev); ++end: ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/************************ private ioctls ******************************/ ++ ++ ++/*********************************************************************** ++** acx_ioctl_set_debug ++*/ ++#if ACX_DEBUG ++static int ++acx_ioctl_set_debug( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ unsigned int debug_new = *((unsigned int *)extra); ++ int result = -EINVAL; ++ ++ log(L_ANY, "setting debug from %04X to %04X\n", acx_debug, debug_new); ++ acx_debug = debug_new; ++ ++ result = OK; ++ return result; ++ ++} ++#endif ++ ++ ++/*********************************************************************** ++** acx_ioctl_list_reg_domain ++*/ ++static int ++acx_ioctl_list_reg_domain( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ int i = 1; ++ const char * const *entry = acx_reg_domain_strings; ++ ++ printk("dom# chan# domain/country\n"); ++ while (*entry) ++ printk("%4d %s\n", i++, *entry++); ++ return OK; ++} ++ ++ ++/*********************************************************************** ++** acx_ioctl_set_reg_domain ++*/ ++static int ++acx_ioctl_set_reg_domain( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ int result; ++ ++ FN_ENTER; ++ ++ if ((*extra < 1) || ((size_t)*extra > acx_reg_domain_ids_len)) { ++ result = -EINVAL; ++ goto end; ++ } ++ ++ acx_sem_lock(adev); ++ ++ adev->reg_dom_id = acx_reg_domain_ids[*extra - 1]; ++ SET_BIT(adev->set_mask, GETSET_REG_DOMAIN); ++ ++ result = -EINPROGRESS; ++ ++ acx_sem_unlock(adev); ++end: ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++** acx_ioctl_get_reg_domain ++*/ ++static int ++acx_ioctl_get_reg_domain( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ int dom,i; ++ ++ /* no locking */ ++ dom = adev->reg_dom_id; ++ ++ for (i = 1; i <= acx_reg_domain_ids_len; i++) { ++ if (acx_reg_domain_ids[i-1] == dom) { ++ log(L_IOCTL, "regulatory domain is currently set " ++ "to %d (0x%X): %s\n", i, dom, ++ acx_reg_domain_strings[i-1]); ++ *extra = i; ++ break; ++ } ++ } ++ ++ return OK; ++} ++ ++ ++/*********************************************************************** ++** acx_ioctl_set_short_preamble ++*/ ++static const char * const ++preamble_modes[] = { ++ "off", ++ "on", ++ "auto (peer capability dependent)", ++ "unknown mode, error" ++}; ++ ++static int ++acx_ioctl_set_short_preamble( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ int i; ++ int result; ++ ++ FN_ENTER; ++ ++ if ((unsigned char)*extra > 2) { ++ result = -EINVAL; ++ goto end; ++ } ++ ++ acx_sem_lock(adev); ++ ++ adev->preamble_mode = (u8)*extra; ++ switch (adev->preamble_mode) { ++ case 0: /* long */ ++ adev->preamble_cur = 0; ++ break; ++ case 1: ++ /* short, kick incapable peers */ ++ adev->preamble_cur = 1; ++ for (i = 0; i < VEC_SIZE(adev->sta_list); i++) { ++ client_t *clt = &adev->sta_list[i]; ++ if (!clt->used) continue; ++ if (!(clt->cap_info & WF_MGMT_CAP_SHORT)) { ++ clt->used = CLIENT_EMPTY_SLOT_0; ++ } ++ } ++ switch (adev->mode) { ++ case ACX_MODE_2_STA: ++ if (adev->ap_client && !adev->ap_client->used) { ++ /* We kicked our AP :) */ ++ SET_BIT(adev->set_mask, GETSET_RESCAN); ++ } ++ } ++ break; ++ case 2: /* auto. short only if all peers are short-capable */ ++ adev->preamble_cur = 1; ++ for (i = 0; i < VEC_SIZE(adev->sta_list); i++) { ++ client_t *clt = &adev->sta_list[i]; ++ if (!clt->used) continue; ++ if (!(clt->cap_info & WF_MGMT_CAP_SHORT)) { ++ adev->preamble_cur = 0; ++ break; ++ } ++ } ++ break; ++ } ++ printk("new short preamble setting: configured %s, active %s\n", ++ preamble_modes[adev->preamble_mode], ++ preamble_modes[adev->preamble_cur]); ++ result = OK; ++ ++ acx_sem_unlock(adev); ++end: ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++** acx_ioctl_get_short_preamble ++*/ ++static int ++acx_ioctl_get_short_preamble( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ ++ acx_sem_lock(adev); ++ ++ printk("current short preamble setting: configured %s, active %s\n", ++ preamble_modes[adev->preamble_mode], ++ preamble_modes[adev->preamble_cur]); ++ ++ *extra = (char)adev->preamble_mode; ++ ++ acx_sem_unlock(adev); ++ ++ return OK; ++} ++ ++ ++/*********************************************************************** ++** acx_ioctl_set_antenna ++** ++** TX and RX antenna can be set separately but this function good ++** for testing 0-4 bits ++*/ ++static int ++acx_ioctl_set_antenna( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ ++ acx_sem_lock(adev); ++ ++ printk("old antenna value: 0x%02X (COMBINED bit mask)\n" ++ "Rx antenna selection:\n" ++ "0x00 ant. 1\n" ++ "0x40 ant. 2\n" ++ "0x80 full diversity\n" ++ "0xc0 partial diversity\n" ++ "0x0f dwell time mask (in units of us)\n" ++ "Tx antenna selection:\n" ++ "0x00 ant. 2\n" /* yep, those ARE reversed! */ ++ "0x20 ant. 1\n" ++ "new antenna value: 0x%02X\n", ++ adev->antenna, (u8)*extra); ++ ++ adev->antenna = (u8)*extra; ++ SET_BIT(adev->set_mask, GETSET_ANTENNA); ++ ++ acx_sem_unlock(adev); ++ ++ return -EINPROGRESS; ++} ++ ++ ++/*********************************************************************** ++** acx_ioctl_get_antenna ++*/ ++static int ++acx_ioctl_get_antenna( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ ++ /* no locking. it's pointless to lock a single load */ ++ printk("current antenna value: 0x%02X (COMBINED bit mask)\n" ++ "Rx antenna selection:\n" ++ "0x00 ant. 1\n" ++ "0x40 ant. 2\n" ++ "0x80 full diversity\n" ++ "0xc0 partial diversity\n" ++ "Tx antenna selection:\n" ++ "0x00 ant. 2\n" /* yep, those ARE reversed! */ ++ "0x20 ant. 1\n", adev->antenna); ++ ++ return 0; ++} ++ ++ ++/*********************************************************************** ++** acx_ioctl_set_rx_antenna ++** ++** 0 = antenna1; 1 = antenna2; 2 = full diversity; 3 = partial diversity ++** Could anybody test which antenna is the external one? ++*/ ++static int ++acx_ioctl_set_rx_antenna( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ int result; ++ ++ FN_ENTER; ++ ++ if (*extra > 3) { ++ result = -EINVAL; ++ goto end; ++ } ++ ++ printk("old antenna value: 0x%02X\n", adev->antenna); ++ ++ acx_sem_lock(adev); ++ ++ adev->antenna &= 0x3f; ++ SET_BIT(adev->antenna, (*extra << 6)); ++ SET_BIT(adev->set_mask, GETSET_ANTENNA); ++ printk("new antenna value: 0x%02X\n", adev->antenna); ++ result = -EINPROGRESS; ++ ++ acx_sem_unlock(adev); ++end: ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++** acx_ioctl_set_tx_antenna ++** ++** Arguments: 0 == antenna2; 1 == antenna1; ++** Could anybody test which antenna is the external one? ++*/ ++static int ++acx_ioctl_set_tx_antenna( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ int result; ++ ++ FN_ENTER; ++ ++ if (*extra > 1) { ++ result = -EINVAL; ++ goto end; ++ } ++ ++ printk("old antenna value: 0x%02X\n", adev->antenna); ++ ++ acx_sem_lock(adev); ++ ++ adev->antenna &= ~0x30; ++ SET_BIT(adev->antenna, ((*extra & 0x01) << 5)); ++ SET_BIT(adev->set_mask, GETSET_ANTENNA); ++ printk("new antenna value: 0x%02X\n", adev->antenna); ++ result = -EINPROGRESS; ++ ++ acx_sem_unlock(adev); ++end: ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++** acx_ioctl_wlansniff ++** ++** can we just remove this in favor of monitor mode? --vda ++*/ ++static int ++acx_ioctl_wlansniff( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ unsigned int *params = (unsigned int*)extra; ++ unsigned int enable = (unsigned int)(params[0] > 0); ++ int result; ++ ++ FN_ENTER; ++ ++ acx_sem_lock(adev); ++ ++ /* not using printk() here, since it distorts kismet display ++ * when printk messages activated */ ++ log(L_IOCTL, "setting monitor to: 0x%02X\n", params[0]); ++ ++ switch (params[0]) { ++ case 0: ++ /* no monitor mode. hmm, should we simply ignore it ++ * or go back to enabling adev->netdev->type ARPHRD_ETHER? */ ++ break; ++ case 1: ++ adev->monitor_type = ARPHRD_IEEE80211_PRISM; ++ break; ++ case 2: ++ adev->monitor_type = ARPHRD_IEEE80211; ++ break; ++ } ++ ++ if (params[0]) { ++ adev->mode = ACX_MODE_MONITOR; ++ SET_BIT(adev->set_mask, GETSET_MODE); ++ } ++ ++ if (enable) { ++ adev->channel = params[1]; ++ SET_BIT(adev->set_mask, GETSET_RX); ++ } ++ result = -EINPROGRESS; ++ ++ acx_sem_unlock(adev); ++ ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++** acx_ioctl_unknown11 ++** FIXME: looks like some sort of "iwpriv kick_sta MAC" but it's broken ++*/ ++static int ++acx_ioctl_unknown11( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++#ifdef BROKEN ++ struct iw_param *vwrq = &wrqu->param; ++ acx_device_t *adev = ndev2adev(ndev); ++ unsigned long flags; ++ client_t client; ++ int result; ++ ++ acx_sem_lock(adev); ++ acx_lock(adev, flags); ++ ++ acx_l_transmit_disassoc(adev, &client); ++ result = OK; ++ ++ acx_unlock(adev, flags); ++ acx_sem_unlock(adev); ++ ++ return result; ++#endif ++ return -EINVAL; ++} ++ ++ ++/*********************************************************************** ++** debug helper function to be able to debug various issues relatively easily ++*/ ++static int ++acx_ioctl_dbg_set_masks( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ const unsigned int *params = (unsigned int*)extra; ++ int result; ++ ++ acx_sem_lock(adev); ++ ++ log(L_IOCTL, "setting flags in settings mask: " ++ "get_mask %08X set_mask %08X\n" ++ "before: get_mask %08X set_mask %08X\n", ++ params[0], params[1], ++ adev->get_mask, adev->set_mask); ++ SET_BIT(adev->get_mask, params[0]); ++ SET_BIT(adev->set_mask, params[1]); ++ log(L_IOCTL, "after: get_mask %08X set_mask %08X\n", ++ adev->get_mask, adev->set_mask); ++ result = -EINPROGRESS; /* immediately call commit handler */ ++ ++ acx_sem_unlock(adev); ++ ++ return result; ++} ++ ++ ++/*********************************************************************** ++* acx_ioctl_set_rates ++* ++* This ioctl takes string parameter. Examples: ++* iwpriv wlan0 SetRates "1,2" ++* use 1 and 2 Mbit rates, both are in basic rate set ++* iwpriv wlan0 SetRates "1,2 5,11" ++* use 1,2,5.5,11 Mbit rates. 1 and 2 are basic ++* iwpriv wlan0 SetRates "1,2 5c,11c" ++* same ('c' means 'CCK modulation' and it is a default for 5 and 11) ++* iwpriv wlan0 SetRates "1,2 5p,11p" ++* use 1,2,5.5,11 Mbit, 1,2 are basic. 5 and 11 are using PBCC ++* iwpriv wlan0 SetRates "1,2,5,11 22p" ++* use 1,2,5.5,11,22 Mbit. 1,2,5.5 and 11 are basic. 22 is using PBCC ++* (this is the maximum acx100 can do (modulo x4 mode)) ++* iwpriv wlan0 SetRates "1,2,5,11 22" ++* same. 802.11 defines only PBCC modulation ++* for 22 and 33 Mbit rates, so there is no ambiguity ++* iwpriv wlan0 SetRates "1,2,5,11 6o,9o,12o,18o,24o,36o,48o,54o" ++* 1,2,5.5 and 11 are basic. 11g OFDM rates are enabled but ++* they are not in basic rate set. 22 Mbit is disabled. ++* iwpriv wlan0 SetRates "1,2,5,11 6,9,12,18,24,36,48,54" ++* same. OFDM is default for 11g rates except 22 and 33 Mbit, ++* thus 'o' is optional ++* iwpriv wlan0 SetRates "1,2,5,11 6d,9d,12d,18d,24d,36d,48d,54d" ++* 1,2,5.5 and 11 are basic. 11g CCK-OFDM rates are enabled ++* (acx111 does not support CCK-OFDM, driver will reject this cmd) ++* iwpriv wlan0 SetRates "6,9,12 18,24,36,48,54" ++* 6,9,12 are basic, rest of 11g rates is enabled. Using OFDM ++*/ ++#include "setrate.c" ++ ++/* disallow: 33Mbit (unsupported by hw) */ ++/* disallow: CCKOFDM (unsupported by hw) */ ++static int ++acx111_supported(int mbit, int modulation, void *opaque) ++{ ++ if (mbit==33) return -ENOTSUPP; ++ if (modulation==DOT11_MOD_CCKOFDM) return -ENOTSUPP; ++ return OK; ++} ++ ++static const u16 ++acx111mask[] = { ++ [DOT11_RATE_1 ] = RATE111_1 , ++ [DOT11_RATE_2 ] = RATE111_2 , ++ [DOT11_RATE_5 ] = RATE111_5 , ++ [DOT11_RATE_11] = RATE111_11, ++ [DOT11_RATE_22] = RATE111_22, ++ /* [DOT11_RATE_33] = */ ++ [DOT11_RATE_6 ] = RATE111_6 , ++ [DOT11_RATE_9 ] = RATE111_9 , ++ [DOT11_RATE_12] = RATE111_12, ++ [DOT11_RATE_18] = RATE111_18, ++ [DOT11_RATE_24] = RATE111_24, ++ [DOT11_RATE_36] = RATE111_36, ++ [DOT11_RATE_48] = RATE111_48, ++ [DOT11_RATE_54] = RATE111_54, ++}; ++ ++static u32 ++acx111_gen_mask(int mbit, int modulation, void *opaque) ++{ ++ /* lower 16 bits show selected 1, 2, CCK and OFDM rates */ ++ /* upper 16 bits show selected PBCC rates */ ++ u32 m = acx111mask[rate_mbit2enum(mbit)]; ++ if (modulation==DOT11_MOD_PBCC) ++ return m<<16; ++ return m; ++} ++ ++static int ++verify_rate(u32 rate, int chip_type) ++{ ++ /* never happens. be paranoid */ ++ if (!rate) return -EINVAL; ++ ++ /* disallow: mixing PBCC and CCK at 5 and 11Mbit ++ ** (can be supported, but needs complicated handling in tx code) */ ++ if (( rate & ((RATE111_11+RATE111_5)<<16) ) ++ && ( rate & (RATE111_11+RATE111_5) ) ++ ) { ++ return -ENOTSUPP; ++ } ++ if (CHIPTYPE_ACX100 == chip_type) { ++ if ( rate & ~(RATE111_ACX100_COMPAT+(RATE111_ACX100_COMPAT<<16)) ) ++ return -ENOTSUPP; ++ } ++ return 0; ++} ++ ++static int ++acx_ioctl_set_rates(struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ unsigned long flags; ++ int result; ++ u32 brate = 0, orate = 0; /* basic, operational rate set */ ++ ++ FN_ENTER; ++ ++ log(L_IOCTL, "set_rates %s\n", extra); ++ result = fill_ratemasks(extra, &brate, &orate, ++ acx111_supported, acx111_gen_mask, 0); ++ if (result) goto end; ++ SET_BIT(orate, brate); ++ log(L_IOCTL, "brate %08X orate %08X\n", brate, orate); ++ ++ result = verify_rate(brate, adev->chip_type); ++ if (result) goto end; ++ result = verify_rate(orate, adev->chip_type); ++ if (result) goto end; ++ ++ acx_sem_lock(adev); ++ acx_lock(adev, flags); ++ ++ adev->rate_basic = brate; ++ adev->rate_oper = orate; ++ /* TODO: ideally, we shall monitor highest basic rate ++ ** which was successfully sent to every peer ++ ** (say, last we checked, everybody could hear 5.5 Mbits) ++ ** and use that for bcasts when we want to reach all peers. ++ ** For beacons, we probably shall use lowest basic rate ++ ** because we want to reach all *potential* new peers too */ ++ adev->rate_bcast = 1 << lowest_bit(brate); ++ if (IS_ACX100(adev)) ++ adev->rate_bcast100 = acx_rate111to100(adev->rate_bcast); ++ adev->rate_auto = !has_only_one_bit(orate); ++ acx_l_update_client_rates(adev, orate); ++ /* TODO: get rid of ratevector, build it only when needed */ ++ acx_l_update_ratevector(adev); ++ ++ /* Do/don't do tx rate fallback; beacon contents and rate */ ++ SET_BIT(adev->set_mask, SET_RATE_FALLBACK|SET_TEMPLATES); ++ result = -EINPROGRESS; ++ ++ acx_unlock(adev, flags); ++ acx_sem_unlock(adev); ++end: ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++** acx_ioctl_get_phy_chan_busy_percentage ++*/ ++static int ++acx_ioctl_get_phy_chan_busy_percentage( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ struct { ++ u16 type; ++ u16 len; ++ u32 busytime; ++ u32 totaltime; ++ } ACX_PACKED usage; ++ int result; ++ ++ acx_sem_lock(adev); ++ ++ if (OK != acx_s_interrogate(adev, &usage, ACX1xx_IE_MEDIUM_USAGE)) { ++ result = NOT_OK; ++ goto end_unlock; ++ } ++ ++ usage.busytime = le32_to_cpu(usage.busytime); ++ usage.totaltime = le32_to_cpu(usage.totaltime); ++ ++ /* yes, this is supposed to be "Medium" (singular of media), ++ not "average"! OK, reword the message to make it obvious... */ ++ printk("%s: busy percentage of medium (since last invocation): %d%% " ++ "(%u of %u microseconds)\n", ++ ndev->name, ++ usage.busytime / ((usage.totaltime / 100) + 1), ++ usage.busytime, usage.totaltime); ++ ++ result = OK; ++ ++end_unlock: ++ acx_sem_unlock(adev); ++ ++ return result; ++} ++ ++ ++/*********************************************************************** ++** acx_ioctl_set_ed_threshold ++*/ ++static inline int ++acx_ioctl_set_ed_threshold( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ ++ acx_sem_lock(adev); ++ ++ printk("old ED threshold value: %d\n", adev->ed_threshold); ++ adev->ed_threshold = (unsigned char)*extra; ++ printk("new ED threshold value: %d\n", (unsigned char)*extra); ++ SET_BIT(adev->set_mask, GETSET_ED_THRESH); ++ ++ acx_sem_unlock(adev); ++ ++ return -EINPROGRESS; ++} ++ ++ ++/*********************************************************************** ++** acx_ioctl_set_cca ++*/ ++static inline int ++acx_ioctl_set_cca( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ int result; ++ ++ acx_sem_lock(adev); ++ ++ printk("old CCA value: 0x%02X\n", adev->cca); ++ adev->cca = (unsigned char)*extra; ++ printk("new CCA value: 0x%02X\n", (unsigned char)*extra); ++ SET_BIT(adev->set_mask, GETSET_CCA); ++ result = -EINPROGRESS; ++ ++ acx_sem_unlock(adev); ++ ++ return result; ++} ++ ++ ++/*********************************************************************** ++*/ ++static const char * const ++scan_modes[] = { "active", "passive", "background" }; ++ ++static void ++acx_print_scan_params(acx_device_t *adev, const char* head) ++{ ++ printk("%s: %smode %d (%s), min chan time %dTU, " ++ "max chan time %dTU, max scan rate byte: %d\n", ++ adev->ndev->name, head, ++ adev->scan_mode, scan_modes[adev->scan_mode], ++ adev->scan_probe_delay, adev->scan_duration, adev->scan_rate); ++} ++ ++static int ++acx_ioctl_set_scan_params( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ int result; ++ const int *params = (int *)extra; ++ ++ acx_sem_lock(adev); ++ ++ acx_print_scan_params(adev, "old scan parameters: "); ++ if ((params[0] != -1) && (params[0] >= 0) && (params[0] <= 2)) ++ adev->scan_mode = params[0]; ++ if (params[1] != -1) ++ adev->scan_probe_delay = params[1]; ++ if (params[2] != -1) ++ adev->scan_duration = params[2]; ++ if ((params[3] != -1) && (params[3] <= 255)) ++ adev->scan_rate = params[3]; ++ acx_print_scan_params(adev, "new scan parameters: "); ++ SET_BIT(adev->set_mask, GETSET_RESCAN); ++ result = -EINPROGRESS; ++ ++ acx_sem_unlock(adev); ++ ++ return result; ++} ++ ++static int ++acx_ioctl_get_scan_params( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ int result; ++ int *params = (int *)extra; ++ ++ acx_sem_lock(adev); ++ ++ acx_print_scan_params(adev, "current scan parameters: "); ++ params[0] = adev->scan_mode; ++ params[1] = adev->scan_probe_delay; ++ params[2] = adev->scan_duration; ++ params[3] = adev->scan_rate; ++ result = OK; ++ ++ acx_sem_unlock(adev); ++ ++ return result; ++} ++ ++ ++/*********************************************************************** ++*/ ++static int ++acx100_ioctl_set_led_power( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ static const char * const led_modes[] = { "off", "on", "LinkQuality" }; ++ ++ acx_device_t *adev = ndev2adev(ndev); ++ int result; ++ ++ acx_sem_lock(adev); ++ ++ printk("%s: power LED status: old %d (%s), ", ++ ndev->name, ++ adev->led_power, ++ led_modes[adev->led_power]); ++ adev->led_power = extra[0]; ++ if (adev->led_power > 2) adev->led_power = 2; ++ printk("new %d (%s)\n", ++ adev->led_power, ++ led_modes[adev->led_power]); ++ ++ if (adev->led_power == 2) { ++ printk("%s: max link quality setting: old %d, ", ++ ndev->name, adev->brange_max_quality); ++ if (extra[1]) ++ adev->brange_max_quality = extra[1]; ++ printk("new %d\n", adev->brange_max_quality); ++ } ++ ++ SET_BIT(adev->set_mask, GETSET_LED_POWER); ++ ++ result = -EINPROGRESS; ++ ++ acx_sem_unlock(adev); ++ ++ return result; ++} ++ ++ ++/*********************************************************************** ++*/ ++static inline int ++acx100_ioctl_get_led_power( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ ++ acx_sem_lock(adev); ++ ++ extra[0] = adev->led_power; ++ if (adev->led_power == 2) ++ extra[1] = adev->brange_max_quality; ++ else ++ extra[1] = -1; ++ ++ acx_sem_unlock(adev); ++ ++ return OK; ++} ++ ++ ++/*********************************************************************** ++*/ ++static int ++acx111_ioctl_info( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ struct iw_param *vwrq = &wrqu->param; ++ if (!IS_PCI(ndev2adev(ndev))) ++ return OK; ++ return acx111pci_ioctl_info(ndev, info, vwrq, extra); ++} ++ ++ ++/*********************************************************************** ++*/ ++static int ++acx100_ioctl_set_phy_amp_bias( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ union iwreq_data *wrqu, ++ char *extra) ++{ ++ struct iw_param *vwrq = &wrqu->param; ++ if (IS_USB(ndev2adev(ndev))) { ++ printk("acx: set_phy_amp_bias() is not supported on USB\n"); ++ return OK; ++ } ++#ifdef ACX_MEM ++ return acx100mem_ioctl_set_phy_amp_bias(ndev, info, vwrq, extra); ++#else ++ return acx100pci_ioctl_set_phy_amp_bias(ndev, info, vwrq, extra); ++#endif ++} ++ ++ ++/*********************************************************************** ++*/ ++static const iw_handler acx_ioctl_handler[] = ++{ ++ acx_ioctl_commit, /* SIOCSIWCOMMIT */ ++ acx_ioctl_get_name, /* SIOCGIWNAME */ ++ NULL, /* SIOCSIWNWID */ ++ NULL, /* SIOCGIWNWID */ ++ acx_ioctl_set_freq, /* SIOCSIWFREQ */ ++ acx_ioctl_get_freq, /* SIOCGIWFREQ */ ++ acx_ioctl_set_mode, /* SIOCSIWMODE */ ++ acx_ioctl_get_mode, /* SIOCGIWMODE */ ++ acx_ioctl_set_sens, /* SIOCSIWSENS */ ++ acx_ioctl_get_sens, /* SIOCGIWSENS */ ++ NULL, /* SIOCSIWRANGE */ ++ acx_ioctl_get_range, /* SIOCGIWRANGE */ ++ NULL, /* SIOCSIWPRIV */ ++ NULL, /* SIOCGIWPRIV */ ++ NULL, /* SIOCSIWSTATS */ ++ NULL, /* SIOCGIWSTATS */ ++#if IW_HANDLER_VERSION > 4 ++ iw_handler_set_spy, /* SIOCSIWSPY */ ++ iw_handler_get_spy, /* SIOCGIWSPY */ ++ iw_handler_set_thrspy, /* SIOCSIWTHRSPY */ ++ iw_handler_get_thrspy, /* SIOCGIWTHRSPY */ ++#else /* IW_HANDLER_VERSION > 4 */ ++#ifdef WIRELESS_SPY ++ NULL /* acx_ioctl_set_spy FIXME */, /* SIOCSIWSPY */ ++ NULL /* acx_ioctl_get_spy */, /* SIOCGIWSPY */ ++#else /* WSPY */ ++ NULL, /* SIOCSIWSPY */ ++ NULL, /* SIOCGIWSPY */ ++#endif /* WSPY */ ++ NULL, /* [nothing] */ ++ NULL, /* [nothing] */ ++#endif /* IW_HANDLER_VERSION > 4 */ ++ acx_ioctl_set_ap, /* SIOCSIWAP */ ++ acx_ioctl_get_ap, /* SIOCGIWAP */ ++ NULL, /* [nothing] */ ++ acx_ioctl_get_aplist, /* SIOCGIWAPLIST */ ++ acx_ioctl_set_scan, /* SIOCSIWSCAN */ ++ acx_ioctl_get_scan, /* SIOCGIWSCAN */ ++ acx_ioctl_set_essid, /* SIOCSIWESSID */ ++ acx_ioctl_get_essid, /* SIOCGIWESSID */ ++ acx_ioctl_set_nick, /* SIOCSIWNICKN */ ++ acx_ioctl_get_nick, /* SIOCGIWNICKN */ ++ NULL, /* [nothing] */ ++ NULL, /* [nothing] */ ++ acx_ioctl_set_rate, /* SIOCSIWRATE */ ++ acx_ioctl_get_rate, /* SIOCGIWRATE */ ++ acx_ioctl_set_rts, /* SIOCSIWRTS */ ++ acx_ioctl_get_rts, /* SIOCGIWRTS */ ++#if ACX_FRAGMENTATION ++ acx_ioctl_set_frag, /* SIOCSIWFRAG */ ++ acx_ioctl_get_frag, /* SIOCGIWFRAG */ ++#else ++ NULL, /* SIOCSIWFRAG */ ++ NULL, /* SIOCGIWFRAG */ ++#endif ++ acx_ioctl_set_txpow, /* SIOCSIWTXPOW */ ++ acx_ioctl_get_txpow, /* SIOCGIWTXPOW */ ++ acx_ioctl_set_retry, /* SIOCSIWRETRY */ ++ acx_ioctl_get_retry, /* SIOCGIWRETRY */ ++ acx_ioctl_set_encode, /* SIOCSIWENCODE */ ++ acx_ioctl_get_encode, /* SIOCGIWENCODE */ ++ acx_ioctl_set_power, /* SIOCSIWPOWER */ ++ acx_ioctl_get_power, /* SIOCGIWPOWER */ ++}; ++ ++ ++/*********************************************************************** ++*/ ++ ++/* if you plan to reorder something, make sure to reorder all other places ++ * accordingly! */ ++/* SET/GET convention: SETs must have even position, GETs odd */ ++#define ACX100_IOCTL SIOCIWFIRSTPRIV ++enum { ++ ACX100_IOCTL_DEBUG = ACX100_IOCTL, ++ ACX100_IOCTL_GET__________UNUSED1, ++ ACX100_IOCTL_SET_PLED, ++ ACX100_IOCTL_GET_PLED, ++ ACX100_IOCTL_SET_RATES, ++ ACX100_IOCTL_LIST_DOM, ++ ACX100_IOCTL_SET_DOM, ++ ACX100_IOCTL_GET_DOM, ++ ACX100_IOCTL_SET_SCAN_PARAMS, ++ ACX100_IOCTL_GET_SCAN_PARAMS, ++ ACX100_IOCTL_SET_PREAMB, ++ ACX100_IOCTL_GET_PREAMB, ++ ACX100_IOCTL_SET_ANT, ++ ACX100_IOCTL_GET_ANT, ++ ACX100_IOCTL_RX_ANT, ++ ACX100_IOCTL_TX_ANT, ++ ACX100_IOCTL_SET_PHY_AMP_BIAS, ++ ACX100_IOCTL_GET_PHY_CHAN_BUSY, ++ ACX100_IOCTL_SET_ED, ++ ACX100_IOCTL_GET__________UNUSED3, ++ ACX100_IOCTL_SET_CCA, ++ ACX100_IOCTL_GET__________UNUSED4, ++ ACX100_IOCTL_MONITOR, ++ ACX100_IOCTL_TEST, ++ ACX100_IOCTL_DBG_SET_MASKS, ++ ACX111_IOCTL_INFO, ++ ACX100_IOCTL_DBG_SET_IO, ++ ACX100_IOCTL_DBG_GET_IO ++}; ++ ++ ++static const iw_handler acx_ioctl_private_handler[] = ++{ ++#if ACX_DEBUG ++[ACX100_IOCTL_DEBUG - ACX100_IOCTL] = acx_ioctl_set_debug, ++#endif ++[ACX100_IOCTL_SET_PLED - ACX100_IOCTL] = acx100_ioctl_set_led_power, ++[ACX100_IOCTL_GET_PLED - ACX100_IOCTL] = acx100_ioctl_get_led_power, ++[ACX100_IOCTL_SET_RATES - ACX100_IOCTL] = acx_ioctl_set_rates, ++[ACX100_IOCTL_LIST_DOM - ACX100_IOCTL] = acx_ioctl_list_reg_domain, ++[ACX100_IOCTL_SET_DOM - ACX100_IOCTL] = acx_ioctl_set_reg_domain, ++[ACX100_IOCTL_GET_DOM - ACX100_IOCTL] = acx_ioctl_get_reg_domain, ++[ACX100_IOCTL_SET_SCAN_PARAMS - ACX100_IOCTL] = acx_ioctl_set_scan_params, ++[ACX100_IOCTL_GET_SCAN_PARAMS - ACX100_IOCTL] = acx_ioctl_get_scan_params, ++[ACX100_IOCTL_SET_PREAMB - ACX100_IOCTL] = acx_ioctl_set_short_preamble, ++[ACX100_IOCTL_GET_PREAMB - ACX100_IOCTL] = acx_ioctl_get_short_preamble, ++[ACX100_IOCTL_SET_ANT - ACX100_IOCTL] = acx_ioctl_set_antenna, ++[ACX100_IOCTL_GET_ANT - ACX100_IOCTL] = acx_ioctl_get_antenna, ++[ACX100_IOCTL_RX_ANT - ACX100_IOCTL] = acx_ioctl_set_rx_antenna, ++[ACX100_IOCTL_TX_ANT - ACX100_IOCTL] = acx_ioctl_set_tx_antenna, ++[ACX100_IOCTL_SET_PHY_AMP_BIAS - ACX100_IOCTL] = acx100_ioctl_set_phy_amp_bias, ++[ACX100_IOCTL_GET_PHY_CHAN_BUSY - ACX100_IOCTL] = acx_ioctl_get_phy_chan_busy_percentage, ++[ACX100_IOCTL_SET_ED - ACX100_IOCTL] = acx_ioctl_set_ed_threshold, ++[ACX100_IOCTL_SET_CCA - ACX100_IOCTL] = acx_ioctl_set_cca, ++[ACX100_IOCTL_MONITOR - ACX100_IOCTL] = acx_ioctl_wlansniff, ++[ACX100_IOCTL_TEST - ACX100_IOCTL] = acx_ioctl_unknown11, ++[ACX100_IOCTL_DBG_SET_MASKS - ACX100_IOCTL] = acx_ioctl_dbg_set_masks, ++[ACX111_IOCTL_INFO - ACX100_IOCTL] = acx111_ioctl_info, ++}; ++ ++ ++static const struct iw_priv_args acx_ioctl_private_args[] = { ++#if ACX_DEBUG ++{ cmd : ACX100_IOCTL_DEBUG, ++ set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, ++ get_args : 0, ++ name : "SetDebug" }, ++#endif ++{ cmd : ACX100_IOCTL_SET_PLED, ++ set_args : IW_PRIV_TYPE_BYTE | 2, ++ get_args : 0, ++ name : "SetLEDPower" }, ++{ cmd : ACX100_IOCTL_GET_PLED, ++ set_args : 0, ++ get_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 2, ++ name : "GetLEDPower" }, ++{ cmd : ACX100_IOCTL_SET_RATES, ++ set_args : IW_PRIV_TYPE_CHAR | 256, ++ get_args : 0, ++ name : "SetRates" }, ++{ cmd : ACX100_IOCTL_LIST_DOM, ++ set_args : 0, ++ get_args : 0, ++ name : "ListRegDomain" }, ++{ cmd : ACX100_IOCTL_SET_DOM, ++ set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, ++ get_args : 0, ++ name : "SetRegDomain" }, ++{ cmd : ACX100_IOCTL_GET_DOM, ++ set_args : 0, ++ get_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, ++ name : "GetRegDomain" }, ++{ cmd : ACX100_IOCTL_SET_SCAN_PARAMS, ++ set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 4, ++ get_args : 0, ++ name : "SetScanParams" }, ++{ cmd : ACX100_IOCTL_GET_SCAN_PARAMS, ++ set_args : 0, ++ get_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 4, ++ name : "GetScanParams" }, ++{ cmd : ACX100_IOCTL_SET_PREAMB, ++ set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, ++ get_args : 0, ++ name : "SetSPreamble" }, ++{ cmd : ACX100_IOCTL_GET_PREAMB, ++ set_args : 0, ++ get_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, ++ name : "GetSPreamble" }, ++{ cmd : ACX100_IOCTL_SET_ANT, ++ set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, ++ get_args : 0, ++ name : "SetAntenna" }, ++{ cmd : ACX100_IOCTL_GET_ANT, ++ set_args : 0, ++ get_args : 0, ++ name : "GetAntenna" }, ++{ cmd : ACX100_IOCTL_RX_ANT, ++ set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, ++ get_args : 0, ++ name : "SetRxAnt" }, ++{ cmd : ACX100_IOCTL_TX_ANT, ++ set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, ++ get_args : 0, ++ name : "SetTxAnt" }, ++{ cmd : ACX100_IOCTL_SET_PHY_AMP_BIAS, ++ set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, ++ get_args : 0, ++ name : "SetPhyAmpBias"}, ++{ cmd : ACX100_IOCTL_GET_PHY_CHAN_BUSY, ++ set_args : 0, ++ get_args : 0, ++ name : "GetPhyChanBusy" }, ++{ cmd : ACX100_IOCTL_SET_ED, ++ set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 1, ++ get_args : 0, ++ name : "SetED" }, ++{ cmd : ACX100_IOCTL_SET_CCA, ++ set_args : IW_PRIV_TYPE_BYTE | IW_PRIV_SIZE_FIXED | 1, ++ get_args : 0, ++ name : "SetCCA" }, ++{ cmd : ACX100_IOCTL_MONITOR, ++ set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, ++ get_args : 0, ++ name : "monitor" }, ++{ cmd : ACX100_IOCTL_TEST, ++ set_args : 0, ++ get_args : 0, ++ name : "Test" }, ++{ cmd : ACX100_IOCTL_DBG_SET_MASKS, ++ set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 2, ++ get_args : 0, ++ name : "DbgSetMasks" }, ++{ cmd : ACX111_IOCTL_INFO, ++ set_args : 0, ++ get_args : 0, ++ name : "GetAcx111Info" }, ++{ cmd : ACX100_IOCTL_DBG_SET_IO, ++ set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 4, ++ get_args : 0, ++ name : "DbgSetIO" }, ++{ cmd : ACX100_IOCTL_DBG_GET_IO, ++ set_args : IW_PRIV_TYPE_INT | IW_PRIV_SIZE_FIXED | 3, ++ get_args : 0, ++ name : "DbgGetIO" }, ++}; ++ ++ ++const struct iw_handler_def acx_ioctl_handler_def = ++{ ++ .num_standard = VEC_SIZE(acx_ioctl_handler), ++ .num_private = VEC_SIZE(acx_ioctl_private_handler), ++ .num_private_args = VEC_SIZE(acx_ioctl_private_args), ++ .standard = (iw_handler *) acx_ioctl_handler, ++ .private = (iw_handler *) acx_ioctl_private_handler, ++ .private_args = (struct iw_priv_args *) acx_ioctl_private_args, ++#if IW_HANDLER_VERSION > 5 ++ .get_wireless_stats = acx_e_get_wireless_stats ++#endif /* IW > 5 */ ++}; +Index: linux-2.6.23/drivers/net/wireless/acx/Kconfig +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.23/drivers/net/wireless/acx/Kconfig 2008-01-20 21:13:40.000000000 +0000 +@@ -0,0 +1,113 @@ ++config ACX ++ tristate "TI acx100/acx111 802.11b/g wireless chipsets" ++ depends on NET_RADIO && EXPERIMENTAL ++ select FW_LOADER ++ ---help--- ++ A driver for 802.11b/g wireless cards based on ++ Texas Instruments acx100 and acx111 chipsets. ++ ++ This driver supports Host AP mode that allows ++ your computer to act as an IEEE 802.11 access point. ++ This driver is new and experimental. ++ ++ Texas Instruments did not take part in development of this driver ++ in any way, shape or form. ++ ++ The driver can be compiled as a module and will be named "acx". ++ ++config ACX_PCI ++ bool "TI acx100/acx111 802.11b/g PCI" ++ depends on ACX && PCI ++ ---help--- ++ Include PCI and CardBus support in acx. ++ ++ acx chipsets need their firmware loaded at startup. ++ You will need to provide a firmware image via hotplug. ++ ++ Firmware may be in a form of single image 40-100kb in size ++ (a 'combined' firmware) or two images - main image ++ (again 40-100kb) and radio image (~10kb or less). ++ ++ Firmware images are requested from hotplug using following names: ++ ++ tiacx100 - main firmware image for acx100 chipset ++ tiacx100rNN - radio acx100 firmware for radio type NN ++ tiacx100cNN - combined acx100 firmware for radio type NN ++ tiacx111 - main acx111 firmware ++ tiacx111rNN - radio acx111 firmware for radio type NN ++ tiacx111cNN - combined acx111 firmware for radio type NN ++ ++ Driver will attempt to load combined image first. ++ If no such image is found, it will try to load main image ++ and radio image instead. ++ ++ Firmware files are not covered by GPL and are not distributed ++ with this driver for legal reasons. ++ ++config ACX_USB ++ bool "TI acx100/acx111 802.11b/g USB" ++ depends on ACX && (USB=y || USB=ACX) ++ ---help--- ++ Include USB support in acx. ++ ++ There is only one currently known device in this category, ++ D-Link DWL-120+, but newer devices seem to be on the horizon. ++ ++ acx chipsets need their firmware loaded at startup. ++ You will need to provide a firmware image via hotplug. ++ ++ Firmware for USB device is requested from hotplug ++ by the 'tiacx100usb' name. ++ ++ Firmware files are not covered by GPL and are not distributed ++ with this driver for legal reasons. ++ ++config ACX_MEM ++ bool "TI acx100/acx111 802.11b/g memory mapped slave 16 interface" ++ depends on ACX ++ ---help--- ++ acx chipsets need their firmware loaded at startup. ++ You will need to provide a firmware image via hotplug. ++ ++ Firmware for USB device is requested from hotplug ++ by the 'tiacx100usb' name. ++ ++ Firmware files are not covered by GPL and are not distributed ++ with this driver for legal reasons. ++ ++config ACX_CS ++ bool "TI acx100/acx111 802.11b/g cardbus interface" ++ depends on ACX ++ ---help--- ++ acx chipsets need their firmware loaded at startup. ++ You will need to provide a firmware image via hotplug. ++ ++ This driver is based on memory mapped driver. ++ ++ Firmware files are not covered by GPL and are not distributed ++ with this driver for legal reasons. ++ ++config ACX_HX4700 ++ tristate "ACX support for the iPAQ hx4700 using ACX_MEM" ++ depends on HX4700_CORE && ACX_MEM ++ ---help--- ++ Include memory interface support in acx for the iPAQ hx4700. ++ ++config ACX_HTCUNIVERSAL ++ tristate "ACX support for the HTC Universal using ACX_MEM" ++ depends on HTCUNIVERSAL_CORE && HTC_ASIC3 && ACX_MEM ++ ---help--- ++ Include memory interface support in acx for the HTC Universal. ++ ++config ACX_HTCSABLE ++ tristate "ACX support for the HTC Sable (IPAQ hw6915) using ACX_MEM" ++ depends on MACH_HW6900 && HTC_ASIC3 && ACX_MEM ++ ---help--- ++ Include memory interface support in acx for the HTC Sable (IPAQ hw6915). ++ ++config ACX_RX3000 ++ tristate "ACX support for the iPAQ RX3000 using ACX_MEM" ++ depends on MACH_RX3715 && ACX_MEM && LEDS_ASIC3 ++ ---help--- ++ Include memory interface support in acx for the IPAQ RX3000. ++ +Index: linux-2.6.23/drivers/net/wireless/acx/Makefile +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.23/drivers/net/wireless/acx/Makefile 2008-01-20 21:13:40.000000000 +0000 +@@ -0,0 +1,21 @@ ++#obj-m += acx.o ++ ++#acx-obj-y += pci.o ++#acx-obj-y += usb.o ++ ++#acx-objs := wlan.o conv.o ioctl.o common.o $(acx-obj-y) ++ ++# Use this if you have proper Kconfig integration: ++ ++obj-$(CONFIG_ACX) += acx.o ++obj-$(CONFIG_ACX_HX4700) += hx4700_acx.o ++obj-$(CONFIG_ACX_HTCUNIVERSAL) += htcuniversal_acx.o ++obj-$(CONFIG_ACX_HTCSABLE) += htcsable_acx.o ++obj-$(CONFIG_ACX_RX3000) += rx3000_acx.o ++# ++acx-obj-$(CONFIG_ACX_PCI) += pci.o ++acx-obj-$(CONFIG_ACX_USB) += usb.o ++acx-obj-$(CONFIG_ACX_MEM) += mem.o ++acx-obj-$(CONFIG_ACX_CS) += cs.o ++# ++acx-objs := wlan.o conv.o ioctl.o common.o $(acx-obj-y) +Index: linux-2.6.23/drivers/net/wireless/acx/mem.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.23/drivers/net/wireless/acx/mem.c 2008-01-20 21:13:40.000000000 +0000 +@@ -0,0 +1,5363 @@ ++/*********************************************************************** ++** Copyright (C) 2003 ACX100 Open Source Project ++** ++** The contents of this file are subject to the Mozilla Public ++** License Version 1.1 (the "License"); you may not use this file ++** except in compliance with the License. You may obtain a copy of ++** the License at http://www.mozilla.org/MPL/ ++** ++** Software distributed under the License is distributed on an "AS ++** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or ++** implied. See the License for the specific language governing ++** rights and limitations under the License. ++** ++** Alternatively, the contents of this file may be used under the ++** terms of the GNU Public License version 2 (the "GPL"), in which ++** case the provisions of the GPL are applicable instead of the ++** above. If you wish to allow the use of your version of this file ++** only under the terms of the GPL and not to allow others to use ++** your version of this file under the MPL, indicate your decision ++** by deleting the provisions above and replace them with the notice ++** and other provisions required by the GPL. If you do not delete ++** the provisions above, a recipient may use your version of this ++** file under either the MPL or the GPL. ++** --------------------------------------------------------------------- ++** Inquiries regarding the ACX100 Open Source Project can be ++** made directly to: ++** ++** acx100-users@lists.sf.net ++** http://acx100.sf.net ++** --------------------------------------------------------------------- ++** ++** Slave memory interface support: ++** ++** Todd Blumer - SDG Systems ++** Bill Reese - HP ++** Eric McCorkle - Shadowsun ++*/ ++#define ACX_MEM 1 ++ ++/* ++ * non-zero makes it dump the ACX memory to the console then ++ * panic when you cat /proc/driver/acx_wlan0_diag ++ */ ++#define DUMP_MEM_DEFINED 1 ++ ++#define DUMP_MEM_DURING_DIAG 0 ++#define DUMP_IF_SLOW 0 ++ ++#define PATCH_AROUND_BAD_SPOTS 1 ++#define HX4700_FIRMWARE_CHECKSUM 0x0036862e ++#define HX4700_ALTERNATE_FIRMWARE_CHECKSUM 0x00368a75 ++ ++#include <linux/version.h> ++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) ++#include <linux/config.h> ++#endif ++ ++/* Linux 2.6.18+ uses <linux/utsrelease.h> */ ++#ifndef UTS_RELEASE ++#include <linux/utsrelease.h> ++#endif ++ ++#include <linux/compiler.h> /* required for Lx 2.6.8 ?? */ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/sched.h> ++#include <linux/types.h> ++#include <linux/skbuff.h> ++#include <linux/slab.h> ++#include <linux/if_arp.h> ++#include <linux/irq.h> ++#include <linux/rtnetlink.h> ++#include <linux/wireless.h> ++#include <net/iw_handler.h> ++#include <linux/netdevice.h> ++#include <linux/ioport.h> ++#include <linux/pci.h> ++#include <linux/platform_device.h> ++#include <linux/pm.h> ++#include <linux/vmalloc.h> ++#include <linux/delay.h> ++#include <linux/workqueue.h> ++#include <linux/inetdevice.h> ++ ++#include "acx.h" ++#include "acx_hw.h" ++ ++/*********************************************************************** ++*/ ++ ++#define CARD_EEPROM_ID_SIZE 6 ++ ++#include <asm/io.h> ++ ++#define REG_ACX_VENDOR_ID 0x900 ++/* ++ * This is the vendor id on the HX4700, anyway ++ */ ++#define ACX_VENDOR_ID 0x8400104c ++ ++typedef enum { ++ ACX_SOFT_RESET = 0, ++ ++ ACX_SLV_REG_ADDR, ++ ACX_SLV_REG_DATA, ++ ACX_SLV_REG_ADATA, ++ ++ ACX_SLV_MEM_CP, ++ ACX_SLV_MEM_ADDR, ++ ACX_SLV_MEM_DATA, ++ ACX_SLV_MEM_CTL, ++} acxreg_t; ++ ++/*********************************************************************** ++*/ ++static void acxmem_i_tx_timeout(struct net_device *ndev); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) ++static irqreturn_t acxmem_i_interrupt(int irq, void *dev_id); ++#else ++static irqreturn_t acxmem_i_interrupt(int irq, void *dev_id, struct pt_regs *regs); ++#endif ++static void acxmem_i_set_multicast_list(struct net_device *ndev); ++ ++static int acxmem_e_open(struct net_device *ndev); ++static int acxmem_e_close(struct net_device *ndev); ++static void acxmem_s_up(struct net_device *ndev); ++static void acxmem_s_down(struct net_device *ndev); ++ ++static void dump_acxmem (acx_device_t *adev, u32 start, int length); ++static int acxmem_complete_hw_reset (acx_device_t *adev); ++static void acxmem_s_delete_dma_regions(acx_device_t *adev); ++ ++static struct platform_device *resume_pdev; ++ ++static int ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) ++acxmem_e_suspend(struct platform_device *pdev, pm_message_t state); ++#else ++acxmem_e_suspend(struct device *pdev, u32 state); ++#endif ++static void ++fw_resumer(struct work_struct *notused); ++//fw_resumer( void *data ); ++ ++static int acx_netdev_event(struct notifier_block *this, unsigned long event, void *ptr) ++{ ++ struct net_device *ndev = ptr; ++ acx_device_t *adev = ndev2adev(ndev); ++ ++ /* ++ * Upper level ioctl() handlers send a NETDEV_CHANGEADDR if the MAC address changes. ++ */ ++ ++ if (NETDEV_CHANGEADDR == event) { ++ /* ++ * the upper layers put the new MAC address in ndev->dev_addr; we just copy ++ * it over and update the ACX with it. ++ */ ++ MAC_COPY(adev->dev_addr, adev->ndev->dev_addr); ++ adev->set_mask |= GETSET_STATION_ID; ++ acx_s_update_card_settings (adev); ++ } ++ ++ return 0; ++} ++ ++static struct notifier_block acx_netdev_notifier = { ++ .notifier_call = acx_netdev_event, ++}; ++ ++/*********************************************************************** ++** Register access ++*/ ++ ++/* Pick one */ ++/* #define INLINE_IO static */ ++#define INLINE_IO static inline ++ ++INLINE_IO u32 ++read_id_register (acx_device_t *adev) ++{ ++ writel (0x24, &adev->iobase[ACX_SLV_REG_ADDR]); ++ return readl (&adev->iobase[ACX_SLV_REG_DATA]); ++} ++ ++INLINE_IO u32 ++read_reg32(acx_device_t *adev, unsigned int offset) ++{ ++ u32 val; ++ u32 addr; ++ ++ if (offset > IO_ACX_ECPU_CTRL) ++ addr = offset; ++ else ++ addr = adev->io[offset]; ++ ++ if (addr < 0x20) { ++ return readl(((u8*)adev->iobase) + addr); ++ } ++ ++ writel( addr, &adev->iobase[ACX_SLV_REG_ADDR] ); ++ val = readl( &adev->iobase[ACX_SLV_REG_DATA] ); ++ ++ return val; ++} ++ ++INLINE_IO u16 ++read_reg16(acx_device_t *adev, unsigned int offset) ++{ ++ u16 lo; ++ u32 addr; ++ ++ if (offset > IO_ACX_ECPU_CTRL) ++ addr = offset; ++ else ++ addr = adev->io[offset]; ++ ++ if (addr < 0x20) { ++ return readw(((u8 *) adev->iobase) + addr); ++ } ++ ++ writel( addr, &adev->iobase[ACX_SLV_REG_ADDR] ); ++ lo = readw( (u16 *)&adev->iobase[ACX_SLV_REG_DATA] ); ++ ++ return lo; ++} ++ ++INLINE_IO u8 ++read_reg8(acx_device_t *adev, unsigned int offset) ++{ ++ u8 lo; ++ u32 addr; ++ ++ if (offset > IO_ACX_ECPU_CTRL) ++ addr = offset; ++ else ++ addr = adev->io[offset]; ++ ++ if (addr < 0x20) ++ return readb(((u8 *)adev->iobase) + addr); ++ ++ writel( addr, &adev->iobase[ACX_SLV_REG_ADDR] ); ++ lo = readw( (u8 *)&adev->iobase[ACX_SLV_REG_DATA] ); ++ ++ return (u8)lo; ++} ++ ++INLINE_IO void ++write_reg32(acx_device_t *adev, unsigned int offset, u32 val) ++{ ++ u32 addr; ++ ++ if (offset > IO_ACX_ECPU_CTRL) ++ addr = offset; ++ else ++ addr = adev->io[offset]; ++ ++ if (addr < 0x20) { ++ writel(val, ((u8*)adev->iobase) + addr); ++ return; ++ } ++ ++ writel( addr, &adev->iobase[ACX_SLV_REG_ADDR] ); ++ writel( val, &adev->iobase[ACX_SLV_REG_DATA] ); ++} ++ ++INLINE_IO void ++write_reg16(acx_device_t *adev, unsigned int offset, u16 val) ++{ ++ u32 addr; ++ ++ if (offset > IO_ACX_ECPU_CTRL) ++ addr = offset; ++ else ++ addr = adev->io[offset]; ++ ++ if (addr < 0x20) { ++ writew(val, ((u8 *)adev->iobase) + addr); ++ return; ++ } ++ writel( addr, &adev->iobase[ACX_SLV_REG_ADDR] ); ++ writew( val, (u16 *) &adev->iobase[ACX_SLV_REG_DATA] ); ++} ++ ++INLINE_IO void ++write_reg8(acx_device_t *adev, unsigned int offset, u8 val) ++{ ++ u32 addr; ++ ++ if (offset > IO_ACX_ECPU_CTRL) ++ addr = offset; ++ else ++ addr = adev->io[offset]; ++ ++ if (addr < 0x20) { ++ writeb(val, ((u8 *) adev->iobase) + addr); ++ return; ++ } ++ writel( addr, &adev->iobase[ACX_SLV_REG_ADDR] ); ++ writeb( val, (u8 *)&adev->iobase[ACX_SLV_REG_DATA] ); ++} ++ ++/* Handle PCI posting properly: ++ * Make sure that writes reach the adapter in case they require to be executed ++ * *before* the next write, by reading a random (and safely accessible) register. ++ * This call has to be made if there is no read following (which would flush the data ++ * to the adapter), yet the written data has to reach the adapter immediately. */ ++INLINE_IO void ++write_flush(acx_device_t *adev) ++{ ++ /* readb(adev->iobase + adev->io[IO_ACX_INFO_MAILBOX_OFFS]); */ ++ /* faster version (accesses the first register, IO_ACX_SOFT_RESET, ++ * which should also be safe): */ ++ (void) readl(adev->iobase); ++} ++ ++INLINE_IO void ++set_regbits (acx_device_t *adev, unsigned int offset, u32 bits) { ++ u32 tmp; ++ ++ tmp = read_reg32 (adev, offset); ++ tmp = tmp | bits; ++ write_reg32 (adev, offset, tmp); ++ write_flush (adev); ++} ++ ++INLINE_IO void ++clear_regbits (acx_device_t *adev, unsigned int offset, u32 bits) { ++ u32 tmp; ++ ++ tmp = read_reg32 (adev, offset); ++ tmp = tmp & ~bits; ++ write_reg32 (adev, offset, tmp); ++ write_flush (adev); ++} ++ ++/* ++ * Copy from PXA memory to the ACX memory. This assumes both the PXA and ACX ++ * addresses are 32 bit aligned. Count is in bytes. ++ */ ++INLINE_IO void ++write_slavemem32 (acx_device_t *adev, u32 slave_address, u32 val) ++{ ++ write_reg32 (adev, IO_ACX_SLV_MEM_CTL, 0x0); ++ write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, slave_address); ++ udelay (10); ++ write_reg32 (adev, IO_ACX_SLV_MEM_DATA, val); ++} ++ ++INLINE_IO u32 ++read_slavemem32 (acx_device_t *adev, u32 slave_address) ++{ ++ u32 val; ++ ++ write_reg32 (adev, IO_ACX_SLV_MEM_CTL, 0x0); ++ write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, slave_address); ++ udelay (10); ++ val = read_reg32 (adev, IO_ACX_SLV_MEM_DATA); ++ ++ return val; ++} ++ ++INLINE_IO void ++write_slavemem8 (acx_device_t *adev, u32 slave_address, u8 val) ++{ ++ u32 data; ++ u32 base; ++ int offset; ++ ++ /* ++ * Get the word containing the target address and the byte offset in that word. ++ */ ++ base = slave_address & ~3; ++ offset = (slave_address & 3) * 8; ++ ++ data = read_slavemem32 (adev, base); ++ data &= ~(0xff << offset); ++ data |= val << offset; ++ write_slavemem32 (adev, base, data); ++} ++ ++INLINE_IO u8 ++read_slavemem8 (acx_device_t *adev, u32 slave_address) ++{ ++ u8 val; ++ u32 base; ++ u32 data; ++ int offset; ++ ++ base = slave_address & ~3; ++ offset = (slave_address & 3) * 8; ++ ++ data = read_slavemem32 (adev, base); ++ ++ val = (data >> offset) & 0xff; ++ ++ return val; ++} ++ ++/* ++ * doesn't split across word boundaries ++ */ ++INLINE_IO void ++write_slavemem16 (acx_device_t *adev, u32 slave_address, u16 val) ++{ ++ u32 data; ++ u32 base; ++ int offset; ++ ++ /* ++ * Get the word containing the target address and the byte offset in that word. ++ */ ++ base = slave_address & ~3; ++ offset = (slave_address & 3) * 8; ++ ++ data = read_slavemem32 (adev, base); ++ data &= ~(0xffff << offset); ++ data |= val << offset; ++ write_slavemem32 (adev, base, data); ++} ++ ++/* ++ * doesn't split across word boundaries ++ */ ++INLINE_IO u16 ++read_slavemem16 (acx_device_t *adev, u32 slave_address) ++{ ++ u16 val; ++ u32 base; ++ u32 data; ++ int offset; ++ ++ base = slave_address & ~3; ++ offset = (slave_address & 3) * 8; ++ ++ data = read_slavemem32 (adev, base); ++ ++ val = (data >> offset) & 0xffff; ++ ++ return val; ++} ++ ++/* ++ * Copy from slave memory ++ * ++ * TODO - rewrite using address autoincrement, handle partial words ++ */ ++void ++copy_from_slavemem (acx_device_t *adev, u8 *destination, u32 source, int count) { ++ u32 tmp = 0; ++ u8 *ptmp = (u8 *) &tmp; ++ ++ /* ++ * Right now I'm making the assumption that the destination is aligned, but ++ * I'd better check. ++ */ ++ if ((u32) destination & 3) { ++ printk ("acx copy_from_slavemem: warning! destination not word-aligned!\n"); ++ } ++ ++ while (count >= 4) { ++ write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, source); ++ udelay (10); ++ *((u32 *) destination) = read_reg32 (adev, IO_ACX_SLV_MEM_DATA); ++ count -= 4; ++ source += 4; ++ destination += 4; ++ } ++ ++ /* ++ * If the word reads above didn't satisfy the count, read one more word ++ * and transfer a byte at a time until the request is satisfied. ++ */ ++ if (count) { ++ write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, source); ++ udelay (10); ++ tmp = read_reg32 (adev, IO_ACX_SLV_MEM_DATA); ++ while (count--) { ++ *destination++ = *ptmp++; ++ } ++ } ++} ++ ++/* ++ * Copy to slave memory ++ * ++ * TODO - rewrite using autoincrement, handle partial words ++ */ ++void ++copy_to_slavemem (acx_device_t *adev, u32 destination, u8 *source, int count) ++{ ++ u32 tmp = 0; ++ u8* ptmp = (u8 *) &tmp; ++ static u8 src[512]; /* make static to avoid huge stack objects */ ++ ++ /* ++ * For now, make sure the source is word-aligned by copying it to a word-aligned ++ * buffer. Someday rewrite to avoid the extra copy. ++ */ ++ if (count > sizeof (src)) { ++ printk ("acx copy_to_slavemem: Warning! buffer overflow!\n"); ++ count = sizeof (src); ++ } ++ memcpy (src, source, count); ++ source = src; ++ ++ while (count >= 4) { ++ write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, destination); ++ udelay (10); ++ write_reg32 (adev, IO_ACX_SLV_MEM_DATA, *((u32 *) source)); ++ count -= 4; ++ source += 4; ++ destination += 4; ++ } ++ ++ /* ++ * If there are leftovers read the next word from the acx and merge in ++ * what they want to write. ++ */ ++ if (count) { ++ write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, destination); ++ udelay (10); ++ tmp = read_reg32 (adev, IO_ACX_SLV_MEM_DATA); ++ while (count--) { ++ *ptmp++ = *source++; ++ } ++ /* ++ * reset address in case we're currently in auto-increment mode ++ */ ++ write_reg32 (adev, IO_ACX_SLV_MEM_ADDR, destination); ++ udelay (10); ++ write_reg32 (adev, IO_ACX_SLV_MEM_DATA, tmp); ++ udelay (10); ++ } ++ ++} ++ ++/* ++ * Block copy to slave buffers using memory block chain mode. Copies to the ACX ++ * transmit buffer structure with minimal intervention on our part. ++ * Interrupts should be disabled when calling this. ++ */ ++void ++chaincopy_to_slavemem (acx_device_t *adev, u32 destination, u8 *source, int count) ++{ ++ u32 val; ++ u32 *data = (u32 *) source; ++ static u8 aligned_source[WLAN_A4FR_MAXLEN_WEP_FCS]; ++ ++ /* ++ * Warn if the pointers don't look right. Destination must fit in [23:5] with ++ * zero elsewhere and source should be 32 bit aligned. ++ * This should never happen since we're in control of both, but I want to know about ++ * it if it does. ++ */ ++ if ((destination & 0x00ffffe0) != destination) { ++ printk ("acx chaincopy: destination block 0x%04x not aligned!\n", destination); ++ } ++ if (count > sizeof aligned_source) { ++ printk( KERN_ERR "chaincopy_to_slavemem overflow!\n" ); ++ count = sizeof aligned_source; ++ } ++ if ((u32) source & 3) { ++ memcpy (aligned_source, source, count); ++ data = (u32 *) aligned_source; ++ } ++ ++ /* ++ * SLV_MEM_CTL[17:16] = memory block chain mode with auto-increment ++ * SLV_MEM_CTL[5:2] = offset to data portion = 1 word ++ */ ++ val = 2 << 16 | 1 << 2; ++ writel (val, &adev->iobase[ACX_SLV_MEM_CTL]); ++ ++ /* ++ * SLV_MEM_CP[23:5] = start of 1st block ++ * SLV_MEM_CP[3:2] = offset to memblkptr = 0 ++ */ ++ val = destination & 0x00ffffe0; ++ writel (val, &adev->iobase[ACX_SLV_MEM_CP]); ++ ++ /* ++ * SLV_MEM_ADDR[23:2] = SLV_MEM_CTL[5:2] + SLV_MEM_CP[23:5] ++ */ ++ val = (destination & 0x00ffffe0) + (1<<2); ++ writel (val, &adev->iobase[ACX_SLV_MEM_ADDR]); ++ ++ /* ++ * Write the data to the slave data register, rounding up to the end ++ * of the word containing the last byte (hence the > 0) ++ */ ++ while (count > 0) { ++ writel (*data++, &adev->iobase[ACX_SLV_MEM_DATA]); ++ count -= 4; ++ } ++} ++ ++ ++/* ++ * Block copy from slave buffers using memory block chain mode. Copies from the ACX ++ * receive buffer structures with minimal intervention on our part. ++ * Interrupts should be disabled when calling this. ++ */ ++void ++chaincopy_from_slavemem (acx_device_t *adev, u8 *destination, u32 source, int count) ++{ ++ u32 val; ++ u32 *data = (u32 *) destination; ++ static u8 aligned_destination[WLAN_A4FR_MAXLEN_WEP_FCS]; ++ int saved_count = count; ++ ++ /* ++ * Warn if the pointers don't look right. Destination must fit in [23:5] with ++ * zero elsewhere and source should be 32 bit aligned. ++ * Turns out the network stack sends unaligned things, so fix them before ++ * copying to the ACX. ++ */ ++ if ((source & 0x00ffffe0) != source) { ++ printk ("acx chaincopy: source block 0x%04x not aligned!\n", source); ++ dump_acxmem (adev, 0, 0x10000); ++ } ++ if ((u32) destination & 3) { ++ //printk ("acx chaincopy: data destination not word aligned!\n"); ++ data = (u32 *) aligned_destination; ++ if (count > sizeof aligned_destination) { ++ printk( KERN_ERR "chaincopy_from_slavemem overflow!\n" ); ++ count = sizeof aligned_destination; ++ } ++ } ++ ++ /* ++ * SLV_MEM_CTL[17:16] = memory block chain mode with auto-increment ++ * SLV_MEM_CTL[5:2] = offset to data portion = 1 word ++ */ ++ val = (2 << 16) | (1 << 2); ++ writel (val, &adev->iobase[ACX_SLV_MEM_CTL]); ++ ++ /* ++ * SLV_MEM_CP[23:5] = start of 1st block ++ * SLV_MEM_CP[3:2] = offset to memblkptr = 0 ++ */ ++ val = source & 0x00ffffe0; ++ writel (val, &adev->iobase[ACX_SLV_MEM_CP]); ++ ++ /* ++ * SLV_MEM_ADDR[23:2] = SLV_MEM_CTL[5:2] + SLV_MEM_CP[23:5] ++ */ ++ val = (source & 0x00ffffe0) + (1<<2); ++ writel (val, &adev->iobase[ACX_SLV_MEM_ADDR]); ++ ++ /* ++ * Read the data from the slave data register, rounding up to the end ++ * of the word containing the last byte (hence the > 0) ++ */ ++ while (count > 0) { ++ *data++ = readl (&adev->iobase[ACX_SLV_MEM_DATA]); ++ count -= 4; ++ } ++ ++ /* ++ * If the destination wasn't aligned, we would have saved it in ++ * the aligned buffer, so copy it where it should go. ++ */ ++ if ((u32) destination & 3) { ++ memcpy (destination, aligned_destination, saved_count); ++ } ++} ++ ++char ++printable (char c) ++{ ++ return ((c >= 20) && (c < 127)) ? c : '.'; ++} ++ ++#if DUMP_MEM_DEFINED > 0 ++static void ++dump_acxmem (acx_device_t *adev, u32 start, int length) ++{ ++ int i; ++ u8 buf[16]; ++ ++ while (length > 0) { ++ printk ("%04x ", start); ++ copy_from_slavemem (adev, buf, start, 16); ++ for (i = 0; (i < 16) && (i < length); i++) { ++ printk ("%02x ", buf[i]); ++ } ++ for (i = 0; (i < 16) && (i < length); i++) { ++ printk ("%c", printable (buf[i])); ++ } ++ printk ("\n"); ++ start += 16; ++ length -= 16; ++ } ++} ++#endif ++ ++static void ++enable_acx_irq(acx_device_t *adev); ++static void ++disable_acx_irq(acx_device_t *adev); ++ ++/* ++ * Return an acx pointer to the next transmit data block. ++ */ ++u32 ++allocate_acx_txbuf_space (acx_device_t *adev, int count) { ++ u32 block, next, last_block; ++ int blocks_needed; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&adev->txbuf_lock, flags); ++ /* ++ * Take 4 off the memory block size to account for the reserved word at the start of ++ * the block. ++ */ ++ blocks_needed = count / (adev->memblocksize - 4); ++ if (count % (adev->memblocksize - 4)) ++ blocks_needed++; ++ ++ if (blocks_needed <= adev->acx_txbuf_blocks_free) { ++ /* ++ * Take blocks at the head of the free list. ++ */ ++ last_block = block = adev->acx_txbuf_free; ++ ++ /* ++ * Follow block pointers through the requested number of blocks both to ++ * find the new head of the free list and to set the flags for the blocks ++ * appropriately. ++ */ ++ while (blocks_needed--) { ++ /* ++ * Keep track of the last block of the allocation ++ */ ++ last_block = adev->acx_txbuf_free; ++ ++ /* ++ * Make sure the end control flag is not set. ++ */ ++ next = read_slavemem32 (adev, adev->acx_txbuf_free) & 0x7ffff; ++ write_slavemem32 (adev, adev->acx_txbuf_free, next); ++ ++ /* ++ * Update the new head of the free list ++ */ ++ adev->acx_txbuf_free = next << 5; ++ adev->acx_txbuf_blocks_free--; ++ ++ } ++ ++ /* ++ * Flag the last block both by clearing out the next pointer ++ * and marking the control field. ++ */ ++ write_slavemem32 (adev, last_block, 0x02000000); ++ ++ /* ++ * If we're out of buffers make sure the free list pointer is NULL ++ */ ++ if (!adev->acx_txbuf_blocks_free) { ++ adev->acx_txbuf_free = 0; ++ } ++ } ++ else { ++ block = 0; ++ } ++ spin_unlock_irqrestore (&adev->txbuf_lock, flags); ++ return block; ++} ++ ++/* ++ * Return buffer space back to the pool by following the next pointers until we find ++ * the block marked as the end. Point the last block to the head of the free list, ++ * then update the head of the free list to point to the newly freed memory. ++ * This routine gets called in interrupt context, so it shouldn't block to protect ++ * the integrity of the linked list. The ISR already holds the lock. ++ */ ++void ++reclaim_acx_txbuf_space (acx_device_t *adev, u32 blockptr) { ++ u32 cur, last, next; ++ unsigned long flags; ++ ++ spin_lock_irqsave (&adev->txbuf_lock, flags); ++ if ((blockptr >= adev->acx_txbuf_start) && ++ (blockptr <= adev->acx_txbuf_start + ++ (adev->acx_txbuf_numblocks - 1) * adev->memblocksize)) { ++ cur = blockptr; ++ do { ++ last = cur; ++ next = read_slavemem32 (adev, cur); ++ ++ /* ++ * Advance to the next block in this allocation ++ */ ++ cur = (next & 0x7ffff) << 5; ++ ++ /* ++ * This block now counts as free. ++ */ ++ adev->acx_txbuf_blocks_free++; ++ } while (!(next & 0x02000000)); ++ ++ /* ++ * last now points to the last block of that allocation. Update the pointer ++ * in that block to point to the free list and reset the free list to the ++ * first block of the free call. If there were no free blocks, make sure ++ * the new end of the list marks itself as truly the end. ++ */ ++ if (adev->acx_txbuf_free) { ++ write_slavemem32 (adev, last, adev->acx_txbuf_free >> 5); ++ } ++ else { ++ write_slavemem32 (adev, last, 0x02000000); ++ } ++ adev->acx_txbuf_free = blockptr; ++ } ++ spin_unlock_irqrestore(&adev->txbuf_lock, flags); ++} ++ ++/* ++ * Initialize the pieces managing the transmit buffer pool on the ACX. The transmit ++ * buffer is a circular queue with one 32 bit word reserved at the beginning of each ++ * block. The upper 13 bits are a control field, of which only 0x02000000 has any ++ * meaning. The lower 19 bits are the address of the next block divided by 32. ++ */ ++void ++init_acx_txbuf (acx_device_t *adev) { ++ ++ /* ++ * acx100_s_init_memory_pools set up txbuf_start and txbuf_numblocks for us. ++ * All we need to do is reset the rest of the bookeeping. ++ */ ++ ++ adev->acx_txbuf_free = adev->acx_txbuf_start; ++ adev->acx_txbuf_blocks_free = adev->acx_txbuf_numblocks; ++ ++ /* ++ * Initialization leaves the last transmit pool block without a pointer back to ++ * the head of the list, but marked as the end of the list. That's how we want ++ * to see it, too, so leave it alone. This is only ever called after a firmware ++ * reset, so the ACX memory is in the state we want. ++ */ ++ ++} ++ ++INLINE_IO int ++adev_present(acx_device_t *adev) ++{ ++ /* fast version (accesses the first register, IO_ACX_SOFT_RESET, ++ * which should be safe): */ ++ return readl(adev->iobase) != 0xffffffff; ++} ++ ++/*********************************************************************** ++*/ ++static inline txdesc_t* ++get_txdesc(acx_device_t *adev, int index) ++{ ++ return (txdesc_t*) (((u8*)adev->txdesc_start) + index * adev->txdesc_size); ++} ++ ++static inline txdesc_t* ++advance_txdesc(acx_device_t *adev, txdesc_t* txdesc, int inc) ++{ ++ return (txdesc_t*) (((u8*)txdesc) + inc * adev->txdesc_size); ++} ++ ++static txhostdesc_t* ++get_txhostdesc(acx_device_t *adev, txdesc_t* txdesc) ++{ ++ int index = (u8*)txdesc - (u8*)adev->txdesc_start; ++ if (unlikely(ACX_DEBUG && (index % adev->txdesc_size))) { ++ printk("bad txdesc ptr %p\n", txdesc); ++ return NULL; ++ } ++ index /= adev->txdesc_size; ++ if (unlikely(ACX_DEBUG && (index >= TX_CNT))) { ++ printk("bad txdesc ptr %p\n", txdesc); ++ return NULL; ++ } ++ return &adev->txhostdesc_start[index*2]; ++} ++ ++static inline client_t* ++get_txc(acx_device_t *adev, txdesc_t* txdesc) ++{ ++ int index = (u8*)txdesc - (u8*)adev->txdesc_start; ++ if (unlikely(ACX_DEBUG && (index % adev->txdesc_size))) { ++ printk("bad txdesc ptr %p\n", txdesc); ++ return NULL; ++ } ++ index /= adev->txdesc_size; ++ if (unlikely(ACX_DEBUG && (index >= TX_CNT))) { ++ printk("bad txdesc ptr %p\n", txdesc); ++ return NULL; ++ } ++ return adev->txc[index]; ++} ++ ++static inline u16 ++get_txr(acx_device_t *adev, txdesc_t* txdesc) ++{ ++ int index = (u8*)txdesc - (u8*)adev->txdesc_start; ++ index /= adev->txdesc_size; ++ return adev->txr[index]; ++} ++ ++static inline void ++put_txcr(acx_device_t *adev, txdesc_t* txdesc, client_t* c, u16 r111) ++{ ++ int index = (u8*)txdesc - (u8*)adev->txdesc_start; ++ if (unlikely(ACX_DEBUG && (index % adev->txdesc_size))) { ++ printk("bad txdesc ptr %p\n", txdesc); ++ return; ++ } ++ index /= adev->txdesc_size; ++ if (unlikely(ACX_DEBUG && (index >= TX_CNT))) { ++ printk("bad txdesc ptr %p\n", txdesc); ++ return; ++ } ++ adev->txc[index] = c; ++ adev->txr[index] = r111; ++} ++ ++ ++/*********************************************************************** ++** EEPROM and PHY read/write helpers ++*/ ++/*********************************************************************** ++** acxmem_read_eeprom_byte ++** ++** Function called to read an octet in the EEPROM. ++** ++** This function is used by acxmem_e_probe to check if the ++** connected card is a legal one or not. ++** ++** Arguments: ++** adev ptr to acx_device structure ++** addr address to read in the EEPROM ++** charbuf ptr to a char. This is where the read octet ++** will be stored ++*/ ++int ++acxmem_read_eeprom_byte(acx_device_t *adev, u32 addr, u8 *charbuf) ++{ ++ int result; ++ int count; ++ ++ write_reg32(adev, IO_ACX_EEPROM_CFG, 0); ++ write_reg32(adev, IO_ACX_EEPROM_ADDR, addr); ++ write_flush(adev); ++ write_reg32(adev, IO_ACX_EEPROM_CTL, 2); ++ ++ count = 0xffff; ++ while (read_reg16(adev, IO_ACX_EEPROM_CTL)) { ++ /* scheduling away instead of CPU burning loop ++ * doesn't seem to work here at all: ++ * awful delay, sometimes also failure. ++ * Doesn't matter anyway (only small delay). */ ++ if (unlikely(!--count)) { ++ printk("%s: timeout waiting for EEPROM read\n", ++ adev->ndev->name); ++ result = NOT_OK; ++ goto fail; ++ } ++ cpu_relax(); ++ } ++ ++ *charbuf = read_reg8(adev, IO_ACX_EEPROM_DATA); ++ log(L_DEBUG, "EEPROM at 0x%04X = 0x%02X\n", addr, *charbuf); ++ result = OK; ++ ++fail: ++ return result; ++} ++ ++ ++/*********************************************************************** ++** We don't lock hw accesses here since we never r/w eeprom in IRQ ++** Note: this function sleeps only because of GFP_KERNEL alloc ++*/ ++#ifdef UNUSED ++int ++acxmem_s_write_eeprom(acx_device_t *adev, u32 addr, u32 len, const u8 *charbuf) ++{ ++ u8 *data_verify = NULL; ++ unsigned long flags; ++ int count, i; ++ int result = NOT_OK; ++ u16 gpio_orig; ++ ++ printk("acx: WARNING! I would write to EEPROM now. " ++ "Since I really DON'T want to unless you know " ++ "what you're doing (THIS CODE WILL PROBABLY " ++ "NOT WORK YET!), I will abort that now. And " ++ "definitely make sure to make a " ++ "/proc/driver/acx_wlan0_eeprom backup copy first!!! " ++ "(the EEPROM content includes the PCI config header!! " ++ "If you kill important stuff, then you WILL " ++ "get in trouble and people DID get in trouble already)\n"); ++ return OK; ++ ++ FN_ENTER; ++ ++ data_verify = kmalloc(len, GFP_KERNEL); ++ if (!data_verify) { ++ goto end; ++ } ++ ++ /* first we need to enable the OE (EEPROM Output Enable) GPIO line ++ * to be able to write to the EEPROM. ++ * NOTE: an EEPROM writing success has been reported, ++ * but you probably have to modify GPIO_OUT, too, ++ * and you probably need to activate a different GPIO ++ * line instead! */ ++ gpio_orig = read_reg16(adev, IO_ACX_GPIO_OE); ++ write_reg16(adev, IO_ACX_GPIO_OE, gpio_orig & ~1); ++ write_flush(adev); ++ ++ /* ok, now start writing the data out */ ++ for (i = 0; i < len; i++) { ++ write_reg32(adev, IO_ACX_EEPROM_CFG, 0); ++ write_reg32(adev, IO_ACX_EEPROM_ADDR, addr + i); ++ write_reg32(adev, IO_ACX_EEPROM_DATA, *(charbuf + i)); ++ write_flush(adev); ++ write_reg32(adev, IO_ACX_EEPROM_CTL, 1); ++ ++ count = 0xffff; ++ while (read_reg16(adev, IO_ACX_EEPROM_CTL)) { ++ if (unlikely(!--count)) { ++ printk("WARNING, DANGER!!! " ++ "Timeout waiting for EEPROM write\n"); ++ goto end; ++ } ++ cpu_relax(); ++ } ++ } ++ ++ /* disable EEPROM writing */ ++ write_reg16(adev, IO_ACX_GPIO_OE, gpio_orig); ++ write_flush(adev); ++ ++ /* now start a verification run */ ++ for (i = 0; i < len; i++) { ++ write_reg32(adev, IO_ACX_EEPROM_CFG, 0); ++ write_reg32(adev, IO_ACX_EEPROM_ADDR, addr + i); ++ write_flush(adev); ++ write_reg32(adev, IO_ACX_EEPROM_CTL, 2); ++ ++ count = 0xffff; ++ while (read_reg16(adev, IO_ACX_EEPROM_CTL)) { ++ if (unlikely(!--count)) { ++ printk("timeout waiting for EEPROM read\n"); ++ goto end; ++ } ++ cpu_relax(); ++ } ++ ++ data_verify[i] = read_reg16(adev, IO_ACX_EEPROM_DATA); ++ } ++ ++ if (0 == memcmp(charbuf, data_verify, len)) ++ result = OK; /* read data matches, success */ ++ ++end: ++ kfree(data_verify); ++ FN_EXIT1(result); ++ return result; ++} ++#endif /* UNUSED */ ++ ++ ++/*********************************************************************** ++** acxmem_s_read_phy_reg ++** ++** Messing with rx/tx disabling and enabling here ++** (write_reg32(adev, IO_ACX_ENABLE, 0b000000xx)) kills traffic ++*/ ++int ++acxmem_s_read_phy_reg(acx_device_t *adev, u32 reg, u8 *charbuf) ++{ ++ int result = NOT_OK; ++ int count; ++ ++ FN_ENTER; ++ ++ write_reg32(adev, IO_ACX_PHY_ADDR, reg); ++ write_flush(adev); ++ write_reg32(adev, IO_ACX_PHY_CTL, 2); ++ ++ count = 0xffff; ++ while (read_reg32(adev, IO_ACX_PHY_CTL)) { ++ /* scheduling away instead of CPU burning loop ++ * doesn't seem to work here at all: ++ * awful delay, sometimes also failure. ++ * Doesn't matter anyway (only small delay). */ ++ if (unlikely(!--count)) { ++ printk("%s: timeout waiting for phy read\n", ++ adev->ndev->name); ++ *charbuf = 0; ++ goto fail; ++ } ++ cpu_relax(); ++ } ++ ++ log(L_DEBUG, "count was %u\n", count); ++ *charbuf = read_reg8(adev, IO_ACX_PHY_DATA); ++ ++ log(L_DEBUG, "radio PHY at 0x%04X = 0x%02X\n", *charbuf, reg); ++ result = OK; ++ goto fail; /* silence compiler warning */ ++fail: ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++*/ ++int ++acxmem_s_write_phy_reg(acx_device_t *adev, u32 reg, u8 value) ++{ ++ int count; ++ FN_ENTER; ++ ++ /* mprusko said that 32bit accesses result in distorted sensitivity ++ * on his card. Unconfirmed, looks like it's not true (most likely since we ++ * now properly flush writes). */ ++ write_reg32(adev, IO_ACX_PHY_DATA, value); ++ write_reg32(adev, IO_ACX_PHY_ADDR, reg); ++ write_flush(adev); ++ write_reg32(adev, IO_ACX_PHY_CTL, 1); ++ write_flush(adev); ++ ++ count = 0xffff; ++ while (read_reg32(adev, IO_ACX_PHY_CTL)) { ++ /* scheduling away instead of CPU burning loop ++ * doesn't seem to work here at all: ++ * awful delay, sometimes also failure. ++ * Doesn't matter anyway (only small delay). */ ++ if (unlikely(!--count)) { ++ printk("%s: timeout waiting for phy read\n", ++ adev->ndev->name); ++ goto fail; ++ } ++ cpu_relax(); ++ } ++ ++ log(L_DEBUG, "radio PHY write 0x%02X at 0x%04X\n", value, reg); ++ fail: ++ FN_EXIT1(OK); ++ return OK; ++} ++ ++ ++#define NO_AUTO_INCREMENT 1 ++ ++/*********************************************************************** ++** acxmem_s_write_fw ++** ++** Write the firmware image into the card. ++** ++** Arguments: ++** adev wlan device structure ++** fw_image firmware image. ++** ++** Returns: ++** 1 firmware image corrupted ++** 0 success ++*/ ++static int ++acxmem_s_write_fw(acx_device_t *adev, const firmware_image_t *fw_image, u32 offset) ++{ ++ int len, size, checkMismatch = -1; ++ u32 sum, v32, tmp, id; ++ /* we skip the first four bytes which contain the control sum */ ++ const u8 *p = (u8*)fw_image + 4; ++ ++ /* start the image checksum by adding the image size value */ ++ sum = p[0]+p[1]+p[2]+p[3]; ++ p += 4; ++ ++#ifdef NOPE ++#if NO_AUTO_INCREMENT ++ write_reg32(adev, IO_ACX_SLV_MEM_CTL, 0); /* use basic mode */ ++#else ++ write_reg32(adev, IO_ACX_SLV_MEM_CTL, 1); /* use autoincrement mode */ ++ write_reg32(adev, IO_ACX_SLV_MEM_ADDR, offset); /* configure start address */ ++ write_flush(adev); ++#endif ++#endif ++ len = 0; ++ size = le32_to_cpu(fw_image->size) & (~3); ++ ++ while (likely(len < size)) { ++ v32 = be32_to_cpu(*(u32*)p); ++ sum += p[0]+p[1]+p[2]+p[3]; ++ p += 4; ++ len += 4; ++ ++#ifdef NOPE ++#if NO_AUTO_INCREMENT ++ write_reg32(adev, IO_ACX_SLV_MEM_ADDR, offset + len - 4); ++ write_flush(adev); ++#endif ++ write_reg32(adev, IO_ACX_SLV_MEM_DATA, v32); ++ write_flush(adev); ++#endif ++ write_slavemem32 (adev, offset + len - 4, v32); ++ ++ id = read_id_register (adev); ++ ++ /* ++ * check the data written ++ */ ++ tmp = read_slavemem32 (adev, offset + len - 4); ++ if (checkMismatch && (tmp != v32)) { ++ printk ("first data mismatch at 0x%08x good 0x%08x bad 0x%08x id 0x%08x\n", ++ offset + len - 4, v32, tmp, id); ++ checkMismatch = 0; ++ } ++ } ++ log(L_DEBUG, "firmware written, size:%d sum1:%x sum2:%x\n", ++ size, sum, le32_to_cpu(fw_image->chksum)); ++ ++ /* compare our checksum with the stored image checksum */ ++ return (sum != le32_to_cpu(fw_image->chksum)); ++} ++ ++ ++/*********************************************************************** ++** acxmem_s_validate_fw ++** ++** Compare the firmware image given with ++** the firmware image written into the card. ++** ++** Arguments: ++** adev wlan device structure ++** fw_image firmware image. ++** ++** Returns: ++** NOT_OK firmware image corrupted or not correctly written ++** OK success ++*/ ++static int ++acxmem_s_validate_fw(acx_device_t *adev, const firmware_image_t *fw_image, ++ u32 offset) ++{ ++ u32 sum, v32, w32; ++ int len, size; ++ int result = OK; ++ /* we skip the first four bytes which contain the control sum */ ++ const u8 *p = (u8*)fw_image + 4; ++ ++ /* start the image checksum by adding the image size value */ ++ sum = p[0]+p[1]+p[2]+p[3]; ++ p += 4; ++ ++ write_reg32(adev, IO_ACX_SLV_END_CTL, 0); ++ ++#if NO_AUTO_INCREMENT ++ write_reg32(adev, IO_ACX_SLV_MEM_CTL, 0); /* use basic mode */ ++#else ++ write_reg32(adev, IO_ACX_SLV_MEM_CTL, 1); /* use autoincrement mode */ ++ write_reg32(adev, IO_ACX_SLV_MEM_ADDR, offset); /* configure start address */ ++#endif ++ ++ len = 0; ++ size = le32_to_cpu(fw_image->size) & (~3); ++ ++ while (likely(len < size)) { ++ v32 = be32_to_cpu(*(u32*)p); ++ p += 4; ++ len += 4; ++ ++#ifdef NOPE ++#if NO_AUTO_INCREMENT ++ write_reg32(adev, IO_ACX_SLV_MEM_ADDR, offset + len - 4); ++#endif ++ udelay(10); ++ w32 = read_reg32(adev, IO_ACX_SLV_MEM_DATA); ++#endif ++ w32 = read_slavemem32 (adev, offset + len - 4); ++ ++ if (unlikely(w32 != v32)) { ++ printk("acx: FATAL: firmware upload: " ++ "data parts at offset %d don't match\n(0x%08X vs. 0x%08X)!\n" ++ "I/O timing issues or defective memory, with DWL-xx0+? " ++ "ACX_IO_WIDTH=16 may help. Please report\n", ++ len, v32, w32); ++ result = NOT_OK; ++ break; ++ } ++ ++ sum += (u8)w32 + (u8)(w32>>8) + (u8)(w32>>16) + (u8)(w32>>24); ++ } ++ ++ /* sum control verification */ ++ if (result != NOT_OK) { ++ if (sum != le32_to_cpu(fw_image->chksum)) { ++ printk("acx: FATAL: firmware upload: " ++ "checksums don't match!\n"); ++ result = NOT_OK; ++ } ++ } ++ ++ return result; ++} ++ ++ ++/*********************************************************************** ++** acxmem_s_upload_fw ++** ++** Called from acx_reset_dev ++*/ ++static int ++acxmem_s_upload_fw(acx_device_t *adev) ++{ ++ firmware_image_t *fw_image = NULL; ++ int res = NOT_OK; ++ int try; ++ u32 file_size; ++ char *filename = "WLANGEN.BIN"; ++#ifdef PATCH_AROUND_BAD_SPOTS ++ u32 offset; ++ int i; ++ /* ++ * arm-linux-objdump -d patch.bin, or ++ * od -Ax -t x4 patch.bin after finding the bounds ++ * of the .text section with arm-linux-objdump -s patch.bin ++ */ ++ u32 patch[] = { ++ 0xe584c030, 0xe59fc008, ++ 0xe92d1000, 0xe59fc004, 0xe8bd8000, 0x0000080c, ++ 0x0000aa68, 0x605a2200, 0x2c0a689c, 0x2414d80a, ++ 0x2f00689f, 0x1c27d007, 0x06241e7c, 0x2f000e24, ++ 0xe000d1f6, 0x602e6018, 0x23036468, 0x480203db, ++ 0x60ca6003, 0xbdf0750a, 0xffff0808 ++ }; ++#endif ++ ++ FN_ENTER; ++ /* No combined image; tell common we need the radio firmware, too */ ++ adev->need_radio_fw = 1; ++ ++ fw_image = acx_s_read_fw(adev->dev, filename, &file_size); ++ if (!fw_image) { ++ FN_EXIT1(NOT_OK); ++ return NOT_OK; ++ } ++ ++ for (try = 1; try <= 5; try++) { ++ res = acxmem_s_write_fw(adev, fw_image, 0); ++ log(L_DEBUG|L_INIT, "acx_write_fw (main): %d\n", res); ++ if (OK == res) { ++ res = acxmem_s_validate_fw(adev, fw_image, 0); ++ log(L_DEBUG|L_INIT, "acx_validate_fw " ++ "(main): %d\n", res); ++ } ++ ++ if (OK == res) { ++ SET_BIT(adev->dev_state_mask, ACX_STATE_FW_LOADED); ++ break; ++ } ++ printk("acx: firmware upload attempt #%d FAILED, " ++ "retrying...\n", try); ++ acx_s_msleep(1000); /* better wait for a while... */ ++ } ++ ++#ifdef PATCH_AROUND_BAD_SPOTS ++ /* ++ * Only want to do this if the firmware is exactly what we expect for an ++ * iPaq 4700; otherwise, bad things would ensue. ++ */ ++ if ((HX4700_FIRMWARE_CHECKSUM == fw_image->chksum) || ++ (HX4700_ALTERNATE_FIRMWARE_CHECKSUM == fw_image->chksum)) { ++ /* ++ * Put the patch after the main firmware image. 0x950c contains ++ * the ACX's idea of the end of the firmware. Use that location to ++ * load ours (which depends on that location being 0xab58) then ++ * update that location to point to after ours. ++ */ ++ ++ offset = read_slavemem32 (adev, 0x950c); ++ ++ log (L_DEBUG, "acx: patching in at 0x%04x\n", offset); ++ ++ for (i = 0; i < sizeof(patch) / sizeof(patch[0]); i++) { ++ write_slavemem32 (adev, offset, patch[i]); ++ offset += sizeof(u32); ++ } ++ ++ /* ++ * Patch the instruction at 0x0804 to branch to our ARM patch at 0xab58 ++ */ ++ write_slavemem32 (adev, 0x0804, 0xea000000 + (0xab58-0x0804-8)/4); ++ ++ /* ++ * Patch the instructions at 0x1f40 to branch to our Thumb patch at 0xab74 ++ * ++ * 4a00 ldr r2, [pc, #0] ++ * 4710 bx r2 ++ * .data 0xab74+1 ++ */ ++ write_slavemem32 (adev, 0x1f40, 0x47104a00); ++ write_slavemem32 (adev, 0x1f44, 0x0000ab74+1); ++ ++ /* ++ * Bump the end of the firmware up to beyond our patch. ++ */ ++ write_slavemem32 (adev, 0x950c, offset); ++ ++ } ++#endif ++ ++ vfree(fw_image); ++ ++ FN_EXIT1(res); ++ return res; ++} ++ ++ ++/*********************************************************************** ++** acxmem_s_upload_radio ++** ++** Uploads the appropriate radio module firmware into the card. ++*/ ++int ++acxmem_s_upload_radio(acx_device_t *adev) ++{ ++ acx_ie_memmap_t mm; ++ firmware_image_t *radio_image; ++ acx_cmd_radioinit_t radioinit; ++ int res = NOT_OK; ++ int try; ++ u32 offset; ++ u32 size; ++ char filename[sizeof("RADIONN.BIN")]; ++ ++ if (!adev->need_radio_fw) return OK; ++ ++ FN_ENTER; ++ ++ acx_s_interrogate(adev, &mm, ACX1xx_IE_MEMORY_MAP); ++ offset = le32_to_cpu(mm.CodeEnd); ++ ++ snprintf(filename, sizeof(filename), "RADIO%02x.BIN", ++ adev->radio_type); ++ radio_image = acx_s_read_fw(adev->dev, filename, &size); ++ if (!radio_image) { ++ printk("acx: can't load radio module '%s'\n", filename); ++ goto fail; ++ } ++ ++ acx_s_issue_cmd(adev, ACX1xx_CMD_SLEEP, NULL, 0); ++ ++ for (try = 1; try <= 5; try++) { ++ res = acxmem_s_write_fw(adev, radio_image, offset); ++ log(L_DEBUG|L_INIT, "acx_write_fw (radio): %d\n", res); ++ if (OK == res) { ++ res = acxmem_s_validate_fw(adev, radio_image, offset); ++ log(L_DEBUG|L_INIT, "acx_validate_fw (radio): %d\n", res); ++ } ++ ++ if (OK == res) ++ break; ++ printk("acx: radio firmware upload attempt #%d FAILED, " ++ "retrying...\n", try); ++ acx_s_msleep(1000); /* better wait for a while... */ ++ } ++ ++ acx_s_issue_cmd(adev, ACX1xx_CMD_WAKE, NULL, 0); ++ radioinit.offset = cpu_to_le32(offset); ++ ++ /* no endian conversion needed, remains in card CPU area: */ ++ radioinit.len = radio_image->size; ++ ++ vfree(radio_image); ++ ++ if (OK != res) ++ goto fail; ++ ++ /* will take a moment so let's have a big timeout */ ++ acx_s_issue_cmd_timeo(adev, ACX1xx_CMD_RADIOINIT, ++ &radioinit, sizeof(radioinit), CMD_TIMEOUT_MS(1000)); ++ ++ res = acx_s_interrogate(adev, &mm, ACX1xx_IE_MEMORY_MAP); ++ ++fail: ++ FN_EXIT1(res); ++ return res; ++} ++ ++/*********************************************************************** ++** acxmem_l_reset_mac ++** ++** MAC will be reset ++** Call context: reset_dev ++*/ ++static void ++acxmem_l_reset_mac(acx_device_t *adev) ++{ ++ int count; ++ FN_ENTER; ++ ++ /* halt eCPU */ ++ set_regbits (adev, IO_ACX_ECPU_CTRL, 0x1); ++ ++ /* now do soft reset of eCPU, set bit */ ++ set_regbits (adev, IO_ACX_SOFT_RESET, 0x1); ++ log(L_DEBUG, "%s: enable soft reset...\n", __func__); ++ ++ /* Windows driver sleeps here for a while with this sequence */ ++ for (count = 0; count < 200; count++) { ++ udelay (50); ++ } ++ ++ /* now clear bit again: deassert eCPU reset */ ++ log(L_DEBUG, "%s: disable soft reset and go to init mode...\n", __func__); ++ clear_regbits (adev, IO_ACX_SOFT_RESET, 0x1); ++ ++ /* now start a burst read from initial EEPROM */ ++ set_regbits (adev, IO_ACX_EE_START, 0x1); ++ ++ /* ++ * Windows driver sleeps here for a while with this sequence ++ */ ++ for (count = 0; count < 200; count++) { ++ udelay (50); ++ } ++ ++ /* Windows driver writes 0x10000 to register 0x808 here */ ++ ++ write_reg32 (adev, 0x808, 0x10000); ++ ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acxmem_s_verify_init ++*/ ++static int ++acxmem_s_verify_init(acx_device_t *adev) ++{ ++ int result = NOT_OK; ++ unsigned long timeout; ++ ++ FN_ENTER; ++ ++ timeout = jiffies + 2*HZ; ++ for (;;) { ++ u32 irqstat = read_reg32(adev, IO_ACX_IRQ_STATUS_NON_DES); ++ if ((irqstat != 0xFFFFFFFF) && (irqstat & HOST_INT_FCS_THRESHOLD)) { ++ result = OK; ++ write_reg32(adev, IO_ACX_IRQ_ACK, HOST_INT_FCS_THRESHOLD); ++ break; ++ } ++ if (time_after(jiffies, timeout)) ++ break; ++ /* Init may take up to ~0.5 sec total */ ++ acx_s_msleep(50); ++ } ++ ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++** A few low-level helpers ++** ++** Note: these functions are not protected by lock ++** and thus are never allowed to be called from IRQ. ++** Also they must not race with fw upload which uses same hw regs ++*/ ++ ++/*********************************************************************** ++** acxmem_write_cmd_type_status ++*/ ++ ++static inline void ++acxmem_write_cmd_type_status(acx_device_t *adev, u16 type, u16 status) ++{ ++ write_slavemem32 (adev, (u32) adev->cmd_area, type | (status << 16)); ++ write_flush(adev); ++} ++ ++ ++/*********************************************************************** ++** acxmem_read_cmd_type_status ++*/ ++static u32 ++acxmem_read_cmd_type_status(acx_device_t *adev) ++{ ++ u32 cmd_type, cmd_status; ++ ++ cmd_type = read_slavemem32 (adev, (u32) adev->cmd_area); ++ ++ cmd_status = (cmd_type >> 16); ++ cmd_type = (u16)cmd_type; ++ ++ log(L_CTL, "cmd_type:%04X cmd_status:%04X [%s]\n", ++ cmd_type, cmd_status, ++ acx_cmd_status_str(cmd_status)); ++ ++ return cmd_status; ++} ++ ++ ++/*********************************************************************** ++** acxmem_s_reset_dev ++** ++** Arguments: ++** netdevice that contains the adev variable ++** Returns: ++** NOT_OK on fail ++** OK on success ++** Side effects: ++** device is hard reset ++** Call context: ++** acxmem_e_probe ++** Comment: ++** This resets the device using low level hardware calls ++** as well as uploads and verifies the firmware to the card ++*/ ++ ++static inline void ++init_mboxes(acx_device_t *adev) ++{ ++ u32 cmd_offs, info_offs; ++ ++ cmd_offs = read_reg32(adev, IO_ACX_CMD_MAILBOX_OFFS); ++ info_offs = read_reg32(adev, IO_ACX_INFO_MAILBOX_OFFS); ++ adev->cmd_area = (u8*) cmd_offs; ++ adev->info_area = (u8*) info_offs; ++ /* ++ log(L_DEBUG, "iobase2=%p\n" ++ */ ++ log( L_DEBUG, "cmd_mbox_offset=%X cmd_area=%p\n" ++ "info_mbox_offset=%X info_area=%p\n", ++ cmd_offs, adev->cmd_area, ++ info_offs, adev->info_area); ++} ++ ++ ++static inline void ++read_eeprom_area(acx_device_t *adev) ++{ ++#if ACX_DEBUG > 1 ++ int offs; ++ u8 tmp; ++ ++ for (offs = 0x8c; offs < 0xb9; offs++) ++ acxmem_read_eeprom_byte(adev, offs, &tmp); ++#endif ++} ++ ++static int ++acxmem_s_reset_dev(acx_device_t *adev) ++{ ++ const char* msg = ""; ++ unsigned long flags; ++ int result = NOT_OK; ++ u16 hardware_info; ++ u16 ecpu_ctrl; ++ int count; ++ u32 tmp; ++ ++ FN_ENTER; ++ /* ++ write_reg32 (adev, IO_ACX_SLV_MEM_CP, 0); ++ */ ++ /* reset the device to make sure the eCPU is stopped ++ * to upload the firmware correctly */ ++ ++ acx_lock(adev, flags); ++ ++ /* Windows driver does some funny things here */ ++ /* ++ * clear bit 0x200 in register 0x2A0 ++ */ ++ clear_regbits (adev, 0x2A0, 0x200); ++ ++ /* ++ * Set bit 0x200 in ACX_GPIO_OUT ++ */ ++ set_regbits (adev, IO_ACX_GPIO_OUT, 0x200); ++ ++ /* ++ * read register 0x900 until its value is 0x8400104C, sleeping ++ * in between reads if it's not immediate ++ */ ++ tmp = read_reg32 (adev, REG_ACX_VENDOR_ID); ++ count = 500; ++ while (count-- && (tmp != ACX_VENDOR_ID)) { ++ mdelay (10); ++ tmp = read_reg32 (adev, REG_ACX_VENDOR_ID); ++ } ++ ++ /* end what Windows driver does */ ++ ++ acxmem_l_reset_mac(adev); ++ ++ ecpu_ctrl = read_reg32(adev, IO_ACX_ECPU_CTRL) & 1; ++ if (!ecpu_ctrl) { ++ msg = "eCPU is already running. "; ++ goto end_unlock; ++ } ++ ++#ifdef WE_DONT_NEED_THAT_DO_WE ++ if (read_reg16(adev, IO_ACX_SOR_CFG) & 2) { ++ /* eCPU most likely means "embedded CPU" */ ++ msg = "eCPU did not start after boot from flash. "; ++ goto end_unlock; ++ } ++ ++ /* check sense on reset flags */ ++ if (read_reg16(adev, IO_ACX_SOR_CFG) & 0x10) { ++ printk("%s: eCPU did not start after boot (SOR), " ++ "is this fatal?\n", adev->ndev->name); ++ } ++#endif ++ /* scan, if any, is stopped now, setting corresponding IRQ bit */ ++ adev->irq_status |= HOST_INT_SCAN_COMPLETE; ++ ++ acx_unlock(adev, flags); ++ ++ /* need to know radio type before fw load */ ++ /* Need to wait for arrival of this information in a loop, ++ * most probably since eCPU runs some init code from EEPROM ++ * (started burst read in reset_mac()) which also ++ * sets the radio type ID */ ++ ++ count = 0xffff; ++ do { ++ hardware_info = read_reg16(adev, IO_ACX_EEPROM_INFORMATION); ++ if (!--count) { ++ msg = "eCPU didn't indicate radio type"; ++ goto end_fail; ++ } ++ cpu_relax(); ++ } while (!(hardware_info & 0xff00)); /* radio type still zero? */ ++ printk("ACX radio type 0x%02x\n", (hardware_info >> 8) & 0xff); ++ /* printk("DEBUG: count %d\n", count); */ ++ adev->form_factor = hardware_info & 0xff; ++ adev->radio_type = hardware_info >> 8; ++ ++ /* load the firmware */ ++ if (OK != acxmem_s_upload_fw(adev)) ++ goto end_fail; ++ ++ /* acx_s_msleep(10); this one really shouldn't be required */ ++ ++ /* now start eCPU by clearing bit */ ++ clear_regbits (adev, IO_ACX_ECPU_CTRL, 0x1); ++ log(L_DEBUG, "booted eCPU up and waiting for completion...\n"); ++ ++ /* Windows driver clears bit 0x200 in register 0x2A0 here */ ++ clear_regbits (adev, 0x2A0, 0x200); ++ ++ /* Windows driver sets bit 0x200 in ACX_GPIO_OUT here */ ++ set_regbits (adev, IO_ACX_GPIO_OUT, 0x200); ++ /* wait for eCPU bootup */ ++ if (OK != acxmem_s_verify_init(adev)) { ++ msg = "timeout waiting for eCPU. "; ++ goto end_fail; ++ } ++ log(L_DEBUG, "eCPU has woken up, card is ready to be configured\n"); ++ init_mboxes(adev); ++ acxmem_write_cmd_type_status(adev, ACX1xx_CMD_RESET, 0); ++ ++ /* test that EEPROM is readable */ ++ read_eeprom_area(adev); ++ ++ result = OK; ++ goto end; ++ ++/* Finish error message. Indicate which function failed */ ++end_unlock: ++ acx_unlock(adev, flags); ++end_fail: ++ printk("acx: %sreset_dev() FAILED\n", msg); ++end: ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++** acxmem_s_issue_cmd_timeo ++** ++** Sends command to fw, extract result ++** ++** NB: we do _not_ take lock inside, so be sure to not touch anything ++** which may interfere with IRQ handler operation ++** ++** TODO: busy wait is a bit silly, so: ++** 1) stop doing many iters - go to sleep after first ++** 2) go to waitqueue based approach: wait, not poll! ++*/ ++#undef FUNC ++#define FUNC "issue_cmd" ++ ++#if !ACX_DEBUG ++int ++acxmem_s_issue_cmd_timeo( ++ acx_device_t *adev, ++ unsigned int cmd, ++ void *buffer, ++ unsigned buflen, ++ unsigned cmd_timeout) ++{ ++#else ++int ++acxmem_s_issue_cmd_timeo_debug( ++ acx_device_t *adev, ++ unsigned cmd, ++ void *buffer, ++ unsigned buflen, ++ unsigned cmd_timeout, ++ const char* cmdstr) ++{ ++ unsigned long start = jiffies; ++#endif ++ const char *devname; ++ unsigned counter; ++ u16 irqtype; ++ int i, j; ++ u8 *p; ++ u16 cmd_status; ++ unsigned long timeout; ++ ++ FN_ENTER; ++ ++ devname = adev->ndev->name; ++ if (!devname || !devname[0] || devname[4]=='%') ++ devname = "acx"; ++ ++ log(L_CTL, FUNC"(cmd:%s,buflen:%u,timeout:%ums,type:0x%04X)\n", ++ cmdstr, buflen, cmd_timeout, ++ buffer ? le16_to_cpu(((acx_ie_generic_t *)buffer)->type) : -1); ++ ++ if (!(adev->dev_state_mask & ACX_STATE_FW_LOADED)) { ++ printk("%s: "FUNC"(): firmware is not loaded yet, " ++ "cannot execute commands!\n", devname); ++ goto bad; ++ } ++ ++ if ((acx_debug & L_DEBUG) && (cmd != ACX1xx_CMD_INTERROGATE)) { ++ printk("input buffer (len=%u):\n", buflen); ++ acx_dump_bytes(buffer, buflen); ++ } ++ ++ /* wait for firmware to become idle for our command submission */ ++ timeout = HZ/5; ++ counter = (timeout * 1000 / HZ) - 1; /* in ms */ ++ timeout += jiffies; ++ do { ++ cmd_status = acxmem_read_cmd_type_status(adev); ++ /* Test for IDLE state */ ++ if (!cmd_status) ++ break; ++ if (counter % 8 == 0) { ++ if (time_after(jiffies, timeout)) { ++ counter = 0; ++ break; ++ } ++ /* we waited 8 iterations, no luck. Sleep 8 ms */ ++ acx_s_msleep(8); ++ } ++ } while (likely(--counter)); ++ ++ if (!counter) { ++ /* the card doesn't get idle, we're in trouble */ ++ printk("%s: "FUNC"(): cmd_status is not IDLE: 0x%04X!=0\n", ++ devname, cmd_status); ++#if DUMP_IF_SLOW > 0 ++ dump_acxmem (adev, 0, 0x10000); ++ panic ("not idle"); ++#endif ++ goto bad; ++ } else if (counter < 190) { /* if waited >10ms... */ ++ log(L_CTL|L_DEBUG, FUNC"(): waited for IDLE %dms. " ++ "Please report\n", 199 - counter); ++ } ++ ++ /* now write the parameters of the command if needed */ ++ if (buffer && buflen) { ++ /* if it's an INTERROGATE command, just pass the length ++ * of parameters to read, as data */ ++#if CMD_DISCOVERY ++ if (cmd == ACX1xx_CMD_INTERROGATE) ++ memset_io(adev->cmd_area + 4, 0xAA, buflen); ++#endif ++ /* ++ * slave memory version ++ */ ++ copy_to_slavemem (adev, (u32) (adev->cmd_area + 4), buffer, ++ (cmd == ACX1xx_CMD_INTERROGATE) ? 4 : buflen); ++ } ++ /* now write the actual command type */ ++ acxmem_write_cmd_type_status(adev, cmd, 0); ++ ++ /* clear CMD_COMPLETE bit. can be set only by IRQ handler: */ ++ adev->irq_status &= ~HOST_INT_CMD_COMPLETE; ++ ++ /* execute command */ ++ write_reg16(adev, IO_ACX_INT_TRIG, INT_TRIG_CMD); ++ write_flush(adev); ++ ++ /* wait for firmware to process command */ ++ ++ /* Ensure nonzero and not too large timeout. ++ ** Also converts e.g. 100->99, 200->199 ++ ** which is nice but not essential */ ++ cmd_timeout = (cmd_timeout-1) | 1; ++ if (unlikely(cmd_timeout > 1199)) ++ cmd_timeout = 1199; ++ ++ /* we schedule away sometimes (timeout can be large) */ ++ counter = cmd_timeout; ++ timeout = jiffies + cmd_timeout * HZ / 1000; ++ do { ++ if (!adev->irqs_active) { /* IRQ disabled: poll */ ++ irqtype = read_reg16(adev, IO_ACX_IRQ_STATUS_NON_DES); ++ if (irqtype & HOST_INT_CMD_COMPLETE) { ++ write_reg16(adev, IO_ACX_IRQ_ACK, ++ HOST_INT_CMD_COMPLETE); ++ break; ++ } ++ } else { /* Wait when IRQ will set the bit */ ++ irqtype = adev->irq_status; ++ if (irqtype & HOST_INT_CMD_COMPLETE) ++ break; ++ } ++ ++ if (counter % 8 == 0) { ++ if (time_after(jiffies, timeout)) { ++ counter = 0; ++ break; ++ } ++ /* we waited 8 iterations, no luck. Sleep 8 ms */ ++ acx_s_msleep(8); ++ } ++ } while (likely(--counter)); ++ ++ /* save state for debugging */ ++ cmd_status = acxmem_read_cmd_type_status(adev); ++ ++ /* put the card in IDLE state */ ++ acxmem_write_cmd_type_status(adev, ACX1xx_CMD_RESET, 0); ++ ++ if (!counter) { /* timed out! */ ++ printk("%s: "FUNC"(): timed out %s for CMD_COMPLETE. " ++ "irq bits:0x%04X irq_status:0x%04X timeout:%dms " ++ "cmd_status:%d (%s)\n", ++ devname, (adev->irqs_active) ? "waiting" : "polling", ++ irqtype, adev->irq_status, cmd_timeout, ++ cmd_status, acx_cmd_status_str(cmd_status)); ++ printk("%s: "FUNC"(): device irq status 0x%04x\n", ++ devname, read_reg16(adev, IO_ACX_IRQ_STATUS_NON_DES)); ++ printk("%s: "FUNC"(): IO_ACX_IRQ_MASK 0x%04x IO_ACX_FEMR 0x%04x\n", ++ devname, ++ read_reg16 (adev, IO_ACX_IRQ_MASK), ++ read_reg16 (adev, IO_ACX_FEMR)); ++ if (read_reg16 (adev, IO_ACX_IRQ_MASK) == 0xffff) { ++ printk ("acxmem: firmware probably hosed - reloading\n"); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) ++ { ++ pm_message_t state; ++ acxmem_e_suspend (resume_pdev, state); ++ } ++#else ++ acxmem_e_suspend (adev->dev, 0); ++#endif ++ { ++ struct work_struct *notused; ++ fw_resumer (notused); ++ } ++ } ++ ++ goto bad; ++ } else if (cmd_timeout - counter > 30) { /* if waited >30ms... */ ++ log(L_CTL|L_DEBUG, FUNC"(): %s for CMD_COMPLETE %dms. " ++ "count:%d. Please report\n", ++ (adev->irqs_active) ? "waited" : "polled", ++ cmd_timeout - counter, counter); ++ } ++ ++ if (1 != cmd_status) { /* it is not a 'Success' */ ++ printk("%s: "FUNC"(): cmd_status is not SUCCESS: %d (%s). " ++ "Took %dms of %d\n", ++ devname, cmd_status, acx_cmd_status_str(cmd_status), ++ cmd_timeout - counter, cmd_timeout); ++ /* zero out result buffer ++ * WARNING: this will trash stack in case of illegally large input ++ * length! */ ++ if (buflen > 388) { ++ /* ++ * 388 is maximum command length ++ */ ++ printk ("invalid length 0x%08x\n", buflen); ++ buflen = 388; ++ } ++ p = (u8 *) buffer; ++ for (i = 0; i < buflen; i+= 16) { ++ printk ("%04x:", i); ++ for (j = 0; (j < 16) && (i+j < buflen); j++) { ++ printk (" %02x", *p++); ++ } ++ printk ("\n"); ++ } ++ ++ if (buffer && buflen) ++ memset(buffer, 0, buflen); ++ goto bad; ++ } ++ ++ /* read in result parameters if needed */ ++ if (buffer && buflen && (cmd == ACX1xx_CMD_INTERROGATE)) { ++ copy_from_slavemem (adev, buffer, (u32) (adev->cmd_area + 4), buflen); ++ if (acx_debug & L_DEBUG) { ++ printk("output buffer (len=%u): ", buflen); ++ acx_dump_bytes(buffer, buflen); ++ } ++ } ++ ++/* ok: */ ++ log(L_CTL, FUNC"(%s): took %ld jiffies to complete\n", ++ cmdstr, jiffies - start); ++ FN_EXIT1(OK); ++ return OK; ++ ++bad: ++ /* Give enough info so that callers can avoid ++ ** printing their own diagnostic messages */ ++#if ACX_DEBUG ++ printk("%s: "FUNC"(cmd:%s) FAILED\n", devname, cmdstr); ++#else ++ printk("%s: "FUNC"(cmd:0x%04X) FAILED\n", devname, cmd); ++#endif ++ dump_stack(); ++ FN_EXIT1(NOT_OK); ++ return NOT_OK; ++} ++ ++ ++/*********************************************************************** ++*/ ++#if defined(NONESSENTIAL_FEATURES) ++typedef struct device_id { ++ unsigned char id[6]; ++ char *descr; ++ char *type; ++} device_id_t; ++ ++static const device_id_t ++device_ids[] = ++{ ++ { ++ {'G', 'l', 'o', 'b', 'a', 'l'}, ++ NULL, ++ NULL, ++ }, ++ { ++ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, ++ "uninitialized", ++ "SpeedStream SS1021 or Gigafast WF721-AEX" ++ }, ++ { ++ {0x80, 0x81, 0x82, 0x83, 0x84, 0x85}, ++ "non-standard", ++ "DrayTek Vigor 520" ++ }, ++ { ++ {'?', '?', '?', '?', '?', '?'}, ++ "non-standard", ++ "Level One WPC-0200" ++ }, ++ { ++ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ++ "empty", ++ "DWL-650+ variant" ++ } ++}; ++ ++static void ++acx_show_card_eeprom_id(acx_device_t *adev) ++{ ++ unsigned char buffer[CARD_EEPROM_ID_SIZE]; ++ int i; ++ ++ memset(&buffer, 0, CARD_EEPROM_ID_SIZE); ++ /* use direct EEPROM access */ ++ for (i = 0; i < CARD_EEPROM_ID_SIZE; i++) { ++ if (OK != acxmem_read_eeprom_byte(adev, ++ ACX100_EEPROM_ID_OFFSET + i, ++ &buffer[i])) { ++ printk("acx: reading EEPROM FAILED\n"); ++ break; ++ } ++ } ++ ++ for (i = 0; i < VEC_SIZE(device_ids); i++) { ++ if (!memcmp(&buffer, device_ids[i].id, CARD_EEPROM_ID_SIZE)) { ++ if (device_ids[i].descr) { ++ printk("acx: EEPROM card ID string check " ++ "found %s card ID: is this %s?\n", ++ device_ids[i].descr, device_ids[i].type); ++ } ++ break; ++ } ++ } ++ if (i == VEC_SIZE(device_ids)) { ++ printk("acx: EEPROM card ID string check found " ++ "unknown card: expected 'Global', got '%.*s\'. " ++ "Please report\n", CARD_EEPROM_ID_SIZE, buffer); ++ } ++} ++#endif /* NONESSENTIAL_FEATURES */ ++ ++/*********************************************************************** ++** acxmem_free_desc_queues ++** ++** Releases the queues that have been allocated, the ++** others have been initialised to NULL so this ++** function can be used if only part of the queues were allocated. ++*/ ++ ++void ++acxmem_free_desc_queues(acx_device_t *adev) ++{ ++#define ACX_FREE_QUEUE(size, ptr, phyaddr) \ ++ if (ptr) { \ ++ kfree(ptr); \ ++ ptr = NULL; \ ++ size = 0; \ ++ } ++ ++ FN_ENTER; ++ ++ ACX_FREE_QUEUE(adev->txhostdesc_area_size, adev->txhostdesc_start, adev->txhostdesc_startphy); ++ ACX_FREE_QUEUE(adev->txbuf_area_size, adev->txbuf_start, adev->txbuf_startphy); ++ ++ adev->txdesc_start = NULL; ++ ++ ACX_FREE_QUEUE(adev->rxhostdesc_area_size, adev->rxhostdesc_start, adev->rxhostdesc_startphy); ++ ACX_FREE_QUEUE(adev->rxbuf_area_size, adev->rxbuf_start, adev->rxbuf_startphy); ++ ++ adev->rxdesc_start = NULL; ++ ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acxmem_s_delete_dma_regions ++*/ ++static void ++acxmem_s_delete_dma_regions(acx_device_t *adev) ++{ ++ unsigned long flags; ++ ++ FN_ENTER; ++ /* disable radio Tx/Rx. Shouldn't we use the firmware commands ++ * here instead? Or are we that much down the road that it's no ++ * longer possible here? */ ++ /* ++ * slave memory interface really doesn't like this. ++ */ ++ /* ++ write_reg16(adev, IO_ACX_ENABLE, 0); ++ */ ++ ++ acx_s_msleep(100); ++ ++ acx_lock(adev, flags); ++ acxmem_free_desc_queues(adev); ++ acx_unlock(adev, flags); ++ ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acxmem_e_probe ++** ++** Probe routine called when a PCI device w/ matching ID is found. ++** Here's the sequence: ++** - Allocate the PCI resources. ++** - Read the PCMCIA attribute memory to make sure we have a WLAN card ++** - Reset the MAC ++** - Initialize the dev and wlan data ++** - Initialize the MAC ++** ++** pdev - ptr to pci device structure containing info about pci configuration ++** id - ptr to the device id entry that matched this device ++*/ ++static const u16 ++IO_ACX100[] = ++{ ++ 0x0000, /* IO_ACX_SOFT_RESET */ ++ ++ 0x0014, /* IO_ACX_SLV_MEM_ADDR */ ++ 0x0018, /* IO_ACX_SLV_MEM_DATA */ ++ 0x001c, /* IO_ACX_SLV_MEM_CTL */ ++ 0x0020, /* IO_ACX_SLV_END_CTL */ ++ ++ 0x0034, /* IO_ACX_FEMR */ ++ ++ 0x007c, /* IO_ACX_INT_TRIG */ ++ 0x0098, /* IO_ACX_IRQ_MASK */ ++ 0x00a4, /* IO_ACX_IRQ_STATUS_NON_DES */ ++ 0x00a8, /* IO_ACX_IRQ_STATUS_CLEAR */ ++ 0x00ac, /* IO_ACX_IRQ_ACK */ ++ 0x00b0, /* IO_ACX_HINT_TRIG */ ++ ++ 0x0104, /* IO_ACX_ENABLE */ ++ ++ 0x0250, /* IO_ACX_EEPROM_CTL */ ++ 0x0254, /* IO_ACX_EEPROM_ADDR */ ++ 0x0258, /* IO_ACX_EEPROM_DATA */ ++ 0x025c, /* IO_ACX_EEPROM_CFG */ ++ ++ 0x0268, /* IO_ACX_PHY_ADDR */ ++ 0x026c, /* IO_ACX_PHY_DATA */ ++ 0x0270, /* IO_ACX_PHY_CTL */ ++ ++ 0x0290, /* IO_ACX_GPIO_OE */ ++ ++ 0x0298, /* IO_ACX_GPIO_OUT */ ++ ++ 0x02a4, /* IO_ACX_CMD_MAILBOX_OFFS */ ++ 0x02a8, /* IO_ACX_INFO_MAILBOX_OFFS */ ++ 0x02ac, /* IO_ACX_EEPROM_INFORMATION */ ++ ++ 0x02d0, /* IO_ACX_EE_START */ ++ 0x02d4, /* IO_ACX_SOR_CFG */ ++ 0x02d8 /* IO_ACX_ECPU_CTRL */ ++}; ++ ++static const u16 ++IO_ACX111[] = ++{ ++ 0x0000, /* IO_ACX_SOFT_RESET */ ++ ++ 0x0014, /* IO_ACX_SLV_MEM_ADDR */ ++ 0x0018, /* IO_ACX_SLV_MEM_DATA */ ++ 0x001c, /* IO_ACX_SLV_MEM_CTL */ ++ 0x0020, /* IO_ACX_SLV_MEM_CP */ ++ ++ 0x0034, /* IO_ACX_FEMR */ ++ ++ 0x00b4, /* IO_ACX_INT_TRIG */ ++ 0x00d4, /* IO_ACX_IRQ_MASK */ ++ /* we do mean NON_DES (0xf0), not NON_DES_MASK which is at 0xe0: */ ++ 0x00f0, /* IO_ACX_IRQ_STATUS_NON_DES */ ++ 0x00e4, /* IO_ACX_IRQ_STATUS_CLEAR */ ++ 0x00e8, /* IO_ACX_IRQ_ACK */ ++ 0x00ec, /* IO_ACX_HINT_TRIG */ ++ ++ 0x01d0, /* IO_ACX_ENABLE */ ++ ++ 0x0338, /* IO_ACX_EEPROM_CTL */ ++ 0x033c, /* IO_ACX_EEPROM_ADDR */ ++ 0x0340, /* IO_ACX_EEPROM_DATA */ ++ 0x0344, /* IO_ACX_EEPROM_CFG */ ++ ++ 0x0350, /* IO_ACX_PHY_ADDR */ ++ 0x0354, /* IO_ACX_PHY_DATA */ ++ 0x0358, /* IO_ACX_PHY_CTL */ ++ ++ 0x0374, /* IO_ACX_GPIO_OE */ ++ ++ 0x037c, /* IO_ACX_GPIO_OUT */ ++ ++ 0x0388, /* IO_ACX_CMD_MAILBOX_OFFS */ ++ 0x038c, /* IO_ACX_INFO_MAILBOX_OFFS */ ++ 0x0390, /* IO_ACX_EEPROM_INFORMATION */ ++ ++ 0x0100, /* IO_ACX_EE_START */ ++ 0x0104, /* IO_ACX_SOR_CFG */ ++ 0x0108, /* IO_ACX_ECPU_CTRL */ ++}; ++ ++static void ++dummy_netdev_init(struct net_device *ndev) {} ++ ++/* ++ * Most of the acx specific pieces of hardware reset. ++ */ ++static int ++acxmem_complete_hw_reset (acx_device_t *adev) ++{ ++ acx111_ie_configoption_t co; ++ ++ /* NB: read_reg() reads may return bogus data before reset_dev(), ++ * since the firmware which directly controls large parts of the I/O ++ * registers isn't initialized yet. ++ * acx100 seems to be more affected than acx111 */ ++ if (OK != acxmem_s_reset_dev (adev)) ++ return -1; ++ ++ if (IS_ACX100(adev)) { ++ /* ACX100: configopt struct in cmd mailbox - directly after reset */ ++ copy_from_slavemem (adev, (u8*) &co, (u32) adev->cmd_area, sizeof (co)); ++ } ++ ++ if (OK != acx_s_init_mac(adev)) ++ return -3; ++ ++ if (IS_ACX111(adev)) { ++ /* ACX111: configopt struct needs to be queried after full init */ ++ acx_s_interrogate(adev, &co, ACX111_IE_CONFIG_OPTIONS); ++ } ++ ++ /* ++ * Set up transmit buffer administration ++ */ ++ init_acx_txbuf (adev); ++ ++ /* ++ * Windows driver writes 0x01000000 to register 0x288, RADIO_CTL, if the form factor ++ * is 3. It also write protects the EEPROM by writing 1<<9 to GPIO_OUT ++ */ ++ if (adev->form_factor == 3) { ++ set_regbits (adev, 0x288, 0x01000000); ++ set_regbits (adev, 0x298, 1<<9); ++ } ++ ++/* TODO: merge them into one function, they are called just once and are the same for pci & usb */ ++ if (OK != acxmem_read_eeprom_byte(adev, 0x05, &adev->eeprom_version)) ++ return -2; ++ ++ acx_s_parse_configoption(adev, &co); ++ acx_s_get_firmware_version(adev); /* needs to be after acx_s_init_mac() */ ++ acx_display_hardware_details(adev); ++ ++ return 0; ++} ++ ++static int __devinit ++acxmem_e_probe(struct platform_device *pdev) ++{ ++ struct acx_hardware_data *hwdata = pdev->dev.platform_data; ++ acx_device_t *adev = NULL; ++ struct net_device *ndev = NULL; ++ const char *chip_name; ++ int result = -EIO; ++ int err; ++ int i; ++ unsigned long addr_size=0; ++ u8 chip_type; ++ ++ FN_ENTER; ++ (void) hwdata->start_hw(); ++ ++ /* FIXME: prism54 calls pci_set_mwi() here, ++ * should we do/support the same? */ ++ ++ /* chiptype is u8 but id->driver_data is ulong ++ ** Works for now (possible values are 1 and 2) */ ++ chip_type = CHIPTYPE_ACX100; ++ /* acx100 and acx111 have different PCI memory regions */ ++ if (chip_type == CHIPTYPE_ACX100) { ++ chip_name = "ACX100"; ++ } else if (chip_type == CHIPTYPE_ACX111) { ++ chip_name = "ACX111"; ++ } else { ++ printk("acx: unknown chip type 0x%04X\n", chip_type); ++ goto fail_unknown_chiptype; ++ } ++ ++ printk("acx: found %s-based wireless network card\n", chip_name); ++ log(L_ANY, "initial debug setting is 0x%04X\n", acx_debug); ++ ++ ndev = alloc_netdev(sizeof(*adev), "wlan%d", dummy_netdev_init); ++ /* (NB: memsets to 0 entire area) */ ++ if (!ndev) { ++ printk("acx: no memory for netdevice struct\n"); ++ goto fail_alloc_netdev; ++ } ++ ++ platform_set_drvdata (pdev, ndev); ++ ++ ether_setup(ndev); ++ ++ /* ++ * use platform_data resources that were provided ++ */ ++ ndev->irq = 0; ++ for (i=0; i<pdev->num_resources; i++) { ++ if (pdev->resource[i].flags == IORESOURCE_IRQ) { ++ ndev->irq = pdev->resource[i].start; ++ } ++ else if (pdev->resource[i].flags == IORESOURCE_MEM) { ++ ndev->base_addr = pdev->resource[i].start; ++ addr_size = pdev->resource[i].end - pdev->resource[i].start; ++ } ++ } ++ if (addr_size == 0 || ndev->irq == 0) ++ goto fail_hw_params; ++ ndev->open = &acxmem_e_open; ++ ndev->stop = &acxmem_e_close; ++ pdev->dev.release = &acxmem_e_release; ++ ndev->hard_start_xmit = &acx_i_start_xmit; ++ ndev->get_stats = &acx_e_get_stats; ++#if IW_HANDLER_VERSION <= 5 ++ ndev->get_wireless_stats = &acx_e_get_wireless_stats; ++#endif ++ ndev->wireless_handlers = (struct iw_handler_def *)&acx_ioctl_handler_def; ++ ndev->set_multicast_list = &acxmem_i_set_multicast_list; ++ ndev->tx_timeout = &acxmem_i_tx_timeout; ++ ndev->change_mtu = &acx_e_change_mtu; ++ ndev->watchdog_timeo = 4 * HZ; ++ ++ adev = ndev2adev(ndev); ++ spin_lock_init(&adev->lock); /* initial state: unlocked */ ++ spin_lock_init(&adev->txbuf_lock); ++ /* We do not start with downed sem: we want PARANOID_LOCKING to work */ ++ sema_init(&adev->sem, 1); /* initial state: 1 (upped) */ ++ /* since nobody can see new netdev yet, we can as well ++ ** just _presume_ that we're under sem (instead of actually taking it): */ ++ /* acx_sem_lock(adev); */ ++ adev->dev = &pdev->dev; ++ adev->ndev = ndev; ++ adev->dev_type = DEVTYPE_MEM; ++ adev->chip_type = chip_type; ++ adev->chip_name = chip_name; ++ adev->io = (CHIPTYPE_ACX100 == chip_type) ? IO_ACX100 : IO_ACX111; ++ adev->membase = (volatile u32 *) ndev->base_addr; ++ adev->iobase = (volatile u32 *) ioremap_nocache (ndev->base_addr, addr_size); ++ /* to find crashes due to weird driver access ++ * to unconfigured interface (ifup) */ ++ adev->mgmt_timer.function = (void (*)(unsigned long))0x0000dead; ++ ++#if defined(NONESSENTIAL_FEATURES) ++ acx_show_card_eeprom_id(adev); ++#endif /* NONESSENTIAL_FEATURES */ ++ ++#ifdef SET_MODULE_OWNER ++ SET_MODULE_OWNER(ndev); ++#endif ++ SET_NETDEV_DEV(ndev, &pdev->dev); ++ ++ log(L_IRQ|L_INIT, "using IRQ %d\n", ndev->irq); ++ ++ /* ok, pci setup is finished, now start initializing the card */ ++ ++ if (OK != acxmem_complete_hw_reset (adev)) ++ goto fail_reset; ++ ++ /* ++ * Set up default things for most of the card settings. ++ */ ++ acx_s_set_defaults(adev); ++ ++ /* Register the card, AFTER everything else has been set up, ++ * since otherwise an ioctl could step on our feet due to ++ * firmware operations happening in parallel or uninitialized data */ ++ err = register_netdev(ndev); ++ if (OK != err) { ++ printk("acx: register_netdev() FAILED: %d\n", err); ++ goto fail_register_netdev; ++ } ++ ++ acx_proc_register_entries(ndev); ++ ++ /* Now we have our device, so make sure the kernel doesn't try ++ * to send packets even though we're not associated to a network yet */ ++ acx_stop_queue(ndev, "on probe"); ++ acx_carrier_off(ndev, "on probe"); ++ ++ /* ++ * Set up a default monitor type so that poor combinations of initialization ++ * sequences in monitor mode don't end up destroying the hardware type. ++ */ ++ adev->monitor_type = ARPHRD_ETHER; ++ ++ /* ++ * Register to receive inetaddr notifier changes. This will allow us to ++ * catch if the user changes the MAC address of the interface. ++ */ ++ register_netdevice_notifier(&acx_netdev_notifier); ++ ++ /* after register_netdev() userspace may start working with dev ++ * (in particular, on other CPUs), we only need to up the sem */ ++ /* acx_sem_unlock(adev); */ ++ ++ printk("acx "ACX_RELEASE": net device %s, driver compiled " ++ "against wireless extensions %d and Linux %s\n", ++ ndev->name, WIRELESS_EXT, UTS_RELEASE); ++ ++#if CMD_DISCOVERY ++ great_inquisitor(adev); ++#endif ++ ++ result = OK; ++ goto done; ++ ++ /* error paths: undo everything in reverse order... */ ++ ++fail_register_netdev: ++ ++ acxmem_s_delete_dma_regions(adev); ++ ++fail_reset: ++fail_hw_params: ++ free_netdev(ndev); ++fail_alloc_netdev: ++fail_unknown_chiptype: ++ ++ ++done: ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++** acxmem_e_remove ++** ++** Shut device down (if not hot unplugged) ++** and deallocate PCI resources for the acx chip. ++** ++** pdev - ptr to PCI device structure containing info about pci configuration ++*/ ++static int __devexit ++acxmem_e_remove(struct platform_device *pdev) ++{ ++ struct acx_hardware_data *hwdata = pdev->dev.platform_data; ++ struct net_device *ndev; ++ acx_device_t *adev; ++ unsigned long flags; ++ ++ FN_ENTER; ++ ++ ndev = (struct net_device*) platform_get_drvdata(pdev); ++ if (!ndev) { ++ log(L_DEBUG, "%s: card is unused. Skipping any release code\n", ++ __func__); ++ goto end; ++ } ++ ++ adev = ndev2adev(ndev); ++ ++ /* If device wasn't hot unplugged... */ ++ if (adev_present(adev)) { ++ ++ acx_sem_lock(adev); ++ ++ /* disable both Tx and Rx to shut radio down properly */ ++ acx_s_issue_cmd(adev, ACX1xx_CMD_DISABLE_TX, NULL, 0); ++ acx_s_issue_cmd(adev, ACX1xx_CMD_DISABLE_RX, NULL, 0); ++ ++#ifdef REDUNDANT ++ /* put the eCPU to sleep to save power ++ * Halting is not possible currently, ++ * since not supported by all firmware versions */ ++ acx_s_issue_cmd(adev, ACX100_CMD_SLEEP, NULL, 0); ++#endif ++ acx_lock(adev, flags); ++ ++ /* disable power LED to save power :-) */ ++ log(L_INIT, "switching off power LED to save power\n"); ++ acxmem_l_power_led(adev, 0); ++ ++ /* stop our eCPU */ ++ if (IS_ACX111(adev)) { ++ /* FIXME: does this actually keep halting the eCPU? ++ * I don't think so... ++ */ ++ acxmem_l_reset_mac(adev); ++ } else { ++ u16 temp; ++ ++ /* halt eCPU */ ++ temp = read_reg16(adev, IO_ACX_ECPU_CTRL) | 0x1; ++ write_reg16(adev, IO_ACX_ECPU_CTRL, temp); ++ write_flush(adev); ++ } ++ ++ acx_unlock(adev, flags); ++ ++ acx_sem_unlock(adev); ++ } ++ ++ ++ /* ++ * Unregister the notifier chain ++ */ ++ unregister_netdevice_notifier(&acx_netdev_notifier); ++ ++ /* unregister the device to not let the kernel ++ * (e.g. ioctls) access a half-deconfigured device ++ * NB: this will cause acxmem_e_close() to be called, ++ * thus we shouldn't call it under sem! */ ++ log(L_INIT, "removing device %s\n", ndev->name); ++ unregister_netdev(ndev); ++ ++ /* unregister_netdev ensures that no references to us left. ++ * For paranoid reasons we continue to follow the rules */ ++ acx_sem_lock(adev); ++ ++ if (adev->dev_state_mask & ACX_STATE_IFACE_UP) { ++ acxmem_s_down(ndev); ++ CLEAR_BIT(adev->dev_state_mask, ACX_STATE_IFACE_UP); ++ } ++ ++ acx_proc_unregister_entries(ndev); ++ ++ acxmem_s_delete_dma_regions(adev); ++ ++ /* finally, clean up PCI bus state */ ++ if (adev->iobase) iounmap((void *)adev->iobase); ++ ++ acx_sem_unlock(adev); ++ ++ /* Free netdev (quite late, ++ * since otherwise we might get caught off-guard ++ * by a netdev timeout handler execution ++ * expecting to see a working dev...) */ ++ free_netdev(ndev); ++ ++ (void) hwdata->stop_hw(); ++ ++ printk ("e_remove done\n"); ++end: ++ FN_EXIT0; ++ ++ return 0; ++} ++ ++ ++/*********************************************************************** ++** TODO: PM code needs to be fixed / debugged / tested. ++*/ ++#ifdef CONFIG_PM ++static int ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) ++acxmem_e_suspend(struct platform_device *pdev, pm_message_t state) ++#else ++acxmem_e_suspend(struct device *pdev, u32 state) ++#endif ++{ ++ struct net_device *ndev = platform_get_drvdata(pdev); ++ acx_device_t *adev; ++ struct acx_hardware_data *hwdata; ++ ++ FN_ENTER; ++ printk("acx: suspend handler is experimental!\n"); ++ printk("sus: dev %p\n", ndev); ++ ++ if (!netif_running(ndev)) ++ goto end; ++ ++ adev = ndev2adev(ndev); ++ printk("sus: adev %p\n", adev); ++ ++ hwdata = adev->dev->platform_data; ++ ++ acx_sem_lock(adev); ++ ++ netif_device_detach(ndev); /* this one cannot sleep */ ++ acxmem_s_down(ndev); ++ /* down() does not set it to 0xffff, but here we really want that */ ++ write_reg16(adev, IO_ACX_IRQ_MASK, 0xffff); ++ write_reg16(adev, IO_ACX_FEMR, 0x0); ++ acxmem_s_delete_dma_regions(adev); ++ ++ /* ++ * Turn the ACX chip off. ++ */ ++ hwdata->stop_hw(); ++ ++ acx_sem_unlock(adev); ++end: ++ FN_EXIT0; ++ return OK; ++} ++ ++ ++ ++static void ++fw_resumer(struct work_struct *notused) ++{ ++ struct platform_device *pdev = resume_pdev; ++ struct net_device *ndev = platform_get_drvdata(pdev); ++ acx_device_t *adev; ++ struct acx_hardware_data *hwdata; ++ ++ printk("acx: resume handler is experimental!\n"); ++ printk("rsm: got dev %p\n", ndev); ++ ++ if (!netif_running(ndev)) ++ return; ++ ++ adev = ndev2adev(ndev); ++ printk("rsm: got adev %p\n", adev); ++ ++ acx_sem_lock(adev); ++ ++ hwdata = adev->dev->platform_data; ++ ++ /* ++ * Turn on the ACX. ++ */ ++ hwdata->start_hw(); ++ ++ acxmem_complete_hw_reset (adev); ++ ++ /* ++ * done by acx_s_set_defaults for initial startup ++ */ ++ acxmem_set_interrupt_mask(adev); ++ ++ printk ("rsm: bringing up interface\n"); ++ SET_BIT (adev->set_mask, GETSET_ALL); ++ acxmem_s_up(ndev); ++ printk("rsm: acx up done\n"); ++ ++ /* now even reload all card parameters as they were before suspend, ++ * and possibly be back in the network again already :-) ++ */ ++ /* - most settings updated in acxmem_s_up() ++ if (ACX_STATE_IFACE_UP & adev->dev_state_mask) { ++ adev->set_mask = GETSET_ALL; ++ acx_s_update_card_settings(adev); ++ printk("rsm: settings updated\n"); ++ } ++ */ ++ netif_device_attach(ndev); ++ printk("rsm: device attached\n"); ++ ++ acx_sem_unlock(adev); ++} ++ ++DECLARE_WORK( fw_resume_work, fw_resumer ); ++ ++static int ++acxmem_e_resume(struct platform_device *pdev) ++{ ++ FN_ENTER; ++ ++ resume_pdev = pdev; ++ schedule_work( &fw_resume_work ); ++ ++ FN_EXIT0; ++ return OK; ++} ++#endif /* CONFIG_PM */ ++ ++ ++/*********************************************************************** ++** acxmem_s_up ++** ++** This function is called by acxmem_e_open (when ifconfig sets the device as up) ++** ++** Side effects: ++** - Enables on-card interrupt requests ++** - calls acx_s_start ++*/ ++ ++static void ++enable_acx_irq(acx_device_t *adev) ++{ ++ FN_ENTER; ++ write_reg16(adev, IO_ACX_IRQ_MASK, adev->irq_mask); ++ write_reg16(adev, IO_ACX_FEMR, 0x8000); ++ adev->irqs_active = 1; ++ FN_EXIT0; ++} ++ ++static void ++acxmem_s_up(struct net_device *ndev) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ unsigned long flags; ++ ++ FN_ENTER; ++ ++ acx_lock(adev, flags); ++ enable_acx_irq(adev); ++ acx_unlock(adev, flags); ++ ++ /* acx fw < 1.9.3.e has a hardware timer, and older drivers ++ ** used to use it. But we don't do that anymore, our OS ++ ** has reliable software timers */ ++ init_timer(&adev->mgmt_timer); ++ adev->mgmt_timer.function = acx_i_timer; ++ adev->mgmt_timer.data = (unsigned long)adev; ++ ++ /* Need to set ACX_STATE_IFACE_UP first, or else ++ ** timer won't be started by acx_set_status() */ ++ SET_BIT(adev->dev_state_mask, ACX_STATE_IFACE_UP); ++ switch (adev->mode) { ++ case ACX_MODE_0_ADHOC: ++ case ACX_MODE_2_STA: ++ /* actual scan cmd will happen in start() */ ++ acx_set_status(adev, ACX_STATUS_1_SCANNING); break; ++ case ACX_MODE_3_AP: ++ case ACX_MODE_MONITOR: ++ acx_set_status(adev, ACX_STATUS_4_ASSOCIATED); break; ++ } ++ ++ acx_s_start(adev); ++ ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acxmem_s_down ++** ++** This disables the netdevice ++** ++** Side effects: ++** - disables on-card interrupt request ++*/ ++ ++static void ++disable_acx_irq(acx_device_t *adev) ++{ ++ FN_ENTER; ++ ++ /* I guess mask is not 0xffff because acx100 won't signal ++ ** cmd completion then (needed for ifup). ++ ** Someone with acx100 please confirm */ ++ write_reg16(adev, IO_ACX_IRQ_MASK, adev->irq_mask_off); ++ write_reg16(adev, IO_ACX_FEMR, 0x0); ++ adev->irqs_active = 0; ++ FN_EXIT0; ++} ++ ++static void ++acxmem_s_down(struct net_device *ndev) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ unsigned long flags; ++ ++ FN_ENTER; ++ ++ /* Disable IRQs first, so that IRQs cannot race with us */ ++ /* then wait until interrupts have finished executing on other CPUs */ ++ acx_lock(adev, flags); ++ disable_acx_irq(adev); ++ synchronize_irq(adev->pdev->irq); ++ acx_unlock(adev, flags); ++ ++ /* we really don't want to have an asynchronous tasklet disturb us ++ ** after something vital for its job has been shut down, so ++ ** end all remaining work now. ++ ** ++ ** NB: carrier_off (done by set_status below) would lead to ++ ** not yet fully understood deadlock in FLUSH_SCHEDULED_WORK(). ++ ** That's why we do FLUSH first. ++ ** ++ ** NB2: we have a bad locking bug here: FLUSH_SCHEDULED_WORK() ++ ** waits for acx_e_after_interrupt_task to complete if it is running ++ ** on another CPU, but acx_e_after_interrupt_task ++ ** will sleep on sem forever, because it is taken by us! ++ ** Work around that by temporary sem unlock. ++ ** This will fail miserably if we'll be hit by concurrent ++ ** iwconfig or something in between. TODO! */ ++ acx_sem_unlock(adev); ++ FLUSH_SCHEDULED_WORK(); ++ acx_sem_lock(adev); ++ ++ /* This is possible: ++ ** FLUSH_SCHEDULED_WORK -> acx_e_after_interrupt_task -> ++ ** -> set_status(ASSOCIATED) -> wake_queue() ++ ** That's why we stop queue _after_ FLUSH_SCHEDULED_WORK ++ ** lock/unlock is just paranoia, maybe not needed */ ++ acx_lock(adev, flags); ++ acx_stop_queue(ndev, "on ifdown"); ++ acx_set_status(adev, ACX_STATUS_0_STOPPED); ++ acx_unlock(adev, flags); ++ ++ /* kernel/timer.c says it's illegal to del_timer_sync() ++ ** a timer which restarts itself. We guarantee this cannot ++ ** ever happen because acx_i_timer() never does this if ++ ** status is ACX_STATUS_0_STOPPED */ ++ del_timer_sync(&adev->mgmt_timer); ++ ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acxmem_e_open ++** ++** Called as a result of SIOCSIFFLAGS ioctl changing the flags bit IFF_UP ++** from clear to set. In other words: ifconfig up. ++** ++** Returns: ++** 0 success ++** >0 f/w reported error ++** <0 driver reported error ++*/ ++static int ++acxmem_e_open(struct net_device *ndev) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ int result = OK; ++ ++ FN_ENTER; ++ ++ acx_sem_lock(adev); ++ ++ acx_init_task_scheduler(adev); ++ ++/* TODO: pci_set_power_state(pdev, PCI_D0); ? */ ++ ++ /* request shared IRQ handler */ ++ if (request_irq(ndev->irq, acxmem_i_interrupt, SA_INTERRUPT, ndev->name, ndev)) { ++ printk("%s: request_irq FAILED\n", ndev->name); ++ result = -EAGAIN; ++ goto done; ++ } ++ set_irq_type (ndev->irq, IRQT_FALLING); ++ log(L_DEBUG|L_IRQ, "request_irq %d successful\n", ndev->irq); ++ ++ /* ifup device */ ++ acxmem_s_up(ndev); ++ ++ /* We don't currently have to do anything else. ++ * The setup of the MAC should be subsequently completed via ++ * the mlme commands. ++ * Higher layers know we're ready from dev->start==1 and ++ * dev->tbusy==0. Our rx path knows to pass up received/ ++ * frames because of dev->flags&IFF_UP is true. ++ */ ++done: ++ acx_sem_unlock(adev); ++ ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++** acxmem_e_close ++** ++** Called as a result of SIOCSIIFFLAGS ioctl changing the flags bit IFF_UP ++** from set to clear. I.e. called by "ifconfig DEV down" ++** ++** Returns: ++** 0 success ++** >0 f/w reported error ++** <0 driver reported error ++*/ ++static int ++acxmem_e_close(struct net_device *ndev) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ ++ FN_ENTER; ++ ++ acx_sem_lock(adev); ++ ++ /* ifdown device */ ++ CLEAR_BIT(adev->dev_state_mask, ACX_STATE_IFACE_UP); ++ if (netif_device_present(ndev)) { ++ acxmem_s_down(ndev); ++ } ++ ++ /* disable all IRQs, release shared IRQ handler */ ++ write_reg16(adev, IO_ACX_IRQ_MASK, 0xffff); ++ write_reg16(adev, IO_ACX_FEMR, 0x0); ++ free_irq(ndev->irq, ndev); ++ ++/* TODO: pci_set_power_state(pdev, PCI_D3hot); ? */ ++ ++ /* We currently don't have to do anything else. ++ * Higher layers know we're not ready from dev->start==0 and ++ * dev->tbusy==1. Our rx path knows to not pass up received ++ * frames because of dev->flags&IFF_UP is false. ++ */ ++ acx_sem_unlock(adev); ++ ++ log(L_INIT, "closed device\n"); ++ FN_EXIT0; ++ return OK; ++} ++ ++ ++/*********************************************************************** ++** acxmem_i_tx_timeout ++** ++** Called from network core. Must not sleep! ++*/ ++static void ++acxmem_i_tx_timeout(struct net_device *ndev) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ unsigned long flags; ++ unsigned int tx_num_cleaned; ++ ++ FN_ENTER; ++ ++ acx_lock(adev, flags); ++ ++ /* clean processed tx descs, they may have been completely full */ ++ tx_num_cleaned = acxmem_l_clean_txdesc(adev); ++ ++ /* nothing cleaned, yet (almost) no free buffers available? ++ * --> clean all tx descs, no matter which status!! ++ * Note that I strongly suspect that doing emergency cleaning ++ * may confuse the firmware. This is a last ditch effort to get ++ * ANYTHING to work again... ++ * ++ * TODO: it's best to simply reset & reinit hw from scratch... ++ */ ++ if ((adev->tx_free <= TX_EMERG_CLEAN) && (tx_num_cleaned == 0)) { ++ printk("%s: FAILED to free any of the many full tx buffers. " ++ "Switching to emergency freeing. " ++ "Please report!\n", ndev->name); ++ acxmem_l_clean_txdesc_emergency(adev); ++ } ++ ++ if (acx_queue_stopped(ndev) && (ACX_STATUS_4_ASSOCIATED == adev->status)) ++ acx_wake_queue(ndev, "after tx timeout"); ++ ++ /* stall may have happened due to radio drift, so recalib radio */ ++ acx_schedule_task(adev, ACX_AFTER_IRQ_CMD_RADIO_RECALIB); ++ ++ /* do unimportant work last */ ++ printk("%s: tx timeout!\n", ndev->name); ++ adev->stats.tx_errors++; ++ ++ acx_unlock(adev, flags); ++ ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acxmem_i_set_multicast_list ++** FIXME: most likely needs refinement ++*/ ++static void ++acxmem_i_set_multicast_list(struct net_device *ndev) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ unsigned long flags; ++ ++ FN_ENTER; ++ ++ acx_lock(adev, flags); ++ ++ /* firmwares don't have allmulti capability, ++ * so just use promiscuous mode instead in this case. */ ++ if (ndev->flags & (IFF_PROMISC|IFF_ALLMULTI)) { ++ SET_BIT(adev->rx_config_1, RX_CFG1_RCV_PROMISCUOUS); ++ CLEAR_BIT(adev->rx_config_1, RX_CFG1_FILTER_ALL_MULTI); ++ SET_BIT(adev->set_mask, SET_RXCONFIG); ++ /* let kernel know in case *we* needed to set promiscuous */ ++ ndev->flags |= (IFF_PROMISC|IFF_ALLMULTI); ++ } else { ++ CLEAR_BIT(adev->rx_config_1, RX_CFG1_RCV_PROMISCUOUS); ++ SET_BIT(adev->rx_config_1, RX_CFG1_FILTER_ALL_MULTI); ++ SET_BIT(adev->set_mask, SET_RXCONFIG); ++ ndev->flags &= ~(IFF_PROMISC|IFF_ALLMULTI); ++ } ++ ++ /* cannot update card settings directly here, atomic context */ ++ acx_schedule_task(adev, ACX_AFTER_IRQ_UPDATE_CARD_CFG); ++ ++ acx_unlock(adev, flags); ++ ++ FN_EXIT0; ++} ++ ++ ++/*************************************************************** ++** acxmem_l_process_rxdesc ++** ++** Called directly and only from the IRQ handler ++*/ ++ ++#if !ACX_DEBUG ++static inline void log_rxbuffer(const acx_device_t *adev) {} ++#else ++static void ++log_rxbuffer(const acx_device_t *adev) ++{ ++ register const struct rxhostdesc *rxhostdesc; ++ int i; ++ /* no FN_ENTER here, we don't want that */ ++ ++ rxhostdesc = adev->rxhostdesc_start; ++ if (unlikely(!rxhostdesc)) return; ++ for (i = 0; i < RX_CNT; i++) { ++ if ((rxhostdesc->Ctl_16 & cpu_to_le16(DESC_CTL_HOSTOWN)) ++ && (rxhostdesc->Status & cpu_to_le32(DESC_STATUS_FULL))) ++ printk("rx: buf %d full\n", i); ++ rxhostdesc++; ++ } ++} ++#endif ++ ++static void ++acxmem_l_process_rxdesc(acx_device_t *adev) ++{ ++ register rxhostdesc_t *hostdesc; ++ register rxdesc_t *rxdesc; ++ unsigned count, tail; ++ u32 addr; ++ u8 Ctl_8; ++ ++ FN_ENTER; ++ ++ if (unlikely(acx_debug & L_BUFR)) ++ log_rxbuffer(adev); ++ ++ /* First, have a loop to determine the first descriptor that's ++ * full, just in case there's a mismatch between our current ++ * rx_tail and the full descriptor we're supposed to handle. */ ++ tail = adev->rx_tail; ++ count = RX_CNT; ++ while (1) { ++ hostdesc = &adev->rxhostdesc_start[tail]; ++ rxdesc = &adev->rxdesc_start[tail]; ++ /* advance tail regardless of outcome of the below test */ ++ tail = (tail + 1) % RX_CNT; ++ ++ /* ++ * Unlike the PCI interface, where the ACX can write directly to ++ * the host descriptors, on the slave memory interface we have to ++ * pull these. All we really need to do is check the Ctl_8 field ++ * in the rx descriptor on the ACX, which should be 0x11000000 if ++ * we should process it. ++ */ ++ Ctl_8 = hostdesc->Ctl_16 = read_slavemem8 (adev, (u32) &(rxdesc->Ctl_8)); ++ if ((Ctl_8 & DESC_CTL_HOSTOWN) && ++ (Ctl_8 & DESC_CTL_ACXDONE)) ++ break; /* found it! */ ++ ++ if (unlikely(!--count)) /* hmm, no luck: all descs empty, bail out */ ++ goto end; ++ } ++ ++ /* now process descriptors, starting with the first we figured out */ ++ while (1) { ++ log(L_BUFR, "rx: tail=%u Ctl_8=%02X\n", tail, Ctl_8); ++ /* ++ * If the ACX has CTL_RECLAIM set on this descriptor there ++ * is no buffer associated; it just wants us to tell it to ++ * reclaim the memory. ++ */ ++ if (!(Ctl_8 & DESC_CTL_RECLAIM)) { ++ ++ /* ++ * slave interface - pull data now ++ */ ++ hostdesc->length = read_slavemem16 (adev, (u32) &(rxdesc->total_length)); ++ ++ /* ++ * hostdesc->data is an rxbuffer_t, which includes header information, ++ * but the length in the data packet doesn't. The header information ++ * takes up an additional 12 bytes, so add that to the length we copy. ++ */ ++ addr = read_slavemem32 (adev, (u32) &(rxdesc->ACXMemPtr)); ++ if (addr) { ++ /* ++ * How can &(rxdesc->ACXMemPtr) above ever be zero? Looks like we ++ * get that now and then - try to trap it for debug. ++ */ ++ if (addr & 0xffff0000) { ++ printk("rxdesc 0x%08x\n", (u32) rxdesc); ++ dump_acxmem (adev, 0, 0x10000); ++ panic ("Bad access!"); ++ } ++ chaincopy_from_slavemem (adev, (u8 *) hostdesc->data, addr, ++ hostdesc->length + ++ (u32) &((rxbuffer_t *)0)->hdr_a3); ++ acx_l_process_rxbuf(adev, hostdesc->data); ++ } ++ } ++ else { ++ printk ("rx reclaim only!\n"); ++ } ++ ++ hostdesc->Status = 0; ++ ++ /* ++ * Let the ACX know we're done. ++ */ ++ CLEAR_BIT (Ctl_8, DESC_CTL_HOSTOWN); ++ SET_BIT (Ctl_8, DESC_CTL_HOSTDONE); ++ SET_BIT (Ctl_8, DESC_CTL_RECLAIM); ++ write_slavemem8 (adev, (u32) &rxdesc->Ctl_8, Ctl_8); ++ ++ /* ++ * Now tell the ACX we've finished with the receive buffer so ++ * it can finish the reclaim. ++ */ ++ write_reg16 (adev, IO_ACX_INT_TRIG, INT_TRIG_RXPRC); ++ ++ /* ok, descriptor is handled, now check the next descriptor */ ++ hostdesc = &adev->rxhostdesc_start[tail]; ++ rxdesc = &adev->rxdesc_start[tail]; ++ ++ Ctl_8 = hostdesc->Ctl_16 = read_slavemem8 (adev, (u32) &(rxdesc->Ctl_8)); ++ ++ /* if next descriptor is empty, then bail out */ ++ if (!(Ctl_8 & DESC_CTL_HOSTOWN) || !(Ctl_8 & DESC_CTL_ACXDONE)) ++ break; ++ ++ tail = (tail + 1) % RX_CNT; ++ } ++end: ++ adev->rx_tail = tail; ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acxmem_i_interrupt ++** ++** IRQ handler (atomic context, must not sleep, blah, blah) ++*/ ++ ++/* scan is complete. all frames now on the receive queue are valid */ ++#define INFO_SCAN_COMPLETE 0x0001 ++#define INFO_WEP_KEY_NOT_FOUND 0x0002 ++/* hw has been reset as the result of a watchdog timer timeout */ ++#define INFO_WATCH_DOG_RESET 0x0003 ++/* failed to send out NULL frame from PS mode notification to AP */ ++/* recommended action: try entering 802.11 PS mode again */ ++#define INFO_PS_FAIL 0x0004 ++/* encryption/decryption process on a packet failed */ ++#define INFO_IV_ICV_FAILURE 0x0005 ++ ++/* Info mailbox format: ++2 bytes: type ++2 bytes: status ++more bytes may follow ++ rumors say about status: ++ 0x0000 info available (set by hw) ++ 0x0001 information received (must be set by host) ++ 0x1000 info available, mailbox overflowed (messages lost) (set by hw) ++ but in practice we've seen: ++ 0x9000 when we did not set status to 0x0001 on prev message ++ 0x1001 when we did set it ++ 0x0000 was never seen ++ conclusion: this is really a bitfield: ++ 0x1000 is 'info available' bit ++ 'mailbox overflowed' bit is 0x8000, not 0x1000 ++ value of 0x0000 probably means that there are no messages at all ++ P.S. I dunno how in hell hw is supposed to notice that messages are lost - ++ it does NOT clear bit 0x0001, and this bit will probably stay forever set ++ after we set it once. Let's hope this will be fixed in firmware someday ++*/ ++ ++static void ++handle_info_irq(acx_device_t *adev) ++{ ++#if ACX_DEBUG ++ static const char * const info_type_msg[] = { ++ "(unknown)", ++ "scan complete", ++ "WEP key not found", ++ "internal watchdog reset was done", ++ "failed to send powersave (NULL frame) notification to AP", ++ "encrypt/decrypt on a packet has failed", ++ "TKIP tx keys disabled", ++ "TKIP rx keys disabled", ++ "TKIP rx: key ID not found", ++ "???", ++ "???", ++ "???", ++ "???", ++ "???", ++ "???", ++ "???", ++ "TKIP IV value exceeds thresh" ++ }; ++#endif ++ u32 info_type, info_status; ++ ++ info_type = read_slavemem32 (adev, (u32) adev->info_area); ++ ++ info_status = (info_type >> 16); ++ info_type = (u16)info_type; ++ ++ /* inform fw that we have read this info message */ ++ write_slavemem32(adev, (u32) adev->info_area, info_type | 0x00010000); ++ write_reg16(adev, IO_ACX_INT_TRIG, INT_TRIG_INFOACK); ++ write_flush(adev); ++ ++ log(L_CTL, "info_type:%04X info_status:%04X\n", ++ info_type, info_status); ++ ++ log(L_IRQ, "got Info IRQ: status %04X type %04X: %s\n", ++ info_status, info_type, ++ info_type_msg[(info_type >= VEC_SIZE(info_type_msg)) ? ++ 0 : info_type] ++ ); ++} ++ ++ ++static void ++log_unusual_irq(u16 irqtype) { ++ /* ++ if (!printk_ratelimit()) ++ return; ++ */ ++ ++ printk("acx: got"); ++ if (irqtype & HOST_INT_TX_XFER) { ++ printk(" Tx_Xfer"); ++ } ++ if (irqtype & HOST_INT_RX_COMPLETE) { ++ printk(" Rx_Complete"); ++ } ++ if (irqtype & HOST_INT_DTIM) { ++ printk(" DTIM"); ++ } ++ if (irqtype & HOST_INT_BEACON) { ++ printk(" Beacon"); ++ } ++ if (irqtype & HOST_INT_TIMER) { ++ log(L_IRQ, " Timer"); ++ } ++ if (irqtype & HOST_INT_KEY_NOT_FOUND) { ++ printk(" Key_Not_Found"); ++ } ++ if (irqtype & HOST_INT_IV_ICV_FAILURE) { ++ printk(" IV_ICV_Failure (crypto)"); ++ } ++ /* HOST_INT_CMD_COMPLETE */ ++ /* HOST_INT_INFO */ ++ if (irqtype & HOST_INT_OVERFLOW) { ++ printk(" Overflow"); ++ } ++ if (irqtype & HOST_INT_PROCESS_ERROR) { ++ printk(" Process_Error"); ++ } ++ /* HOST_INT_SCAN_COMPLETE */ ++ if (irqtype & HOST_INT_FCS_THRESHOLD) { ++ printk(" FCS_Threshold"); ++ } ++ if (irqtype & HOST_INT_UNKNOWN) { ++ printk(" Unknown"); ++ } ++ printk(" IRQ(s)\n"); ++} ++ ++ ++static void ++update_link_quality_led(acx_device_t *adev) ++{ ++ int qual; ++ ++ qual = acx_signal_determine_quality(adev->wstats.qual.level, adev->wstats.qual.noise); ++ if (qual > adev->brange_max_quality) ++ qual = adev->brange_max_quality; ++ ++ if (time_after(jiffies, adev->brange_time_last_state_change + ++ (HZ/2 - HZ/2 * (unsigned long)qual / adev->brange_max_quality ) )) { ++ acxmem_l_power_led(adev, (adev->brange_last_state == 0)); ++ adev->brange_last_state ^= 1; /* toggle */ ++ adev->brange_time_last_state_change = jiffies; ++ } ++} ++ ++ ++#define MAX_IRQLOOPS_PER_JIFFY (20000/HZ) /* a la orinoco.c */ ++ ++static irqreturn_t ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) ++acxmem_i_interrupt(int irq, void *dev_id) ++#else ++acxmwm_i_interrupt(int irq, void *dev_id, struct pt_regs *regs) ++#endif ++{ ++ acx_device_t *adev; ++ unsigned long flags; ++ unsigned int irqcount = MAX_IRQLOOPS_PER_JIFFY; ++ register u16 irqtype; ++ u16 unmasked; ++ ++ adev = ndev2adev((struct net_device*)dev_id); ++ ++ /* LOCKING: can just spin_lock() since IRQs are disabled anyway. ++ * I am paranoid */ ++ acx_lock(adev, flags); ++ ++ unmasked = read_reg16(adev, IO_ACX_IRQ_STATUS_CLEAR); ++ if (unlikely(0xffff == unmasked)) { ++ /* 0xffff value hints at missing hardware, ++ * so don't do anything. ++ * Not very clean, but other drivers do the same... */ ++ log(L_IRQ, "IRQ type:FFFF - device removed? IRQ_NONE\n"); ++ goto none; ++ } ++ ++ /* We will check only "interesting" IRQ types */ ++ irqtype = unmasked & ~adev->irq_mask; ++ if (!irqtype) { ++ /* We are on a shared IRQ line and it wasn't our IRQ */ ++ log(L_IRQ, "IRQ type:%04X, mask:%04X - all are masked, IRQ_NONE\n", ++ unmasked, adev->irq_mask); ++ goto none; ++ } ++ ++ /* Done here because IRQ_NONEs taking three lines of log ++ ** drive me crazy */ ++ FN_ENTER; ++ ++#define IRQ_ITERATE 1 ++#if IRQ_ITERATE ++if (jiffies != adev->irq_last_jiffies) { ++ adev->irq_loops_this_jiffy = 0; ++ adev->irq_last_jiffies = jiffies; ++} ++ ++/* safety condition; we'll normally abort loop below ++ * in case no IRQ type occurred */ ++while (likely(--irqcount)) { ++#endif ++ /* ACK all IRQs ASAP */ ++ write_reg16(adev, IO_ACX_IRQ_ACK, 0xffff); ++ ++ log(L_IRQ, "IRQ type:%04X, mask:%04X, type & ~mask:%04X\n", ++ unmasked, adev->irq_mask, irqtype); ++ ++ /* Handle most important IRQ types first */ ++ if (irqtype & HOST_INT_RX_DATA) { ++ log(L_IRQ, "got Rx_Data IRQ\n"); ++ acxmem_l_process_rxdesc(adev); ++ } ++ if (irqtype & HOST_INT_TX_COMPLETE) { ++ log(L_IRQ, "got Tx_Complete IRQ\n"); ++ /* don't clean up on each Tx complete, wait a bit ++ * unless we're going towards full, in which case ++ * we do it immediately, too (otherwise we might lockup ++ * with a full Tx buffer if we go into ++ * acxmem_l_clean_txdesc() at a time when we won't wakeup ++ * the net queue in there for some reason...) */ ++ if (adev->tx_free <= TX_START_CLEAN) { ++#if TX_CLEANUP_IN_SOFTIRQ ++ acx_schedule_task(adev, ACX_AFTER_IRQ_TX_CLEANUP); ++#else ++ acxmem_l_clean_txdesc(adev); ++#endif ++ } ++ } ++ ++ /* Less frequent ones */ ++ if (irqtype & (0 ++ | HOST_INT_CMD_COMPLETE ++ | HOST_INT_INFO ++ | HOST_INT_SCAN_COMPLETE ++ )) { ++ if (irqtype & HOST_INT_CMD_COMPLETE) { ++ log(L_IRQ, "got Command_Complete IRQ\n"); ++ /* save the state for the running issue_cmd() */ ++ SET_BIT(adev->irq_status, HOST_INT_CMD_COMPLETE); ++ } ++ if (irqtype & HOST_INT_INFO) { ++ handle_info_irq(adev); ++ } ++ if (irqtype & HOST_INT_SCAN_COMPLETE) { ++ log(L_IRQ, "got Scan_Complete IRQ\n"); ++ /* need to do that in process context */ ++ acx_schedule_task(adev, ACX_AFTER_IRQ_COMPLETE_SCAN); ++ /* remember that fw is not scanning anymore */ ++ SET_BIT(adev->irq_status, HOST_INT_SCAN_COMPLETE); ++ } ++ } ++ ++ /* These we just log, but either they happen rarely ++ * or we keep them masked out */ ++ if (irqtype & (0 ++ /* | HOST_INT_RX_DATA */ ++ /* | HOST_INT_TX_COMPLETE */ ++ | HOST_INT_TX_XFER ++ | HOST_INT_RX_COMPLETE ++ | HOST_INT_DTIM ++ | HOST_INT_BEACON ++ | HOST_INT_TIMER ++ | HOST_INT_KEY_NOT_FOUND ++ | HOST_INT_IV_ICV_FAILURE ++ /* | HOST_INT_CMD_COMPLETE */ ++ /* | HOST_INT_INFO */ ++ | HOST_INT_OVERFLOW ++ | HOST_INT_PROCESS_ERROR ++ /* | HOST_INT_SCAN_COMPLETE */ ++ | HOST_INT_FCS_THRESHOLD ++ | HOST_INT_UNKNOWN ++ )) { ++ log_unusual_irq(irqtype); ++ } ++ ++#if IRQ_ITERATE ++ unmasked = read_reg16(adev, IO_ACX_IRQ_STATUS_CLEAR); ++ irqtype = unmasked & ~adev->irq_mask; ++ /* Bail out if no new IRQ bits or if all are masked out */ ++ if (!irqtype) ++ break; ++ ++ if (unlikely(++adev->irq_loops_this_jiffy > MAX_IRQLOOPS_PER_JIFFY)) { ++ printk(KERN_ERR "acx: too many interrupts per jiffy!\n"); ++ /* Looks like card floods us with IRQs! Try to stop that */ ++ write_reg16(adev, IO_ACX_IRQ_MASK, 0xffff); ++ /* This will short-circuit all future attempts to handle IRQ. ++ * We cant do much more... */ ++ adev->irq_mask = 0; ++ break; ++ } ++} ++#endif ++ /* Routine to perform blink with range */ ++ if (unlikely(adev->led_power == 2)) ++ update_link_quality_led(adev); ++ ++/* handled: */ ++ /* write_flush(adev); - not needed, last op was read anyway */ ++ acx_unlock(adev, flags); ++ FN_EXIT0; ++ return IRQ_HANDLED; ++ ++none: ++ acx_unlock(adev, flags); ++ return IRQ_NONE; ++} ++ ++ ++/*********************************************************************** ++** acxmem_l_power_led ++*/ ++void ++acxmem_l_power_led(acx_device_t *adev, int enable) ++{ ++ u16 gpio_pled = IS_ACX111(adev) ? 0x0040 : 0x0800; ++ ++ /* A hack. Not moving message rate limiting to adev->xxx ++ * (it's only a debug message after all) */ ++ static int rate_limit = 0; ++ ++ if (rate_limit++ < 3) ++ log(L_IOCTL, "Please report in case toggling the power " ++ "LED doesn't work for your card!\n"); ++ if (enable) ++ write_reg16(adev, IO_ACX_GPIO_OUT, ++ read_reg16(adev, IO_ACX_GPIO_OUT) & ~gpio_pled); ++ else ++ write_reg16(adev, IO_ACX_GPIO_OUT, ++ read_reg16(adev, IO_ACX_GPIO_OUT) | gpio_pled); ++} ++ ++ ++/*********************************************************************** ++** Ioctls ++*/ ++ ++/*********************************************************************** ++*/ ++int ++acx111pci_ioctl_info( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ struct iw_param *vwrq, ++ char *extra) ++{ ++#if ACX_DEBUG > 1 ++ acx_device_t *adev = ndev2adev(ndev); ++ rxdesc_t *rxdesc; ++ txdesc_t *txdesc; ++ rxhostdesc_t *rxhostdesc; ++ txhostdesc_t *txhostdesc; ++ struct acx111_ie_memoryconfig memconf; ++ struct acx111_ie_queueconfig queueconf; ++ unsigned long flags; ++ int i; ++ char memmap[0x34]; ++ char rxconfig[0x8]; ++ char fcserror[0x8]; ++ char ratefallback[0x5]; ++ ++ if ( !(acx_debug & (L_IOCTL|L_DEBUG)) ) ++ return OK; ++ /* using printk() since we checked debug flag already */ ++ ++ acx_sem_lock(adev); ++ ++ if (!IS_ACX111(adev)) { ++ printk("acx111-specific function called " ++ "with non-acx111 chip, aborting\n"); ++ goto end_ok; ++ } ++ ++ /* get Acx111 Memory Configuration */ ++ memset(&memconf, 0, sizeof(memconf)); ++ /* BTW, fails with 12 (Write only) error code. ++ ** Retained for easy testing of issue_cmd error handling :) */ ++ printk ("Interrogating queue config\n"); ++ acx_s_interrogate(adev, &memconf, ACX1xx_IE_QUEUE_CONFIG); ++ printk ("done with queue config\n"); ++ ++ /* get Acx111 Queue Configuration */ ++ memset(&queueconf, 0, sizeof(queueconf)); ++ printk ("Interrogating mem config options\n"); ++ acx_s_interrogate(adev, &queueconf, ACX1xx_IE_MEMORY_CONFIG_OPTIONS); ++ printk ("done with mem config options\n"); ++ ++ /* get Acx111 Memory Map */ ++ memset(memmap, 0, sizeof(memmap)); ++ printk ("Interrogating mem map\n"); ++ acx_s_interrogate(adev, &memmap, ACX1xx_IE_MEMORY_MAP); ++ printk ("done with mem map\n"); ++ ++ /* get Acx111 Rx Config */ ++ memset(rxconfig, 0, sizeof(rxconfig)); ++ printk ("Interrogating rxconfig\n"); ++ acx_s_interrogate(adev, &rxconfig, ACX1xx_IE_RXCONFIG); ++ printk ("done with queue rxconfig\n"); ++ ++ /* get Acx111 fcs error count */ ++ memset(fcserror, 0, sizeof(fcserror)); ++ printk ("Interrogating fcs err count\n"); ++ acx_s_interrogate(adev, &fcserror, ACX1xx_IE_FCS_ERROR_COUNT); ++ printk ("done with err count\n"); ++ ++ /* get Acx111 rate fallback */ ++ memset(ratefallback, 0, sizeof(ratefallback)); ++ printk ("Interrogating rate fallback\n"); ++ acx_s_interrogate(adev, &ratefallback, ACX1xx_IE_RATE_FALLBACK); ++ printk ("done with rate fallback\n"); ++ ++ /* force occurrence of a beacon interrupt */ ++ /* TODO: comment why is this necessary */ ++ write_reg16(adev, IO_ACX_HINT_TRIG, HOST_INT_BEACON); ++ ++ /* dump Acx111 Mem Configuration */ ++ printk("dump mem config:\n" ++ "data read: %d, struct size: %d\n" ++ "Number of stations: %1X\n" ++ "Memory block size: %1X\n" ++ "tx/rx memory block allocation: %1X\n" ++ "count rx: %X / tx: %X queues\n" ++ "options %1X\n" ++ "fragmentation %1X\n" ++ "Rx Queue 1 Count Descriptors: %X\n" ++ "Rx Queue 1 Host Memory Start: %X\n" ++ "Tx Queue 1 Count Descriptors: %X\n" ++ "Tx Queue 1 Attributes: %X\n", ++ memconf.len, (int) sizeof(memconf), ++ memconf.no_of_stations, ++ memconf.memory_block_size, ++ memconf.tx_rx_memory_block_allocation, ++ memconf.count_rx_queues, memconf.count_tx_queues, ++ memconf.options, ++ memconf.fragmentation, ++ memconf.rx_queue1_count_descs, ++ acx2cpu(memconf.rx_queue1_host_rx_start), ++ memconf.tx_queue1_count_descs, ++ memconf.tx_queue1_attributes); ++ ++ /* dump Acx111 Queue Configuration */ ++ printk("dump queue head:\n" ++ "data read: %d, struct size: %d\n" ++ "tx_memory_block_address (from card): %X\n" ++ "rx_memory_block_address (from card): %X\n" ++ "rx1_queue address (from card): %X\n" ++ "tx1_queue address (from card): %X\n" ++ "tx1_queue attributes (from card): %X\n", ++ queueconf.len, (int) sizeof(queueconf), ++ queueconf.tx_memory_block_address, ++ queueconf.rx_memory_block_address, ++ queueconf.rx1_queue_address, ++ queueconf.tx1_queue_address, ++ queueconf.tx1_attributes); ++ ++ /* dump Acx111 Mem Map */ ++ printk("dump mem map:\n" ++ "data read: %d, struct size: %d\n" ++ "Code start: %X\n" ++ "Code end: %X\n" ++ "WEP default key start: %X\n" ++ "WEP default key end: %X\n" ++ "STA table start: %X\n" ++ "STA table end: %X\n" ++ "Packet template start: %X\n" ++ "Packet template end: %X\n" ++ "Queue memory start: %X\n" ++ "Queue memory end: %X\n" ++ "Packet memory pool start: %X\n" ++ "Packet memory pool end: %X\n" ++ "iobase: %p\n" ++ "iobase2: %p\n", ++ *((u16 *)&memmap[0x02]), (int) sizeof(memmap), ++ *((u32 *)&memmap[0x04]), ++ *((u32 *)&memmap[0x08]), ++ *((u32 *)&memmap[0x0C]), ++ *((u32 *)&memmap[0x10]), ++ *((u32 *)&memmap[0x14]), ++ *((u32 *)&memmap[0x18]), ++ *((u32 *)&memmap[0x1C]), ++ *((u32 *)&memmap[0x20]), ++ *((u32 *)&memmap[0x24]), ++ *((u32 *)&memmap[0x28]), ++ *((u32 *)&memmap[0x2C]), ++ *((u32 *)&memmap[0x30]), ++ adev->iobase, ++ adev->iobase2); ++ ++ /* dump Acx111 Rx Config */ ++ printk("dump rx config:\n" ++ "data read: %d, struct size: %d\n" ++ "rx config: %X\n" ++ "rx filter config: %X\n", ++ *((u16 *)&rxconfig[0x02]), (int) sizeof(rxconfig), ++ *((u16 *)&rxconfig[0x04]), ++ *((u16 *)&rxconfig[0x06])); ++ ++ /* dump Acx111 fcs error */ ++ printk("dump fcserror:\n" ++ "data read: %d, struct size: %d\n" ++ "fcserrors: %X\n", ++ *((u16 *)&fcserror[0x02]), (int) sizeof(fcserror), ++ *((u32 *)&fcserror[0x04])); ++ ++ /* dump Acx111 rate fallback */ ++ printk("dump rate fallback:\n" ++ "data read: %d, struct size: %d\n" ++ "ratefallback: %X\n", ++ *((u16 *)&ratefallback[0x02]), (int) sizeof(ratefallback), ++ *((u8 *)&ratefallback[0x04])); ++ ++ /* protect against IRQ */ ++ acx_lock(adev, flags); ++ ++ /* dump acx111 internal rx descriptor ring buffer */ ++ rxdesc = adev->rxdesc_start; ++ ++ /* loop over complete receive pool */ ++ if (rxdesc) for (i = 0; i < RX_CNT; i++) { ++ printk("\ndump internal rxdesc %d:\n" ++ "mem pos %p\n" ++ "next 0x%X\n" ++ "acx mem pointer (dynamic) 0x%X\n" ++ "CTL (dynamic) 0x%X\n" ++ "Rate (dynamic) 0x%X\n" ++ "RxStatus (dynamic) 0x%X\n" ++ "Mod/Pre (dynamic) 0x%X\n", ++ i, ++ rxdesc, ++ acx2cpu(rxdesc->pNextDesc), ++ acx2cpu(rxdesc->ACXMemPtr), ++ rxdesc->Ctl_8, ++ rxdesc->rate, ++ rxdesc->error, ++ rxdesc->SNR); ++ rxdesc++; ++ } ++ ++ /* dump host rx descriptor ring buffer */ ++ ++ rxhostdesc = adev->rxhostdesc_start; ++ ++ /* loop over complete receive pool */ ++ if (rxhostdesc) for (i = 0; i < RX_CNT; i++) { ++ printk("\ndump host rxdesc %d:\n" ++ "mem pos %p\n" ++ "buffer mem pos 0x%X\n" ++ "buffer mem offset 0x%X\n" ++ "CTL 0x%X\n" ++ "Length 0x%X\n" ++ "next 0x%X\n" ++ "Status 0x%X\n", ++ i, ++ rxhostdesc, ++ acx2cpu(rxhostdesc->data_phy), ++ rxhostdesc->data_offset, ++ le16_to_cpu(rxhostdesc->Ctl_16), ++ le16_to_cpu(rxhostdesc->length), ++ acx2cpu(rxhostdesc->desc_phy_next), ++ rxhostdesc->Status); ++ rxhostdesc++; ++ } ++ ++ /* dump acx111 internal tx descriptor ring buffer */ ++ txdesc = adev->txdesc_start; ++ ++ /* loop over complete transmit pool */ ++ if (txdesc) for (i = 0; i < TX_CNT; i++) { ++ printk("\ndump internal txdesc %d:\n" ++ "size 0x%X\n" ++ "mem pos %p\n" ++ "next 0x%X\n" ++ "acx mem pointer (dynamic) 0x%X\n" ++ "host mem pointer (dynamic) 0x%X\n" ++ "length (dynamic) 0x%X\n" ++ "CTL (dynamic) 0x%X\n" ++ "CTL2 (dynamic) 0x%X\n" ++ "Status (dynamic) 0x%X\n" ++ "Rate (dynamic) 0x%X\n", ++ i, ++ (int) sizeof(struct txdesc), ++ txdesc, ++ acx2cpu(txdesc->pNextDesc), ++ acx2cpu(txdesc->AcxMemPtr), ++ acx2cpu(txdesc->HostMemPtr), ++ le16_to_cpu(txdesc->total_length), ++ txdesc->Ctl_8, ++ txdesc->Ctl2_8, txdesc->error, ++ txdesc->u.r1.rate); ++ txdesc = advance_txdesc(adev, txdesc, 1); ++ } ++ ++ /* dump host tx descriptor ring buffer */ ++ ++ txhostdesc = adev->txhostdesc_start; ++ ++ /* loop over complete host send pool */ ++ if (txhostdesc) for (i = 0; i < TX_CNT * 2; i++) { ++ printk("\ndump host txdesc %d:\n" ++ "mem pos %p\n" ++ "buffer mem pos 0x%X\n" ++ "buffer mem offset 0x%X\n" ++ "CTL 0x%X\n" ++ "Length 0x%X\n" ++ "next 0x%X\n" ++ "Status 0x%X\n", ++ i, ++ txhostdesc, ++ acx2cpu(txhostdesc->data_phy), ++ txhostdesc->data_offset, ++ le16_to_cpu(txhostdesc->Ctl_16), ++ le16_to_cpu(txhostdesc->length), ++ acx2cpu(txhostdesc->desc_phy_next), ++ le32_to_cpu(txhostdesc->Status)); ++ txhostdesc++; ++ } ++ ++ /* write_reg16(adev, 0xb4, 0x4); */ ++ ++ acx_unlock(adev, flags); ++end_ok: ++ ++ acx_sem_unlock(adev); ++#endif /* ACX_DEBUG */ ++ return OK; ++} ++ ++ ++/*********************************************************************** ++*/ ++int ++acx100mem_ioctl_set_phy_amp_bias( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ struct iw_param *vwrq, ++ char *extra) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ unsigned long flags; ++ u16 gpio_old; ++ ++ if (!IS_ACX100(adev)) { ++ /* WARNING!!! ++ * Removing this check *might* damage ++ * hardware, since we're tweaking GPIOs here after all!!! ++ * You've been warned... ++ * WARNING!!! */ ++ printk("acx: sorry, setting bias level for non-acx100 " ++ "is not supported yet\n"); ++ return OK; ++ } ++ ++ if (*extra > 7) { ++ printk("acx: invalid bias parameter, range is 0-7\n"); ++ return -EINVAL; ++ } ++ ++ acx_sem_lock(adev); ++ ++ /* Need to lock accesses to [IO_ACX_GPIO_OUT]: ++ * IRQ handler uses it to update LED */ ++ acx_lock(adev, flags); ++ gpio_old = read_reg16(adev, IO_ACX_GPIO_OUT); ++ write_reg16(adev, IO_ACX_GPIO_OUT, (gpio_old & 0xf8ff) | ((u16)*extra << 8)); ++ acx_unlock(adev, flags); ++ ++ log(L_DEBUG, "gpio_old: 0x%04X\n", gpio_old); ++ printk("%s: PHY power amplifier bias: old:%d, new:%d\n", ++ ndev->name, ++ (gpio_old & 0x0700) >> 8, (unsigned char)*extra); ++ ++ acx_sem_unlock(adev); ++ ++ return OK; ++} ++ ++/*************************************************************** ++** acxmem_l_alloc_tx ++** Actually returns a txdesc_t* ptr ++** ++** FIXME: in case of fragments, should allocate multiple descrs ++** after figuring out how many we need and whether we still have ++** sufficiently many. ++*/ ++tx_t* ++acxmem_l_alloc_tx(acx_device_t *adev) ++{ ++ struct txdesc *txdesc; ++ unsigned head; ++ u8 ctl8; ++ static int txattempts = 0; ++ ++ FN_ENTER; ++ ++ if (unlikely(!adev->tx_free)) { ++ printk("acx: BUG: no free txdesc left\n"); ++ /* ++ * Probably the ACX ignored a transmit attempt and now there's a packet ++ * sitting in the queue we think should be transmitting but the ACX doesn't ++ * know about. ++ * On the first pass, send the ACX a TxProc interrupt to try moving ++ * things along, and if that doesn't work (ie, we get called again) completely ++ * flush the transmit queue. ++ */ ++ if (txattempts < 10) { ++ txattempts++; ++ printk ("acx: trying to wake up ACX\n"); ++ write_reg16(adev, IO_ACX_INT_TRIG, INT_TRIG_TXPRC); ++ write_flush(adev); } ++ else { ++ txattempts = 0; ++ printk ("acx: flushing transmit queue.\n"); ++ acxmem_l_clean_txdesc_emergency (adev); ++ } ++ txdesc = NULL; ++ goto end; ++ } ++ ++ /* ++ * Make a quick check to see if there is transmit buffer space on ++ * the ACX. This can't guarantee there is enough space for the packet ++ * since we don't yet know how big it is, but it will prevent at least some ++ * annoyances. ++ */ ++ if (!adev->acx_txbuf_blocks_free) { ++ txdesc = NULL; ++ goto end; ++ } ++ ++ head = adev->tx_head; ++ /* ++ * txdesc points to ACX memory ++ */ ++ txdesc = get_txdesc(adev, head); ++ ctl8 = read_slavemem8 (adev, (u32) &(txdesc->Ctl_8)); ++ ++ /* ++ * If we don't own the buffer (HOSTOWN) it is certainly not free; however, ++ * we may have previously thought we had enough memory to send ++ * a packet, allocated the buffer then gave up when we found not enough ++ * transmit buffer space on the ACX. In that case, HOSTOWN and ++ * ACXDONE will both be set. ++ */ ++ if (unlikely(DESC_CTL_HOSTOWN != (ctl8 & DESC_CTL_HOSTOWN))) { ++ /* whoops, descr at current index is not free, so probably ++ * ring buffer already full */ ++ printk("acx: BUG: tx_head:%d Ctl8:0x%02X - failed to find " ++ "free txdesc\n", head, ctl8); ++ txdesc = NULL; ++ goto end; ++ } ++ ++ /* Needed in case txdesc won't be eventually submitted for tx */ ++ write_slavemem8 (adev, (u32) &(txdesc->Ctl_8), DESC_CTL_ACXDONE_HOSTOWN); ++ ++ adev->tx_free--; ++ log(L_BUFT, "tx: got desc %u, %u remain\n", ++ head, adev->tx_free); ++ /* Keep a few free descs between head and tail of tx ring. ++ ** It is not absolutely needed, just feels safer */ ++ if (adev->tx_free < TX_STOP_QUEUE) { ++ log(L_BUF, "stop queue (%u tx desc left)\n", ++ adev->tx_free); ++ acx_stop_queue(adev->ndev, NULL); ++ } ++ ++ /* returning current descriptor, so advance to next free one */ ++ adev->tx_head = (head + 1) % TX_CNT; ++end: ++ FN_EXIT0; ++ ++ return (tx_t*)txdesc; ++} ++ ++ ++/*************************************************************** ++** acxmem_l_dealloc_tx ++** Clears out a previously allocatedvoid acxmem_l_dealloc_tx(tx_t *tx_opaque); ++ transmit descriptor. The ACX ++** can get confused if we skip transmit descriptors in the queue, ++** so when we don't need a descriptor return it to its original ++** state and move the queue head pointer back. ++** ++*/ ++void ++acxmem_l_dealloc_tx(acx_device_t *adev, tx_t *tx_opaque) ++{ ++ /* ++ * txdesc is the address of the descriptor on the ACX. ++ */ ++ txdesc_t *txdesc = (txdesc_t*)tx_opaque; ++ txdesc_t tmptxdesc; ++ int index; ++ ++ memset (&tmptxdesc, 0, sizeof(tmptxdesc)); ++ tmptxdesc.Ctl_8 = DESC_CTL_HOSTOWN | DESC_CTL_FIRSTFRAG; ++ tmptxdesc.u.r1.rate = 0x0a; ++ ++ /* ++ * Clear out all of the transmit descriptor except for the next pointer ++ */ ++ copy_to_slavemem (adev, (u32) &(txdesc->HostMemPtr), ++ (u8 *) &(tmptxdesc.HostMemPtr), ++ sizeof (tmptxdesc) - sizeof(tmptxdesc.pNextDesc)); ++ ++ /* ++ * This is only called immediately after we've allocated, so we should ++ * be able to set the head back to this descriptor. ++ */ ++ index = ((u8*) txdesc - (u8*)adev->txdesc_start) / adev->txdesc_size; ++ printk ("acx_dealloc: moving head from %d to %d\n", adev->tx_head, index); ++ adev->tx_head = index; ++} ++ ++ ++/*********************************************************************** ++*/ ++void* ++acxmem_l_get_txbuf(acx_device_t *adev, tx_t* tx_opaque) ++{ ++ return get_txhostdesc(adev, (txdesc_t*)tx_opaque)->data; ++} ++ ++ ++/*********************************************************************** ++** acxmem_l_tx_data ++** ++** Can be called from IRQ (rx -> (AP bridging or mgmt response) -> tx). ++** Can be called from acx_i_start_xmit (data frames from net core). ++** ++** FIXME: in case of fragments, should loop over the number of ++** pre-allocated tx descrs, properly setting up transfer data and ++** CTL_xxx flags according to fragment number. ++*/ ++void ++acxmem_update_queue_indicator (acx_device_t *adev, int txqueue) ++{ ++#ifdef USING_MORE_THAN_ONE_TRANSMIT_QUEUE ++ u32 indicator; ++ unsigned long flags; ++ int count; ++ ++ /* ++ * Can't handle an interrupt while we're fiddling with the ACX's lock, ++ * according to TI. The ACX is supposed to hold fw_lock for at most ++ * 500ns. ++ */ ++ local_irq_save (flags); ++ ++ /* ++ * Wait for ACX to release the lock (at most 500ns). ++ */ ++ count = 0; ++ while (read_slavemem16 (adev, (u32) &(adev->acx_queue_indicator->fw_lock)) ++ && (count++ < 50)) { ++ ndelay (10); ++ } ++ if (count < 50) { ++ ++ /* ++ * Take out the host lock - anything non-zero will work, so don't worry about ++ * be/le ++ */ ++ write_slavemem16 (adev, (u32) &(adev->acx_queue_indicator->host_lock), 1); ++ ++ /* ++ * Avoid a race condition ++ */ ++ count = 0; ++ while (read_slavemem16 (adev, (u32) &(adev->acx_queue_indicator->fw_lock)) ++ && (count++ < 50)) { ++ ndelay (10); ++ } ++ ++ if (count < 50) { ++ /* ++ * Mark the queue active ++ */ ++ indicator = read_slavemem32 (adev, (u32) &(adev->acx_queue_indicator->indicator)); ++ indicator |= cpu_to_le32 (1 << txqueue); ++ write_slavemem32 (adev, (u32) &(adev->acx_queue_indicator->indicator), indicator); ++ } ++ ++ /* ++ * Release the host lock ++ */ ++ write_slavemem16 (adev, (u32) &(adev->acx_queue_indicator->host_lock), 0); ++ ++ } ++ ++ /* ++ * Restore interrupts ++ */ ++ local_irq_restore (flags); ++#endif ++} ++ ++void ++acxmem_l_tx_data(acx_device_t *adev, tx_t* tx_opaque, int len) ++{ ++ /* ++ * txdesc is the address on the ACX ++ */ ++ txdesc_t *txdesc = (txdesc_t*)tx_opaque; ++ txhostdesc_t *hostdesc1, *hostdesc2; ++ client_t *clt; ++ u16 rate_cur; ++ u8 Ctl_8, Ctl2_8; ++ u32 addr; ++ ++ FN_ENTER; ++ /* fw doesn't tx such packets anyhow */ ++ if (unlikely(len < WLAN_HDR_A3_LEN)) ++ goto end; ++ ++ hostdesc1 = get_txhostdesc(adev, txdesc); ++ /* modify flag status in separate variable to be able to write it back ++ * in one big swoop later (also in order to have less device memory ++ * accesses) */ ++ Ctl_8 = read_slavemem8 (adev, (u32) &(txdesc->Ctl_8)); ++ Ctl2_8 = 0; /* really need to init it to 0, not txdesc->Ctl2_8, it seems */ ++ ++ hostdesc2 = hostdesc1 + 1; ++ ++ /* DON'T simply set Ctl field to 0 here globally, ++ * it needs to maintain a consistent flag status (those are state flags!!), ++ * otherwise it may lead to severe disruption. Only set or reset particular ++ * flags at the exact moment this is needed... */ ++ ++ /* let chip do RTS/CTS handshaking before sending ++ * in case packet size exceeds threshold */ ++ if (len > adev->rts_threshold) ++ SET_BIT(Ctl2_8, DESC_CTL2_RTS); ++ else ++ CLEAR_BIT(Ctl2_8, DESC_CTL2_RTS); ++ ++ switch (adev->mode) { ++ case ACX_MODE_0_ADHOC: ++ case ACX_MODE_3_AP: ++ clt = acx_l_sta_list_get(adev, ((wlan_hdr_t*)hostdesc1->data)->a1); ++ break; ++ case ACX_MODE_2_STA: ++ clt = adev->ap_client; ++ break; ++#if 0 ++/* testing was done on acx111: */ ++ case ACX_MODE_MONITOR: ++ SET_BIT(Ctl2_8, 0 ++/* sends CTS to self before packet */ ++ + DESC_CTL2_SEQ /* don't increase sequence field */ ++/* not working (looks like good fcs is still added) */ ++ + DESC_CTL2_FCS /* don't add the FCS */ ++/* not tested */ ++ + DESC_CTL2_MORE_FRAG ++/* not tested */ ++ + DESC_CTL2_RETRY /* don't increase retry field */ ++/* not tested */ ++ + DESC_CTL2_POWER /* don't increase power mgmt. field */ ++/* no effect */ ++ + DESC_CTL2_WEP /* encrypt this frame */ ++/* not tested */ ++ + DESC_CTL2_DUR /* don't increase duration field */ ++ ); ++ /* fallthrough */ ++#endif ++ default: /* ACX_MODE_OFF, ACX_MODE_MONITOR */ ++ clt = NULL; ++ break; ++ } ++ ++ rate_cur = clt ? clt->rate_cur : adev->rate_bcast; ++ if (unlikely(!rate_cur)) { ++ printk("acx: driver bug! bad ratemask\n"); ++ goto end; ++ } ++ ++ /* used in tx cleanup routine for auto rate and accounting: */ ++ put_txcr(adev, txdesc, clt, rate_cur); ++ ++ write_slavemem16 (adev, (u32) &(txdesc->total_length), cpu_to_le16(len)); ++ hostdesc2->length = cpu_to_le16(len - WLAN_HDR_A3_LEN); ++ if (IS_ACX111(adev)) { ++ /* note that if !txdesc->do_auto, txrate->cur ++ ** has only one nonzero bit */ ++ txdesc->u.r2.rate111 = cpu_to_le16( ++ rate_cur ++ /* WARNING: I was never able to make it work with prism54 AP. ++ ** It was falling down to 1Mbit where shortpre is not applicable, ++ ** and not working at all at "5,11 basic rates only" setting. ++ ** I even didn't see tx packets in radio packet capture. ++ ** Disabled for now --vda */ ++ /*| ((clt->shortpre && clt->cur!=RATE111_1) ? RATE111_SHORTPRE : 0) */ ++ ); ++#ifdef TODO_FIGURE_OUT_WHEN_TO_SET_THIS ++ /* should add this to rate111 above as necessary */ ++ | (clt->pbcc511 ? RATE111_PBCC511 : 0) ++#endif ++ hostdesc1->length = cpu_to_le16(len); ++ } else { /* ACX100 */ ++ u8 rate_100 = clt ? clt->rate_100 : adev->rate_bcast100; ++ write_slavemem8 (adev, (u32) &(txdesc->u.r1.rate), rate_100); ++#ifdef TODO_FIGURE_OUT_WHEN_TO_SET_THIS ++ if (clt->pbcc511) { ++ if (n == RATE100_5 || n == RATE100_11) ++ n |= RATE100_PBCC511; ++ } ++ ++ if (clt->shortpre && (clt->cur != RATE111_1)) ++ SET_BIT(Ctl_8, DESC_CTL_SHORT_PREAMBLE); /* set Short Preamble */ ++#endif ++ /* set autodma and reclaim and 1st mpdu */ ++ SET_BIT(Ctl_8, DESC_CTL_FIRSTFRAG); ++ ++#if ACX_FRAGMENTATION ++ /* SET_BIT(Ctl2_8, DESC_CTL2_MORE_FRAG); cannot set it unconditionally, needs to be set for all non-last fragments */ ++#endif ++ hostdesc1->length = cpu_to_le16(WLAN_HDR_A3_LEN); ++ ++ /* ++ * Since we're not using autodma copy the packet data to the acx now. ++ * Even host descriptors point to the packet header, and the odd indexed ++ * descriptor following points to the packet data. ++ * ++ * The first step is to find free memory in the ACX transmit buffers. ++ * They don't necessarily map one to one with the transmit queue entries, ++ * so search through them starting just after the last one used. ++ */ ++ addr = allocate_acx_txbuf_space (adev, len); ++ if (addr) { ++ chaincopy_to_slavemem (adev, addr, hostdesc1->data, len); ++ } ++ else { ++ /* ++ * Bummer. We thought we might have enough room in the transmit ++ * buffers to send this packet, but it turns out we don't. alloc_tx ++ * has already marked this transmit descriptor as HOSTOWN and ACXDONE, ++ * which means the ACX will hang when it gets to this descriptor unless ++ * we do something about it. Having a bubble in the transmit queue just ++ * doesn't seem to work, so we have to reset this transmit queue entry's ++ * state to its original value and back up our head pointer to point ++ * back to this entry. ++ */ ++ hostdesc1->length = 0; ++ hostdesc2->length = 0; ++ write_slavemem16 (adev, (u32) &(txdesc->total_length), 0); ++ write_slavemem8 (adev, (u32) &(txdesc->Ctl_8), DESC_CTL_HOSTOWN | DESC_CTL_FIRSTFRAG); ++ adev->tx_head = ((u8*) txdesc - (u8*) adev->txdesc_start) / adev->txdesc_size; ++ goto end; ++ } ++ /* ++ * Tell the ACX where the packet is. ++ */ ++ write_slavemem32 (adev, (u32) &(txdesc->AcxMemPtr), addr); ++ ++ } ++ /* don't need to clean ack/rts statistics here, already ++ * done on descr cleanup */ ++ ++ /* clears HOSTOWN and ACXDONE bits, thus telling that the descriptors ++ * are now owned by the acx100; do this as LAST operation */ ++ CLEAR_BIT(Ctl_8, DESC_CTL_ACXDONE_HOSTOWN); ++ /* flush writes before we release hostdesc to the adapter here */ ++ //wmb(); ++ ++ /* write back modified flags */ ++ /* ++ * At this point Ctl_8 should just be FIRSTFRAG ++ */ ++ write_slavemem8 (adev, (u32) &(txdesc->Ctl2_8),Ctl2_8); ++ write_slavemem8 (adev, (u32) &(txdesc->Ctl_8), Ctl_8); ++ /* unused: txdesc->tx_time = cpu_to_le32(jiffies); */ ++ ++ /* ++ * Update the queue indicator to say there's data on the first queue. ++ */ ++ acxmem_update_queue_indicator (adev, 0); ++ ++ /* flush writes before we tell the adapter that it's its turn now */ ++ mmiowb(); ++ write_reg16(adev, IO_ACX_INT_TRIG, INT_TRIG_TXPRC); ++ write_flush(adev); ++ ++ /* log the packet content AFTER sending it, ++ * in order to not delay sending any further than absolutely needed ++ * Do separate logs for acx100/111 to have human-readable rates */ ++ if (unlikely(acx_debug & (L_XFER|L_DATA))) { ++ u16 fc = ((wlan_hdr_t*)hostdesc1->data)->fc; ++ if (IS_ACX111(adev)) ++ printk("tx: pkt (%s): len %d " ++ "rate %04X%s status %u\n", ++ acx_get_packet_type_string(le16_to_cpu(fc)), len, ++ le16_to_cpu(txdesc->u.r2.rate111), ++ (le16_to_cpu(txdesc->u.r2.rate111) & RATE111_SHORTPRE) ? "(SPr)" : "", ++ adev->status); ++ else ++ printk("tx: pkt (%s): len %d rate %03u%s status %u\n", ++ acx_get_packet_type_string(fc), len, ++ read_slavemem8 (adev, (u32) &(txdesc->u.r1.rate)), ++ (Ctl_8 & DESC_CTL_SHORT_PREAMBLE) ? "(SPr)" : "", ++ adev->status); ++ ++ if (acx_debug & L_DATA) { ++ printk("tx: 802.11 [%d]: ", len); ++ acx_dump_bytes(hostdesc1->data, len); ++ } ++ } ++end: ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acxmem_l_clean_txdesc ++** ++** This function resets the txdescs' status when the ACX100 ++** signals the TX done IRQ (txdescs have been processed), starting with ++** the pool index of the descriptor which we would use next, ++** in order to make sure that we can be as fast as possible ++** in filling new txdescs. ++** Everytime we get called we know where the next packet to be cleaned is. ++*/ ++ ++#if !ACX_DEBUG ++static inline void log_txbuffer(const acx_device_t *adev) {} ++#else ++static void ++log_txbuffer(acx_device_t *adev) ++{ ++ txdesc_t *txdesc; ++ int i; ++ u8 Ctl_8; ++ ++ /* no FN_ENTER here, we don't want that */ ++ /* no locks here, since it's entirely non-critical code */ ++ txdesc = adev->txdesc_start; ++ if (unlikely(!txdesc)) return; ++ printk("tx: desc->Ctl8's:"); ++ for (i = 0; i < TX_CNT; i++) { ++ Ctl_8 = read_slavemem8 (adev, (u32) &(txdesc->Ctl_8)); ++ printk(" %02X", Ctl_8); ++ txdesc = advance_txdesc(adev, txdesc, 1); ++ } ++ printk("\n"); ++} ++#endif ++ ++ ++static void ++handle_tx_error(acx_device_t *adev, u8 error, unsigned int finger) ++{ ++ const char *err = "unknown error"; ++ ++ /* hmm, should we handle this as a mask ++ * of *several* bits? ++ * For now I think only caring about ++ * individual bits is ok... */ ++ switch (error) { ++ case 0x01: ++ err = "no Tx due to error in other fragment"; ++ adev->wstats.discard.fragment++; ++ break; ++ case 0x02: ++ err = "Tx aborted"; ++ adev->stats.tx_aborted_errors++; ++ break; ++ case 0x04: ++ err = "Tx desc wrong parameters"; ++ adev->wstats.discard.misc++; ++ break; ++ case 0x08: ++ err = "WEP key not found"; ++ adev->wstats.discard.misc++; ++ break; ++ case 0x10: ++ err = "MSDU lifetime timeout? - try changing " ++ "'iwconfig retry lifetime XXX'"; ++ adev->wstats.discard.misc++; ++ break; ++ case 0x20: ++ err = "excessive Tx retries due to either distance " ++ "too high or unable to Tx or Tx frame error - " ++ "try changing 'iwconfig txpower XXX' or " ++ "'sens'itivity or 'retry'"; ++ adev->wstats.discard.retries++; ++ /* Tx error 0x20 also seems to occur on ++ * overheating, so I'm not sure whether we ++ * actually want to do aggressive radio recalibration, ++ * since people maybe won't notice then that their hardware ++ * is slowly getting cooked... ++ * Or is it still a safe long distance from utter ++ * radio non-functionality despite many radio recalibs ++ * to final destructive overheating of the hardware? ++ * In this case we really should do recalib here... ++ * I guess the only way to find out is to do a ++ * potentially fatal self-experiment :-\ ++ * Or maybe only recalib in case we're using Tx ++ * rate auto (on errors switching to lower speed ++ * --> less heat?) or 802.11 power save mode? ++ * ++ * ok, just do it. */ ++ if (++adev->retry_errors_msg_ratelimit % 4 == 0) { ++ if (adev->retry_errors_msg_ratelimit <= 20) { ++ printk("%s: several excessive Tx " ++ "retry errors occurred, attempting " ++ "to recalibrate radio. Radio " ++ "drift might be caused by increasing " ++ "card temperature, please check the card " ++ "before it's too late!\n", ++ adev->ndev->name); ++ if (adev->retry_errors_msg_ratelimit == 20) ++ printk("disabling above message\n"); ++ } ++ ++ acx_schedule_task(adev, ACX_AFTER_IRQ_CMD_RADIO_RECALIB); ++ } ++ break; ++ case 0x40: ++ err = "Tx buffer overflow"; ++ adev->stats.tx_fifo_errors++; ++ break; ++ case 0x80: ++ err = "DMA error"; ++ adev->wstats.discard.misc++; ++ break; ++ } ++ adev->stats.tx_errors++; ++ if (adev->stats.tx_errors <= 20) ++ printk("%s: tx error 0x%02X, buf %02u! (%s)\n", ++ adev->ndev->name, error, finger, err); ++ else ++ printk("%s: tx error 0x%02X, buf %02u!\n", ++ adev->ndev->name, error, finger); ++} ++ ++ ++unsigned int ++acxmem_l_clean_txdesc(acx_device_t *adev) ++{ ++ txdesc_t *txdesc; ++ unsigned finger; ++ int num_cleaned; ++ u16 r111; ++ u8 error, ack_failures, rts_failures, rts_ok, r100, Ctl_8; ++ u32 acxmem; ++ txdesc_t tmptxdesc; ++ ++ FN_ENTER; ++ ++ /* ++ * Set up a template descriptor for re-initialization. The only ++ * things that get set are Ctl_8 and the rate, and the rate defaults ++ * to 1Mbps. ++ */ ++ memset (&tmptxdesc, 0, sizeof (tmptxdesc)); ++ tmptxdesc.Ctl_8 = DESC_CTL_HOSTOWN | DESC_CTL_FIRSTFRAG; ++ tmptxdesc.u.r1.rate = 0x0a; ++ ++ if (unlikely(acx_debug & L_DEBUG)) ++ log_txbuffer(adev); ++ ++ log(L_BUFT, "tx: cleaning up bufs from %u\n", adev->tx_tail); ++ ++ /* We know first descr which is not free yet. We advance it as far ++ ** as we see correct bits set in following descs (if next desc ++ ** is NOT free, we shouldn't advance at all). We know that in ++ ** front of tx_tail may be "holes" with isolated free descs. ++ ** We will catch up when all intermediate descs will be freed also */ ++ ++ finger = adev->tx_tail; ++ num_cleaned = 0; ++ while (likely(finger != adev->tx_head)) { ++ txdesc = get_txdesc(adev, finger); ++ ++ /* If we allocated txdesc on tx path but then decided ++ ** to NOT use it, then it will be left as a free "bubble" ++ ** in the "allocated for tx" part of the ring. ++ ** We may meet it on the next ring pass here. */ ++ ++ /* stop if not marked as "tx finished" and "host owned" */ ++ Ctl_8 = read_slavemem8 (adev, (u32) &(txdesc->Ctl_8)); ++ if ((Ctl_8 & DESC_CTL_ACXDONE_HOSTOWN) ++ != DESC_CTL_ACXDONE_HOSTOWN) { ++ if (unlikely(!num_cleaned)) { /* maybe remove completely */ ++ log(L_BUFT, "clean_txdesc: tail isn't free. " ++ "tail:%d head:%d\n", ++ adev->tx_tail, adev->tx_head); ++ } ++ break; ++ } ++ ++ /* remember desc values... */ ++ error = read_slavemem8 (adev, (u32) &(txdesc->error)); ++ ack_failures = read_slavemem8 (adev, (u32) &(txdesc->ack_failures)); ++ rts_failures = read_slavemem8 (adev, (u32) &(txdesc->u.rts.rts_failures)); ++ rts_ok = read_slavemem8 (adev, (u32) &(txdesc->u.rts.rts_ok)); ++ r100 = read_slavemem8 (adev, (u32) &(txdesc->u.r1.rate)); ++ r111 = le16_to_cpu(read_slavemem16 (adev, (u32) &(txdesc->u.r2.rate111))); ++ ++ /* need to check for certain error conditions before we ++ * clean the descriptor: we still need valid descr data here */ ++ if (unlikely(0x30 & error)) { ++ /* only send IWEVTXDROP in case of retry or lifetime exceeded; ++ * all other errors mean we screwed up locally */ ++ union iwreq_data wrqu; ++ wlan_hdr_t *hdr; ++ txhostdesc_t *hostdesc; ++ ++ hostdesc = get_txhostdesc(adev, txdesc); ++ hdr = (wlan_hdr_t *)hostdesc->data; ++ MAC_COPY(wrqu.addr.sa_data, hdr->a1); ++ wireless_send_event(adev->ndev, IWEVTXDROP, &wrqu, NULL); ++ } ++ ++ /* ++ * Free up the transmit data buffers ++ */ ++ acxmem = read_slavemem32 (adev, (u32) &(txdesc->AcxMemPtr)); ++ if (acxmem) { ++ reclaim_acx_txbuf_space (adev, acxmem); ++ } ++ ++ /* ...and free the desc by clearing all the fields ++ except the next pointer */ ++ copy_to_slavemem (adev, ++ (u32) &(txdesc->HostMemPtr), ++ (u8 *) &(tmptxdesc.HostMemPtr), ++ sizeof (tmptxdesc) - sizeof(tmptxdesc.pNextDesc) ++ ); ++ ++ adev->tx_free++; ++ num_cleaned++; ++ ++ if ((adev->tx_free >= TX_START_QUEUE) ++ && (adev->status == ACX_STATUS_4_ASSOCIATED) ++ && (acx_queue_stopped(adev->ndev)) ++ ) { ++ log(L_BUF, "tx: wake queue (avail. Tx desc %u)\n", ++ adev->tx_free); ++ acx_wake_queue(adev->ndev, NULL); ++ } ++ ++ /* do error checking, rate handling and logging ++ * AFTER having done the work, it's faster */ ++ ++ /* do rate handling */ ++ if (adev->rate_auto) { ++ struct client *clt = get_txc(adev, txdesc); ++ if (clt) { ++ u16 cur = get_txr(adev, txdesc); ++ if (clt->rate_cur == cur) { ++ acx_l_handle_txrate_auto(adev, clt, ++ cur, /* intended rate */ ++ r100, r111, /* actually used rate */ ++ (error & 0x30), /* was there an error? */ ++ TX_CNT + TX_CLEAN_BACKLOG - adev->tx_free); ++ } ++ } ++ } ++ ++ if (unlikely(error)) ++ handle_tx_error(adev, error, finger); ++ ++ if (IS_ACX111(adev)) ++ log(L_BUFT, "tx: cleaned %u: !ACK=%u !RTS=%u RTS=%u r111=%04X\n", ++ finger, ack_failures, rts_failures, rts_ok, r111); ++ else ++ log(L_BUFT, "tx: cleaned %u: !ACK=%u !RTS=%u RTS=%u rate=%u\n", ++ finger, ack_failures, rts_failures, rts_ok, r100); ++ ++ /* update pointer for descr to be cleaned next */ ++ finger = (finger + 1) % TX_CNT; ++ } ++ ++ /* remember last position */ ++ adev->tx_tail = finger; ++/* end: */ ++ FN_EXIT1(num_cleaned); ++ return num_cleaned; ++} ++ ++/* clean *all* Tx descriptors, and regardless of their previous state. ++ * Used for brute-force reset handling. */ ++void ++acxmem_l_clean_txdesc_emergency(acx_device_t *adev) ++{ ++ txdesc_t *txdesc; ++ int i; ++ u32 acxmem; ++ ++ FN_ENTER; ++ ++ for (i = 0; i < TX_CNT; i++) { ++ txdesc = get_txdesc(adev, i); ++ ++ /* free it */ ++ write_slavemem8 (adev, (u32) &(txdesc->ack_failures), 0); ++ write_slavemem8 (adev, (u32) &(txdesc->u.rts.rts_failures), 0); ++ write_slavemem8 (adev, (u32) &(txdesc->u.rts.rts_ok), 0); ++ write_slavemem8 (adev, (u32) &(txdesc->error), 0); ++ write_slavemem8 (adev, (u32) &(txdesc->Ctl_8), DESC_CTL_HOSTOWN); ++ ++ /* ++ * Clean up the memory allocated on the ACX for this transmit descriptor. ++ */ ++ acxmem = read_slavemem32 (adev, (u32) &(txdesc->AcxMemPtr)); ++ if (acxmem) { ++ reclaim_acx_txbuf_space (adev, acxmem); ++ } ++ ++ write_slavemem32 (adev, (u32) &(txdesc->AcxMemPtr), 0); ++ } ++ ++ adev->tx_free = TX_CNT; ++ ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acxmem_s_create_tx_host_desc_queue ++*/ ++ ++static void* ++allocate(acx_device_t *adev, size_t size, dma_addr_t *phy, const char *msg) ++{ ++ void *ptr; ++ ptr = kmalloc (size, GFP_KERNEL); ++ /* ++ * The ACX can't use the physical address, so we'll have to fake it ++ * later and it might be handy to have the virtual address. ++ */ ++ *phy = (dma_addr_t) NULL; ++ ++ if (ptr) { ++ log(L_DEBUG, "%s sz=%d adr=0x%p phy=0x%08llx\n", ++ msg, (int)size, ptr, (unsigned long long)*phy); ++ memset(ptr, 0, size); ++ return ptr; ++ } ++ printk(KERN_ERR "acx: %s allocation FAILED (%d bytes)\n", ++ msg, (int)size); ++ return NULL; ++} ++ ++ ++/* ++ * In the generic slave memory access mode, most of the stuff in ++ * the txhostdesc_t is unused. It's only here because the rest of ++ * the ACX driver expects it to be since the PCI version uses indirect ++ * host memory organization with DMA. Since we're not using DMA the ++ * only use we have for the host descriptors is to store the packets ++ * on the way out. ++ */ ++static int ++acxmem_s_create_tx_host_desc_queue(acx_device_t *adev) ++{ ++ txhostdesc_t *hostdesc; ++ u8 *txbuf; ++ int i; ++ ++ FN_ENTER; ++ ++ /* allocate TX buffer */ ++ adev->txbuf_area_size = TX_CNT * WLAN_A4FR_MAXLEN_WEP_FCS; ++ ++ adev->txbuf_start = allocate(adev, adev->txbuf_area_size, ++ &adev->txbuf_startphy, "txbuf_start"); ++ if (!adev->txbuf_start) ++ goto fail; ++ ++ /* allocate the TX host descriptor queue pool */ ++ adev->txhostdesc_area_size = TX_CNT * 2*sizeof(*hostdesc); ++ ++ adev->txhostdesc_start = allocate(adev, adev->txhostdesc_area_size, ++ &adev->txhostdesc_startphy, "txhostdesc_start"); ++ if (!adev->txhostdesc_start) ++ goto fail; ++ ++ /* check for proper alignment of TX host descriptor pool */ ++ if ((long) adev->txhostdesc_start & 3) { ++ printk("acx: driver bug: dma alloc returns unaligned address\n"); ++ goto fail; ++ } ++ ++ hostdesc = adev->txhostdesc_start; ++ txbuf = adev->txbuf_start; ++ ++#if 0 ++/* Each tx buffer is accessed by hardware via ++** txdesc -> txhostdesc(s) -> txbuffer(s). ++** We use only one txhostdesc per txdesc, but it looks like ++** acx111 is buggy: it accesses second txhostdesc ++** (via hostdesc.desc_phy_next field) even if ++** txdesc->length == hostdesc->length and thus ++** entire packet was placed into first txhostdesc. ++** Due to this bug acx111 hangs unless second txhostdesc ++** has le16_to_cpu(hostdesc.length) = 3 (or larger) ++** Storing NULL into hostdesc.desc_phy_next ++** doesn't seem to help. ++** ++** Update: although it worked on Xterasys XN-2522g ++** with len=3 trick, WG311v2 is even more bogus, doesn't work. ++** Keeping this code (#ifdef'ed out) for documentational purposes. ++*/ ++ for (i = 0; i < TX_CNT*2; i++) { ++ hostdesc_phy += sizeof(*hostdesc); ++ if (!(i & 1)) { ++ hostdesc->data_phy = cpu2acx(txbuf_phy); ++ /* hostdesc->data_offset = ... */ ++ /* hostdesc->reserved = ... */ ++ hostdesc->Ctl_16 = cpu_to_le16(DESC_CTL_HOSTOWN); ++ /* hostdesc->length = ... */ ++ hostdesc->desc_phy_next = cpu2acx(hostdesc_phy); ++ hostdesc->pNext = ptr2acx(NULL); ++ /* hostdesc->Status = ... */ ++ /* below: non-hardware fields */ ++ hostdesc->data = txbuf; ++ ++ txbuf += WLAN_A4FR_MAXLEN_WEP_FCS; ++ txbuf_phy += WLAN_A4FR_MAXLEN_WEP_FCS; ++ } else { ++ /* hostdesc->data_phy = ... */ ++ /* hostdesc->data_offset = ... */ ++ /* hostdesc->reserved = ... */ ++ /* hostdesc->Ctl_16 = ... */ ++ hostdesc->length = cpu_to_le16(3); /* bug workaround */ ++ /* hostdesc->desc_phy_next = ... */ ++ /* hostdesc->pNext = ... */ ++ /* hostdesc->Status = ... */ ++ /* below: non-hardware fields */ ++ /* hostdesc->data = ... */ ++ } ++ hostdesc++; ++ } ++#endif ++/* We initialize two hostdescs so that they point to adjacent ++** memory areas. Thus txbuf is really just a contiguous memory area */ ++ for (i = 0; i < TX_CNT*2; i++) { ++ /* ->data is a non-hardware field: */ ++ hostdesc->data = txbuf; ++ ++ if (!(i & 1)) { ++ txbuf += WLAN_HDR_A3_LEN; ++ } else { ++ txbuf += WLAN_A4FR_MAXLEN_WEP_FCS - WLAN_HDR_A3_LEN; ++ } ++ hostdesc++; ++ } ++ hostdesc--; ++ ++ FN_EXIT1(OK); ++ return OK; ++fail: ++ printk("acx: create_tx_host_desc_queue FAILED\n"); ++ /* dealloc will be done by free function on error case */ ++ FN_EXIT1(NOT_OK); ++ return NOT_OK; ++} ++ ++ ++/*************************************************************** ++** acxmem_s_create_rx_host_desc_queue ++*/ ++/* the whole size of a data buffer (header plus data body) ++ * plus 32 bytes safety offset at the end */ ++#define RX_BUFFER_SIZE (sizeof(rxbuffer_t) + 32) ++ ++static int ++acxmem_s_create_rx_host_desc_queue(acx_device_t *adev) ++{ ++ rxhostdesc_t *hostdesc; ++ rxbuffer_t *rxbuf; ++ int i; ++ ++ FN_ENTER; ++ ++ /* allocate the RX host descriptor queue pool */ ++ adev->rxhostdesc_area_size = RX_CNT * sizeof(*hostdesc); ++ ++ adev->rxhostdesc_start = allocate(adev, adev->rxhostdesc_area_size, ++ &adev->rxhostdesc_startphy, "rxhostdesc_start"); ++ if (!adev->rxhostdesc_start) ++ goto fail; ++ ++ /* check for proper alignment of RX host descriptor pool */ ++ if ((long) adev->rxhostdesc_start & 3) { ++ printk("acx: driver bug: dma alloc returns unaligned address\n"); ++ goto fail; ++ } ++ ++ /* allocate Rx buffer pool which will be used by the acx ++ * to store the whole content of the received frames in it */ ++ adev->rxbuf_area_size = RX_CNT * RX_BUFFER_SIZE; ++ ++ adev->rxbuf_start = allocate(adev, adev->rxbuf_area_size, ++ &adev->rxbuf_startphy, "rxbuf_start"); ++ if (!adev->rxbuf_start) ++ goto fail; ++ ++ rxbuf = adev->rxbuf_start; ++ hostdesc = adev->rxhostdesc_start; ++ ++ /* don't make any popular C programming pointer arithmetic mistakes ++ * here, otherwise I'll kill you... ++ * (and don't dare asking me why I'm warning you about that...) */ ++ for (i = 0; i < RX_CNT; i++) { ++ hostdesc->data = rxbuf; ++ hostdesc->length = cpu_to_le16(RX_BUFFER_SIZE); ++ rxbuf++; ++ hostdesc++; ++ } ++ hostdesc--; ++ FN_EXIT1(OK); ++ return OK; ++fail: ++ printk("acx: create_rx_host_desc_queue FAILED\n"); ++ /* dealloc will be done by free function on error case */ ++ FN_EXIT1(NOT_OK); ++ return NOT_OK; ++} ++ ++ ++/*************************************************************** ++** acxmem_s_create_hostdesc_queues ++*/ ++int ++acxmem_s_create_hostdesc_queues(acx_device_t *adev) ++{ ++ int result; ++ result = acxmem_s_create_tx_host_desc_queue(adev); ++ if (OK != result) return result; ++ result = acxmem_s_create_rx_host_desc_queue(adev); ++ return result; ++} ++ ++ ++/*************************************************************** ++** acxmem_create_tx_desc_queue ++*/ ++static void ++acxmem_create_tx_desc_queue(acx_device_t *adev, u32 tx_queue_start) ++{ ++ txdesc_t *txdesc; ++ u32 clr; ++ int i; ++ ++ FN_ENTER; ++ ++ if (IS_ACX100(adev)) ++ adev->txdesc_size = sizeof(*txdesc); ++ else ++ /* the acx111 txdesc is 4 bytes larger */ ++ adev->txdesc_size = sizeof(*txdesc) + 4; ++ ++ /* ++ * This refers to an ACX address, not one of ours ++ */ ++ adev->txdesc_start = (txdesc_t *) tx_queue_start; ++ ++ log(L_DEBUG, "adev->txdesc_start=%p\n", ++ adev->txdesc_start); ++ ++ adev->tx_free = TX_CNT; ++ /* done by memset: adev->tx_head = 0; */ ++ /* done by memset: adev->tx_tail = 0; */ ++ txdesc = adev->txdesc_start; ++ ++ if (IS_ACX111(adev)) { ++ /* ACX111 has a preinitialized Tx buffer! */ ++ /* loop over whole send pool */ ++ /* FIXME: do we have to do the hostmemptr stuff here?? */ ++ for (i = 0; i < TX_CNT; i++) { ++ txdesc->Ctl_8 = DESC_CTL_HOSTOWN; ++ /* reserve two (hdr desc and payload desc) */ ++ txdesc = advance_txdesc(adev, txdesc, 1); ++ } ++ } else { ++ /* ACX100 Tx buffer needs to be initialized by us */ ++ /* clear whole send pool. sizeof is safe here (we are acx100) */ ++ ++ /* ++ * adev->txdesc_start refers to device memory, so we can't write ++ * directly to it. ++ */ ++ clr = (u32) adev->txdesc_start; ++ while (clr < (u32) adev->txdesc_start + (TX_CNT * sizeof(*txdesc))) { ++ write_slavemem32 (adev, clr, 0); ++ clr += 4; ++ } ++ ++ /* loop over whole send pool */ ++ for (i = 0; i < TX_CNT; i++) { ++ log(L_DEBUG, "configure card tx descriptor: 0x%p, " ++ "size: 0x%X\n", txdesc, adev->txdesc_size); ++ ++ /* initialise ctl */ ++ /* ++ * No auto DMA here ++ */ ++ write_slavemem8 (adev, (u32) &(txdesc->Ctl_8), ++ (u8) (DESC_CTL_HOSTOWN | DESC_CTL_FIRSTFRAG)); ++ /* done by memset(0): txdesc->Ctl2_8 = 0; */ ++ ++ /* point to next txdesc */ ++ write_slavemem32 (adev, (u32) &(txdesc->pNextDesc), ++ (u32) cpu_to_le32 ((u8 *) txdesc + adev->txdesc_size)); ++ ++ /* go to the next one */ ++ /* ++ is safe here (we are acx100) */ ++ txdesc++; ++ } ++ /* go back to the last one */ ++ txdesc--; ++ /* and point to the first making it a ring buffer */ ++ write_slavemem32 (adev, (u32) &(txdesc->pNextDesc), ++ (u32) cpu_to_le32 (tx_queue_start)); ++ } ++ FN_EXIT0; ++} ++ ++ ++/*************************************************************** ++** acxmem_create_rx_desc_queue ++*/ ++static void ++acxmem_create_rx_desc_queue(acx_device_t *adev, u32 rx_queue_start) ++{ ++ rxdesc_t *rxdesc; ++ u32 mem_offs; ++ int i; ++ ++ FN_ENTER; ++ ++ /* done by memset: adev->rx_tail = 0; */ ++ ++ /* ACX111 doesn't need any further config: preconfigures itself. ++ * Simply print ring buffer for debugging */ ++ if (IS_ACX111(adev)) { ++ /* rxdesc_start already set here */ ++ ++ adev->rxdesc_start = (rxdesc_t *) rx_queue_start; ++ ++ rxdesc = adev->rxdesc_start; ++ for (i = 0; i < RX_CNT; i++) { ++ log(L_DEBUG, "rx descriptor %d @ 0x%p\n", i, rxdesc); ++ rxdesc = adev->rxdesc_start = (rxdesc_t *) ++ acx2cpu(rxdesc->pNextDesc); ++ } ++ } else { ++ /* we didn't pre-calculate rxdesc_start in case of ACX100 */ ++ /* rxdesc_start should be right AFTER Tx pool */ ++ adev->rxdesc_start = (rxdesc_t *) ++ ((u8 *) adev->txdesc_start + (TX_CNT * sizeof(txdesc_t))); ++ /* NB: sizeof(txdesc_t) above is valid because we know ++ ** we are in if (acx100) block. Beware of cut-n-pasting elsewhere! ++ ** acx111's txdesc is larger! */ ++ ++ mem_offs = (u32) adev->rxdesc_start; ++ while (mem_offs < (u32) adev->rxdesc_start + (RX_CNT * sizeof (*rxdesc))) { ++ write_slavemem32 (adev, mem_offs, 0); ++ mem_offs += 4; ++ } ++ ++ /* loop over whole receive pool */ ++ rxdesc = adev->rxdesc_start; ++ for (i = 0; i < RX_CNT; i++) { ++ log(L_DEBUG, "rx descriptor @ 0x%p\n", rxdesc); ++ /* point to next rxdesc */ ++ write_slavemem32 (adev, (u32) &(rxdesc->pNextDesc), ++ (u32) cpu_to_le32 ((u8 *) rxdesc + sizeof(*rxdesc))); ++ /* go to the next one */ ++ rxdesc++; ++ } ++ /* go to the last one */ ++ rxdesc--; ++ ++ /* and point to the first making it a ring buffer */ ++ write_slavemem32 (adev, (u32) &(rxdesc->pNextDesc), ++ (u32) cpu_to_le32 (rx_queue_start)); ++ } ++ FN_EXIT0; ++} ++ ++ ++/*************************************************************** ++** acxmem_create_desc_queues ++*/ ++void ++acxmem_create_desc_queues(acx_device_t *adev, u32 tx_queue_start, u32 rx_queue_start) ++{ ++ u32 *p; ++ int i; ++ ++ acxmem_create_tx_desc_queue(adev, tx_queue_start); ++ acxmem_create_rx_desc_queue(adev, rx_queue_start); ++ p = (u32 *) adev->acx_queue_indicator; ++ for (i = 0; i < 4; i++) { ++ write_slavemem32 (adev, (u32) p, 0); ++ p++; ++ } ++} ++ ++ ++/*************************************************************** ++** acxmem_s_proc_diag_output ++*/ ++char* ++acxmem_s_proc_diag_output(char *p, acx_device_t *adev) ++{ ++ const char *rtl, *thd, *ttl; ++ txdesc_t *txdesc; ++ u8 Ctl_8; ++ rxdesc_t *rxdesc; ++ int i; ++ u32 tmp; ++ txdesc_t txd; ++ u8 buf[0x200]; ++ int j, k; ++ ++ FN_ENTER; ++ ++#if DUMP_MEM_DURING_DIAG > 0 ++ dump_acxmem (adev, 0, 0x10000); ++ panic ("dump finished"); ++#endif ++ ++ p += sprintf(p, "** Rx buf **\n"); ++ rxdesc = adev->rxdesc_start; ++ if (rxdesc) for (i = 0; i < RX_CNT; i++) { ++ rtl = (i == adev->rx_tail) ? " [tail]" : ""; ++ Ctl_8 = read_slavemem8 (adev, (u32) &(rxdesc->Ctl_8)); ++ if (Ctl_8 & DESC_CTL_HOSTOWN) ++ p += sprintf(p, "%02u (%02x) FULL%s\n", i, Ctl_8, rtl); ++ else ++ p += sprintf(p, "%02u (%02x) empty%s\n", i, Ctl_8, rtl); ++ rxdesc++; ++ } ++ p += sprintf(p, "** Tx buf (free %d, Linux netqueue %s) **\n", adev->tx_free, ++ acx_queue_stopped(adev->ndev) ? "STOPPED" : "running"); ++ ++ p += sprintf(p, "** Tx buf %d blocks total, %d available, free list head %04x\n", ++ adev->acx_txbuf_numblocks, adev->acx_txbuf_blocks_free, adev->acx_txbuf_free); ++ txdesc = adev->txdesc_start; ++ if (txdesc) { ++ for (i = 0; i < TX_CNT; i++) { ++ thd = (i == adev->tx_head) ? " [head]" : ""; ++ ttl = (i == adev->tx_tail) ? " [tail]" : ""; ++ copy_from_slavemem (adev, (u8 *) &txd, (u32) txdesc, sizeof (txd)); ++ Ctl_8 = read_slavemem8 (adev, (u32) &(txdesc->Ctl_8)); ++ if (Ctl_8 & DESC_CTL_ACXDONE) ++ p += sprintf(p, "%02u ready to free (%02X)%s%s", i, Ctl_8, thd, ttl); ++ else if (Ctl_8 & DESC_CTL_HOSTOWN) ++ p += sprintf(p, "%02u available (%02X)%s%s", i, Ctl_8, thd, ttl); ++ else ++ p += sprintf(p, "%02u busy (%02X)%s%s", i, Ctl_8, thd, ttl); ++ tmp = read_slavemem32 (adev, (u32) &(txdesc->AcxMemPtr)); ++ if (tmp) { ++ p += sprintf (p, " %04x", tmp); ++ while ((tmp = read_slavemem32 (adev, (u32) tmp)) != 0x02000000) { ++ tmp <<= 5; ++ p += sprintf (p, " %04x", tmp); ++ } ++ } ++ p += sprintf (p, "\n"); ++ p += sprintf (p, " %04x: %04x %04x %04x %04x %04x %04x %04x %04x %04x %04x %02x %02x %02x %02x\n" ++ "%02x %02x %02x %02x %04x\n", ++ (u32) txdesc, ++ txd.pNextDesc.v, txd.HostMemPtr.v, txd.AcxMemPtr.v, txd.tx_time, ++ txd.total_length, txd.Reserved, ++ txd.dummy[0], txd.dummy[1], txd.dummy[2], txd.dummy[3], ++ txd.Ctl_8, txd.Ctl2_8, txd.error, txd.ack_failures, ++ txd.u.rts.rts_failures, txd.u.rts.rts_ok, txd.u.r1.rate, txd.u.r1.queue_ctrl, ++ txd.queue_info ++ ); ++ if (txd.AcxMemPtr.v) { ++ copy_from_slavemem (adev, buf, txd.AcxMemPtr.v, sizeof (buf)); ++ for (j = 0; (j < txd.total_length) && (j<(sizeof(buf)-4)); j+=16) { ++ p += sprintf (p, " "); ++ for (k = 0; (k < 16) && (j+k < txd.total_length); k++) { ++ p += sprintf (p, " %02x", buf[j+k+4]); ++ } ++ p += sprintf (p, "\n"); ++ } ++ } ++ txdesc = advance_txdesc(adev, txdesc, 1); ++ } ++ } ++ ++ p += sprintf(p, ++ "\n" ++ "** Generic slave data **\n" ++ "irq_mask 0x%04x irq_status 0x%04x irq on acx 0x%04x\n" ++ "txbuf_start 0x%p, txbuf_area_size %u\n" ++ "txdesc_size %u, txdesc_start 0x%p\n" ++ "txhostdesc_start 0x%p, txhostdesc_area_size %u\n" ++ "txbuf start 0x%04x, txbuf size %d\n" ++ "rxdesc_start 0x%p\n" ++ "rxhostdesc_start 0x%p, rxhostdesc_area_size %u\n" ++ "rxbuf_start 0x%p, rxbuf_area_size %u\n", ++ adev->irq_mask, adev->irq_status, read_reg32(adev, IO_ACX_IRQ_STATUS_NON_DES), ++ adev->txbuf_start, adev->txbuf_area_size, ++ adev->txdesc_size, adev->txdesc_start, ++ adev->txhostdesc_start, adev->txhostdesc_area_size, ++ adev->acx_txbuf_start, adev->acx_txbuf_numblocks * adev->memblocksize, ++ adev->rxdesc_start, ++ adev->rxhostdesc_start, adev->rxhostdesc_area_size, ++ adev->rxbuf_start, adev->rxbuf_area_size); ++ FN_EXIT0; ++ return p; ++} ++ ++ ++/*********************************************************************** ++*/ ++int ++acxmem_proc_eeprom_output(char *buf, acx_device_t *adev) ++{ ++ char *p = buf; ++ int i; ++ ++ FN_ENTER; ++ ++ for (i = 0; i < 0x400; i++) { ++ acxmem_read_eeprom_byte(adev, i, p++); ++ } ++ ++ FN_EXIT1(p - buf); ++ return p - buf; ++} ++ ++ ++/*********************************************************************** ++*/ ++void ++acxmem_set_interrupt_mask(acx_device_t *adev) ++{ ++ if (IS_ACX111(adev)) { ++ adev->irq_mask = (u16) ~(0 ++ | HOST_INT_RX_DATA ++ | HOST_INT_TX_COMPLETE ++ /* | HOST_INT_TX_XFER */ ++ /* | HOST_INT_RX_COMPLETE */ ++ /* | HOST_INT_DTIM */ ++ /* | HOST_INT_BEACON */ ++ /* | HOST_INT_TIMER */ ++ /* | HOST_INT_KEY_NOT_FOUND */ ++ | HOST_INT_IV_ICV_FAILURE ++ | HOST_INT_CMD_COMPLETE ++ | HOST_INT_INFO ++ | HOST_INT_OVERFLOW ++ /* | HOST_INT_PROCESS_ERROR */ ++ | HOST_INT_SCAN_COMPLETE ++ | HOST_INT_FCS_THRESHOLD ++ | HOST_INT_UNKNOWN ++ ); ++ /* Or else acx100 won't signal cmd completion, right? */ ++ adev->irq_mask_off = (u16)~( HOST_INT_CMD_COMPLETE ); /* 0xfdff */ ++ } else { ++ adev->irq_mask = (u16) ~(0 ++ | HOST_INT_RX_DATA ++ | HOST_INT_TX_COMPLETE ++ /* | HOST_INT_TX_XFER */ ++ /* | HOST_INT_RX_COMPLETE */ ++ /* | HOST_INT_DTIM */ ++ /* | HOST_INT_BEACON */ ++ /* | HOST_INT_TIMER */ ++ /* | HOST_INT_KEY_NOT_FOUND */ ++ /* | HOST_INT_IV_ICV_FAILURE */ ++ | HOST_INT_CMD_COMPLETE ++ | HOST_INT_INFO ++ /* | HOST_INT_OVERFLOW */ ++ /* | HOST_INT_PROCESS_ERROR */ ++ | HOST_INT_SCAN_COMPLETE ++ /* | HOST_INT_FCS_THRESHOLD */ ++ /* | HOST_INT_BEACON_MISSED */ ++ ); ++ adev->irq_mask_off = (u16)~( HOST_INT_UNKNOWN ); /* 0x7fff */ ++ } ++} ++ ++ ++/*********************************************************************** ++*/ ++int ++acx100mem_s_set_tx_level(acx_device_t *adev, u8 level_dbm) ++{ ++ struct acx111_ie_tx_level tx_level; ++ ++ /* since it can be assumed that at least the Maxim radio has a ++ * maximum power output of 20dBm and since it also can be ++ * assumed that these values drive the DAC responsible for ++ * setting the linear Tx level, I'd guess that these values ++ * should be the corresponding linear values for a dBm value, ++ * in other words: calculate the values from that formula: ++ * Y [dBm] = 10 * log (X [mW]) ++ * then scale the 0..63 value range onto the 1..100mW range (0..20 dBm) ++ * and you're done... ++ * Hopefully that's ok, but you never know if we're actually ++ * right... (especially since Windows XP doesn't seem to show ++ * actual Tx dBm values :-P) */ ++ ++ /* NOTE: on Maxim, value 30 IS 30mW, and value 10 IS 10mW - so the ++ * values are EXACTLY mW!!! Not sure about RFMD and others, ++ * though... */ ++ static const u8 dbm2val_maxim[21] = { ++ 63, 63, 63, 62, ++ 61, 61, 60, 60, ++ 59, 58, 57, 55, ++ 53, 50, 47, 43, ++ 38, 31, 23, 13, ++ 0 ++ }; ++ static const u8 dbm2val_rfmd[21] = { ++ 0, 0, 0, 1, ++ 2, 2, 3, 3, ++ 4, 5, 6, 8, ++ 10, 13, 16, 20, ++ 25, 32, 41, 50, ++ 63 ++ }; ++ const u8 *table; ++ ++ switch (adev->radio_type) { ++ case RADIO_MAXIM_0D: ++ table = &dbm2val_maxim[0]; ++ break; ++ case RADIO_RFMD_11: ++ case RADIO_RALINK_15: ++ table = &dbm2val_rfmd[0]; ++ break; ++ default: ++ printk("%s: unknown/unsupported radio type, " ++ "cannot modify tx power level yet!\n", ++ adev->ndev->name); ++ return NOT_OK; ++ } ++ /* ++ * The hx4700 EEPROM, at least, only supports 1 power setting. The configure ++ * routine matches the PA bias with the gain, so just use its default value. ++ * The values are: 0x2b for the gain and 0x03 for the PA bias. The firmware ++ * writes the gain level to the Tx gain control DAC and the PA bias to the Maxim ++ * radio's PA bias register. The firmware limits itself to 0 - 64 when writing to the ++ * gain control DAC. ++ * ++ * Physically between the ACX and the radio, higher Tx gain control DAC values result ++ * in less power output; 0 volts to the Maxim radio results in the highest output power ++ * level, which I'm assuming matches up with 0 in the Tx Gain DAC register. ++ * ++ * Although there is only the 1 power setting, one of the radio firmware functions adjusts ++ * the transmit power level up and down. That function is called by the ACX FIQ handler ++ * under certain conditions. ++ */ ++ tx_level.level = 1; ++ //return acx_s_configure(adev, &tx_level, ACX1xx_IE_DOT11_TX_POWER_LEVEL); ++ ++ printk("%s: changing radio power level to %u dBm (%u)\n", ++ adev->ndev->name, level_dbm, table[level_dbm]); ++ acxmem_s_write_phy_reg(adev, 0x11, table[level_dbm]); ++ ++ return 0; ++} ++ ++ ++static struct platform_driver ++acxmem_drv_id = { ++ .driver = { ++ .name = "acx-mem", ++ }, ++ .probe = acxmem_e_probe, ++ .remove = __devexit_p(acxmem_e_remove), ++#ifdef CONFIG_PM ++ .suspend = acxmem_e_suspend, ++ .resume = acxmem_e_resume ++#endif /* CONFIG_PM */ ++}; ++ ++ ++/*********************************************************************** ++** acxmem_e_init_module ++** ++** Module initialization routine, called once at module load time ++*/ ++int __init ++acxmem_e_init_module(void) ++{ ++ int res; ++ ++ FN_ENTER; ++ ++#if (ACX_IO_WIDTH==32) ++ printk("acx: compiled to use 32bit I/O access. " ++ "I/O timing issues might occur, such as " ++ "non-working firmware upload. Report them\n"); ++#else ++ printk("acx: compiled to use 16bit I/O access only " ++ "(compatibility mode)\n"); ++#endif ++ ++#ifdef __LITTLE_ENDIAN ++#define ENDIANNESS_STRING "running on a little-endian CPU\n" ++#else ++#define ENDIANNESS_STRING "running on a BIG-ENDIAN CPU\n" ++#endif ++ log(L_INIT, ++ ENDIANNESS_STRING ++ "PCI module " ACX_RELEASE " initialized, " ++ "waiting for cards to probe...\n" ++ ); ++ ++ res = platform_driver_register (&acxmem_drv_id); ++ FN_EXIT1(res); ++ return res; ++} ++ ++ ++/*********************************************************************** ++** acxmem_e_cleanup_module ++** ++** Called at module unload time. This is our last chance to ++** clean up after ourselves. ++*/ ++void __exit ++acxmem_e_cleanup_module(void) ++{ ++ FN_ENTER; ++ ++ printk ("cleanup_module\n"); ++ platform_driver_unregister( &acxmem_drv_id ); ++ ++ FN_EXIT0; ++} ++ ++void acxmem_e_release(struct device *dev) { ++} ++ ++MODULE_AUTHOR( "Todd Blumer <todd@sdgsystems.com>" ); ++MODULE_DESCRIPTION( "ACX Slave Memory Driver" ); ++MODULE_LICENSE( "GPL" ); ++ +Index: linux-2.6.23/drivers/net/wireless/acx/pci.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.23/drivers/net/wireless/acx/pci.c 2008-01-20 21:13:40.000000000 +0000 +@@ -0,0 +1,4234 @@ ++/*********************************************************************** ++** Copyright (C) 2003 ACX100 Open Source Project ++** ++** The contents of this file are subject to the Mozilla Public ++** License Version 1.1 (the "License"); you may not use this file ++** except in compliance with the License. You may obtain a copy of ++** the License at http://www.mozilla.org/MPL/ ++** ++** Software distributed under the License is distributed on an "AS ++** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or ++** implied. See the License for the specific language governing ++** rights and limitations under the License. ++** ++** Alternatively, the contents of this file may be used under the ++** terms of the GNU Public License version 2 (the "GPL"), in which ++** case the provisions of the GPL are applicable instead of the ++** above. If you wish to allow the use of your version of this file ++** only under the terms of the GPL and not to allow others to use ++** your version of this file under the MPL, indicate your decision ++** by deleting the provisions above and replace them with the notice ++** and other provisions required by the GPL. If you do not delete ++** the provisions above, a recipient may use your version of this ++** file under either the MPL or the GPL. ++** --------------------------------------------------------------------- ++** Inquiries regarding the ACX100 Open Source Project can be ++** made directly to: ++** ++** acx100-users@lists.sf.net ++** http://acx100.sf.net ++** --------------------------------------------------------------------- ++*/ ++#define ACX_PCI 1 ++ ++#include <linux/version.h> ++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) ++#include <linux/config.h> ++#endif ++ ++/* Linux 2.6.18+ uses <linux/utsrelease.h> */ ++#ifndef UTS_RELEASE ++#include <linux/utsrelease.h> ++#endif ++ ++#include <linux/compiler.h> /* required for Lx 2.6.8 ?? */ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/sched.h> ++#include <linux/types.h> ++#include <linux/skbuff.h> ++#include <linux/slab.h> ++#include <linux/if_arp.h> ++#include <linux/rtnetlink.h> ++#include <linux/wireless.h> ++#include <net/iw_handler.h> ++#include <linux/netdevice.h> ++#include <linux/ioport.h> ++#include <linux/pci.h> ++#include <linux/pm.h> ++#include <linux/vmalloc.h> ++#include <linux/dma-mapping.h> ++ ++#include "acx.h" ++ ++ ++/*********************************************************************** ++*/ ++#define PCI_TYPE (PCI_USES_MEM | PCI_ADDR0 | PCI_NO_ACPI_WAKE) ++#define PCI_ACX100_REGION1 0x01 ++#define PCI_ACX100_REGION1_SIZE 0x1000 /* Memory size - 4K bytes */ ++#define PCI_ACX100_REGION2 0x02 ++#define PCI_ACX100_REGION2_SIZE 0x10000 /* Memory size - 64K bytes */ ++ ++#define PCI_ACX111_REGION1 0x00 ++#define PCI_ACX111_REGION1_SIZE 0x2000 /* Memory size - 8K bytes */ ++#define PCI_ACX111_REGION2 0x01 ++#define PCI_ACX111_REGION2_SIZE 0x20000 /* Memory size - 128K bytes */ ++ ++/* Texas Instruments Vendor ID */ ++#define PCI_VENDOR_ID_TI 0x104c ++ ++/* ACX100 22Mb/s WLAN controller */ ++#define PCI_DEVICE_ID_TI_TNETW1100A 0x8400 ++#define PCI_DEVICE_ID_TI_TNETW1100B 0x8401 ++ ++/* ACX111 54Mb/s WLAN controller */ ++#define PCI_DEVICE_ID_TI_TNETW1130 0x9066 ++ ++/* PCI Class & Sub-Class code, Network-'Other controller' */ ++#define PCI_CLASS_NETWORK_OTHERS 0x0280 ++ ++#define CARD_EEPROM_ID_SIZE 6 ++ ++#ifndef PCI_D0 ++/* From include/linux/pci.h */ ++#define PCI_D0 0 ++#define PCI_D1 1 ++#define PCI_D2 2 ++#define PCI_D3hot 3 ++#define PCI_D3cold 4 ++#define PCI_UNKNOWN 5 ++#define PCI_POWER_ERROR -1 ++#endif ++ ++ ++/*********************************************************************** ++*/ ++static void acxpci_i_tx_timeout(struct net_device *ndev); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) ++static irqreturn_t acxpci_i_interrupt(int irq, void *dev_id); ++#else ++static irqreturn_t acxpci_i_interrupt(int irq, void *dev_id, struct pt_regs *regs); ++#endif ++static void acxpci_i_set_multicast_list(struct net_device *ndev); ++ ++static int acxpci_e_open(struct net_device *ndev); ++static int acxpci_e_close(struct net_device *ndev); ++static void acxpci_s_up(struct net_device *ndev); ++static void acxpci_s_down(struct net_device *ndev); ++ ++ ++/*********************************************************************** ++** Register access ++*/ ++ ++/* Pick one */ ++/* #define INLINE_IO static */ ++#define INLINE_IO static inline ++ ++INLINE_IO u32 ++read_reg32(acx_device_t *adev, unsigned int offset) ++{ ++#if ACX_IO_WIDTH == 32 ++ return readl((u8 *)adev->iobase + adev->io[offset]); ++#else ++ return readw((u8 *)adev->iobase + adev->io[offset]) ++ + (readw((u8 *)adev->iobase + adev->io[offset] + 2) << 16); ++#endif ++} ++ ++INLINE_IO u16 ++read_reg16(acx_device_t *adev, unsigned int offset) ++{ ++ return readw((u8 *)adev->iobase + adev->io[offset]); ++} ++ ++INLINE_IO u8 ++read_reg8(acx_device_t *adev, unsigned int offset) ++{ ++ return readb((u8 *)adev->iobase + adev->io[offset]); ++} ++ ++INLINE_IO void ++write_reg32(acx_device_t *adev, unsigned int offset, u32 val) ++{ ++#if ACX_IO_WIDTH == 32 ++ writel(val, (u8 *)adev->iobase + adev->io[offset]); ++#else ++ writew(val & 0xffff, (u8 *)adev->iobase + adev->io[offset]); ++ writew(val >> 16, (u8 *)adev->iobase + adev->io[offset] + 2); ++#endif ++} ++ ++INLINE_IO void ++write_reg16(acx_device_t *adev, unsigned int offset, u16 val) ++{ ++ writew(val, (u8 *)adev->iobase + adev->io[offset]); ++} ++ ++INLINE_IO void ++write_reg8(acx_device_t *adev, unsigned int offset, u8 val) ++{ ++ writeb(val, (u8 *)adev->iobase + adev->io[offset]); ++} ++ ++/* Handle PCI posting properly: ++ * Make sure that writes reach the adapter in case they require to be executed ++ * *before* the next write, by reading a random (and safely accessible) register. ++ * This call has to be made if there is no read following (which would flush the data ++ * to the adapter), yet the written data has to reach the adapter immediately. */ ++INLINE_IO void ++write_flush(acx_device_t *adev) ++{ ++ /* readb(adev->iobase + adev->io[IO_ACX_INFO_MAILBOX_OFFS]); */ ++ /* faster version (accesses the first register, IO_ACX_SOFT_RESET, ++ * which should also be safe): */ ++ readb(adev->iobase); ++} ++ ++INLINE_IO int ++adev_present(acx_device_t *adev) ++{ ++ /* fast version (accesses the first register, IO_ACX_SOFT_RESET, ++ * which should be safe): */ ++ return readl(adev->iobase) != 0xffffffff; ++} ++ ++ ++/*********************************************************************** ++*/ ++static inline txdesc_t* ++get_txdesc(acx_device_t *adev, int index) ++{ ++ return (txdesc_t*) (((u8*)adev->txdesc_start) + index * adev->txdesc_size); ++} ++ ++static inline txdesc_t* ++advance_txdesc(acx_device_t *adev, txdesc_t* txdesc, int inc) ++{ ++ return (txdesc_t*) (((u8*)txdesc) + inc * adev->txdesc_size); ++} ++ ++static txhostdesc_t* ++get_txhostdesc(acx_device_t *adev, txdesc_t* txdesc) ++{ ++ int index = (u8*)txdesc - (u8*)adev->txdesc_start; ++ if (unlikely(ACX_DEBUG && (index % adev->txdesc_size))) { ++ printk("bad txdesc ptr %p\n", txdesc); ++ return NULL; ++ } ++ index /= adev->txdesc_size; ++ if (unlikely(ACX_DEBUG && (index >= TX_CNT))) { ++ printk("bad txdesc ptr %p\n", txdesc); ++ return NULL; ++ } ++ return &adev->txhostdesc_start[index*2]; ++} ++ ++static inline client_t* ++get_txc(acx_device_t *adev, txdesc_t* txdesc) ++{ ++ int index = (u8*)txdesc - (u8*)adev->txdesc_start; ++ if (unlikely(ACX_DEBUG && (index % adev->txdesc_size))) { ++ printk("bad txdesc ptr %p\n", txdesc); ++ return NULL; ++ } ++ index /= adev->txdesc_size; ++ if (unlikely(ACX_DEBUG && (index >= TX_CNT))) { ++ printk("bad txdesc ptr %p\n", txdesc); ++ return NULL; ++ } ++ return adev->txc[index]; ++} ++ ++static inline u16 ++get_txr(acx_device_t *adev, txdesc_t* txdesc) ++{ ++ int index = (u8*)txdesc - (u8*)adev->txdesc_start; ++ index /= adev->txdesc_size; ++ return adev->txr[index]; ++} ++ ++static inline void ++put_txcr(acx_device_t *adev, txdesc_t* txdesc, client_t* c, u16 r111) ++{ ++ int index = (u8*)txdesc - (u8*)adev->txdesc_start; ++ if (unlikely(ACX_DEBUG && (index % adev->txdesc_size))) { ++ printk("bad txdesc ptr %p\n", txdesc); ++ return; ++ } ++ index /= adev->txdesc_size; ++ if (unlikely(ACX_DEBUG && (index >= TX_CNT))) { ++ printk("bad txdesc ptr %p\n", txdesc); ++ return; ++ } ++ adev->txc[index] = c; ++ adev->txr[index] = r111; ++} ++ ++ ++/*********************************************************************** ++** EEPROM and PHY read/write helpers ++*/ ++/*********************************************************************** ++** acxpci_read_eeprom_byte ++** ++** Function called to read an octet in the EEPROM. ++** ++** This function is used by acxpci_e_probe to check if the ++** connected card is a legal one or not. ++** ++** Arguments: ++** adev ptr to acx_device structure ++** addr address to read in the EEPROM ++** charbuf ptr to a char. This is where the read octet ++** will be stored ++*/ ++int ++acxpci_read_eeprom_byte(acx_device_t *adev, u32 addr, u8 *charbuf) ++{ ++ int result; ++ int count; ++ ++ write_reg32(adev, IO_ACX_EEPROM_CFG, 0); ++ write_reg32(adev, IO_ACX_EEPROM_ADDR, addr); ++ write_flush(adev); ++ write_reg32(adev, IO_ACX_EEPROM_CTL, 2); ++ ++ count = 0xffff; ++ while (read_reg16(adev, IO_ACX_EEPROM_CTL)) { ++ /* scheduling away instead of CPU burning loop ++ * doesn't seem to work here at all: ++ * awful delay, sometimes also failure. ++ * Doesn't matter anyway (only small delay). */ ++ if (unlikely(!--count)) { ++ printk("%s: timeout waiting for EEPROM read\n", ++ adev->ndev->name); ++ result = NOT_OK; ++ goto fail; ++ } ++ cpu_relax(); ++ } ++ ++ *charbuf = read_reg8(adev, IO_ACX_EEPROM_DATA); ++ log(L_DEBUG, "EEPROM at 0x%04X = 0x%02X\n", addr, *charbuf); ++ result = OK; ++ ++fail: ++ return result; ++} ++ ++ ++/*********************************************************************** ++** We don't lock hw accesses here since we never r/w eeprom in IRQ ++** Note: this function sleeps only because of GFP_KERNEL alloc ++*/ ++#ifdef UNUSED ++int ++acxpci_s_write_eeprom(acx_device_t *adev, u32 addr, u32 len, const u8 *charbuf) ++{ ++ u8 *data_verify = NULL; ++ unsigned long flags; ++ int count, i; ++ int result = NOT_OK; ++ u16 gpio_orig; ++ ++ printk("acx: WARNING! I would write to EEPROM now. " ++ "Since I really DON'T want to unless you know " ++ "what you're doing (THIS CODE WILL PROBABLY " ++ "NOT WORK YET!), I will abort that now. And " ++ "definitely make sure to make a " ++ "/proc/driver/acx_wlan0_eeprom backup copy first!!! " ++ "(the EEPROM content includes the PCI config header!! " ++ "If you kill important stuff, then you WILL " ++ "get in trouble and people DID get in trouble already)\n"); ++ return OK; ++ ++ FN_ENTER; ++ ++ data_verify = kmalloc(len, GFP_KERNEL); ++ if (!data_verify) { ++ goto end; ++ } ++ ++ /* first we need to enable the OE (EEPROM Output Enable) GPIO line ++ * to be able to write to the EEPROM. ++ * NOTE: an EEPROM writing success has been reported, ++ * but you probably have to modify GPIO_OUT, too, ++ * and you probably need to activate a different GPIO ++ * line instead! */ ++ gpio_orig = read_reg16(adev, IO_ACX_GPIO_OE); ++ write_reg16(adev, IO_ACX_GPIO_OE, gpio_orig & ~1); ++ write_flush(adev); ++ ++ /* ok, now start writing the data out */ ++ for (i = 0; i < len; i++) { ++ write_reg32(adev, IO_ACX_EEPROM_CFG, 0); ++ write_reg32(adev, IO_ACX_EEPROM_ADDR, addr + i); ++ write_reg32(adev, IO_ACX_EEPROM_DATA, *(charbuf + i)); ++ write_flush(adev); ++ write_reg32(adev, IO_ACX_EEPROM_CTL, 1); ++ ++ count = 0xffff; ++ while (read_reg16(adev, IO_ACX_EEPROM_CTL)) { ++ if (unlikely(!--count)) { ++ printk("WARNING, DANGER!!! " ++ "Timeout waiting for EEPROM write\n"); ++ goto end; ++ } ++ cpu_relax(); ++ } ++ } ++ ++ /* disable EEPROM writing */ ++ write_reg16(adev, IO_ACX_GPIO_OE, gpio_orig); ++ write_flush(adev); ++ ++ /* now start a verification run */ ++ for (i = 0; i < len; i++) { ++ write_reg32(adev, IO_ACX_EEPROM_CFG, 0); ++ write_reg32(adev, IO_ACX_EEPROM_ADDR, addr + i); ++ write_flush(adev); ++ write_reg32(adev, IO_ACX_EEPROM_CTL, 2); ++ ++ count = 0xffff; ++ while (read_reg16(adev, IO_ACX_EEPROM_CTL)) { ++ if (unlikely(!--count)) { ++ printk("timeout waiting for EEPROM read\n"); ++ goto end; ++ } ++ cpu_relax(); ++ } ++ ++ data_verify[i] = read_reg16(adev, IO_ACX_EEPROM_DATA); ++ } ++ ++ if (0 == memcmp(charbuf, data_verify, len)) ++ result = OK; /* read data matches, success */ ++ ++end: ++ kfree(data_verify); ++ FN_EXIT1(result); ++ return result; ++} ++#endif /* UNUSED */ ++ ++ ++/*********************************************************************** ++** acxpci_s_read_phy_reg ++** ++** Messing with rx/tx disabling and enabling here ++** (write_reg32(adev, IO_ACX_ENABLE, 0b000000xx)) kills traffic ++*/ ++int ++acxpci_s_read_phy_reg(acx_device_t *adev, u32 reg, u8 *charbuf) ++{ ++ int result = NOT_OK; ++ int count; ++ ++ FN_ENTER; ++ ++ write_reg32(adev, IO_ACX_PHY_ADDR, reg); ++ write_flush(adev); ++ write_reg32(adev, IO_ACX_PHY_CTL, 2); ++ ++ count = 0xffff; ++ while (read_reg32(adev, IO_ACX_PHY_CTL)) { ++ /* scheduling away instead of CPU burning loop ++ * doesn't seem to work here at all: ++ * awful delay, sometimes also failure. ++ * Doesn't matter anyway (only small delay). */ ++ if (unlikely(!--count)) { ++ printk("%s: timeout waiting for phy read\n", ++ adev->ndev->name); ++ *charbuf = 0; ++ goto fail; ++ } ++ cpu_relax(); ++ } ++ ++ log(L_DEBUG, "count was %u\n", count); ++ *charbuf = read_reg8(adev, IO_ACX_PHY_DATA); ++ ++ log(L_DEBUG, "radio PHY at 0x%04X = 0x%02X\n", *charbuf, reg); ++ result = OK; ++ goto fail; /* silence compiler warning */ ++fail: ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++*/ ++int ++acxpci_s_write_phy_reg(acx_device_t *adev, u32 reg, u8 value) ++{ ++ FN_ENTER; ++ ++ /* mprusko said that 32bit accesses result in distorted sensitivity ++ * on his card. Unconfirmed, looks like it's not true (most likely since we ++ * now properly flush writes). */ ++ write_reg32(adev, IO_ACX_PHY_DATA, value); ++ write_reg32(adev, IO_ACX_PHY_ADDR, reg); ++ write_flush(adev); ++ write_reg32(adev, IO_ACX_PHY_CTL, 1); ++ write_flush(adev); ++ log(L_DEBUG, "radio PHY write 0x%02X at 0x%04X\n", value, reg); ++ ++ FN_EXIT1(OK); ++ return OK; ++} ++ ++ ++#define NO_AUTO_INCREMENT 1 ++ ++/*********************************************************************** ++** acxpci_s_write_fw ++** ++** Write the firmware image into the card. ++** ++** Arguments: ++** adev wlan device structure ++** fw_image firmware image. ++** ++** Returns: ++** 1 firmware image corrupted ++** 0 success ++*/ ++static int ++acxpci_s_write_fw(acx_device_t *adev, const firmware_image_t *fw_image, u32 offset) ++{ ++ int len, size; ++ u32 sum, v32; ++ /* we skip the first four bytes which contain the control sum */ ++ const u8 *p = (u8*)fw_image + 4; ++ ++ /* start the image checksum by adding the image size value */ ++ sum = p[0]+p[1]+p[2]+p[3]; ++ p += 4; ++ ++ write_reg32(adev, IO_ACX_SLV_END_CTL, 0); ++ ++#if NO_AUTO_INCREMENT ++ write_reg32(adev, IO_ACX_SLV_MEM_CTL, 0); /* use basic mode */ ++#else ++ write_reg32(adev, IO_ACX_SLV_MEM_CTL, 1); /* use autoincrement mode */ ++ write_reg32(adev, IO_ACX_SLV_MEM_ADDR, offset); /* configure start address */ ++ write_flush(adev); ++#endif ++ ++ len = 0; ++ size = le32_to_cpu(fw_image->size) & (~3); ++ ++ while (likely(len < size)) { ++ v32 = be32_to_cpu(*(u32*)p); ++ sum += p[0]+p[1]+p[2]+p[3]; ++ p += 4; ++ len += 4; ++ ++#if NO_AUTO_INCREMENT ++ write_reg32(adev, IO_ACX_SLV_MEM_ADDR, offset + len - 4); ++ write_flush(adev); ++#endif ++ write_reg32(adev, IO_ACX_SLV_MEM_DATA, v32); ++ } ++ ++ log(L_DEBUG, "firmware written, size:%d sum1:%x sum2:%x\n", ++ size, sum, le32_to_cpu(fw_image->chksum)); ++ ++ /* compare our checksum with the stored image checksum */ ++ return (sum != le32_to_cpu(fw_image->chksum)); ++} ++ ++ ++/*********************************************************************** ++** acxpci_s_validate_fw ++** ++** Compare the firmware image given with ++** the firmware image written into the card. ++** ++** Arguments: ++** adev wlan device structure ++** fw_image firmware image. ++** ++** Returns: ++** NOT_OK firmware image corrupted or not correctly written ++** OK success ++*/ ++static int ++acxpci_s_validate_fw(acx_device_t *adev, const firmware_image_t *fw_image, ++ u32 offset) ++{ ++ u32 sum, v32, w32; ++ int len, size; ++ int result = OK; ++ /* we skip the first four bytes which contain the control sum */ ++ const u8 *p = (u8*)fw_image + 4; ++ ++ /* start the image checksum by adding the image size value */ ++ sum = p[0]+p[1]+p[2]+p[3]; ++ p += 4; ++ ++ write_reg32(adev, IO_ACX_SLV_END_CTL, 0); ++ ++#if NO_AUTO_INCREMENT ++ write_reg32(adev, IO_ACX_SLV_MEM_CTL, 0); /* use basic mode */ ++#else ++ write_reg32(adev, IO_ACX_SLV_MEM_CTL, 1); /* use autoincrement mode */ ++ write_reg32(adev, IO_ACX_SLV_MEM_ADDR, offset); /* configure start address */ ++#endif ++ ++ len = 0; ++ size = le32_to_cpu(fw_image->size) & (~3); ++ ++ while (likely(len < size)) { ++ v32 = be32_to_cpu(*(u32*)p); ++ p += 4; ++ len += 4; ++ ++#if NO_AUTO_INCREMENT ++ write_reg32(adev, IO_ACX_SLV_MEM_ADDR, offset + len - 4); ++#endif ++ w32 = read_reg32(adev, IO_ACX_SLV_MEM_DATA); ++ ++ if (unlikely(w32 != v32)) { ++ printk("acx: FATAL: firmware upload: " ++ "data parts at offset %d don't match (0x%08X vs. 0x%08X)! " ++ "I/O timing issues or defective memory, with DWL-xx0+? " ++ "ACX_IO_WIDTH=16 may help. Please report\n", ++ len, v32, w32); ++ result = NOT_OK; ++ break; ++ } ++ ++ sum += (u8)w32 + (u8)(w32>>8) + (u8)(w32>>16) + (u8)(w32>>24); ++ } ++ ++ /* sum control verification */ ++ if (result != NOT_OK) { ++ if (sum != le32_to_cpu(fw_image->chksum)) { ++ printk("acx: FATAL: firmware upload: " ++ "checksums don't match!\n"); ++ result = NOT_OK; ++ } ++ } ++ ++ return result; ++} ++ ++ ++/*********************************************************************** ++** acxpci_s_upload_fw ++** ++** Called from acx_reset_dev ++*/ ++static int ++acxpci_s_upload_fw(acx_device_t *adev) ++{ ++ firmware_image_t *fw_image = NULL; ++ int res = NOT_OK; ++ int try; ++ u32 file_size; ++ char filename[sizeof("tiacx1NNcNN")]; ++ ++ FN_ENTER; ++ ++ /* print exact chipset and radio ID to make sure people really get a clue on which files exactly they are supposed to provide, ++ * since firmware loading is the biggest enduser PITA with these chipsets. ++ * Not printing radio ID in 0xHEX in order to not confuse them into wrong file naming */ ++ printk( "acx: need to load firmware for acx1%02d chipset with radio ID %02x, please provide via firmware hotplug:\n" ++ "acx: either one file only (<c>ombined firmware image file, radio-specific) or two files (radio-less base image file *plus* separate <r>adio-specific extension file)\n", ++ IS_ACX111(adev)*11, adev->radio_type); ++ ++ /* Try combined, then main image */ ++ adev->need_radio_fw = 0; ++ snprintf(filename, sizeof(filename), "tiacx1%02dc%02X", ++ IS_ACX111(adev)*11, adev->radio_type); ++ ++ fw_image = acx_s_read_fw(&adev->pdev->dev, filename, &file_size); ++ if (!fw_image) { ++ adev->need_radio_fw = 1; ++ filename[sizeof("tiacx1NN")-1] = '\0'; ++ fw_image = acx_s_read_fw(&adev->pdev->dev, filename, &file_size); ++ if (!fw_image) { ++ FN_EXIT1(NOT_OK); ++ return NOT_OK; ++ } ++ } ++ ++ for (try = 1; try <= 5; try++) { ++ res = acxpci_s_write_fw(adev, fw_image, 0); ++ log(L_DEBUG|L_INIT, "acx_write_fw (main/combined): %d\n", res); ++ if (OK == res) { ++ res = acxpci_s_validate_fw(adev, fw_image, 0); ++ log(L_DEBUG|L_INIT, "acx_validate_fw " ++ "(main/combined): %d\n", res); ++ } ++ ++ if (OK == res) { ++ SET_BIT(adev->dev_state_mask, ACX_STATE_FW_LOADED); ++ break; ++ } ++ printk("acx: firmware upload attempt #%d FAILED, " ++ "retrying...\n", try); ++ acx_s_msleep(1000); /* better wait for a while... */ ++ } ++ ++ vfree(fw_image); ++ ++ FN_EXIT1(res); ++ return res; ++} ++ ++ ++/*********************************************************************** ++** acxpci_s_upload_radio ++** ++** Uploads the appropriate radio module firmware into the card. ++*/ ++int ++acxpci_s_upload_radio(acx_device_t *adev) ++{ ++ acx_ie_memmap_t mm; ++ firmware_image_t *radio_image; ++ acx_cmd_radioinit_t radioinit; ++ int res = NOT_OK; ++ int try; ++ u32 offset; ++ u32 size; ++ char filename[sizeof("tiacx1NNrNN")]; ++ ++ if (!adev->need_radio_fw) return OK; ++ ++ FN_ENTER; ++ ++ acx_s_interrogate(adev, &mm, ACX1xx_IE_MEMORY_MAP); ++ offset = le32_to_cpu(mm.CodeEnd); ++ ++ snprintf(filename, sizeof(filename), "tiacx1%02dr%02X", ++ IS_ACX111(adev)*11, ++ adev->radio_type); ++ radio_image = acx_s_read_fw(&adev->pdev->dev, filename, &size); ++ if (!radio_image) { ++ printk("acx: can't load radio module '%s'\n", filename); ++ goto fail; ++ } ++ ++ acx_s_issue_cmd(adev, ACX1xx_CMD_SLEEP, NULL, 0); ++ ++ for (try = 1; try <= 5; try++) { ++ res = acxpci_s_write_fw(adev, radio_image, offset); ++ log(L_DEBUG|L_INIT, "acx_write_fw (radio): %d\n", res); ++ if (OK == res) { ++ res = acxpci_s_validate_fw(adev, radio_image, offset); ++ log(L_DEBUG|L_INIT, "acx_validate_fw (radio): %d\n", res); ++ } ++ ++ if (OK == res) ++ break; ++ printk("acx: radio firmware upload attempt #%d FAILED, " ++ "retrying...\n", try); ++ acx_s_msleep(1000); /* better wait for a while... */ ++ } ++ ++ acx_s_issue_cmd(adev, ACX1xx_CMD_WAKE, NULL, 0); ++ radioinit.offset = cpu_to_le32(offset); ++ /* no endian conversion needed, remains in card CPU area: */ ++ radioinit.len = radio_image->size; ++ ++ vfree(radio_image); ++ ++ if (OK != res) ++ goto fail; ++ ++ /* will take a moment so let's have a big timeout */ ++ acx_s_issue_cmd_timeo(adev, ACX1xx_CMD_RADIOINIT, ++ &radioinit, sizeof(radioinit), CMD_TIMEOUT_MS(1000)); ++ ++ res = acx_s_interrogate(adev, &mm, ACX1xx_IE_MEMORY_MAP); ++fail: ++ FN_EXIT1(res); ++ return res; ++} ++ ++ ++/*********************************************************************** ++** acxpci_l_reset_mac ++** ++** MAC will be reset ++** Call context: reset_dev ++*/ ++static void ++acxpci_l_reset_mac(acx_device_t *adev) ++{ ++ u16 temp; ++ ++ FN_ENTER; ++ ++ /* halt eCPU */ ++ temp = read_reg16(adev, IO_ACX_ECPU_CTRL) | 0x1; ++ write_reg16(adev, IO_ACX_ECPU_CTRL, temp); ++ ++ /* now do soft reset of eCPU, set bit */ ++ temp = read_reg16(adev, IO_ACX_SOFT_RESET) | 0x1; ++ log(L_DEBUG, "%s: enable soft reset...\n", __func__); ++ write_reg16(adev, IO_ACX_SOFT_RESET, temp); ++ write_flush(adev); ++ ++ /* now clear bit again: deassert eCPU reset */ ++ log(L_DEBUG, "%s: disable soft reset and go to init mode...\n", __func__); ++ write_reg16(adev, IO_ACX_SOFT_RESET, temp & ~0x1); ++ ++ /* now start a burst read from initial EEPROM */ ++ temp = read_reg16(adev, IO_ACX_EE_START) | 0x1; ++ write_reg16(adev, IO_ACX_EE_START, temp); ++ write_flush(adev); ++ ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acxpci_s_verify_init ++*/ ++static int ++acxpci_s_verify_init(acx_device_t *adev) ++{ ++ int result = NOT_OK; ++ unsigned long timeout; ++ ++ FN_ENTER; ++ ++ timeout = jiffies + 2*HZ; ++ for (;;) { ++ u16 irqstat = read_reg16(adev, IO_ACX_IRQ_STATUS_NON_DES); ++ if (irqstat & HOST_INT_FCS_THRESHOLD) { ++ result = OK; ++ write_reg16(adev, IO_ACX_IRQ_ACK, HOST_INT_FCS_THRESHOLD); ++ break; ++ } ++ if (time_after(jiffies, timeout)) ++ break; ++ /* Init may take up to ~0.5 sec total */ ++ acx_s_msleep(50); ++ } ++ ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++** A few low-level helpers ++** ++** Note: these functions are not protected by lock ++** and thus are never allowed to be called from IRQ. ++** Also they must not race with fw upload which uses same hw regs ++*/ ++ ++/*********************************************************************** ++** acxpci_write_cmd_type_status ++*/ ++ ++static inline void ++acxpci_write_cmd_type_status(acx_device_t *adev, u16 type, u16 status) ++{ ++ writel(type | (status << 16), adev->cmd_area); ++ write_flush(adev); ++} ++ ++ ++/*********************************************************************** ++** acxpci_read_cmd_type_status ++*/ ++static u32 ++acxpci_read_cmd_type_status(acx_device_t *adev) ++{ ++ u32 cmd_type, cmd_status; ++ ++ cmd_type = readl(adev->cmd_area); ++ cmd_status = (cmd_type >> 16); ++ cmd_type = (u16)cmd_type; ++ ++ log(L_CTL, "cmd_type:%04X cmd_status:%04X [%s]\n", ++ cmd_type, cmd_status, ++ acx_cmd_status_str(cmd_status)); ++ ++ return cmd_status; ++} ++ ++ ++/*********************************************************************** ++** acxpci_s_reset_dev ++** ++** Arguments: ++** netdevice that contains the adev variable ++** Returns: ++** NOT_OK on fail ++** OK on success ++** Side effects: ++** device is hard reset ++** Call context: ++** acxpci_e_probe ++** Comment: ++** This resets the device using low level hardware calls ++** as well as uploads and verifies the firmware to the card ++*/ ++ ++static inline void ++init_mboxes(acx_device_t *adev) ++{ ++ u32 cmd_offs, info_offs; ++ ++ cmd_offs = read_reg32(adev, IO_ACX_CMD_MAILBOX_OFFS); ++ info_offs = read_reg32(adev, IO_ACX_INFO_MAILBOX_OFFS); ++ adev->cmd_area = (u8 *)adev->iobase2 + cmd_offs; ++ adev->info_area = (u8 *)adev->iobase2 + info_offs; ++ log(L_DEBUG, "iobase2=%p\n" ++ "cmd_mbox_offset=%X cmd_area=%p\n" ++ "info_mbox_offset=%X info_area=%p\n", ++ adev->iobase2, ++ cmd_offs, adev->cmd_area, ++ info_offs, adev->info_area); ++} ++ ++ ++static inline void ++read_eeprom_area(acx_device_t *adev) ++{ ++#if ACX_DEBUG > 1 ++ int offs; ++ u8 tmp; ++ ++ for (offs = 0x8c; offs < 0xb9; offs++) ++ acxpci_read_eeprom_byte(adev, offs, &tmp); ++#endif ++} ++ ++ ++static int ++acxpci_s_reset_dev(acx_device_t *adev) ++{ ++ const char* msg = ""; ++ unsigned long flags; ++ int result = NOT_OK; ++ u16 hardware_info; ++ u16 ecpu_ctrl; ++ int count; ++ ++ FN_ENTER; ++ ++ /* reset the device to make sure the eCPU is stopped ++ * to upload the firmware correctly */ ++ ++ acx_lock(adev, flags); ++ ++ acxpci_l_reset_mac(adev); ++ ++ ecpu_ctrl = read_reg16(adev, IO_ACX_ECPU_CTRL) & 1; ++ if (!ecpu_ctrl) { ++ msg = "eCPU is already running. "; ++ goto end_unlock; ++ } ++ ++#ifdef WE_DONT_NEED_THAT_DO_WE ++ if (read_reg16(adev, IO_ACX_SOR_CFG) & 2) { ++ /* eCPU most likely means "embedded CPU" */ ++ msg = "eCPU did not start after boot from flash. "; ++ goto end_unlock; ++ } ++ ++ /* check sense on reset flags */ ++ if (read_reg16(adev, IO_ACX_SOR_CFG) & 0x10) { ++ printk("%s: eCPU did not start after boot (SOR), " ++ "is this fatal?\n", adev->ndev->name); ++ } ++#endif ++ /* scan, if any, is stopped now, setting corresponding IRQ bit */ ++ adev->irq_status |= HOST_INT_SCAN_COMPLETE; ++ ++ acx_unlock(adev, flags); ++ ++ /* need to know radio type before fw load */ ++ /* Need to wait for arrival of this information in a loop, ++ * most probably since eCPU runs some init code from EEPROM ++ * (started burst read in reset_mac()) which also ++ * sets the radio type ID */ ++ ++ count = 0xffff; ++ do { ++ hardware_info = read_reg16(adev, IO_ACX_EEPROM_INFORMATION); ++ if (!--count) { ++ msg = "eCPU didn't indicate radio type"; ++ goto end_fail; ++ } ++ cpu_relax(); ++ } while (!(hardware_info & 0xff00)); /* radio type still zero? */ ++ ++ /* printk("DEBUG: count %d\n", count); */ ++ adev->form_factor = hardware_info & 0xff; ++ adev->radio_type = hardware_info >> 8; ++ ++ /* load the firmware */ ++ if (OK != acxpci_s_upload_fw(adev)) ++ goto end_fail; ++ ++ /* acx_s_msleep(10); this one really shouldn't be required */ ++ ++ /* now start eCPU by clearing bit */ ++ write_reg16(adev, IO_ACX_ECPU_CTRL, ecpu_ctrl & ~0x1); ++ log(L_DEBUG, "booted eCPU up and waiting for completion...\n"); ++ ++ /* wait for eCPU bootup */ ++ if (OK != acxpci_s_verify_init(adev)) { ++ msg = "timeout waiting for eCPU. "; ++ goto end_fail; ++ } ++ log(L_DEBUG, "eCPU has woken up, card is ready to be configured\n"); ++ ++ init_mboxes(adev); ++ acxpci_write_cmd_type_status(adev, 0, 0); ++ ++ /* test that EEPROM is readable */ ++ read_eeprom_area(adev); ++ ++ result = OK; ++ goto end; ++ ++/* Finish error message. Indicate which function failed */ ++end_unlock: ++ acx_unlock(adev, flags); ++end_fail: ++ printk("acx: %sreset_dev() FAILED\n", msg); ++end: ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++** acxpci_s_issue_cmd_timeo ++** ++** Sends command to fw, extract result ++** ++** NB: we do _not_ take lock inside, so be sure to not touch anything ++** which may interfere with IRQ handler operation ++** ++** TODO: busy wait is a bit silly, so: ++** 1) stop doing many iters - go to sleep after first ++** 2) go to waitqueue based approach: wait, not poll! ++*/ ++#undef FUNC ++#define FUNC "issue_cmd" ++ ++#if !ACX_DEBUG ++int ++acxpci_s_issue_cmd_timeo( ++ acx_device_t *adev, ++ unsigned int cmd, ++ void *buffer, ++ unsigned buflen, ++ unsigned cmd_timeout) ++{ ++#else ++int ++acxpci_s_issue_cmd_timeo_debug( ++ acx_device_t *adev, ++ unsigned cmd, ++ void *buffer, ++ unsigned buflen, ++ unsigned cmd_timeout, ++ const char* cmdstr) ++{ ++ unsigned long start = jiffies; ++#endif ++ const char *devname; ++ unsigned counter; ++ u16 irqtype; ++ u16 cmd_status; ++ unsigned long timeout; ++ ++ FN_ENTER; ++ ++ devname = adev->ndev->name; ++ if (!devname || !devname[0] || devname[4]=='%') ++ devname = "acx"; ++ ++ log(L_CTL, FUNC"(cmd:%s,buflen:%u,timeout:%ums,type:0x%04X)\n", ++ cmdstr, buflen, cmd_timeout, ++ buffer ? le16_to_cpu(((acx_ie_generic_t *)buffer)->type) : -1); ++ ++ if (!(adev->dev_state_mask & ACX_STATE_FW_LOADED)) { ++ printk("%s: "FUNC"(): firmware is not loaded yet, " ++ "cannot execute commands!\n", devname); ++ goto bad; ++ } ++ ++ if ((acx_debug & L_DEBUG) && (cmd != ACX1xx_CMD_INTERROGATE)) { ++ printk("input buffer (len=%u):\n", buflen); ++ acx_dump_bytes(buffer, buflen); ++ } ++ ++ /* wait for firmware to become idle for our command submission */ ++ timeout = HZ/5; ++ counter = (timeout * 1000 / HZ) - 1; /* in ms */ ++ timeout += jiffies; ++ do { ++ cmd_status = acxpci_read_cmd_type_status(adev); ++ /* Test for IDLE state */ ++ if (!cmd_status) ++ break; ++ if (counter % 8 == 0) { ++ if (time_after(jiffies, timeout)) { ++ counter = 0; ++ break; ++ } ++ /* we waited 8 iterations, no luck. Sleep 8 ms */ ++ acx_s_msleep(8); ++ } ++ } while (likely(--counter)); ++ ++ if (!counter) { ++ /* the card doesn't get idle, we're in trouble */ ++ printk("%s: "FUNC"(): cmd_status is not IDLE: 0x%04X!=0\n", ++ devname, cmd_status); ++ goto bad; ++ } else if (counter < 190) { /* if waited >10ms... */ ++ log(L_CTL|L_DEBUG, FUNC"(): waited for IDLE %dms. " ++ "Please report\n", 199 - counter); ++ } ++ ++ /* now write the parameters of the command if needed */ ++ if (buffer && buflen) { ++ /* if it's an INTERROGATE command, just pass the length ++ * of parameters to read, as data */ ++#if CMD_DISCOVERY ++ if (cmd == ACX1xx_CMD_INTERROGATE) ++ memset_io(adev->cmd_area + 4, 0xAA, buflen); ++#endif ++ /* adev->cmd_area points to PCI device's memory, not to RAM! */ ++ memcpy_toio(adev->cmd_area + 4, buffer, ++ (cmd == ACX1xx_CMD_INTERROGATE) ? 4 : buflen); ++ } ++ /* now write the actual command type */ ++ acxpci_write_cmd_type_status(adev, cmd, 0); ++ /* execute command */ ++ write_reg16(adev, IO_ACX_INT_TRIG, INT_TRIG_CMD); ++ write_flush(adev); ++ ++ /* wait for firmware to process command */ ++ ++ /* Ensure nonzero and not too large timeout. ++ ** Also converts e.g. 100->99, 200->199 ++ ** which is nice but not essential */ ++ cmd_timeout = (cmd_timeout-1) | 1; ++ if (unlikely(cmd_timeout > 1199)) ++ cmd_timeout = 1199; ++ /* clear CMD_COMPLETE bit. can be set only by IRQ handler: */ ++ adev->irq_status &= ~HOST_INT_CMD_COMPLETE; ++ ++ /* we schedule away sometimes (timeout can be large) */ ++ counter = cmd_timeout; ++ timeout = jiffies + cmd_timeout * HZ / 1000; ++ do { ++ if (!adev->irqs_active) { /* IRQ disabled: poll */ ++ irqtype = read_reg16(adev, IO_ACX_IRQ_STATUS_NON_DES); ++ if (irqtype & HOST_INT_CMD_COMPLETE) { ++ write_reg16(adev, IO_ACX_IRQ_ACK, ++ HOST_INT_CMD_COMPLETE); ++ break; ++ } ++ } else { /* Wait when IRQ will set the bit */ ++ irqtype = adev->irq_status; ++ if (irqtype & HOST_INT_CMD_COMPLETE) ++ break; ++ } ++ ++ if (counter % 8 == 0) { ++ if (time_after(jiffies, timeout)) { ++ counter = 0; ++ break; ++ } ++ /* we waited 8 iterations, no luck. Sleep 8 ms */ ++ acx_s_msleep(8); ++ } ++ } while (likely(--counter)); ++ ++ /* save state for debugging */ ++ cmd_status = acxpci_read_cmd_type_status(adev); ++ ++ /* put the card in IDLE state */ ++ acxpci_write_cmd_type_status(adev, 0, 0); ++ ++ if (!counter) { /* timed out! */ ++ printk("%s: "FUNC"(): timed out %s for CMD_COMPLETE. " ++ "irq bits:0x%04X irq_status:0x%04X timeout:%dms " ++ "cmd_status:%d (%s)\n", ++ devname, (adev->irqs_active) ? "waiting" : "polling", ++ irqtype, adev->irq_status, cmd_timeout, ++ cmd_status, acx_cmd_status_str(cmd_status)); ++ goto bad; ++ } else if (cmd_timeout - counter > 30) { /* if waited >30ms... */ ++ log(L_CTL|L_DEBUG, FUNC"(): %s for CMD_COMPLETE %dms. " ++ "count:%d. Please report\n", ++ (adev->irqs_active) ? "waited" : "polled", ++ cmd_timeout - counter, counter); ++ } ++ ++ if (1 != cmd_status) { /* it is not a 'Success' */ ++ printk("%s: "FUNC"(): cmd_status is not SUCCESS: %d (%s). " ++ "Took %dms of %d\n", ++ devname, cmd_status, acx_cmd_status_str(cmd_status), ++ cmd_timeout - counter, cmd_timeout); ++ /* zero out result buffer ++ * WARNING: this will trash stack in case of illegally large input ++ * length! */ ++ if (buffer && buflen) ++ memset(buffer, 0, buflen); ++ goto bad; ++ } ++ ++ /* read in result parameters if needed */ ++ if (buffer && buflen && (cmd == ACX1xx_CMD_INTERROGATE)) { ++ /* adev->cmd_area points to PCI device's memory, not to RAM! */ ++ memcpy_fromio(buffer, adev->cmd_area + 4, buflen); ++ if (acx_debug & L_DEBUG) { ++ printk("output buffer (len=%u): ", buflen); ++ acx_dump_bytes(buffer, buflen); ++ } ++ } ++/* ok: */ ++ log(L_CTL, FUNC"(%s): took %ld jiffies to complete\n", ++ cmdstr, jiffies - start); ++ FN_EXIT1(OK); ++ return OK; ++ ++bad: ++ /* Give enough info so that callers can avoid ++ ** printing their own diagnostic messages */ ++#if ACX_DEBUG ++ printk("%s: "FUNC"(cmd:%s) FAILED\n", devname, cmdstr); ++#else ++ printk("%s: "FUNC"(cmd:0x%04X) FAILED\n", devname, cmd); ++#endif ++ dump_stack(); ++ FN_EXIT1(NOT_OK); ++ return NOT_OK; ++} ++ ++ ++/*********************************************************************** ++*/ ++#ifdef NONESSENTIAL_FEATURES ++typedef struct device_id { ++ unsigned char id[6]; ++ char *descr; ++ char *type; ++} device_id_t; ++ ++static const device_id_t ++device_ids[] = ++{ ++ { ++ {'G', 'l', 'o', 'b', 'a', 'l'}, ++ NULL, ++ NULL, ++ }, ++ { ++ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, ++ "uninitialized", ++ "SpeedStream SS1021 or Gigafast WF721-AEX" ++ }, ++ { ++ {0x80, 0x81, 0x82, 0x83, 0x84, 0x85}, ++ "non-standard", ++ "DrayTek Vigor 520" ++ }, ++ { ++ {'?', '?', '?', '?', '?', '?'}, ++ "non-standard", ++ "Level One WPC-0200" ++ }, ++ { ++ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, ++ "empty", ++ "DWL-650+ variant" ++ } ++}; ++ ++static void ++acx_show_card_eeprom_id(acx_device_t *adev) ++{ ++ unsigned char buffer[CARD_EEPROM_ID_SIZE]; ++ int i; ++ ++ memset(&buffer, 0, CARD_EEPROM_ID_SIZE); ++ /* use direct EEPROM access */ ++ for (i = 0; i < CARD_EEPROM_ID_SIZE; i++) { ++ if (OK != acxpci_read_eeprom_byte(adev, ++ ACX100_EEPROM_ID_OFFSET + i, ++ &buffer[i])) { ++ printk("acx: reading EEPROM FAILED\n"); ++ break; ++ } ++ } ++ ++ for (i = 0; i < VEC_SIZE(device_ids); i++) { ++ if (!memcmp(&buffer, device_ids[i].id, CARD_EEPROM_ID_SIZE)) { ++ if (device_ids[i].descr) { ++ printk("acx: EEPROM card ID string check " ++ "found %s card ID: is this %s?\n", ++ device_ids[i].descr, device_ids[i].type); ++ } ++ break; ++ } ++ } ++ if (i == VEC_SIZE(device_ids)) { ++ printk("acx: EEPROM card ID string check found " ++ "unknown card: expected 'Global', got '%.*s\'. " ++ "Please report\n", CARD_EEPROM_ID_SIZE, buffer); ++ } ++} ++#endif /* NONESSENTIAL_FEATURES */ ++ ++ ++/*********************************************************************** ++** acxpci_free_desc_queues ++** ++** Releases the queues that have been allocated, the ++** others have been initialised to NULL so this ++** function can be used if only part of the queues were allocated. ++*/ ++ ++static inline void ++free_coherent(struct pci_dev *hwdev, size_t size, ++ void *vaddr, dma_addr_t dma_handle) ++{ ++ dma_free_coherent(hwdev == NULL ? NULL : &hwdev->dev, ++ size, vaddr, dma_handle); ++} ++ ++void ++acxpci_free_desc_queues(acx_device_t *adev) ++{ ++#define ACX_FREE_QUEUE(size, ptr, phyaddr) \ ++ if (ptr) { \ ++ free_coherent(0, size, ptr, phyaddr); \ ++ ptr = NULL; \ ++ size = 0; \ ++ } ++ ++ FN_ENTER; ++ ++ ACX_FREE_QUEUE(adev->txhostdesc_area_size, adev->txhostdesc_start, adev->txhostdesc_startphy); ++ ACX_FREE_QUEUE(adev->txbuf_area_size, adev->txbuf_start, adev->txbuf_startphy); ++ ++ adev->txdesc_start = NULL; ++ ++ ACX_FREE_QUEUE(adev->rxhostdesc_area_size, adev->rxhostdesc_start, adev->rxhostdesc_startphy); ++ ACX_FREE_QUEUE(adev->rxbuf_area_size, adev->rxbuf_start, adev->rxbuf_startphy); ++ ++ adev->rxdesc_start = NULL; ++ ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acxpci_s_delete_dma_regions ++*/ ++static void ++acxpci_s_delete_dma_regions(acx_device_t *adev) ++{ ++ unsigned long flags; ++ ++ FN_ENTER; ++ /* disable radio Tx/Rx. Shouldn't we use the firmware commands ++ * here instead? Or are we that much down the road that it's no ++ * longer possible here? */ ++ write_reg16(adev, IO_ACX_ENABLE, 0); ++ ++ acx_s_msleep(100); ++ ++ acx_lock(adev, flags); ++ acxpci_free_desc_queues(adev); ++ acx_unlock(adev, flags); ++ ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acxpci_e_probe ++** ++** Probe routine called when a PCI device w/ matching ID is found. ++** Here's the sequence: ++** - Allocate the PCI resources. ++** - Read the PCMCIA attribute memory to make sure we have a WLAN card ++** - Reset the MAC ++** - Initialize the dev and wlan data ++** - Initialize the MAC ++** ++** pdev - ptr to pci device structure containing info about pci configuration ++** id - ptr to the device id entry that matched this device ++*/ ++static const u16 ++IO_ACX100[] = ++{ ++ 0x0000, /* IO_ACX_SOFT_RESET */ ++ ++ 0x0014, /* IO_ACX_SLV_MEM_ADDR */ ++ 0x0018, /* IO_ACX_SLV_MEM_DATA */ ++ 0x001c, /* IO_ACX_SLV_MEM_CTL */ ++ 0x0020, /* IO_ACX_SLV_END_CTL */ ++ ++ 0x0034, /* IO_ACX_FEMR */ ++ ++ 0x007c, /* IO_ACX_INT_TRIG */ ++ 0x0098, /* IO_ACX_IRQ_MASK */ ++ 0x00a4, /* IO_ACX_IRQ_STATUS_NON_DES */ ++ 0x00a8, /* IO_ACX_IRQ_STATUS_CLEAR */ ++ 0x00ac, /* IO_ACX_IRQ_ACK */ ++ 0x00b0, /* IO_ACX_HINT_TRIG */ ++ ++ 0x0104, /* IO_ACX_ENABLE */ ++ ++ 0x0250, /* IO_ACX_EEPROM_CTL */ ++ 0x0254, /* IO_ACX_EEPROM_ADDR */ ++ 0x0258, /* IO_ACX_EEPROM_DATA */ ++ 0x025c, /* IO_ACX_EEPROM_CFG */ ++ ++ 0x0268, /* IO_ACX_PHY_ADDR */ ++ 0x026c, /* IO_ACX_PHY_DATA */ ++ 0x0270, /* IO_ACX_PHY_CTL */ ++ ++ 0x0290, /* IO_ACX_GPIO_OE */ ++ ++ 0x0298, /* IO_ACX_GPIO_OUT */ ++ ++ 0x02a4, /* IO_ACX_CMD_MAILBOX_OFFS */ ++ 0x02a8, /* IO_ACX_INFO_MAILBOX_OFFS */ ++ 0x02ac, /* IO_ACX_EEPROM_INFORMATION */ ++ ++ 0x02d0, /* IO_ACX_EE_START */ ++ 0x02d4, /* IO_ACX_SOR_CFG */ ++ 0x02d8 /* IO_ACX_ECPU_CTRL */ ++}; ++ ++static const u16 ++IO_ACX111[] = ++{ ++ 0x0000, /* IO_ACX_SOFT_RESET */ ++ ++ 0x0014, /* IO_ACX_SLV_MEM_ADDR */ ++ 0x0018, /* IO_ACX_SLV_MEM_DATA */ ++ 0x001c, /* IO_ACX_SLV_MEM_CTL */ ++ 0x0020, /* IO_ACX_SLV_END_CTL */ ++ ++ 0x0034, /* IO_ACX_FEMR */ ++ ++ 0x00b4, /* IO_ACX_INT_TRIG */ ++ 0x00d4, /* IO_ACX_IRQ_MASK */ ++ /* we do mean NON_DES (0xf0), not NON_DES_MASK which is at 0xe0: */ ++ 0x00f0, /* IO_ACX_IRQ_STATUS_NON_DES */ ++ 0x00e4, /* IO_ACX_IRQ_STATUS_CLEAR */ ++ 0x00e8, /* IO_ACX_IRQ_ACK */ ++ 0x00ec, /* IO_ACX_HINT_TRIG */ ++ ++ 0x01d0, /* IO_ACX_ENABLE */ ++ ++ 0x0338, /* IO_ACX_EEPROM_CTL */ ++ 0x033c, /* IO_ACX_EEPROM_ADDR */ ++ 0x0340, /* IO_ACX_EEPROM_DATA */ ++ 0x0344, /* IO_ACX_EEPROM_CFG */ ++ ++ 0x0350, /* IO_ACX_PHY_ADDR */ ++ 0x0354, /* IO_ACX_PHY_DATA */ ++ 0x0358, /* IO_ACX_PHY_CTL */ ++ ++ 0x0374, /* IO_ACX_GPIO_OE */ ++ ++ 0x037c, /* IO_ACX_GPIO_OUT */ ++ ++ 0x0388, /* IO_ACX_CMD_MAILBOX_OFFS */ ++ 0x038c, /* IO_ACX_INFO_MAILBOX_OFFS */ ++ 0x0390, /* IO_ACX_EEPROM_INFORMATION */ ++ ++ 0x0100, /* IO_ACX_EE_START */ ++ 0x0104, /* IO_ACX_SOR_CFG */ ++ 0x0108, /* IO_ACX_ECPU_CTRL */ ++}; ++ ++static void ++dummy_netdev_init(struct net_device *ndev) {} ++ ++static int __devinit ++acxpci_e_probe(struct pci_dev *pdev, const struct pci_device_id *id) ++{ ++ acx111_ie_configoption_t co; ++ unsigned long mem_region1 = 0; ++ unsigned long mem_region2 = 0; ++ unsigned long mem_region1_size; ++ unsigned long mem_region2_size; ++ unsigned long phymem1; ++ unsigned long phymem2; ++ void *mem1 = NULL; ++ void *mem2 = NULL; ++ acx_device_t *adev = NULL; ++ struct net_device *ndev = NULL; ++ const char *chip_name; ++ int result = -EIO; ++ int err; ++ u8 chip_type; ++ ++ FN_ENTER; ++ ++ /* Enable the PCI device */ ++ if (pci_enable_device(pdev)) { ++ printk("acx: pci_enable_device() FAILED\n"); ++ result = -ENODEV; ++ goto fail_pci_enable_device; ++ } ++ ++ /* enable busmastering (required for CardBus) */ ++ pci_set_master(pdev); ++ ++ /* FIXME: prism54 calls pci_set_mwi() here, ++ * should we do/support the same? */ ++ ++ /* chiptype is u8 but id->driver_data is ulong ++ ** Works for now (possible values are 1 and 2) */ ++ chip_type = (u8)id->driver_data; ++ /* acx100 and acx111 have different PCI memory regions */ ++ if (chip_type == CHIPTYPE_ACX100) { ++ chip_name = "ACX100"; ++ mem_region1 = PCI_ACX100_REGION1; ++ mem_region1_size = PCI_ACX100_REGION1_SIZE; ++ ++ mem_region2 = PCI_ACX100_REGION2; ++ mem_region2_size = PCI_ACX100_REGION2_SIZE; ++ } else if (chip_type == CHIPTYPE_ACX111) { ++ chip_name = "ACX111"; ++ mem_region1 = PCI_ACX111_REGION1; ++ mem_region1_size = PCI_ACX111_REGION1_SIZE; ++ ++ mem_region2 = PCI_ACX111_REGION2; ++ mem_region2_size = PCI_ACX111_REGION2_SIZE; ++ } else { ++ printk("acx: unknown chip type 0x%04X\n", chip_type); ++ goto fail_unknown_chiptype; ++ } ++ ++ /* Figure out our resources */ ++ phymem1 = pci_resource_start(pdev, mem_region1); ++ phymem2 = pci_resource_start(pdev, mem_region2); ++ if (!request_mem_region(phymem1, pci_resource_len(pdev, mem_region1), "acx_1")) { ++ printk("acx: cannot reserve PCI memory region 1 (are you sure " ++ "you have CardBus support in kernel?)\n"); ++ goto fail_request_mem_region1; ++ } ++ if (!request_mem_region(phymem2, pci_resource_len(pdev, mem_region2), "acx_2")) { ++ printk("acx: cannot reserve PCI memory region 2\n"); ++ goto fail_request_mem_region2; ++ } ++ ++ /* this used to be ioremap(), but ioremap_nocache() ++ * is much less risky, right? (and slower?) ++ * FIXME: we may want to go back to cached variant if it's ++ * certain that our code really properly handles ++ * cached operation (memory barriers, volatile?, ...) ++ * (but always keep this comment here regardless!) ++ * Possibly make this a driver config setting? */ ++ ++ mem1 = ioremap_nocache(phymem1, mem_region1_size); ++ if (!mem1) { ++ printk("acx: ioremap() FAILED\n"); ++ goto fail_ioremap1; ++ } ++ mem2 = ioremap_nocache(phymem2, mem_region2_size); ++ if (!mem2) { ++ printk("acx: ioremap() #2 FAILED\n"); ++ goto fail_ioremap2; ++ } ++ ++ printk("acx: found %s-based wireless network card at %s, irq:%d, " ++ "phymem1:0x%lX, phymem2:0x%lX, mem1:0x%p, mem1_size:%ld, " ++ "mem2:0x%p, mem2_size:%ld\n", ++ chip_name, pci_name(pdev), pdev->irq, phymem1, phymem2, ++ mem1, mem_region1_size, ++ mem2, mem_region2_size); ++ log(L_ANY, "initial debug setting is 0x%04X\n", acx_debug); ++ ++ if (0 == pdev->irq) { ++ printk("acx: can't use IRQ 0\n"); ++ goto fail_irq; ++ } ++ ++ ndev = alloc_netdev(sizeof(*adev), "wlan%d", dummy_netdev_init); ++ /* (NB: memsets to 0 entire area) */ ++ if (!ndev) { ++ printk("acx: no memory for netdevice struct\n"); ++ goto fail_alloc_netdev; ++ } ++ ++ ether_setup(ndev); ++ ndev->open = &acxpci_e_open; ++ ndev->stop = &acxpci_e_close; ++ ndev->hard_start_xmit = &acx_i_start_xmit; ++ ndev->get_stats = &acx_e_get_stats; ++#if IW_HANDLER_VERSION <= 5 ++ ndev->get_wireless_stats = &acx_e_get_wireless_stats; ++#endif ++ ndev->wireless_handlers = (struct iw_handler_def *)&acx_ioctl_handler_def; ++ ndev->set_multicast_list = &acxpci_i_set_multicast_list; ++ ndev->tx_timeout = &acxpci_i_tx_timeout; ++ ndev->change_mtu = &acx_e_change_mtu; ++ ndev->watchdog_timeo = 4 * HZ; ++ ndev->irq = pdev->irq; ++ ndev->base_addr = pci_resource_start(pdev, 0); ++ ++ adev = ndev2adev(ndev); ++ spin_lock_init(&adev->lock); /* initial state: unlocked */ ++ /* We do not start with downed sem: we want PARANOID_LOCKING to work */ ++ sema_init(&adev->sem, 1); /* initial state: 1 (upped) */ ++ /* since nobody can see new netdev yet, we can as well ++ ** just _presume_ that we're under sem (instead of actually taking it): */ ++ /* acx_sem_lock(adev); */ ++ adev->pdev = pdev; ++ adev->ndev = ndev; ++ adev->dev_type = DEVTYPE_PCI; ++ adev->chip_type = chip_type; ++ adev->chip_name = chip_name; ++ adev->io = (CHIPTYPE_ACX100 == chip_type) ? IO_ACX100 : IO_ACX111; ++ adev->membase = phymem1; ++ adev->iobase = mem1; ++ adev->membase2 = phymem2; ++ adev->iobase2 = mem2; ++ /* to find crashes due to weird driver access ++ * to unconfigured interface (ifup) */ ++ adev->mgmt_timer.function = (void (*)(unsigned long))0x0000dead; ++ ++#ifdef NONESSENTIAL_FEATURES ++ acx_show_card_eeprom_id(adev); ++#endif /* NONESSENTIAL_FEATURES */ ++ ++#ifdef SET_MODULE_OWNER ++ SET_MODULE_OWNER(ndev); ++#endif ++ SET_NETDEV_DEV(ndev, &pdev->dev); ++ ++ log(L_IRQ|L_INIT, "using IRQ %d\n", pdev->irq); ++ ++ /* need to be able to restore PCI state after a suspend */ ++ pci_save_state(pdev); ++ pci_set_drvdata(pdev, ndev); ++ ++ /* ok, pci setup is finished, now start initializing the card */ ++ ++ /* NB: read_reg() reads may return bogus data before reset_dev(), ++ * since the firmware which directly controls large parts of the I/O ++ * registers isn't initialized yet. ++ * acx100 seems to be more affected than acx111 */ ++ if (OK != acxpci_s_reset_dev(adev)) ++ goto fail_reset; ++ ++ if (IS_ACX100(adev)) { ++ /* ACX100: configopt struct in cmd mailbox - directly after reset */ ++ memcpy_fromio(&co, adev->cmd_area, sizeof(co)); ++ } ++ ++ if (OK != acx_s_init_mac(adev)) ++ goto fail_init_mac; ++ ++ if (IS_ACX111(adev)) { ++ /* ACX111: configopt struct needs to be queried after full init */ ++ acx_s_interrogate(adev, &co, ACX111_IE_CONFIG_OPTIONS); ++ } ++ ++/* TODO: merge them into one function, they are called just once and are the same for pci & usb */ ++ if (OK != acxpci_read_eeprom_byte(adev, 0x05, &adev->eeprom_version)) ++ goto fail_read_eeprom_version; ++ ++ acx_s_parse_configoption(adev, &co); ++ acx_s_set_defaults(adev); ++ acx_s_get_firmware_version(adev); /* needs to be after acx_s_init_mac() */ ++ acx_display_hardware_details(adev); ++ ++ /* Register the card, AFTER everything else has been set up, ++ * since otherwise an ioctl could step on our feet due to ++ * firmware operations happening in parallel or uninitialized data */ ++ err = register_netdev(ndev); ++ if (OK != err) { ++ printk("acx: register_netdev() FAILED: %d\n", err); ++ goto fail_register_netdev; ++ } ++ ++ acx_proc_register_entries(ndev); ++ ++ /* Now we have our device, so make sure the kernel doesn't try ++ * to send packets even though we're not associated to a network yet */ ++ acx_stop_queue(ndev, "on probe"); ++ acx_carrier_off(ndev, "on probe"); ++ ++ /* after register_netdev() userspace may start working with dev ++ * (in particular, on other CPUs), we only need to up the sem */ ++ /* acx_sem_unlock(adev); */ ++ ++ printk("acx "ACX_RELEASE": net device %s, driver compiled " ++ "against wireless extensions %d and Linux %s\n", ++ ndev->name, WIRELESS_EXT, UTS_RELEASE); ++ ++#if CMD_DISCOVERY ++ great_inquisitor(adev); ++#endif ++ ++ result = OK; ++ goto done; ++ ++ /* error paths: undo everything in reverse order... */ ++ ++fail_register_netdev: ++ ++ acxpci_s_delete_dma_regions(adev); ++ pci_set_drvdata(pdev, NULL); ++ ++fail_init_mac: ++fail_read_eeprom_version: ++fail_reset: ++ ++ free_netdev(ndev); ++fail_alloc_netdev: ++fail_irq: ++ ++ iounmap(mem2); ++fail_ioremap2: ++ ++ iounmap(mem1); ++fail_ioremap1: ++ ++ release_mem_region(pci_resource_start(pdev, mem_region2), ++ pci_resource_len(pdev, mem_region2)); ++fail_request_mem_region2: ++ ++ release_mem_region(pci_resource_start(pdev, mem_region1), ++ pci_resource_len(pdev, mem_region1)); ++fail_request_mem_region1: ++fail_unknown_chiptype: ++ ++ pci_disable_device(pdev); ++fail_pci_enable_device: ++ ++ pci_set_power_state(pdev, PCI_D3hot); ++ ++done: ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++** acxpci_e_remove ++** ++** Shut device down (if not hot unplugged) ++** and deallocate PCI resources for the acx chip. ++** ++** pdev - ptr to PCI device structure containing info about pci configuration ++*/ ++static void __devexit ++acxpci_e_remove(struct pci_dev *pdev) ++{ ++ struct net_device *ndev; ++ acx_device_t *adev; ++ unsigned long mem_region1, mem_region2; ++ unsigned long flags; ++ ++ FN_ENTER; ++ ++ ndev = (struct net_device*) pci_get_drvdata(pdev); ++ if (!ndev) { ++ log(L_DEBUG, "%s: card is unused. Skipping any release code\n", ++ __func__); ++ goto end; ++ } ++ ++ adev = ndev2adev(ndev); ++ ++ /* If device wasn't hot unplugged... */ ++ if (adev_present(adev)) { ++ ++ acx_sem_lock(adev); ++ ++ /* disable both Tx and Rx to shut radio down properly */ ++ acx_s_issue_cmd(adev, ACX1xx_CMD_DISABLE_TX, NULL, 0); ++ acx_s_issue_cmd(adev, ACX1xx_CMD_DISABLE_RX, NULL, 0); ++ ++#ifdef REDUNDANT ++ /* put the eCPU to sleep to save power ++ * Halting is not possible currently, ++ * since not supported by all firmware versions */ ++ acx_s_issue_cmd(adev, ACX100_CMD_SLEEP, NULL, 0); ++#endif ++ acx_lock(adev, flags); ++ /* disable power LED to save power :-) */ ++ log(L_INIT, "switching off power LED to save power\n"); ++ acxpci_l_power_led(adev, 0); ++ /* stop our eCPU */ ++ if (IS_ACX111(adev)) { ++ /* FIXME: does this actually keep halting the eCPU? ++ * I don't think so... ++ */ ++ acxpci_l_reset_mac(adev); ++ } else { ++ u16 temp; ++ /* halt eCPU */ ++ temp = read_reg16(adev, IO_ACX_ECPU_CTRL) | 0x1; ++ write_reg16(adev, IO_ACX_ECPU_CTRL, temp); ++ write_flush(adev); ++ } ++ acx_unlock(adev, flags); ++ ++ acx_sem_unlock(adev); ++ } ++ ++ /* unregister the device to not let the kernel ++ * (e.g. ioctls) access a half-deconfigured device ++ * NB: this will cause acxpci_e_close() to be called, ++ * thus we shouldn't call it under sem! */ ++ log(L_INIT, "removing device %s\n", ndev->name); ++ unregister_netdev(ndev); ++ ++ /* unregister_netdev ensures that no references to us left. ++ * For paranoid reasons we continue to follow the rules */ ++ acx_sem_lock(adev); ++ ++ if (adev->dev_state_mask & ACX_STATE_IFACE_UP) { ++ acxpci_s_down(ndev); ++ CLEAR_BIT(adev->dev_state_mask, ACX_STATE_IFACE_UP); ++ } ++ ++ acx_proc_unregister_entries(ndev); ++ ++ if (IS_ACX100(adev)) { ++ mem_region1 = PCI_ACX100_REGION1; ++ mem_region2 = PCI_ACX100_REGION2; ++ } else { ++ mem_region1 = PCI_ACX111_REGION1; ++ mem_region2 = PCI_ACX111_REGION2; ++ } ++ ++ /* finally, clean up PCI bus state */ ++ acxpci_s_delete_dma_regions(adev); ++ if (adev->iobase) iounmap(adev->iobase); ++ if (adev->iobase2) iounmap(adev->iobase2); ++ release_mem_region(pci_resource_start(pdev, mem_region1), ++ pci_resource_len(pdev, mem_region1)); ++ release_mem_region(pci_resource_start(pdev, mem_region2), ++ pci_resource_len(pdev, mem_region2)); ++ pci_disable_device(pdev); ++ ++ /* remove dev registration */ ++ pci_set_drvdata(pdev, NULL); ++ ++ acx_sem_unlock(adev); ++ ++ /* Free netdev (quite late, ++ * since otherwise we might get caught off-guard ++ * by a netdev timeout handler execution ++ * expecting to see a working dev...) */ ++ free_netdev(ndev); ++ ++ /* put device into ACPI D3 mode (shutdown) */ ++ pci_set_power_state(pdev, PCI_D3hot); ++ ++end: ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** TODO: PM code needs to be fixed / debugged / tested. ++*/ ++#ifdef CONFIG_PM ++static int ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) ++acxpci_e_suspend(struct pci_dev *pdev, pm_message_t state) ++#else ++acxpci_e_suspend(struct pci_dev *pdev, u32 state) ++#endif ++{ ++ struct net_device *ndev = pci_get_drvdata(pdev); ++ acx_device_t *adev; ++ ++ FN_ENTER; ++ printk("acx: suspend handler is experimental!\n"); ++ printk("sus: dev %p\n", ndev); ++ ++ if (!netif_running(ndev)) ++ goto end; ++ ++ adev = ndev2adev(ndev); ++ printk("sus: adev %p\n", adev); ++ ++ acx_sem_lock(adev); ++ ++ netif_device_detach(ndev); /* this one cannot sleep */ ++ acxpci_s_down(ndev); ++ /* down() does not set it to 0xffff, but here we really want that */ ++ write_reg16(adev, IO_ACX_IRQ_MASK, 0xffff); ++ write_reg16(adev, IO_ACX_FEMR, 0x0); ++ acxpci_s_delete_dma_regions(adev); ++ pci_save_state(pdev); ++ pci_set_power_state(pdev, PCI_D3hot); ++ ++ acx_sem_unlock(adev); ++end: ++ FN_EXIT0; ++ return OK; ++} ++ ++ ++static int ++acxpci_e_resume(struct pci_dev *pdev) ++{ ++ struct net_device *ndev = pci_get_drvdata(pdev); ++ acx_device_t *adev; ++ ++ FN_ENTER; ++ ++ printk("acx: resume handler is experimental!\n"); ++ printk("rsm: got dev %p\n", ndev); ++ ++ if (!netif_running(ndev)) ++ goto end; ++ ++ adev = ndev2adev(ndev); ++ printk("rsm: got adev %p\n", adev); ++ ++ acx_sem_lock(adev); ++ ++ pci_set_power_state(pdev, PCI_D0); ++ printk("rsm: power state PCI_D0 set\n"); ++ pci_restore_state(pdev); ++ printk("rsm: PCI state restored\n"); ++ ++ if (OK != acxpci_s_reset_dev(adev)) ++ goto end_unlock; ++ printk("rsm: device reset done\n"); ++ if (OK != acx_s_init_mac(adev)) ++ goto end_unlock; ++ printk("rsm: init MAC done\n"); ++ ++ acxpci_s_up(ndev); ++ printk("rsm: acx up done\n"); ++ ++ /* now even reload all card parameters as they were before suspend, ++ * and possibly be back in the network again already :-) */ ++ if (ACX_STATE_IFACE_UP & adev->dev_state_mask) { ++ adev->set_mask = GETSET_ALL; ++ acx_s_update_card_settings(adev); ++ printk("rsm: settings updated\n"); ++ } ++ netif_device_attach(ndev); ++ printk("rsm: device attached\n"); ++ ++end_unlock: ++ acx_sem_unlock(adev); ++end: ++ /* we need to return OK here anyway, right? */ ++ FN_EXIT0; ++ return OK; ++} ++#endif /* CONFIG_PM */ ++ ++ ++/*********************************************************************** ++** acxpci_s_up ++** ++** This function is called by acxpci_e_open (when ifconfig sets the device as up) ++** ++** Side effects: ++** - Enables on-card interrupt requests ++** - calls acx_s_start ++*/ ++ ++static void ++enable_acx_irq(acx_device_t *adev) ++{ ++ FN_ENTER; ++ write_reg16(adev, IO_ACX_IRQ_MASK, adev->irq_mask); ++ write_reg16(adev, IO_ACX_FEMR, 0x8000); ++ adev->irqs_active = 1; ++ FN_EXIT0; ++} ++ ++static void ++acxpci_s_up(struct net_device *ndev) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ unsigned long flags; ++ ++ FN_ENTER; ++ ++ acx_lock(adev, flags); ++ enable_acx_irq(adev); ++ acx_unlock(adev, flags); ++ ++ /* acx fw < 1.9.3.e has a hardware timer, and older drivers ++ ** used to use it. But we don't do that anymore, our OS ++ ** has reliable software timers */ ++ init_timer(&adev->mgmt_timer); ++ adev->mgmt_timer.function = acx_i_timer; ++ adev->mgmt_timer.data = (unsigned long)adev; ++ ++ /* Need to set ACX_STATE_IFACE_UP first, or else ++ ** timer won't be started by acx_set_status() */ ++ SET_BIT(adev->dev_state_mask, ACX_STATE_IFACE_UP); ++ switch (adev->mode) { ++ case ACX_MODE_0_ADHOC: ++ case ACX_MODE_2_STA: ++ /* actual scan cmd will happen in start() */ ++ acx_set_status(adev, ACX_STATUS_1_SCANNING); break; ++ case ACX_MODE_3_AP: ++ case ACX_MODE_MONITOR: ++ acx_set_status(adev, ACX_STATUS_4_ASSOCIATED); break; ++ } ++ ++ acx_s_start(adev); ++ ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acxpci_s_down ++** ++** NB: device may be already hot unplugged if called from acxpci_e_remove() ++** ++** Disables on-card interrupt request, stops softirq and timer, stops queue, ++** sets status == STOPPED ++*/ ++ ++static void ++disable_acx_irq(acx_device_t *adev) ++{ ++ FN_ENTER; ++ ++ /* I guess mask is not 0xffff because acx100 won't signal ++ ** cmd completion then (needed for ifup). ++ ** Someone with acx100 please confirm */ ++ write_reg16(adev, IO_ACX_IRQ_MASK, adev->irq_mask_off); ++ write_reg16(adev, IO_ACX_FEMR, 0x0); ++ adev->irqs_active = 0; ++ FN_EXIT0; ++} ++ ++static void ++acxpci_s_down(struct net_device *ndev) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ unsigned long flags; ++ ++ FN_ENTER; ++ ++ /* Disable IRQs first, so that IRQs cannot race with us */ ++ /* then wait until interrupts have finished executing on other CPUs */ ++ acx_lock(adev, flags); ++ disable_acx_irq(adev); ++ synchronize_irq(adev->pdev->irq); ++ acx_unlock(adev, flags); ++ ++ /* we really don't want to have an asynchronous tasklet disturb us ++ ** after something vital for its job has been shut down, so ++ ** end all remaining work now. ++ ** ++ ** NB: carrier_off (done by set_status below) would lead to ++ ** not yet fully understood deadlock in FLUSH_SCHEDULED_WORK(). ++ ** That's why we do FLUSH first. ++ ** ++ ** NB2: we have a bad locking bug here: FLUSH_SCHEDULED_WORK() ++ ** waits for acx_e_after_interrupt_task to complete if it is running ++ ** on another CPU, but acx_e_after_interrupt_task ++ ** will sleep on sem forever, because it is taken by us! ++ ** Work around that by temporary sem unlock. ++ ** This will fail miserably if we'll be hit by concurrent ++ ** iwconfig or something in between. TODO! */ ++ acx_sem_unlock(adev); ++ FLUSH_SCHEDULED_WORK(); ++ acx_sem_lock(adev); ++ ++ /* This is possible: ++ ** FLUSH_SCHEDULED_WORK -> acx_e_after_interrupt_task -> ++ ** -> set_status(ASSOCIATED) -> wake_queue() ++ ** That's why we stop queue _after_ FLUSH_SCHEDULED_WORK ++ ** lock/unlock is just paranoia, maybe not needed */ ++ acx_lock(adev, flags); ++ acx_stop_queue(ndev, "on ifdown"); ++ acx_set_status(adev, ACX_STATUS_0_STOPPED); ++ acx_unlock(adev, flags); ++ ++ /* kernel/timer.c says it's illegal to del_timer_sync() ++ ** a timer which restarts itself. We guarantee this cannot ++ ** ever happen because acx_i_timer() never does this if ++ ** status is ACX_STATUS_0_STOPPED */ ++ del_timer_sync(&adev->mgmt_timer); ++ ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acxpci_e_open ++** ++** Called as a result of SIOCSIFFLAGS ioctl changing the flags bit IFF_UP ++** from clear to set. In other words: ifconfig up. ++** ++** Returns: ++** 0 success ++** >0 f/w reported error ++** <0 driver reported error ++*/ ++static int ++acxpci_e_open(struct net_device *ndev) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ int result = OK; ++ ++ FN_ENTER; ++ ++ acx_sem_lock(adev); ++ ++ acx_init_task_scheduler(adev); ++ ++/* TODO: pci_set_power_state(pdev, PCI_D0); ? */ ++ ++ /* request shared IRQ handler */ ++ if (request_irq(ndev->irq, acxpci_i_interrupt, SA_SHIRQ, ndev->name, ndev)) { ++ printk("%s: request_irq FAILED\n", ndev->name); ++ result = -EAGAIN; ++ goto done; ++ } ++ log(L_DEBUG|L_IRQ, "request_irq %d successful\n", ndev->irq); ++ ++ /* ifup device */ ++ acxpci_s_up(ndev); ++ ++ /* We don't currently have to do anything else. ++ * The setup of the MAC should be subsequently completed via ++ * the mlme commands. ++ * Higher layers know we're ready from dev->start==1 and ++ * dev->tbusy==0. Our rx path knows to pass up received/ ++ * frames because of dev->flags&IFF_UP is true. ++ */ ++done: ++ acx_sem_unlock(adev); ++ ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++** acxpci_e_close ++** ++** Called as a result of SIOCSIIFFLAGS ioctl changing the flags bit IFF_UP ++** from set to clear. I.e. called by "ifconfig DEV down" ++** ++** Returns: ++** 0 success ++** >0 f/w reported error ++** <0 driver reported error ++*/ ++static int ++acxpci_e_close(struct net_device *ndev) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ ++ FN_ENTER; ++ ++ acx_sem_lock(adev); ++ ++ /* ifdown device */ ++ CLEAR_BIT(adev->dev_state_mask, ACX_STATE_IFACE_UP); ++ if (netif_device_present(ndev)) { ++ acxpci_s_down(ndev); ++ } ++ ++ /* disable all IRQs, release shared IRQ handler */ ++ write_reg16(adev, IO_ACX_IRQ_MASK, 0xffff); ++ write_reg16(adev, IO_ACX_FEMR, 0x0); ++ free_irq(ndev->irq, ndev); ++ ++/* TODO: pci_set_power_state(pdev, PCI_D3hot); ? */ ++ ++ /* We currently don't have to do anything else. ++ * Higher layers know we're not ready from dev->start==0 and ++ * dev->tbusy==1. Our rx path knows to not pass up received ++ * frames because of dev->flags&IFF_UP is false. ++ */ ++ acx_sem_unlock(adev); ++ ++ log(L_INIT, "closed device\n"); ++ FN_EXIT0; ++ return OK; ++} ++ ++ ++/*********************************************************************** ++** acxpci_i_tx_timeout ++** ++** Called from network core. Must not sleep! ++*/ ++static void ++acxpci_i_tx_timeout(struct net_device *ndev) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ unsigned long flags; ++ unsigned int tx_num_cleaned; ++ ++ FN_ENTER; ++ ++ acx_lock(adev, flags); ++ ++ /* clean processed tx descs, they may have been completely full */ ++ tx_num_cleaned = acxpci_l_clean_txdesc(adev); ++ ++ /* nothing cleaned, yet (almost) no free buffers available? ++ * --> clean all tx descs, no matter which status!! ++ * Note that I strongly suspect that doing emergency cleaning ++ * may confuse the firmware. This is a last ditch effort to get ++ * ANYTHING to work again... ++ * ++ * TODO: it's best to simply reset & reinit hw from scratch... ++ */ ++ if ((adev->tx_free <= TX_EMERG_CLEAN) && (tx_num_cleaned == 0)) { ++ printk("%s: FAILED to free any of the many full tx buffers. " ++ "Switching to emergency freeing. " ++ "Please report!\n", ndev->name); ++ acxpci_l_clean_txdesc_emergency(adev); ++ } ++ ++ if (acx_queue_stopped(ndev) && (ACX_STATUS_4_ASSOCIATED == adev->status)) ++ acx_wake_queue(ndev, "after tx timeout"); ++ ++ /* stall may have happened due to radio drift, so recalib radio */ ++ acx_schedule_task(adev, ACX_AFTER_IRQ_CMD_RADIO_RECALIB); ++ ++ /* do unimportant work last */ ++ printk("%s: tx timeout!\n", ndev->name); ++ adev->stats.tx_errors++; ++ ++ acx_unlock(adev, flags); ++ ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acxpci_i_set_multicast_list ++** FIXME: most likely needs refinement ++*/ ++static void ++acxpci_i_set_multicast_list(struct net_device *ndev) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ unsigned long flags; ++ ++ FN_ENTER; ++ ++ acx_lock(adev, flags); ++ ++ /* firmwares don't have allmulti capability, ++ * so just use promiscuous mode instead in this case. */ ++ if (ndev->flags & (IFF_PROMISC|IFF_ALLMULTI)) { ++ SET_BIT(adev->rx_config_1, RX_CFG1_RCV_PROMISCUOUS); ++ CLEAR_BIT(adev->rx_config_1, RX_CFG1_FILTER_ALL_MULTI); ++ SET_BIT(adev->set_mask, SET_RXCONFIG); ++ /* let kernel know in case *we* needed to set promiscuous */ ++ ndev->flags |= (IFF_PROMISC|IFF_ALLMULTI); ++ } else { ++ CLEAR_BIT(adev->rx_config_1, RX_CFG1_RCV_PROMISCUOUS); ++ SET_BIT(adev->rx_config_1, RX_CFG1_FILTER_ALL_MULTI); ++ SET_BIT(adev->set_mask, SET_RXCONFIG); ++ ndev->flags &= ~(IFF_PROMISC|IFF_ALLMULTI); ++ } ++ ++ /* cannot update card settings directly here, atomic context */ ++ acx_schedule_task(adev, ACX_AFTER_IRQ_UPDATE_CARD_CFG); ++ ++ acx_unlock(adev, flags); ++ ++ FN_EXIT0; ++} ++ ++ ++/*************************************************************** ++** acxpci_l_process_rxdesc ++** ++** Called directly and only from the IRQ handler ++*/ ++ ++#if !ACX_DEBUG ++static inline void log_rxbuffer(const acx_device_t *adev) {} ++#else ++static void ++log_rxbuffer(const acx_device_t *adev) ++{ ++ register const struct rxhostdesc *rxhostdesc; ++ int i; ++ /* no FN_ENTER here, we don't want that */ ++ ++ rxhostdesc = adev->rxhostdesc_start; ++ if (unlikely(!rxhostdesc)) return; ++ for (i = 0; i < RX_CNT; i++) { ++ if ((rxhostdesc->Ctl_16 & cpu_to_le16(DESC_CTL_HOSTOWN)) ++ && (rxhostdesc->Status & cpu_to_le32(DESC_STATUS_FULL))) ++ printk("rx: buf %d full\n", i); ++ rxhostdesc++; ++ } ++} ++#endif ++ ++static void ++acxpci_l_process_rxdesc(acx_device_t *adev) ++{ ++ register rxhostdesc_t *hostdesc; ++ unsigned count, tail; ++ ++ FN_ENTER; ++ ++ if (unlikely(acx_debug & L_BUFR)) ++ log_rxbuffer(adev); ++ ++ /* First, have a loop to determine the first descriptor that's ++ * full, just in case there's a mismatch between our current ++ * rx_tail and the full descriptor we're supposed to handle. */ ++ tail = adev->rx_tail; ++ count = RX_CNT; ++ while (1) { ++ hostdesc = &adev->rxhostdesc_start[tail]; ++ /* advance tail regardless of outcome of the below test */ ++ tail = (tail + 1) % RX_CNT; ++ ++ if ((hostdesc->Ctl_16 & cpu_to_le16(DESC_CTL_HOSTOWN)) ++ && (hostdesc->Status & cpu_to_le32(DESC_STATUS_FULL))) ++ break; /* found it! */ ++ ++ if (unlikely(!--count)) /* hmm, no luck: all descs empty, bail out */ ++ goto end; ++ } ++ ++ /* now process descriptors, starting with the first we figured out */ ++ while (1) { ++ log(L_BUFR, "rx: tail=%u Ctl_16=%04X Status=%08X\n", ++ tail, hostdesc->Ctl_16, hostdesc->Status); ++ ++ acx_l_process_rxbuf(adev, hostdesc->data); ++ ++ hostdesc->Status = 0; ++ /* flush all writes before adapter sees CTL_HOSTOWN change */ ++ wmb(); ++ /* Host no longer owns this, needs to be LAST */ ++ CLEAR_BIT(hostdesc->Ctl_16, cpu_to_le16(DESC_CTL_HOSTOWN)); ++ ++ /* ok, descriptor is handled, now check the next descriptor */ ++ hostdesc = &adev->rxhostdesc_start[tail]; ++ ++ /* if next descriptor is empty, then bail out */ ++ if (!(hostdesc->Ctl_16 & cpu_to_le16(DESC_CTL_HOSTOWN)) ++ || !(hostdesc->Status & cpu_to_le32(DESC_STATUS_FULL))) ++ break; ++ ++ tail = (tail + 1) % RX_CNT; ++ } ++end: ++ adev->rx_tail = tail; ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acxpci_i_interrupt ++** ++** IRQ handler (atomic context, must not sleep, blah, blah) ++*/ ++ ++/* scan is complete. all frames now on the receive queue are valid */ ++#define INFO_SCAN_COMPLETE 0x0001 ++#define INFO_WEP_KEY_NOT_FOUND 0x0002 ++/* hw has been reset as the result of a watchdog timer timeout */ ++#define INFO_WATCH_DOG_RESET 0x0003 ++/* failed to send out NULL frame from PS mode notification to AP */ ++/* recommended action: try entering 802.11 PS mode again */ ++#define INFO_PS_FAIL 0x0004 ++/* encryption/decryption process on a packet failed */ ++#define INFO_IV_ICV_FAILURE 0x0005 ++ ++/* Info mailbox format: ++2 bytes: type ++2 bytes: status ++more bytes may follow ++ rumors say about status: ++ 0x0000 info available (set by hw) ++ 0x0001 information received (must be set by host) ++ 0x1000 info available, mailbox overflowed (messages lost) (set by hw) ++ but in practice we've seen: ++ 0x9000 when we did not set status to 0x0001 on prev message ++ 0x1001 when we did set it ++ 0x0000 was never seen ++ conclusion: this is really a bitfield: ++ 0x1000 is 'info available' bit ++ 'mailbox overflowed' bit is 0x8000, not 0x1000 ++ value of 0x0000 probably means that there are no messages at all ++ P.S. I dunno how in hell hw is supposed to notice that messages are lost - ++ it does NOT clear bit 0x0001, and this bit will probably stay forever set ++ after we set it once. Let's hope this will be fixed in firmware someday ++*/ ++ ++static void ++handle_info_irq(acx_device_t *adev) ++{ ++#if ACX_DEBUG ++ static const char * const info_type_msg[] = { ++ "(unknown)", ++ "scan complete", ++ "WEP key not found", ++ "internal watchdog reset was done", ++ "failed to send powersave (NULL frame) notification to AP", ++ "encrypt/decrypt on a packet has failed", ++ "TKIP tx keys disabled", ++ "TKIP rx keys disabled", ++ "TKIP rx: key ID not found", ++ "???", ++ "???", ++ "???", ++ "???", ++ "???", ++ "???", ++ "???", ++ "TKIP IV value exceeds thresh" ++ }; ++#endif ++ u32 info_type, info_status; ++ ++ info_type = readl(adev->info_area); ++ info_status = (info_type >> 16); ++ info_type = (u16)info_type; ++ ++ /* inform fw that we have read this info message */ ++ writel(info_type | 0x00010000, adev->info_area); ++ write_reg16(adev, IO_ACX_INT_TRIG, INT_TRIG_INFOACK); ++ write_flush(adev); ++ ++ log(L_CTL, "info_type:%04X info_status:%04X\n", ++ info_type, info_status); ++ ++ log(L_IRQ, "got Info IRQ: status %04X type %04X: %s\n", ++ info_status, info_type, ++ info_type_msg[(info_type >= VEC_SIZE(info_type_msg)) ? ++ 0 : info_type] ++ ); ++} ++ ++ ++static void ++log_unusual_irq(u16 irqtype) { ++ /* ++ if (!printk_ratelimit()) ++ return; ++ */ ++ ++ printk("acx: got"); ++ if (irqtype & HOST_INT_RX_DATA) { ++ printk(" Rx_Data"); ++ } ++ /* HOST_INT_TX_COMPLETE */ ++ if (irqtype & HOST_INT_TX_XFER) { ++ printk(" Tx_Xfer"); ++ } ++ /* HOST_INT_RX_COMPLETE */ ++ if (irqtype & HOST_INT_DTIM) { ++ printk(" DTIM"); ++ } ++ if (irqtype & HOST_INT_BEACON) { ++ printk(" Beacon"); ++ } ++ if (irqtype & HOST_INT_TIMER) { ++ log(L_IRQ, " Timer"); ++ } ++ if (irqtype & HOST_INT_KEY_NOT_FOUND) { ++ printk(" Key_Not_Found"); ++ } ++ if (irqtype & HOST_INT_IV_ICV_FAILURE) { ++ printk(" IV_ICV_Failure (crypto)"); ++ } ++ /* HOST_INT_CMD_COMPLETE */ ++ /* HOST_INT_INFO */ ++ if (irqtype & HOST_INT_OVERFLOW) { ++ printk(" Overflow"); ++ } ++ if (irqtype & HOST_INT_PROCESS_ERROR) { ++ printk(" Process_Error"); ++ } ++ /* HOST_INT_SCAN_COMPLETE */ ++ if (irqtype & HOST_INT_FCS_THRESHOLD) { ++ printk(" FCS_Threshold"); ++ } ++ if (irqtype & HOST_INT_UNKNOWN) { ++ printk(" Unknown"); ++ } ++ printk(" IRQ(s)\n"); ++} ++ ++ ++static void ++update_link_quality_led(acx_device_t *adev) ++{ ++ int qual; ++ ++ qual = acx_signal_determine_quality(adev->wstats.qual.level, adev->wstats.qual.noise); ++ if (qual > adev->brange_max_quality) ++ qual = adev->brange_max_quality; ++ ++ if (time_after(jiffies, adev->brange_time_last_state_change + ++ (HZ/2 - HZ/2 * (unsigned long)qual / adev->brange_max_quality ) )) { ++ acxpci_l_power_led(adev, (adev->brange_last_state == 0)); ++ adev->brange_last_state ^= 1; /* toggle */ ++ adev->brange_time_last_state_change = jiffies; ++ } ++} ++ ++ ++#define MAX_IRQLOOPS_PER_JIFFY (20000/HZ) /* a la orinoco.c */ ++ ++static irqreturn_t ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) ++acxpci_i_interrupt(int irq, void *dev_id) ++#else ++acxpci_i_interrupt(int irq, void *dev_id, struct pt_regs *regs) ++#endif ++{ ++ acx_device_t *adev; ++ unsigned long flags; ++ unsigned int irqcount = MAX_IRQLOOPS_PER_JIFFY; ++ register u16 irqtype; ++ u16 unmasked; ++ ++ adev = ndev2adev((struct net_device*)dev_id); ++ ++ /* LOCKING: can just spin_lock() since IRQs are disabled anyway. ++ * I am paranoid */ ++ acx_lock(adev, flags); ++ ++ unmasked = read_reg16(adev, IO_ACX_IRQ_STATUS_CLEAR); ++ if (unlikely(0xffff == unmasked)) { ++ /* 0xffff value hints at missing hardware, ++ * so don't do anything. ++ * Not very clean, but other drivers do the same... */ ++ log(L_IRQ, "IRQ type:FFFF - device removed? IRQ_NONE\n"); ++ goto none; ++ } ++ ++ /* We will check only "interesting" IRQ types */ ++ irqtype = unmasked & ~adev->irq_mask; ++ if (!irqtype) { ++ /* We are on a shared IRQ line and it wasn't our IRQ */ ++ log(L_IRQ, "IRQ type:%04X, mask:%04X - all are masked, IRQ_NONE\n", ++ unmasked, adev->irq_mask); ++ goto none; ++ } ++ ++ /* Done here because IRQ_NONEs taking three lines of log ++ ** drive me crazy */ ++ FN_ENTER; ++ ++#define IRQ_ITERATE 1 ++#if IRQ_ITERATE ++if (jiffies != adev->irq_last_jiffies) { ++ adev->irq_loops_this_jiffy = 0; ++ adev->irq_last_jiffies = jiffies; ++} ++ ++/* safety condition; we'll normally abort loop below ++ * in case no IRQ type occurred */ ++while (likely(--irqcount)) { ++#endif ++ /* ACK all IRQs ASAP */ ++ write_reg16(adev, IO_ACX_IRQ_ACK, 0xffff); ++ ++ log(L_IRQ, "IRQ type:%04X, mask:%04X, type & ~mask:%04X\n", ++ unmasked, adev->irq_mask, irqtype); ++ ++ /* Handle most important IRQ types first */ ++ if (irqtype & HOST_INT_RX_COMPLETE) { ++ log(L_IRQ, "got Rx_Complete IRQ\n"); ++ acxpci_l_process_rxdesc(adev); ++ } ++ if (irqtype & HOST_INT_TX_COMPLETE) { ++ log(L_IRQ, "got Tx_Complete IRQ\n"); ++ /* don't clean up on each Tx complete, wait a bit ++ * unless we're going towards full, in which case ++ * we do it immediately, too (otherwise we might lockup ++ * with a full Tx buffer if we go into ++ * acxpci_l_clean_txdesc() at a time when we won't wakeup ++ * the net queue in there for some reason...) */ ++ if (adev->tx_free <= TX_START_CLEAN) { ++#if TX_CLEANUP_IN_SOFTIRQ ++ acx_schedule_task(adev, ACX_AFTER_IRQ_TX_CLEANUP); ++#else ++ acxpci_l_clean_txdesc(adev); ++#endif ++ } ++ } ++ ++ /* Less frequent ones */ ++ if (irqtype & (0 ++ | HOST_INT_CMD_COMPLETE ++ | HOST_INT_INFO ++ | HOST_INT_SCAN_COMPLETE ++ )) { ++ if (irqtype & HOST_INT_CMD_COMPLETE) { ++ log(L_IRQ, "got Command_Complete IRQ\n"); ++ /* save the state for the running issue_cmd() */ ++ SET_BIT(adev->irq_status, HOST_INT_CMD_COMPLETE); ++ } ++ if (irqtype & HOST_INT_INFO) { ++ handle_info_irq(adev); ++ } ++ if (irqtype & HOST_INT_SCAN_COMPLETE) { ++ log(L_IRQ, "got Scan_Complete IRQ\n"); ++ /* need to do that in process context */ ++ acx_schedule_task(adev, ACX_AFTER_IRQ_COMPLETE_SCAN); ++ /* remember that fw is not scanning anymore */ ++ SET_BIT(adev->irq_status, HOST_INT_SCAN_COMPLETE); ++ } ++ } ++ ++ /* These we just log, but either they happen rarely ++ * or we keep them masked out */ ++ if (irqtype & (0 ++ | HOST_INT_RX_DATA ++ /* | HOST_INT_TX_COMPLETE */ ++ | HOST_INT_TX_XFER ++ /* | HOST_INT_RX_COMPLETE */ ++ | HOST_INT_DTIM ++ | HOST_INT_BEACON ++ | HOST_INT_TIMER ++ | HOST_INT_KEY_NOT_FOUND ++ | HOST_INT_IV_ICV_FAILURE ++ /* | HOST_INT_CMD_COMPLETE */ ++ /* | HOST_INT_INFO */ ++ | HOST_INT_OVERFLOW ++ | HOST_INT_PROCESS_ERROR ++ /* | HOST_INT_SCAN_COMPLETE */ ++ | HOST_INT_FCS_THRESHOLD ++ | HOST_INT_UNKNOWN ++ )) { ++ log_unusual_irq(irqtype); ++ } ++ ++#if IRQ_ITERATE ++ unmasked = read_reg16(adev, IO_ACX_IRQ_STATUS_CLEAR); ++ irqtype = unmasked & ~adev->irq_mask; ++ /* Bail out if no new IRQ bits or if all are masked out */ ++ if (!irqtype) ++ break; ++ ++ if (unlikely(++adev->irq_loops_this_jiffy > MAX_IRQLOOPS_PER_JIFFY)) { ++ printk(KERN_ERR "acx: too many interrupts per jiffy!\n"); ++ /* Looks like card floods us with IRQs! Try to stop that */ ++ write_reg16(adev, IO_ACX_IRQ_MASK, 0xffff); ++ /* This will short-circuit all future attempts to handle IRQ. ++ * We cant do much more... */ ++ adev->irq_mask = 0; ++ break; ++ } ++} ++#endif ++ /* Routine to perform blink with range */ ++ if (unlikely(adev->led_power == 2)) ++ update_link_quality_led(adev); ++ ++/* handled: */ ++ /* write_flush(adev); - not needed, last op was read anyway */ ++ acx_unlock(adev, flags); ++ FN_EXIT0; ++ return IRQ_HANDLED; ++ ++none: ++ acx_unlock(adev, flags); ++ return IRQ_NONE; ++} ++ ++ ++/*********************************************************************** ++** acxpci_l_power_led ++*/ ++void ++acxpci_l_power_led(acx_device_t *adev, int enable) ++{ ++ u16 gpio_pled = IS_ACX111(adev) ? 0x0040 : 0x0800; ++ ++ /* A hack. Not moving message rate limiting to adev->xxx ++ * (it's only a debug message after all) */ ++ static int rate_limit = 0; ++ ++ if (rate_limit++ < 3) ++ log(L_IOCTL, "Please report in case toggling the power " ++ "LED doesn't work for your card!\n"); ++ if (enable) ++ write_reg16(adev, IO_ACX_GPIO_OUT, ++ read_reg16(adev, IO_ACX_GPIO_OUT) & ~gpio_pled); ++ else ++ write_reg16(adev, IO_ACX_GPIO_OUT, ++ read_reg16(adev, IO_ACX_GPIO_OUT) | gpio_pled); ++} ++ ++ ++/*********************************************************************** ++** Ioctls ++*/ ++ ++/*********************************************************************** ++*/ ++int ++acx111pci_ioctl_info( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ struct iw_param *vwrq, ++ char *extra) ++{ ++#if ACX_DEBUG > 1 ++ acx_device_t *adev = ndev2adev(ndev); ++ rxdesc_t *rxdesc; ++ txdesc_t *txdesc; ++ rxhostdesc_t *rxhostdesc; ++ txhostdesc_t *txhostdesc; ++ struct acx111_ie_memoryconfig memconf; ++ struct acx111_ie_queueconfig queueconf; ++ unsigned long flags; ++ int i; ++ char memmap[0x34]; ++ char rxconfig[0x8]; ++ char fcserror[0x8]; ++ char ratefallback[0x5]; ++ ++ if ( !(acx_debug & (L_IOCTL|L_DEBUG)) ) ++ return OK; ++ /* using printk() since we checked debug flag already */ ++ ++ acx_sem_lock(adev); ++ ++ if (!IS_ACX111(adev)) { ++ printk("acx111-specific function called " ++ "with non-acx111 chip, aborting\n"); ++ goto end_ok; ++ } ++ ++ /* get Acx111 Memory Configuration */ ++ memset(&memconf, 0, sizeof(memconf)); ++ /* BTW, fails with 12 (Write only) error code. ++ ** Retained for easy testing of issue_cmd error handling :) */ ++ acx_s_interrogate(adev, &memconf, ACX1xx_IE_QUEUE_CONFIG); ++ ++ /* get Acx111 Queue Configuration */ ++ memset(&queueconf, 0, sizeof(queueconf)); ++ acx_s_interrogate(adev, &queueconf, ACX1xx_IE_MEMORY_CONFIG_OPTIONS); ++ ++ /* get Acx111 Memory Map */ ++ memset(memmap, 0, sizeof(memmap)); ++ acx_s_interrogate(adev, &memmap, ACX1xx_IE_MEMORY_MAP); ++ ++ /* get Acx111 Rx Config */ ++ memset(rxconfig, 0, sizeof(rxconfig)); ++ acx_s_interrogate(adev, &rxconfig, ACX1xx_IE_RXCONFIG); ++ ++ /* get Acx111 fcs error count */ ++ memset(fcserror, 0, sizeof(fcserror)); ++ acx_s_interrogate(adev, &fcserror, ACX1xx_IE_FCS_ERROR_COUNT); ++ ++ /* get Acx111 rate fallback */ ++ memset(ratefallback, 0, sizeof(ratefallback)); ++ acx_s_interrogate(adev, &ratefallback, ACX1xx_IE_RATE_FALLBACK); ++ ++ /* force occurrence of a beacon interrupt */ ++ /* TODO: comment why is this necessary */ ++ write_reg16(adev, IO_ACX_HINT_TRIG, HOST_INT_BEACON); ++ ++ /* dump Acx111 Mem Configuration */ ++ printk("dump mem config:\n" ++ "data read: %d, struct size: %d\n" ++ "Number of stations: %1X\n" ++ "Memory block size: %1X\n" ++ "tx/rx memory block allocation: %1X\n" ++ "count rx: %X / tx: %X queues\n" ++ "options %1X\n" ++ "fragmentation %1X\n" ++ "Rx Queue 1 Count Descriptors: %X\n" ++ "Rx Queue 1 Host Memory Start: %X\n" ++ "Tx Queue 1 Count Descriptors: %X\n" ++ "Tx Queue 1 Attributes: %X\n", ++ memconf.len, (int) sizeof(memconf), ++ memconf.no_of_stations, ++ memconf.memory_block_size, ++ memconf.tx_rx_memory_block_allocation, ++ memconf.count_rx_queues, memconf.count_tx_queues, ++ memconf.options, ++ memconf.fragmentation, ++ memconf.rx_queue1_count_descs, ++ acx2cpu(memconf.rx_queue1_host_rx_start), ++ memconf.tx_queue1_count_descs, ++ memconf.tx_queue1_attributes); ++ ++ /* dump Acx111 Queue Configuration */ ++ printk("dump queue head:\n" ++ "data read: %d, struct size: %d\n" ++ "tx_memory_block_address (from card): %X\n" ++ "rx_memory_block_address (from card): %X\n" ++ "rx1_queue address (from card): %X\n" ++ "tx1_queue address (from card): %X\n" ++ "tx1_queue attributes (from card): %X\n", ++ queueconf.len, (int) sizeof(queueconf), ++ queueconf.tx_memory_block_address, ++ queueconf.rx_memory_block_address, ++ queueconf.rx1_queue_address, ++ queueconf.tx1_queue_address, ++ queueconf.tx1_attributes); ++ ++ /* dump Acx111 Mem Map */ ++ printk("dump mem map:\n" ++ "data read: %d, struct size: %d\n" ++ "Code start: %X\n" ++ "Code end: %X\n" ++ "WEP default key start: %X\n" ++ "WEP default key end: %X\n" ++ "STA table start: %X\n" ++ "STA table end: %X\n" ++ "Packet template start: %X\n" ++ "Packet template end: %X\n" ++ "Queue memory start: %X\n" ++ "Queue memory end: %X\n" ++ "Packet memory pool start: %X\n" ++ "Packet memory pool end: %X\n" ++ "iobase: %p\n" ++ "iobase2: %p\n", ++ *((u16 *)&memmap[0x02]), (int) sizeof(memmap), ++ *((u32 *)&memmap[0x04]), ++ *((u32 *)&memmap[0x08]), ++ *((u32 *)&memmap[0x0C]), ++ *((u32 *)&memmap[0x10]), ++ *((u32 *)&memmap[0x14]), ++ *((u32 *)&memmap[0x18]), ++ *((u32 *)&memmap[0x1C]), ++ *((u32 *)&memmap[0x20]), ++ *((u32 *)&memmap[0x24]), ++ *((u32 *)&memmap[0x28]), ++ *((u32 *)&memmap[0x2C]), ++ *((u32 *)&memmap[0x30]), ++ adev->iobase, ++ adev->iobase2); ++ ++ /* dump Acx111 Rx Config */ ++ printk("dump rx config:\n" ++ "data read: %d, struct size: %d\n" ++ "rx config: %X\n" ++ "rx filter config: %X\n", ++ *((u16 *)&rxconfig[0x02]), (int) sizeof(rxconfig), ++ *((u16 *)&rxconfig[0x04]), ++ *((u16 *)&rxconfig[0x06])); ++ ++ /* dump Acx111 fcs error */ ++ printk("dump fcserror:\n" ++ "data read: %d, struct size: %d\n" ++ "fcserrors: %X\n", ++ *((u16 *)&fcserror[0x02]), (int) sizeof(fcserror), ++ *((u32 *)&fcserror[0x04])); ++ ++ /* dump Acx111 rate fallback */ ++ printk("dump rate fallback:\n" ++ "data read: %d, struct size: %d\n" ++ "ratefallback: %X\n", ++ *((u16 *)&ratefallback[0x02]), (int) sizeof(ratefallback), ++ *((u8 *)&ratefallback[0x04])); ++ ++ /* protect against IRQ */ ++ acx_lock(adev, flags); ++ ++ /* dump acx111 internal rx descriptor ring buffer */ ++ rxdesc = adev->rxdesc_start; ++ ++ /* loop over complete receive pool */ ++ if (rxdesc) for (i = 0; i < RX_CNT; i++) { ++ printk("\ndump internal rxdesc %d:\n" ++ "mem pos %p\n" ++ "next 0x%X\n" ++ "acx mem pointer (dynamic) 0x%X\n" ++ "CTL (dynamic) 0x%X\n" ++ "Rate (dynamic) 0x%X\n" ++ "RxStatus (dynamic) 0x%X\n" ++ "Mod/Pre (dynamic) 0x%X\n", ++ i, ++ rxdesc, ++ acx2cpu(rxdesc->pNextDesc), ++ acx2cpu(rxdesc->ACXMemPtr), ++ rxdesc->Ctl_8, ++ rxdesc->rate, ++ rxdesc->error, ++ rxdesc->SNR); ++ rxdesc++; ++ } ++ ++ /* dump host rx descriptor ring buffer */ ++ ++ rxhostdesc = adev->rxhostdesc_start; ++ ++ /* loop over complete receive pool */ ++ if (rxhostdesc) for (i = 0; i < RX_CNT; i++) { ++ printk("\ndump host rxdesc %d:\n" ++ "mem pos %p\n" ++ "buffer mem pos 0x%X\n" ++ "buffer mem offset 0x%X\n" ++ "CTL 0x%X\n" ++ "Length 0x%X\n" ++ "next 0x%X\n" ++ "Status 0x%X\n", ++ i, ++ rxhostdesc, ++ acx2cpu(rxhostdesc->data_phy), ++ rxhostdesc->data_offset, ++ le16_to_cpu(rxhostdesc->Ctl_16), ++ le16_to_cpu(rxhostdesc->length), ++ acx2cpu(rxhostdesc->desc_phy_next), ++ rxhostdesc->Status); ++ rxhostdesc++; ++ } ++ ++ /* dump acx111 internal tx descriptor ring buffer */ ++ txdesc = adev->txdesc_start; ++ ++ /* loop over complete transmit pool */ ++ if (txdesc) for (i = 0; i < TX_CNT; i++) { ++ printk("\ndump internal txdesc %d:\n" ++ "size 0x%X\n" ++ "mem pos %p\n" ++ "next 0x%X\n" ++ "acx mem pointer (dynamic) 0x%X\n" ++ "host mem pointer (dynamic) 0x%X\n" ++ "length (dynamic) 0x%X\n" ++ "CTL (dynamic) 0x%X\n" ++ "CTL2 (dynamic) 0x%X\n" ++ "Status (dynamic) 0x%X\n" ++ "Rate (dynamic) 0x%X\n", ++ i, ++ (int) sizeof(struct txdesc), ++ txdesc, ++ acx2cpu(txdesc->pNextDesc), ++ acx2cpu(txdesc->AcxMemPtr), ++ acx2cpu(txdesc->HostMemPtr), ++ le16_to_cpu(txdesc->total_length), ++ txdesc->Ctl_8, ++ txdesc->Ctl2_8, txdesc->error, ++ txdesc->u.r1.rate); ++ txdesc = advance_txdesc(adev, txdesc, 1); ++ } ++ ++ /* dump host tx descriptor ring buffer */ ++ ++ txhostdesc = adev->txhostdesc_start; ++ ++ /* loop over complete host send pool */ ++ if (txhostdesc) for (i = 0; i < TX_CNT * 2; i++) { ++ printk("\ndump host txdesc %d:\n" ++ "mem pos %p\n" ++ "buffer mem pos 0x%X\n" ++ "buffer mem offset 0x%X\n" ++ "CTL 0x%X\n" ++ "Length 0x%X\n" ++ "next 0x%X\n" ++ "Status 0x%X\n", ++ i, ++ txhostdesc, ++ acx2cpu(txhostdesc->data_phy), ++ txhostdesc->data_offset, ++ le16_to_cpu(txhostdesc->Ctl_16), ++ le16_to_cpu(txhostdesc->length), ++ acx2cpu(txhostdesc->desc_phy_next), ++ le32_to_cpu(txhostdesc->Status)); ++ txhostdesc++; ++ } ++ ++ /* write_reg16(adev, 0xb4, 0x4); */ ++ ++ acx_unlock(adev, flags); ++end_ok: ++ ++ acx_sem_unlock(adev); ++#endif /* ACX_DEBUG */ ++ return OK; ++} ++ ++ ++/*********************************************************************** ++*/ ++int ++acx100pci_ioctl_set_phy_amp_bias( ++ struct net_device *ndev, ++ struct iw_request_info *info, ++ struct iw_param *vwrq, ++ char *extra) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ unsigned long flags; ++ u16 gpio_old; ++ ++ if (!IS_ACX100(adev)) { ++ /* WARNING!!! ++ * Removing this check *might* damage ++ * hardware, since we're tweaking GPIOs here after all!!! ++ * You've been warned... ++ * WARNING!!! */ ++ printk("acx: sorry, setting bias level for non-acx100 " ++ "is not supported yet\n"); ++ return OK; ++ } ++ ++ if (*extra > 7) { ++ printk("acx: invalid bias parameter, range is 0-7\n"); ++ return -EINVAL; ++ } ++ ++ acx_sem_lock(adev); ++ ++ /* Need to lock accesses to [IO_ACX_GPIO_OUT]: ++ * IRQ handler uses it to update LED */ ++ acx_lock(adev, flags); ++ gpio_old = read_reg16(adev, IO_ACX_GPIO_OUT); ++ write_reg16(adev, IO_ACX_GPIO_OUT, (gpio_old & 0xf8ff) | ((u16)*extra << 8)); ++ acx_unlock(adev, flags); ++ ++ log(L_DEBUG, "gpio_old: 0x%04X\n", gpio_old); ++ printk("%s: PHY power amplifier bias: old:%d, new:%d\n", ++ ndev->name, ++ (gpio_old & 0x0700) >> 8, (unsigned char)*extra); ++ ++ acx_sem_unlock(adev); ++ ++ return OK; ++} ++ ++ ++/*************************************************************** ++** acxpci_l_alloc_tx ++** Actually returns a txdesc_t* ptr ++** ++** FIXME: in case of fragments, should allocate multiple descrs ++** after figuring out how many we need and whether we still have ++** sufficiently many. ++*/ ++tx_t* ++acxpci_l_alloc_tx(acx_device_t *adev) ++{ ++ struct txdesc *txdesc; ++ unsigned head; ++ u8 ctl8; ++ ++ FN_ENTER; ++ ++ if (unlikely(!adev->tx_free)) { ++ printk("acx: BUG: no free txdesc left\n"); ++ txdesc = NULL; ++ goto end; ++ } ++ ++ head = adev->tx_head; ++ txdesc = get_txdesc(adev, head); ++ ctl8 = txdesc->Ctl_8; ++ ++ /* 2005-10-11: there were several bug reports on this happening ++ ** but now cause seems to be understood & fixed */ ++ if (unlikely(DESC_CTL_HOSTOWN != (ctl8 & DESC_CTL_ACXDONE_HOSTOWN))) { ++ /* whoops, descr at current index is not free, so probably ++ * ring buffer already full */ ++ printk("acx: BUG: tx_head:%d Ctl8:0x%02X - failed to find " ++ "free txdesc\n", head, ctl8); ++ txdesc = NULL; ++ goto end; ++ } ++ ++ /* Needed in case txdesc won't be eventually submitted for tx */ ++ txdesc->Ctl_8 = DESC_CTL_ACXDONE_HOSTOWN; ++ ++ adev->tx_free--; ++ log(L_BUFT, "tx: got desc %u, %u remain\n", ++ head, adev->tx_free); ++ /* Keep a few free descs between head and tail of tx ring. ++ ** It is not absolutely needed, just feels safer */ ++ if (adev->tx_free < TX_STOP_QUEUE) { ++ log(L_BUF, "stop queue (%u tx desc left)\n", ++ adev->tx_free); ++ acx_stop_queue(adev->ndev, NULL); ++ } ++ ++ /* returning current descriptor, so advance to next free one */ ++ adev->tx_head = (head + 1) % TX_CNT; ++end: ++ FN_EXIT0; ++ ++ return (tx_t*)txdesc; ++} ++ ++ ++/*********************************************************************** ++*/ ++void* ++acxpci_l_get_txbuf(acx_device_t *adev, tx_t* tx_opaque) ++{ ++ return get_txhostdesc(adev, (txdesc_t*)tx_opaque)->data; ++} ++ ++ ++/*********************************************************************** ++** acxpci_l_tx_data ++** ++** Can be called from IRQ (rx -> (AP bridging or mgmt response) -> tx). ++** Can be called from acx_i_start_xmit (data frames from net core). ++** ++** FIXME: in case of fragments, should loop over the number of ++** pre-allocated tx descrs, properly setting up transfer data and ++** CTL_xxx flags according to fragment number. ++*/ ++void ++acxpci_l_tx_data(acx_device_t *adev, tx_t* tx_opaque, int len) ++{ ++ txdesc_t *txdesc = (txdesc_t*)tx_opaque; ++ txhostdesc_t *hostdesc1, *hostdesc2; ++ client_t *clt; ++ u16 rate_cur; ++ u8 Ctl_8, Ctl2_8; ++ ++ FN_ENTER; ++ ++ /* fw doesn't tx such packets anyhow */ ++ if (unlikely(len < WLAN_HDR_A3_LEN)) ++ goto end; ++ ++ hostdesc1 = get_txhostdesc(adev, txdesc); ++ /* modify flag status in separate variable to be able to write it back ++ * in one big swoop later (also in order to have less device memory ++ * accesses) */ ++ Ctl_8 = txdesc->Ctl_8; ++ Ctl2_8 = 0; /* really need to init it to 0, not txdesc->Ctl2_8, it seems */ ++ ++ hostdesc2 = hostdesc1 + 1; ++ ++ /* DON'T simply set Ctl field to 0 here globally, ++ * it needs to maintain a consistent flag status (those are state flags!!), ++ * otherwise it may lead to severe disruption. Only set or reset particular ++ * flags at the exact moment this is needed... */ ++ ++ /* let chip do RTS/CTS handshaking before sending ++ * in case packet size exceeds threshold */ ++ if (len > adev->rts_threshold) ++ SET_BIT(Ctl2_8, DESC_CTL2_RTS); ++ else ++ CLEAR_BIT(Ctl2_8, DESC_CTL2_RTS); ++ ++ switch (adev->mode) { ++ case ACX_MODE_0_ADHOC: ++ case ACX_MODE_3_AP: ++ clt = acx_l_sta_list_get(adev, ((wlan_hdr_t*)hostdesc1->data)->a1); ++ break; ++ case ACX_MODE_2_STA: ++ clt = adev->ap_client; ++ break; ++#if 0 ++/* testing was done on acx111: */ ++ case ACX_MODE_MONITOR: ++ SET_BIT(Ctl2_8, 0 ++/* sends CTS to self before packet */ ++ + DESC_CTL2_SEQ /* don't increase sequence field */ ++/* not working (looks like good fcs is still added) */ ++ + DESC_CTL2_FCS /* don't add the FCS */ ++/* not tested */ ++ + DESC_CTL2_MORE_FRAG ++/* not tested */ ++ + DESC_CTL2_RETRY /* don't increase retry field */ ++/* not tested */ ++ + DESC_CTL2_POWER /* don't increase power mgmt. field */ ++/* no effect */ ++ + DESC_CTL2_WEP /* encrypt this frame */ ++/* not tested */ ++ + DESC_CTL2_DUR /* don't increase duration field */ ++ ); ++ /* fallthrough */ ++#endif ++ default: /* ACX_MODE_OFF, ACX_MODE_MONITOR */ ++ clt = NULL; ++ break; ++ } ++ ++ rate_cur = clt ? clt->rate_cur : adev->rate_bcast; ++ if (unlikely(!rate_cur)) { ++ printk("acx: driver bug! bad ratemask\n"); ++ goto end; ++ } ++ ++ /* used in tx cleanup routine for auto rate and accounting: */ ++ put_txcr(adev, txdesc, clt, rate_cur); ++ ++ txdesc->total_length = cpu_to_le16(len); ++ hostdesc2->length = cpu_to_le16(len - WLAN_HDR_A3_LEN); ++ if (IS_ACX111(adev)) { ++ /* note that if !txdesc->do_auto, txrate->cur ++ ** has only one nonzero bit */ ++ txdesc->u.r2.rate111 = cpu_to_le16( ++ rate_cur ++ /* WARNING: I was never able to make it work with prism54 AP. ++ ** It was falling down to 1Mbit where shortpre is not applicable, ++ ** and not working at all at "5,11 basic rates only" setting. ++ ** I even didn't see tx packets in radio packet capture. ++ ** Disabled for now --vda */ ++ /*| ((clt->shortpre && clt->cur!=RATE111_1) ? RATE111_SHORTPRE : 0) */ ++ ); ++#ifdef TODO_FIGURE_OUT_WHEN_TO_SET_THIS ++ /* should add this to rate111 above as necessary */ ++ | (clt->pbcc511 ? RATE111_PBCC511 : 0) ++#endif ++ hostdesc1->length = cpu_to_le16(len); ++ } else { /* ACX100 */ ++ u8 rate_100 = clt ? clt->rate_100 : adev->rate_bcast100; ++ txdesc->u.r1.rate = rate_100; ++#ifdef TODO_FIGURE_OUT_WHEN_TO_SET_THIS ++ if (clt->pbcc511) { ++ if (n == RATE100_5 || n == RATE100_11) ++ n |= RATE100_PBCC511; ++ } ++ ++ if (clt->shortpre && (clt->cur != RATE111_1)) ++ SET_BIT(Ctl_8, DESC_CTL_SHORT_PREAMBLE); /* set Short Preamble */ ++#endif ++ /* set autodma and reclaim and 1st mpdu */ ++ SET_BIT(Ctl_8, DESC_CTL_AUTODMA | DESC_CTL_RECLAIM | DESC_CTL_FIRSTFRAG); ++#if ACX_FRAGMENTATION ++ /* SET_BIT(Ctl2_8, DESC_CTL2_MORE_FRAG); cannot set it unconditionally, needs to be set for all non-last fragments */ ++#endif ++ hostdesc1->length = cpu_to_le16(WLAN_HDR_A3_LEN); ++ } ++ /* don't need to clean ack/rts statistics here, already ++ * done on descr cleanup */ ++ ++ /* clears HOSTOWN and ACXDONE bits, thus telling that the descriptors ++ * are now owned by the acx100; do this as LAST operation */ ++ CLEAR_BIT(Ctl_8, DESC_CTL_ACXDONE_HOSTOWN); ++ /* flush writes before we release hostdesc to the adapter here */ ++ wmb(); ++ CLEAR_BIT(hostdesc1->Ctl_16, cpu_to_le16(DESC_CTL_HOSTOWN)); ++ CLEAR_BIT(hostdesc2->Ctl_16, cpu_to_le16(DESC_CTL_HOSTOWN)); ++ ++ /* write back modified flags */ ++ txdesc->Ctl2_8 = Ctl2_8; ++ txdesc->Ctl_8 = Ctl_8; ++ /* unused: txdesc->tx_time = cpu_to_le32(jiffies); */ ++ ++ /* flush writes before we tell the adapter that it's its turn now */ ++ mmiowb(); ++ write_reg16(adev, IO_ACX_INT_TRIG, INT_TRIG_TXPRC); ++ write_flush(adev); ++ ++ /* log the packet content AFTER sending it, ++ * in order to not delay sending any further than absolutely needed ++ * Do separate logs for acx100/111 to have human-readable rates */ ++ if (unlikely(acx_debug & (L_XFER|L_DATA))) { ++ u16 fc = ((wlan_hdr_t*)hostdesc1->data)->fc; ++ if (IS_ACX111(adev)) ++ printk("tx: pkt (%s): len %d " ++ "rate %04X%s status %u\n", ++ acx_get_packet_type_string(le16_to_cpu(fc)), len, ++ le16_to_cpu(txdesc->u.r2.rate111), ++ (le16_to_cpu(txdesc->u.r2.rate111) & RATE111_SHORTPRE) ? "(SPr)" : "", ++ adev->status); ++ else ++ printk("tx: pkt (%s): len %d rate %03u%s status %u\n", ++ acx_get_packet_type_string(fc), len, ++ txdesc->u.r1.rate, ++ (Ctl_8 & DESC_CTL_SHORT_PREAMBLE) ? "(SPr)" : "", ++ adev->status); ++ ++ if (acx_debug & L_DATA) { ++ printk("tx: 802.11 [%d]: ", len); ++ acx_dump_bytes(hostdesc1->data, len); ++ } ++ } ++end: ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acxpci_l_clean_txdesc ++** ++** This function resets the txdescs' status when the ACX100 ++** signals the TX done IRQ (txdescs have been processed), starting with ++** the pool index of the descriptor which we would use next, ++** in order to make sure that we can be as fast as possible ++** in filling new txdescs. ++** Everytime we get called we know where the next packet to be cleaned is. ++*/ ++ ++#if !ACX_DEBUG ++static inline void log_txbuffer(const acx_device_t *adev) {} ++#else ++static void ++log_txbuffer(acx_device_t *adev) ++{ ++ txdesc_t *txdesc; ++ int i; ++ ++ /* no FN_ENTER here, we don't want that */ ++ /* no locks here, since it's entirely non-critical code */ ++ txdesc = adev->txdesc_start; ++ if (unlikely(!txdesc)) return; ++ printk("tx: desc->Ctl8's:"); ++ for (i = 0; i < TX_CNT; i++) { ++ printk(" %02X", txdesc->Ctl_8); ++ txdesc = advance_txdesc(adev, txdesc, 1); ++ } ++ printk("\n"); ++} ++#endif ++ ++ ++static void ++handle_tx_error(acx_device_t *adev, u8 error, unsigned int finger) ++{ ++ const char *err = "unknown error"; ++ ++ /* hmm, should we handle this as a mask ++ * of *several* bits? ++ * For now I think only caring about ++ * individual bits is ok... */ ++ switch (error) { ++ case 0x01: ++ err = "no Tx due to error in other fragment"; ++ adev->wstats.discard.fragment++; ++ break; ++ case 0x02: ++ err = "Tx aborted"; ++ adev->stats.tx_aborted_errors++; ++ break; ++ case 0x04: ++ err = "Tx desc wrong parameters"; ++ adev->wstats.discard.misc++; ++ break; ++ case 0x08: ++ err = "WEP key not found"; ++ adev->wstats.discard.misc++; ++ break; ++ case 0x10: ++ err = "MSDU lifetime timeout? - try changing " ++ "'iwconfig retry lifetime XXX'"; ++ adev->wstats.discard.misc++; ++ break; ++ case 0x20: ++ err = "excessive Tx retries due to either distance " ++ "too high or unable to Tx or Tx frame error - " ++ "try changing 'iwconfig txpower XXX' or " ++ "'sens'itivity or 'retry'"; ++ adev->wstats.discard.retries++; ++ /* Tx error 0x20 also seems to occur on ++ * overheating, so I'm not sure whether we ++ * actually want to do aggressive radio recalibration, ++ * since people maybe won't notice then that their hardware ++ * is slowly getting cooked... ++ * Or is it still a safe long distance from utter ++ * radio non-functionality despite many radio recalibs ++ * to final destructive overheating of the hardware? ++ * In this case we really should do recalib here... ++ * I guess the only way to find out is to do a ++ * potentially fatal self-experiment :-\ ++ * Or maybe only recalib in case we're using Tx ++ * rate auto (on errors switching to lower speed ++ * --> less heat?) or 802.11 power save mode? ++ * ++ * ok, just do it. */ ++ if (++adev->retry_errors_msg_ratelimit % 4 == 0) { ++ if (adev->retry_errors_msg_ratelimit <= 20) { ++ printk("%s: several excessive Tx " ++ "retry errors occurred, attempting " ++ "to recalibrate radio. Radio " ++ "drift might be caused by increasing " ++ "card temperature, please check the card " ++ "before it's too late!\n", ++ adev->ndev->name); ++ if (adev->retry_errors_msg_ratelimit == 20) ++ printk("disabling above message\n"); ++ } ++ ++ acx_schedule_task(adev, ACX_AFTER_IRQ_CMD_RADIO_RECALIB); ++ } ++ break; ++ case 0x40: ++ err = "Tx buffer overflow"; ++ adev->stats.tx_fifo_errors++; ++ break; ++ case 0x80: ++ /* possibly ACPI C-state powersaving related!!! ++ * (DMA timeout due to excessively high wakeup ++ * latency after C-state activation!?) ++ * Disable C-State powersaving and try again, ++ * then PLEASE REPORT, I'm VERY interested in ++ * whether my theory is correct that this is ++ * actually the problem here. ++ * In that case, use new Linux idle wakeup latency ++ * requirements kernel API to prevent this issue. */ ++ err = "DMA error"; ++ adev->wstats.discard.misc++; ++ break; ++ } ++ adev->stats.tx_errors++; ++ if (adev->stats.tx_errors <= 20) ++ printk("%s: tx error 0x%02X, buf %02u! (%s)\n", ++ adev->ndev->name, error, finger, err); ++ else ++ printk("%s: tx error 0x%02X, buf %02u!\n", ++ adev->ndev->name, error, finger); ++} ++ ++ ++unsigned int ++acxpci_l_clean_txdesc(acx_device_t *adev) ++{ ++ txdesc_t *txdesc; ++ unsigned finger; ++ int num_cleaned; ++ u16 r111; ++ u8 error, ack_failures, rts_failures, rts_ok, r100; ++ ++ FN_ENTER; ++ ++ if (unlikely(acx_debug & L_DEBUG)) ++ log_txbuffer(adev); ++ ++ log(L_BUFT, "tx: cleaning up bufs from %u\n", adev->tx_tail); ++ ++ /* We know first descr which is not free yet. We advance it as far ++ ** as we see correct bits set in following descs (if next desc ++ ** is NOT free, we shouldn't advance at all). We know that in ++ ** front of tx_tail may be "holes" with isolated free descs. ++ ** We will catch up when all intermediate descs will be freed also */ ++ ++ finger = adev->tx_tail; ++ num_cleaned = 0; ++ while (likely(finger != adev->tx_head)) { ++ txdesc = get_txdesc(adev, finger); ++ ++ /* If we allocated txdesc on tx path but then decided ++ ** to NOT use it, then it will be left as a free "bubble" ++ ** in the "allocated for tx" part of the ring. ++ ** We may meet it on the next ring pass here. */ ++ ++ /* stop if not marked as "tx finished" and "host owned" */ ++ if ((txdesc->Ctl_8 & DESC_CTL_ACXDONE_HOSTOWN) ++ != DESC_CTL_ACXDONE_HOSTOWN) { ++ if (unlikely(!num_cleaned)) { /* maybe remove completely */ ++ log(L_BUFT, "clean_txdesc: tail isn't free. " ++ "tail:%d head:%d\n", ++ adev->tx_tail, adev->tx_head); ++ } ++ break; ++ } ++ ++ /* remember desc values... */ ++ error = txdesc->error; ++ ack_failures = txdesc->ack_failures; ++ rts_failures = txdesc->rts_failures; ++ rts_ok = txdesc->rts_ok; ++ r100 = txdesc->u.r1.rate; ++ r111 = le16_to_cpu(txdesc->u.r2.rate111); ++ ++ /* need to check for certain error conditions before we ++ * clean the descriptor: we still need valid descr data here */ ++ if (unlikely(0x30 & error)) { ++ /* only send IWEVTXDROP in case of retry or lifetime exceeded; ++ * all other errors mean we screwed up locally */ ++ union iwreq_data wrqu; ++ wlan_hdr_t *hdr; ++ txhostdesc_t *hostdesc; ++ ++ hostdesc = get_txhostdesc(adev, txdesc); ++ hdr = (wlan_hdr_t *)hostdesc->data; ++ MAC_COPY(wrqu.addr.sa_data, hdr->a1); ++ wireless_send_event(adev->ndev, IWEVTXDROP, &wrqu, NULL); ++ } ++ ++ /* ...and free the desc */ ++ txdesc->error = 0; ++ txdesc->ack_failures = 0; ++ txdesc->rts_failures = 0; ++ txdesc->rts_ok = 0; ++ /* signal host owning it LAST, since ACX already knows that this ++ ** descriptor is finished since it set Ctl_8 accordingly. */ ++ txdesc->Ctl_8 = DESC_CTL_HOSTOWN; ++ ++ adev->tx_free++; ++ num_cleaned++; ++ ++ if ((adev->tx_free >= TX_START_QUEUE) ++ && (adev->status == ACX_STATUS_4_ASSOCIATED) ++ && (acx_queue_stopped(adev->ndev)) ++ ) { ++ log(L_BUF, "tx: wake queue (avail. Tx desc %u)\n", ++ adev->tx_free); ++ acx_wake_queue(adev->ndev, NULL); ++ } ++ ++ /* do error checking, rate handling and logging ++ * AFTER having done the work, it's faster */ ++ ++ /* do rate handling */ ++ if (adev->rate_auto) { ++ struct client *clt = get_txc(adev, txdesc); ++ if (clt) { ++ u16 cur = get_txr(adev, txdesc); ++ if (clt->rate_cur == cur) { ++ acx_l_handle_txrate_auto(adev, clt, ++ cur, /* intended rate */ ++ r100, r111, /* actually used rate */ ++ (error & 0x30), /* was there an error? */ ++ TX_CNT + TX_CLEAN_BACKLOG - adev->tx_free); ++ } ++ } ++ } ++ ++ if (unlikely(error)) ++ handle_tx_error(adev, error, finger); ++ ++ if (IS_ACX111(adev)) ++ log(L_BUFT, "tx: cleaned %u: !ACK=%u !RTS=%u RTS=%u r111=%04X\n", ++ finger, ack_failures, rts_failures, rts_ok, r111); ++ else ++ log(L_BUFT, "tx: cleaned %u: !ACK=%u !RTS=%u RTS=%u rate=%u\n", ++ finger, ack_failures, rts_failures, rts_ok, r100); ++ ++ /* update pointer for descr to be cleaned next */ ++ finger = (finger + 1) % TX_CNT; ++ } ++ ++ /* remember last position */ ++ adev->tx_tail = finger; ++/* end: */ ++ FN_EXIT1(num_cleaned); ++ return num_cleaned; ++} ++ ++/* clean *all* Tx descriptors, and regardless of their previous state. ++ * Used for brute-force reset handling. */ ++void ++acxpci_l_clean_txdesc_emergency(acx_device_t *adev) ++{ ++ txdesc_t *txdesc; ++ int i; ++ ++ FN_ENTER; ++ ++ for (i = 0; i < TX_CNT; i++) { ++ txdesc = get_txdesc(adev, i); ++ ++ /* free it */ ++ txdesc->ack_failures = 0; ++ txdesc->rts_failures = 0; ++ txdesc->rts_ok = 0; ++ txdesc->error = 0; ++ txdesc->Ctl_8 = DESC_CTL_HOSTOWN; ++ } ++ ++ adev->tx_free = TX_CNT; ++ ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acxpci_s_create_tx_host_desc_queue ++*/ ++ ++static void* ++allocate(acx_device_t *adev, size_t size, dma_addr_t *phy, const char *msg) ++{ ++ void *ptr; ++ ++ ptr = dma_alloc_coherent(adev->pdev ? &adev->pdev->dev : NULL, ++ size, phy, GFP_KERNEL); ++ ++ if (ptr) { ++ log(L_DEBUG, "%s sz=%d adr=0x%p phy=0x%08llx\n", ++ msg, (int)size, ptr, (unsigned long long)*phy); ++ memset(ptr, 0, size); ++ return ptr; ++ } ++ printk(KERN_ERR "acx: %s allocation FAILED (%d bytes)\n", ++ msg, (int)size); ++ return NULL; ++} ++ ++ ++static int ++acxpci_s_create_tx_host_desc_queue(acx_device_t *adev) ++{ ++ txhostdesc_t *hostdesc; ++ u8 *txbuf; ++ dma_addr_t hostdesc_phy; ++ dma_addr_t txbuf_phy; ++ int i; ++ ++ FN_ENTER; ++ ++ /* allocate TX buffer */ ++ adev->txbuf_area_size = TX_CNT * WLAN_A4FR_MAXLEN_WEP_FCS; ++ adev->txbuf_start = allocate(adev, adev->txbuf_area_size, ++ &adev->txbuf_startphy, "txbuf_start"); ++ if (!adev->txbuf_start) ++ goto fail; ++ ++ /* allocate the TX host descriptor queue pool */ ++ adev->txhostdesc_area_size = TX_CNT * 2*sizeof(*hostdesc); ++ adev->txhostdesc_start = allocate(adev, adev->txhostdesc_area_size, ++ &adev->txhostdesc_startphy, "txhostdesc_start"); ++ if (!adev->txhostdesc_start) ++ goto fail; ++ /* check for proper alignment of TX host descriptor pool */ ++ if ((long) adev->txhostdesc_start & 3) { ++ printk("acx: driver bug: dma alloc returns unaligned address\n"); ++ goto fail; ++ } ++ ++ hostdesc = adev->txhostdesc_start; ++ hostdesc_phy = adev->txhostdesc_startphy; ++ txbuf = adev->txbuf_start; ++ txbuf_phy = adev->txbuf_startphy; ++ ++#if 0 ++/* Each tx buffer is accessed by hardware via ++** txdesc -> txhostdesc(s) -> txbuffer(s). ++** We use only one txhostdesc per txdesc, but it looks like ++** acx111 is buggy: it accesses second txhostdesc ++** (via hostdesc.desc_phy_next field) even if ++** txdesc->length == hostdesc->length and thus ++** entire packet was placed into first txhostdesc. ++** Due to this bug acx111 hangs unless second txhostdesc ++** has le16_to_cpu(hostdesc.length) = 3 (or larger) ++** Storing NULL into hostdesc.desc_phy_next ++** doesn't seem to help. ++** ++** Update: although it worked on Xterasys XN-2522g ++** with len=3 trick, WG311v2 is even more bogus, doesn't work. ++** Keeping this code (#ifdef'ed out) for documentational purposes. ++*/ ++ for (i = 0; i < TX_CNT*2; i++) { ++ hostdesc_phy += sizeof(*hostdesc); ++ if (!(i & 1)) { ++ hostdesc->data_phy = cpu2acx(txbuf_phy); ++ /* hostdesc->data_offset = ... */ ++ /* hostdesc->reserved = ... */ ++ hostdesc->Ctl_16 = cpu_to_le16(DESC_CTL_HOSTOWN); ++ /* hostdesc->length = ... */ ++ hostdesc->desc_phy_next = cpu2acx(hostdesc_phy); ++ hostdesc->pNext = ptr2acx(NULL); ++ /* hostdesc->Status = ... */ ++ /* below: non-hardware fields */ ++ hostdesc->data = txbuf; ++ ++ txbuf += WLAN_A4FR_MAXLEN_WEP_FCS; ++ txbuf_phy += WLAN_A4FR_MAXLEN_WEP_FCS; ++ } else { ++ /* hostdesc->data_phy = ... */ ++ /* hostdesc->data_offset = ... */ ++ /* hostdesc->reserved = ... */ ++ /* hostdesc->Ctl_16 = ... */ ++ hostdesc->length = cpu_to_le16(3); /* bug workaround */ ++ /* hostdesc->desc_phy_next = ... */ ++ /* hostdesc->pNext = ... */ ++ /* hostdesc->Status = ... */ ++ /* below: non-hardware fields */ ++ /* hostdesc->data = ... */ ++ } ++ hostdesc++; ++ } ++#endif ++/* We initialize two hostdescs so that they point to adjacent ++** memory areas. Thus txbuf is really just a contiguous memory area */ ++ for (i = 0; i < TX_CNT*2; i++) { ++ hostdesc_phy += sizeof(*hostdesc); ++ ++ hostdesc->data_phy = cpu2acx(txbuf_phy); ++ /* done by memset(0): hostdesc->data_offset = 0; */ ++ /* hostdesc->reserved = ... */ ++ hostdesc->Ctl_16 = cpu_to_le16(DESC_CTL_HOSTOWN); ++ /* hostdesc->length = ... */ ++ hostdesc->desc_phy_next = cpu2acx(hostdesc_phy); ++ /* done by memset(0): hostdesc->pNext = ptr2acx(NULL); */ ++ /* hostdesc->Status = ... */ ++ /* ->data is a non-hardware field: */ ++ hostdesc->data = txbuf; ++ ++ if (!(i & 1)) { ++ txbuf += WLAN_HDR_A3_LEN; ++ txbuf_phy += WLAN_HDR_A3_LEN; ++ } else { ++ txbuf += WLAN_A4FR_MAXLEN_WEP_FCS - WLAN_HDR_A3_LEN; ++ txbuf_phy += WLAN_A4FR_MAXLEN_WEP_FCS - WLAN_HDR_A3_LEN; ++ } ++ hostdesc++; ++ } ++ hostdesc--; ++ hostdesc->desc_phy_next = cpu2acx(adev->txhostdesc_startphy); ++ ++ FN_EXIT1(OK); ++ return OK; ++fail: ++ printk("acx: create_tx_host_desc_queue FAILED\n"); ++ /* dealloc will be done by free function on error case */ ++ FN_EXIT1(NOT_OK); ++ return NOT_OK; ++} ++ ++ ++/*************************************************************** ++** acxpci_s_create_rx_host_desc_queue ++*/ ++/* the whole size of a data buffer (header plus data body) ++ * plus 32 bytes safety offset at the end */ ++#define RX_BUFFER_SIZE (sizeof(rxbuffer_t) + 32) ++ ++static int ++acxpci_s_create_rx_host_desc_queue(acx_device_t *adev) ++{ ++ rxhostdesc_t *hostdesc; ++ rxbuffer_t *rxbuf; ++ dma_addr_t hostdesc_phy; ++ dma_addr_t rxbuf_phy; ++ int i; ++ ++ FN_ENTER; ++ ++ /* allocate the RX host descriptor queue pool */ ++ adev->rxhostdesc_area_size = RX_CNT * sizeof(*hostdesc); ++ adev->rxhostdesc_start = allocate(adev, adev->rxhostdesc_area_size, ++ &adev->rxhostdesc_startphy, "rxhostdesc_start"); ++ if (!adev->rxhostdesc_start) ++ goto fail; ++ /* check for proper alignment of RX host descriptor pool */ ++ if ((long) adev->rxhostdesc_start & 3) { ++ printk("acx: driver bug: dma alloc returns unaligned address\n"); ++ goto fail; ++ } ++ ++ /* allocate Rx buffer pool which will be used by the acx ++ * to store the whole content of the received frames in it */ ++ adev->rxbuf_area_size = RX_CNT * RX_BUFFER_SIZE; ++ adev->rxbuf_start = allocate(adev, adev->rxbuf_area_size, ++ &adev->rxbuf_startphy, "rxbuf_start"); ++ if (!adev->rxbuf_start) ++ goto fail; ++ ++ rxbuf = adev->rxbuf_start; ++ rxbuf_phy = adev->rxbuf_startphy; ++ hostdesc = adev->rxhostdesc_start; ++ hostdesc_phy = adev->rxhostdesc_startphy; ++ ++ /* don't make any popular C programming pointer arithmetic mistakes ++ * here, otherwise I'll kill you... ++ * (and don't dare asking me why I'm warning you about that...) */ ++ for (i = 0; i < RX_CNT; i++) { ++ hostdesc->data = rxbuf; ++ hostdesc->data_phy = cpu2acx(rxbuf_phy); ++ hostdesc->length = cpu_to_le16(RX_BUFFER_SIZE); ++ CLEAR_BIT(hostdesc->Ctl_16, cpu_to_le16(DESC_CTL_HOSTOWN)); ++ rxbuf++; ++ rxbuf_phy += sizeof(*rxbuf); ++ hostdesc_phy += sizeof(*hostdesc); ++ hostdesc->desc_phy_next = cpu2acx(hostdesc_phy); ++ hostdesc++; ++ } ++ hostdesc--; ++ hostdesc->desc_phy_next = cpu2acx(adev->rxhostdesc_startphy); ++ FN_EXIT1(OK); ++ return OK; ++fail: ++ printk("acx: create_rx_host_desc_queue FAILED\n"); ++ /* dealloc will be done by free function on error case */ ++ FN_EXIT1(NOT_OK); ++ return NOT_OK; ++} ++ ++ ++/*************************************************************** ++** acxpci_s_create_hostdesc_queues ++*/ ++int ++acxpci_s_create_hostdesc_queues(acx_device_t *adev) ++{ ++ int result; ++ result = acxpci_s_create_tx_host_desc_queue(adev); ++ if (OK != result) return result; ++ result = acxpci_s_create_rx_host_desc_queue(adev); ++ return result; ++} ++ ++ ++/*************************************************************** ++** acxpci_create_tx_desc_queue ++*/ ++static void ++acxpci_create_tx_desc_queue(acx_device_t *adev, u32 tx_queue_start) ++{ ++ txdesc_t *txdesc; ++ txhostdesc_t *hostdesc; ++ dma_addr_t hostmemptr; ++ u32 mem_offs; ++ int i; ++ ++ FN_ENTER; ++ ++ if (IS_ACX100(adev)) ++ adev->txdesc_size = sizeof(*txdesc); ++ else ++ /* the acx111 txdesc is 4 bytes larger */ ++ adev->txdesc_size = sizeof(*txdesc) + 4; ++ ++ adev->txdesc_start = (txdesc_t *) (adev->iobase2 + tx_queue_start); ++ ++ log(L_DEBUG, "adev->iobase2=%p\n" ++ "tx_queue_start=%08X\n" ++ "adev->txdesc_start=%p\n", ++ adev->iobase2, ++ tx_queue_start, ++ adev->txdesc_start); ++ ++ adev->tx_free = TX_CNT; ++ /* done by memset: adev->tx_head = 0; */ ++ /* done by memset: adev->tx_tail = 0; */ ++ txdesc = adev->txdesc_start; ++ mem_offs = tx_queue_start; ++ hostmemptr = adev->txhostdesc_startphy; ++ hostdesc = adev->txhostdesc_start; ++ ++ if (IS_ACX111(adev)) { ++ /* ACX111 has a preinitialized Tx buffer! */ ++ /* loop over whole send pool */ ++ /* FIXME: do we have to do the hostmemptr stuff here?? */ ++ for (i = 0; i < TX_CNT; i++) { ++ txdesc->HostMemPtr = ptr2acx(hostmemptr); ++ txdesc->Ctl_8 = DESC_CTL_HOSTOWN; ++ /* reserve two (hdr desc and payload desc) */ ++ hostdesc += 2; ++ hostmemptr += 2 * sizeof(*hostdesc); ++ txdesc = advance_txdesc(adev, txdesc, 1); ++ } ++ } else { ++ /* ACX100 Tx buffer needs to be initialized by us */ ++ /* clear whole send pool. sizeof is safe here (we are acx100) */ ++ memset(adev->txdesc_start, 0, TX_CNT * sizeof(*txdesc)); ++ ++ /* loop over whole send pool */ ++ for (i = 0; i < TX_CNT; i++) { ++ log(L_DEBUG, "configure card tx descriptor: 0x%p, " ++ "size: 0x%X\n", txdesc, adev->txdesc_size); ++ ++ /* pointer to hostdesc memory */ ++ txdesc->HostMemPtr = ptr2acx(hostmemptr); ++ /* initialise ctl */ ++ txdesc->Ctl_8 = ( DESC_CTL_HOSTOWN | DESC_CTL_RECLAIM ++ | DESC_CTL_AUTODMA | DESC_CTL_FIRSTFRAG); ++ /* done by memset(0): txdesc->Ctl2_8 = 0; */ ++ /* point to next txdesc */ ++ txdesc->pNextDesc = cpu2acx(mem_offs + adev->txdesc_size); ++ /* reserve two (hdr desc and payload desc) */ ++ hostdesc += 2; ++ hostmemptr += 2 * sizeof(*hostdesc); ++ /* go to the next one */ ++ mem_offs += adev->txdesc_size; ++ /* ++ is safe here (we are acx100) */ ++ txdesc++; ++ } ++ /* go back to the last one */ ++ txdesc--; ++ /* and point to the first making it a ring buffer */ ++ txdesc->pNextDesc = cpu2acx(tx_queue_start); ++ } ++ FN_EXIT0; ++} ++ ++ ++/*************************************************************** ++** acxpci_create_rx_desc_queue ++*/ ++static void ++acxpci_create_rx_desc_queue(acx_device_t *adev, u32 rx_queue_start) ++{ ++ rxdesc_t *rxdesc; ++ u32 mem_offs; ++ int i; ++ ++ FN_ENTER; ++ ++ /* done by memset: adev->rx_tail = 0; */ ++ ++ /* ACX111 doesn't need any further config: preconfigures itself. ++ * Simply print ring buffer for debugging */ ++ if (IS_ACX111(adev)) { ++ /* rxdesc_start already set here */ ++ ++ adev->rxdesc_start = (rxdesc_t *) ((u8 *)adev->iobase2 + rx_queue_start); ++ ++ rxdesc = adev->rxdesc_start; ++ for (i = 0; i < RX_CNT; i++) { ++ log(L_DEBUG, "rx descriptor %d @ 0x%p\n", i, rxdesc); ++ rxdesc = adev->rxdesc_start = (rxdesc_t *) ++ (adev->iobase2 + acx2cpu(rxdesc->pNextDesc)); ++ } ++ } else { ++ /* we didn't pre-calculate rxdesc_start in case of ACX100 */ ++ /* rxdesc_start should be right AFTER Tx pool */ ++ adev->rxdesc_start = (rxdesc_t *) ++ ((u8 *) adev->txdesc_start + (TX_CNT * sizeof(txdesc_t))); ++ /* NB: sizeof(txdesc_t) above is valid because we know ++ ** we are in if (acx100) block. Beware of cut-n-pasting elsewhere! ++ ** acx111's txdesc is larger! */ ++ ++ memset(adev->rxdesc_start, 0, RX_CNT * sizeof(*rxdesc)); ++ ++ /* loop over whole receive pool */ ++ rxdesc = adev->rxdesc_start; ++ mem_offs = rx_queue_start; ++ for (i = 0; i < RX_CNT; i++) { ++ log(L_DEBUG, "rx descriptor @ 0x%p\n", rxdesc); ++ rxdesc->Ctl_8 = DESC_CTL_RECLAIM | DESC_CTL_AUTODMA; ++ /* point to next rxdesc */ ++ rxdesc->pNextDesc = cpu2acx(mem_offs + sizeof(*rxdesc)); ++ /* go to the next one */ ++ mem_offs += sizeof(*rxdesc); ++ rxdesc++; ++ } ++ /* go to the last one */ ++ rxdesc--; ++ ++ /* and point to the first making it a ring buffer */ ++ rxdesc->pNextDesc = cpu2acx(rx_queue_start); ++ } ++ FN_EXIT0; ++} ++ ++ ++/*************************************************************** ++** acxpci_create_desc_queues ++*/ ++void ++acxpci_create_desc_queues(acx_device_t *adev, u32 tx_queue_start, u32 rx_queue_start) ++{ ++ acxpci_create_tx_desc_queue(adev, tx_queue_start); ++ acxpci_create_rx_desc_queue(adev, rx_queue_start); ++} ++ ++ ++/*************************************************************** ++** acxpci_s_proc_diag_output ++*/ ++char* ++acxpci_s_proc_diag_output(char *p, acx_device_t *adev) ++{ ++ const char *rtl, *thd, *ttl; ++ rxhostdesc_t *rxhostdesc; ++ txdesc_t *txdesc; ++ int i; ++ ++ FN_ENTER; ++ ++ p += sprintf(p, "** Rx buf **\n"); ++ rxhostdesc = adev->rxhostdesc_start; ++ if (rxhostdesc) for (i = 0; i < RX_CNT; i++) { ++ rtl = (i == adev->rx_tail) ? " [tail]" : ""; ++ if ((rxhostdesc->Ctl_16 & cpu_to_le16(DESC_CTL_HOSTOWN)) ++ && (rxhostdesc->Status & cpu_to_le32(DESC_STATUS_FULL)) ) ++ p += sprintf(p, "%02u FULL%s\n", i, rtl); ++ else ++ p += sprintf(p, "%02u empty%s\n", i, rtl); ++ rxhostdesc++; ++ } ++ p += sprintf(p, "** Tx buf (free %d, Linux netqueue %s) **\n", adev->tx_free, ++ acx_queue_stopped(adev->ndev) ? "STOPPED" : "running"); ++ txdesc = adev->txdesc_start; ++ if (txdesc) for (i = 0; i < TX_CNT; i++) { ++ thd = (i == adev->tx_head) ? " [head]" : ""; ++ ttl = (i == adev->tx_tail) ? " [tail]" : ""; ++ if (txdesc->Ctl_8 & DESC_CTL_ACXDONE) ++ p += sprintf(p, "%02u free (%02X)%s%s\n", i, txdesc->Ctl_8, thd, ttl); ++ else ++ p += sprintf(p, "%02u tx (%02X)%s%s\n", i, txdesc->Ctl_8, thd, ttl); ++ txdesc = advance_txdesc(adev, txdesc, 1); ++ } ++ p += sprintf(p, ++ "\n" ++ "** PCI data **\n" ++ "txbuf_start %p, txbuf_area_size %u, txbuf_startphy %08llx\n" ++ "txdesc_size %u, txdesc_start %p\n" ++ "txhostdesc_start %p, txhostdesc_area_size %u, txhostdesc_startphy %08llx\n" ++ "rxdesc_start %p\n" ++ "rxhostdesc_start %p, rxhostdesc_area_size %u, rxhostdesc_startphy %08llx\n" ++ "rxbuf_start %p, rxbuf_area_size %u, rxbuf_startphy %08llx\n", ++ adev->txbuf_start, adev->txbuf_area_size, ++ (unsigned long long)adev->txbuf_startphy, ++ adev->txdesc_size, adev->txdesc_start, ++ adev->txhostdesc_start, adev->txhostdesc_area_size, ++ (unsigned long long)adev->txhostdesc_startphy, ++ adev->rxdesc_start, ++ adev->rxhostdesc_start, adev->rxhostdesc_area_size, ++ (unsigned long long)adev->rxhostdesc_startphy, ++ adev->rxbuf_start, adev->rxbuf_area_size, ++ (unsigned long long)adev->rxbuf_startphy); ++ ++ FN_EXIT0; ++ return p; ++} ++ ++ ++/*********************************************************************** ++*/ ++int ++acxpci_proc_eeprom_output(char *buf, acx_device_t *adev) ++{ ++ char *p = buf; ++ int i; ++ ++ FN_ENTER; ++ ++ for (i = 0; i < 0x400; i++) { ++ acxpci_read_eeprom_byte(adev, i, p++); ++ } ++ ++ FN_EXIT1(p - buf); ++ return p - buf; ++} ++ ++ ++/*********************************************************************** ++*/ ++void ++acxpci_set_interrupt_mask(acx_device_t *adev) ++{ ++ if (IS_ACX111(adev)) { ++ adev->irq_mask = (u16) ~(0 ++ /* | HOST_INT_RX_DATA */ ++ | HOST_INT_TX_COMPLETE ++ /* | HOST_INT_TX_XFER */ ++ | HOST_INT_RX_COMPLETE ++ /* | HOST_INT_DTIM */ ++ /* | HOST_INT_BEACON */ ++ /* | HOST_INT_TIMER */ ++ /* | HOST_INT_KEY_NOT_FOUND */ ++ | HOST_INT_IV_ICV_FAILURE ++ | HOST_INT_CMD_COMPLETE ++ | HOST_INT_INFO ++ /* | HOST_INT_OVERFLOW */ ++ /* | HOST_INT_PROCESS_ERROR */ ++ | HOST_INT_SCAN_COMPLETE ++ | HOST_INT_FCS_THRESHOLD ++ /* | HOST_INT_UNKNOWN */ ++ ); ++ /* Or else acx100 won't signal cmd completion, right? */ ++ adev->irq_mask_off = (u16)~( HOST_INT_CMD_COMPLETE ); /* 0xfdff */ ++ } else { ++ adev->irq_mask = (u16) ~(0 ++ /* | HOST_INT_RX_DATA */ ++ | HOST_INT_TX_COMPLETE ++ /* | HOST_INT_TX_XFER */ ++ | HOST_INT_RX_COMPLETE ++ /* | HOST_INT_DTIM */ ++ /* | HOST_INT_BEACON */ ++ /* | HOST_INT_TIMER */ ++ /* | HOST_INT_KEY_NOT_FOUND */ ++ /* | HOST_INT_IV_ICV_FAILURE */ ++ | HOST_INT_CMD_COMPLETE ++ | HOST_INT_INFO ++ /* | HOST_INT_OVERFLOW */ ++ /* | HOST_INT_PROCESS_ERROR */ ++ | HOST_INT_SCAN_COMPLETE ++ /* | HOST_INT_FCS_THRESHOLD */ ++ /* | HOST_INT_UNKNOWN */ ++ ); ++ adev->irq_mask_off = (u16)~( HOST_INT_UNKNOWN ); /* 0x7fff */ ++ } ++} ++ ++ ++/*********************************************************************** ++*/ ++int ++acx100pci_s_set_tx_level(acx_device_t *adev, u8 level_dbm) ++{ ++ /* since it can be assumed that at least the Maxim radio has a ++ * maximum power output of 20dBm and since it also can be ++ * assumed that these values drive the DAC responsible for ++ * setting the linear Tx level, I'd guess that these values ++ * should be the corresponding linear values for a dBm value, ++ * in other words: calculate the values from that formula: ++ * Y [dBm] = 10 * log (X [mW]) ++ * then scale the 0..63 value range onto the 1..100mW range (0..20 dBm) ++ * and you're done... ++ * Hopefully that's ok, but you never know if we're actually ++ * right... (especially since Windows XP doesn't seem to show ++ * actual Tx dBm values :-P) */ ++ ++ /* NOTE: on Maxim, value 30 IS 30mW, and value 10 IS 10mW - so the ++ * values are EXACTLY mW!!! Not sure about RFMD and others, ++ * though... */ ++ static const u8 dbm2val_maxim[21] = { ++ 63, 63, 63, 62, ++ 61, 61, 60, 60, ++ 59, 58, 57, 55, ++ 53, 50, 47, 43, ++ 38, 31, 23, 13, ++ 0 ++ }; ++ static const u8 dbm2val_rfmd[21] = { ++ 0, 0, 0, 1, ++ 2, 2, 3, 3, ++ 4, 5, 6, 8, ++ 10, 13, 16, 20, ++ 25, 32, 41, 50, ++ 63 ++ }; ++ const u8 *table; ++ ++ switch (adev->radio_type) { ++ case RADIO_MAXIM_0D: ++ table = &dbm2val_maxim[0]; ++ break; ++ case RADIO_RFMD_11: ++ case RADIO_RALINK_15: ++ table = &dbm2val_rfmd[0]; ++ break; ++ default: ++ printk("%s: unknown/unsupported radio type, " ++ "cannot modify tx power level yet!\n", ++ adev->ndev->name); ++ return NOT_OK; ++ } ++ printk("%s: changing radio power level to %u dBm (%u)\n", ++ adev->ndev->name, level_dbm, table[level_dbm]); ++ acxpci_s_write_phy_reg(adev, 0x11, table[level_dbm]); ++ return OK; ++} ++ ++ ++/*********************************************************************** ++** Data for init_module/cleanup_module ++*/ ++static const struct pci_device_id ++acxpci_id_tbl[] __devinitdata = { ++ { ++ .vendor = PCI_VENDOR_ID_TI, ++ .device = PCI_DEVICE_ID_TI_TNETW1100A, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ .driver_data = CHIPTYPE_ACX100, ++ }, ++ { ++ .vendor = PCI_VENDOR_ID_TI, ++ .device = PCI_DEVICE_ID_TI_TNETW1100B, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ .driver_data = CHIPTYPE_ACX100, ++ }, ++ { ++ .vendor = PCI_VENDOR_ID_TI, ++ .device = PCI_DEVICE_ID_TI_TNETW1130, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ .driver_data = CHIPTYPE_ACX111, ++ }, ++ { ++ .vendor = 0, ++ .device = 0, ++ .subvendor = 0, ++ .subdevice = 0, ++ .driver_data = 0, ++ } ++}; ++ ++MODULE_DEVICE_TABLE(pci, acxpci_id_tbl); ++ ++/* FIXME: checks should be removed once driver is included in the kernel */ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2, 6, 11) ++/* pci_name() got introduced at start of 2.6.x, ++ * got mandatory (slot_name member removed) in 2.6.11-bk1 */ ++#define pci_name(x) x->slot_name ++#endif ++ ++static struct pci_driver ++acxpci_drv_id = { ++ .name = "acx_pci", ++ .id_table = acxpci_id_tbl, ++ .probe = acxpci_e_probe, ++ .remove = __devexit_p(acxpci_e_remove), ++#ifdef CONFIG_PM ++ .suspend = acxpci_e_suspend, ++ .resume = acxpci_e_resume ++#endif /* CONFIG_PM */ ++}; ++ ++ ++/*********************************************************************** ++** acxpci_e_init_module ++** ++** Module initialization routine, called once at module load time ++*/ ++int __init ++acxpci_e_init_module(void) ++{ ++ int res; ++ ++ FN_ENTER; ++ ++#if (ACX_IO_WIDTH==32) ++ printk("acx: compiled to use 32bit I/O access. " ++ "I/O timing issues might occur, such as " ++ "non-working firmware upload. Report them\n"); ++#else ++ printk("acx: compiled to use 16bit I/O access only " ++ "(compatibility mode)\n"); ++#endif ++ ++#ifdef __LITTLE_ENDIAN ++#define ENDIANNESS_STRING "running on a little-endian CPU\n" ++#else ++#define ENDIANNESS_STRING "running on a BIG-ENDIAN CPU\n" ++#endif ++ log(L_INIT, ++ ENDIANNESS_STRING ++ "PCI module " ACX_RELEASE " initialized, " ++ "waiting for cards to probe...\n" ++ ); ++ ++ res = pci_register_driver(&acxpci_drv_id); ++ FN_EXIT1(res); ++ return res; ++} ++ ++ ++/*********************************************************************** ++** acxpci_e_cleanup_module ++** ++** Called at module unload time. This is our last chance to ++** clean up after ourselves. ++*/ ++void __exit ++acxpci_e_cleanup_module(void) ++{ ++ FN_ENTER; ++ ++ pci_unregister_driver(&acxpci_drv_id); ++ ++ FN_EXIT0; ++} +Index: linux-2.6.23/drivers/net/wireless/acx/rx3000_acx.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.23/drivers/net/wireless/acx/rx3000_acx.c 2008-01-20 21:13:40.000000000 +0000 +@@ -0,0 +1,110 @@ ++/* ++ * WLAN (TI TNETW1100B) support in the HP iPAQ RX3000 ++ * ++ * Copyright (c) 2006 SDG Systems, LLC ++ * Copyright (c) 2006 Roman Moravcik ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive for ++ * more details. ++ * ++ * Based on hx4700_acx.c ++ */ ++ ++ ++#include <linux/kernel.h> ++#include <linux/platform_device.h> ++#include <linux/delay.h> ++#include <linux/dpm.h> ++#include <linux/leds.h> ++ ++#include <asm/hardware.h> ++ ++#include <asm/arch/regs-gpio.h> ++#include <linux/mfd/asic3_base.h> ++#include <asm/arch/rx3000.h> ++#include <asm/arch/rx3000-asic3.h> ++#include <asm/io.h> ++ ++#include "acx_hw.h" ++ ++extern struct platform_device s3c_device_asic3; ++ ++static int rx3000_wlan_start(void) ++{ ++ DPM_DEBUG("rx3000_acx: Turning on\n"); ++ asic3_set_gpio_out_b(&s3c_device_asic3.dev, ASIC3_GPB3, ASIC3_GPB3); ++ mdelay(20); ++ asic3_set_gpio_out_c(&s3c_device_asic3.dev, ASIC3_GPC13, ASIC3_GPC13); ++ mdelay(20); ++ asic3_set_gpio_out_c(&s3c_device_asic3.dev, ASIC3_GPC11, ASIC3_GPC11); ++ mdelay(100); ++ asic3_set_gpio_out_b(&s3c_device_asic3.dev, ASIC3_GPB3, ASIC3_GPB3); ++ mdelay(20); ++ s3c2410_gpio_cfgpin(S3C2410_GPA15, S3C2410_GPA15_nGCS4); ++ mdelay(100); ++ s3c2410_gpio_setpin(S3C2410_GPA11, 0); ++ mdelay(50); ++ s3c2410_gpio_setpin(S3C2410_GPA11, 1); ++ led_trigger_event_shared(rx3000_radio_trig, LED_FULL); ++ return 0; ++} ++ ++static int rx3000_wlan_stop(void) ++{ ++ DPM_DEBUG("rx3000_acx: Turning off\n"); ++ s3c2410_gpio_setpin(S3C2410_GPA15, 1); ++ s3c2410_gpio_cfgpin(S3C2410_GPA15, S3C2410_GPA15_OUT); ++ asic3_set_gpio_out_b(&s3c_device_asic3.dev, ASIC3_GPB3, 0); ++ asic3_set_gpio_out_c(&s3c_device_asic3.dev, ASIC3_GPC13, 0); ++ asic3_set_gpio_out_c(&s3c_device_asic3.dev, ASIC3_GPC11, 0); ++ led_trigger_event_shared(rx3000_radio_trig, LED_OFF); ++ return 0; ++} ++ ++static struct resource acx_resources[] = { ++ [0] = { ++ .start = RX3000_PA_WLAN, ++ .end = RX3000_PA_WLAN + 0x20, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_EINT16, ++ .end = IRQ_EINT16, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static struct acx_hardware_data acx_data = { ++ .start_hw = rx3000_wlan_start, ++ .stop_hw = rx3000_wlan_stop, ++}; ++ ++static struct platform_device acx_device = { ++ .name = "acx-mem", ++ .dev = { ++ .platform_data = &acx_data, ++ }, ++ .num_resources = ARRAY_SIZE(acx_resources), ++ .resource = acx_resources, ++}; ++ ++static int __init rx3000_wlan_init(void) ++{ ++ printk("rx3000_wlan_init: acx-mem platform_device_register\n"); ++ return platform_device_register(&acx_device); ++} ++ ++ ++static void __exit rx3000_wlan_exit(void) ++{ ++ platform_device_unregister(&acx_device); ++} ++ ++module_init(rx3000_wlan_init); ++module_exit(rx3000_wlan_exit); ++ ++MODULE_AUTHOR("Todd Blumer <todd@sdgsystems.com>, Roman Moravcik <roman.moravcik@gmail.com>"); ++MODULE_DESCRIPTION("WLAN driver for HP iPAQ RX3000"); ++MODULE_LICENSE("GPL"); ++ +Index: linux-2.6.23/drivers/net/wireless/acx/setrate.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.23/drivers/net/wireless/acx/setrate.c 2008-01-20 21:13:40.000000000 +0000 +@@ -0,0 +1,213 @@ ++/* TODO: stop #including, move into wireless.c ++ * until then, keep in sync copies in prism54/ and acx/ dirs ++ * code+data size: less than 1k */ ++ ++enum { ++ DOT11_RATE_1, ++ DOT11_RATE_2, ++ DOT11_RATE_5, ++ DOT11_RATE_11, ++ DOT11_RATE_22, ++ DOT11_RATE_33, ++ DOT11_RATE_6, ++ DOT11_RATE_9, ++ DOT11_RATE_12, ++ DOT11_RATE_18, ++ DOT11_RATE_24, ++ DOT11_RATE_36, ++ DOT11_RATE_48, ++ DOT11_RATE_54 ++}; ++enum { ++ DOT11_MOD_DBPSK, ++ DOT11_MOD_DQPSK, ++ DOT11_MOD_CCK, ++ DOT11_MOD_OFDM, ++ DOT11_MOD_CCKOFDM, ++ DOT11_MOD_PBCC ++}; ++static const u8 ratelist[] = { 1,2,5,11,22,33,6,9,12,18,24,36,48,54 }; ++static const u8 dot11ratebyte[] = { 1*2,2*2,11,11*2,22*2,33*2,6*2,9*2,12*2,18*2,24*2,36*2,48*2,54*2 }; ++static const u8 default_modulation[] = { ++ DOT11_MOD_DBPSK, ++ DOT11_MOD_DQPSK, ++ DOT11_MOD_CCK, ++ DOT11_MOD_CCK, ++ DOT11_MOD_PBCC, ++ DOT11_MOD_PBCC, ++ DOT11_MOD_OFDM, ++ DOT11_MOD_OFDM, ++ DOT11_MOD_OFDM, ++ DOT11_MOD_OFDM, ++ DOT11_MOD_OFDM, ++ DOT11_MOD_OFDM, ++ DOT11_MOD_OFDM, ++ DOT11_MOD_OFDM ++}; ++ ++static /* TODO: remove 'static' when moved to wireless.c */ ++int ++rate_mbit2enum(int n) { ++ int i=0; ++ while(i<sizeof(ratelist)) { ++ if(n==ratelist[i]) return i; ++ i++; ++ } ++ return -EINVAL; ++} ++ ++static int ++get_modulation(int r_enum, char suffix) { ++ if(suffix==',' || suffix==' ' || suffix=='\0') { ++ /* could shorten default_mod by 8 bytes: ++ if(r_enum>=DOT11_RATE_6) return DOT11_MOD_OFDM; */ ++ return default_modulation[r_enum]; ++ } ++ if(suffix=='c') { ++ if(r_enum<DOT11_RATE_5 || r_enum>DOT11_RATE_11) return -EINVAL; ++ return DOT11_MOD_CCK; ++ } ++ if(suffix=='p') { ++ if(r_enum<DOT11_RATE_5 || r_enum>DOT11_RATE_33) return -EINVAL; ++ return DOT11_MOD_PBCC; ++ } ++ if(suffix=='o') { ++ if(r_enum<DOT11_RATE_6) return -EINVAL; ++ return DOT11_MOD_OFDM; ++ } ++ if(suffix=='d') { ++ if(r_enum<DOT11_RATE_6) return -EINVAL; ++ return DOT11_MOD_CCKOFDM; ++ } ++ return -EINVAL; ++} ++ ++#ifdef UNUSED ++static int ++fill_ratevector(const char **pstr, u8 *vector, int size, ++ int (*supported)(int mbit, int mod, void *opaque), void *opaque, int or_mask) ++{ ++ unsigned long rate_mbit; ++ int rate_enum,mod; ++ const char *str = *pstr; ++ char c; ++ ++ do { ++ rate_mbit = simple_strtoul(str, (char**)&str, 10); ++ if(rate_mbit>INT_MAX) return -EINVAL; ++ ++ rate_enum = rate_mbit2enum(rate_mbit); ++ if(rate_enum<0) return rate_enum; ++ ++ c = *str; ++ mod = get_modulation(rate_enum, c); ++ if(mod<0) return mod; ++ ++ if(c>='a' && c<='z') c = *++str; ++ if(c!=',' && c!=' ' && c!='\0') return -EINVAL; ++ ++ if(supported) { ++ int r = supported(rate_mbit, mod, opaque); ++ if(r) return r; ++ } ++ ++ *vector++ = dot11ratebyte[rate_enum] | or_mask; ++ ++ size--; ++ str++; ++ } while(size>0 && c==','); ++ ++ if(size<1) return -E2BIG; ++ *vector=0; /* TODO: sort, remove dups? */ ++ ++ *pstr = str-1; ++ return 0; ++} ++ ++static /* TODO: remove 'static' when moved to wireless.c */ ++int ++fill_ratevectors(const char *str, u8 *brate, u8 *orate, int size, ++ int (*supported)(int mbit, int mod, void *opaque), void *opaque) ++{ ++ int r; ++ ++ r = fill_ratevector(&str, brate, size, supported, opaque, 0x80); ++ if(r) return r; ++ ++ orate[0] = 0; ++ if(*str==' ') { ++ str++; ++ r = fill_ratevector(&str, orate, size, supported, opaque, 0); ++ if(r) return r; ++ /* TODO: sanitize, e.g. remove/error on rates already in basic rate set? */ ++ } ++ if(*str) ++ return -EINVAL; ++ ++ return 0; ++} ++#endif ++ ++/* TODO: use u64 masks? */ ++ ++static int ++fill_ratemask(const char **pstr, u32* mask, ++ int (*supported)(int mbit, int mod,void *opaque), ++ u32 (*gen_mask)(int mbit, int mod,void *opaque), ++ void *opaque) ++{ ++ unsigned long rate_mbit; ++ int rate_enum,mod; ++ u32 m = 0; ++ const char *str = *pstr; ++ char c; ++ ++ do { ++ rate_mbit = simple_strtoul(str, (char**)&str, 10); ++ if(rate_mbit>INT_MAX) return -EINVAL; ++ ++ rate_enum = rate_mbit2enum(rate_mbit); ++ if(rate_enum<0) return rate_enum; ++ ++ c = *str; ++ mod = get_modulation(rate_enum, c); ++ if(mod<0) return mod; ++ ++ if(c>='a' && c<='z') c = *++str; ++ if(c!=',' && c!=' ' && c!='\0') return -EINVAL; ++ ++ if(supported) { ++ int r = supported(rate_mbit, mod, opaque); ++ if(r) return r; ++ } ++ ++ m |= gen_mask(rate_mbit, mod, opaque); ++ str++; ++ } while(c==','); ++ ++ *pstr = str-1; ++ *mask |= m; ++ return 0; ++} ++ ++static /* TODO: remove 'static' when moved to wireless.c */ ++int ++fill_ratemasks(const char *str, u32 *bmask, u32 *omask, ++ int (*supported)(int mbit, int mod,void *opaque), ++ u32 (*gen_mask)(int mbit, int mod,void *opaque), ++ void *opaque) ++{ ++ int r; ++ ++ r = fill_ratemask(&str, bmask, supported, gen_mask, opaque); ++ if(r) return r; ++ ++ if(*str==' ') { ++ str++; ++ r = fill_ratemask(&str, omask, supported, gen_mask, opaque); ++ if(r) return r; ++ } ++ if(*str) ++ return -EINVAL; ++ return 0; ++} +Index: linux-2.6.23/drivers/net/wireless/acx/usb.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.23/drivers/net/wireless/acx/usb.c 2008-01-20 21:13:40.000000000 +0000 +@@ -0,0 +1,1922 @@ ++/*********************************************************************** ++** Copyright (C) 2003 ACX100 Open Source Project ++** ++** The contents of this file are subject to the Mozilla Public ++** License Version 1.1 (the "License"); you may not use this file ++** except in compliance with the License. You may obtain a copy of ++** the License at http://www.mozilla.org/MPL/ ++** ++** Software distributed under the License is distributed on an "AS ++** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or ++** implied. See the License for the specific language governing ++** rights and limitations under the License. ++** ++** Alternatively, the contents of this file may be used under the ++** terms of the GNU Public License version 2 (the "GPL"), in which ++** case the provisions of the GPL are applicable instead of the ++** above. If you wish to allow the use of your version of this file ++** only under the terms of the GPL and not to allow others to use ++** your version of this file under the MPL, indicate your decision ++** by deleting the provisions above and replace them with the notice ++** and other provisions required by the GPL. If you do not delete ++** the provisions above, a recipient may use your version of this ++** file under either the MPL or the GPL. ++** --------------------------------------------------------------------- ++** Inquiries regarding the ACX100 Open Source Project can be ++** made directly to: ++** ++** acx100-users@lists.sf.net ++** http://acx100.sf.net ++** --------------------------------------------------------------------- ++*/ ++ ++/*********************************************************************** ++** USB support for TI ACX100 based devices. Many parts are taken from ++** the PCI driver. ++** ++** Authors: ++** Martin Wawro <martin.wawro AT uni-dortmund.de> ++** Andreas Mohr <andi AT lisas.de> ++** ++** LOCKING ++** callback functions called by USB core are running in interrupt context ++** and thus have names with _i_. ++*/ ++#define ACX_USB 1 ++ ++#include <linux/version.h> ++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) ++#include <linux/config.h> ++#endif ++#include <linux/types.h> ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/kernel.h> ++#include <linux/usb.h> ++#include <linux/netdevice.h> ++#include <linux/rtnetlink.h> ++#include <linux/etherdevice.h> ++#include <linux/wireless.h> ++#include <net/iw_handler.h> ++#include <linux/vmalloc.h> ++ ++#include "acx.h" ++ ++ ++/*********************************************************************** ++*/ ++/* number of endpoints of an interface */ ++#define NUM_EP(intf) (intf)->altsetting[0].desc.bNumEndpoints ++#define EP(intf, nr) (intf)->altsetting[0].endpoint[(nr)].desc ++#define GET_DEV(udev) usb_get_dev((udev)) ++#define PUT_DEV(udev) usb_put_dev((udev)) ++#define SET_NETDEV_OWNER(ndev, owner) /* not needed anymore ??? */ ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,14) ++/* removed in 2.6.14. We will use fake value for now */ ++#define URB_ASYNC_UNLINK 0 ++#endif ++ ++ ++/*********************************************************************** ++*/ ++/* ACX100 (TNETW1100) USB device: D-Link DWL-120+ */ ++#define ACX100_VENDOR_ID 0x2001 ++#define ACX100_PRODUCT_ID_UNBOOTED 0x3B01 ++#define ACX100_PRODUCT_ID_BOOTED 0x3B00 ++ ++/* TNETW1450 USB devices */ ++#define VENDOR_ID_DLINK 0x07b8 /* D-Link Corp. */ ++#define PRODUCT_ID_WUG2400 0xb21a /* AboCom WUG2400 or SafeCom SWLUT-54125 */ ++#define VENDOR_ID_AVM_GMBH 0x057c ++#define PRODUCT_ID_AVM_WLAN_USB 0x5601 ++#define PRODUCT_ID_AVM_WLAN_USB_si 0x6201 /* "self install" named Version: driver kills kernel on inbound scans from fritz box ??? */ ++#define VENDOR_ID_ZCOM 0x0cde ++#define PRODUCT_ID_ZCOM_XG750 0x0017 /* not tested yet */ ++#define VENDOR_ID_TI 0x0451 ++#define PRODUCT_ID_TI_UNKNOWN 0x60c5 /* not tested yet */ ++ ++#define ACX_USB_CTRL_TIMEOUT 5500 /* steps in ms */ ++ ++/* Buffer size for fw upload, same for both ACX100 USB and TNETW1450 */ ++#define USB_RWMEM_MAXLEN 2048 ++ ++/* The number of bulk URBs to use */ ++#define ACX_TX_URB_CNT 8 ++#define ACX_RX_URB_CNT 2 ++ ++/* Should be sent to the bulkout endpoint */ ++#define ACX_USB_REQ_UPLOAD_FW 0x10 ++#define ACX_USB_REQ_ACK_CS 0x11 ++#define ACX_USB_REQ_CMD 0x12 ++ ++/*********************************************************************** ++** Prototypes ++*/ ++static int acxusb_e_probe(struct usb_interface *, const struct usb_device_id *); ++static void acxusb_e_disconnect(struct usb_interface *); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) ++static void acxusb_i_complete_tx(struct urb *); ++static void acxusb_i_complete_rx(struct urb *); ++#else ++static void acxusb_i_complete_tx(struct urb *, struct pt_regs *); ++static void acxusb_i_complete_rx(struct urb *, struct pt_regs *); ++#endif ++static int acxusb_e_open(struct net_device *); ++static int acxusb_e_close(struct net_device *); ++static void acxusb_i_set_rx_mode(struct net_device *); ++static int acxusb_boot(struct usb_device *, int is_tnetw1450, int *radio_type); ++ ++static void acxusb_l_poll_rx(acx_device_t *adev, usb_rx_t* rx); ++ ++static void acxusb_i_tx_timeout(struct net_device *); ++ ++/* static void dump_device(struct usb_device *); */ ++/* static void dump_device_descriptor(struct usb_device_descriptor *); */ ++/* static void dump_config_descriptor(struct usb_config_descriptor *); */ ++ ++/*********************************************************************** ++** Module Data ++*/ ++#define TXBUFSIZE sizeof(usb_txbuffer_t) ++/* ++ * Now, this is just plain lying, but the device insists in giving us ++ * huge packets. We supply extra space after rxbuffer. Need to understand ++ * it better... ++ */ ++#define RXBUFSIZE (sizeof(rxbuffer_t) + \ ++ (sizeof(usb_rx_t) - sizeof(struct usb_rx_plain))) ++ ++static const struct usb_device_id ++acxusb_ids[] = { ++ { USB_DEVICE(ACX100_VENDOR_ID, ACX100_PRODUCT_ID_BOOTED) }, ++ { USB_DEVICE(ACX100_VENDOR_ID, ACX100_PRODUCT_ID_UNBOOTED) }, ++ { USB_DEVICE(VENDOR_ID_DLINK, PRODUCT_ID_WUG2400) }, ++ { USB_DEVICE(VENDOR_ID_AVM_GMBH, PRODUCT_ID_AVM_WLAN_USB) }, ++ { USB_DEVICE(VENDOR_ID_AVM_GMBH, PRODUCT_ID_AVM_WLAN_USB_si) }, ++ { USB_DEVICE(VENDOR_ID_ZCOM, PRODUCT_ID_ZCOM_XG750) }, ++ { USB_DEVICE(VENDOR_ID_TI, PRODUCT_ID_TI_UNKNOWN) }, ++ {} ++}; ++ ++MODULE_DEVICE_TABLE(usb, acxusb_ids); ++ ++/* USB driver data structure as required by the kernel's USB core */ ++static struct usb_driver ++acxusb_driver = { ++ .name = "acx_usb", ++ .probe = acxusb_e_probe, ++ .disconnect = acxusb_e_disconnect, ++ .id_table = acxusb_ids ++}; ++ ++ ++/*********************************************************************** ++** USB helper ++** ++** ldd3 ch13 says: ++** When the function is usb_kill_urb, the urb lifecycle is stopped. This ++** function is usually used when the device is disconnected from the system, ++** in the disconnect callback. For some drivers, the usb_unlink_urb function ++** should be used to tell the USB core to stop an urb. This function does not ++** wait for the urb to be fully stopped before returning to the caller. ++** This is useful for stoppingthe urb while in an interrupt handler or when ++** a spinlock is held, as waiting for a urb to fully stop requires the ability ++** for the USB core to put the calling process to sleep. This function requires ++** that the URB_ASYNC_UNLINK flag value be set in the urb that is being asked ++** to be stopped in order to work properly. ++** ++** (URB_ASYNC_UNLINK is obsolete, usb_unlink_urb will always be ++** asynchronous while usb_kill_urb is synchronous and should be called ++** directly (drivers/usb/core/urb.c)) ++** ++** In light of this, timeout is just for paranoid reasons... ++* ++* Actually, it's useful for debugging. If we reach timeout, we're doing ++* something wrong with the urbs. ++*/ ++static void ++acxusb_unlink_urb(struct urb* urb) ++{ ++ if (!urb) ++ return; ++ ++ if (urb->status == -EINPROGRESS) { ++ int timeout = 10; ++ ++ usb_unlink_urb(urb); ++ while (--timeout && urb->status == -EINPROGRESS) { ++ mdelay(1); ++ } ++ if (!timeout) { ++ printk("acx_usb: urb unlink timeout!\n"); ++ } ++ } ++} ++ ++ ++/*********************************************************************** ++** EEPROM and PHY read/write helpers ++*/ ++/*********************************************************************** ++** acxusb_s_read_phy_reg ++*/ ++int ++acxusb_s_read_phy_reg(acx_device_t *adev, u32 reg, u8 *charbuf) ++{ ++ /* mem_read_write_t mem; */ ++ ++ FN_ENTER; ++ ++ printk("%s doesn't seem to work yet, disabled.\n", __func__); ++ ++ /* ++ mem.addr = cpu_to_le16(reg); ++ mem.type = cpu_to_le16(0x82); ++ mem.len = cpu_to_le32(4); ++ acx_s_issue_cmd(adev, ACX1xx_CMD_MEM_READ, &mem, sizeof(mem)); ++ *charbuf = mem.data; ++ log(L_DEBUG, "read radio PHY[0x%04X]=0x%02X\n", reg, *charbuf); ++ */ ++ ++ FN_EXIT1(OK); ++ return OK; ++} ++ ++ ++/*********************************************************************** ++*/ ++int ++acxusb_s_write_phy_reg(acx_device_t *adev, u32 reg, u8 value) ++{ ++ mem_read_write_t mem; ++ ++ FN_ENTER; ++ ++ mem.addr = cpu_to_le16(reg); ++ mem.type = cpu_to_le16(0x82); ++ mem.len = cpu_to_le32(4); ++ mem.data = value; ++ acx_s_issue_cmd(adev, ACX1xx_CMD_MEM_WRITE, &mem, sizeof(mem)); ++ log(L_DEBUG, "write radio PHY[0x%04X]=0x%02X\n", reg, value); ++ ++ FN_EXIT1(OK); ++ return OK; ++} ++ ++ ++/*********************************************************************** ++** acxusb_s_issue_cmd_timeo ++** Excecutes a command in the command mailbox ++** ++** buffer = a pointer to the data. ++** The data must not include 4 byte command header ++*/ ++ ++/* TODO: ideally we shall always know how much we need ++** and this shall be 0 */ ++#define BOGUS_SAFETY_PADDING 0x40 ++ ++#undef FUNC ++#define FUNC "issue_cmd" ++ ++#if !ACX_DEBUG ++int ++acxusb_s_issue_cmd_timeo( ++ acx_device_t *adev, ++ unsigned cmd, ++ void *buffer, ++ unsigned buflen, ++ unsigned timeout) ++{ ++#else ++int ++acxusb_s_issue_cmd_timeo_debug( ++ acx_device_t *adev, ++ unsigned cmd, ++ void *buffer, ++ unsigned buflen, ++ unsigned timeout, ++ const char* cmdstr) ++{ ++#endif ++ /* USB ignores timeout param */ ++ ++ struct usb_device *usbdev; ++ struct { ++ u16 cmd; ++ u16 status; ++ u8 data[1]; ++ } ACX_PACKED *loc; ++ const char *devname; ++ int acklen, blocklen, inpipe, outpipe; ++ int cmd_status; ++ int result; ++ ++ FN_ENTER; ++ ++ devname = adev->ndev->name; ++ /* no "wlan%%d: ..." please */ ++ if (!devname || !devname[0] || devname[4]=='%') ++ devname = "acx"; ++ ++ log(L_CTL, FUNC"(cmd:%s,buflen:%u,type:0x%04X)\n", ++ cmdstr, buflen, ++ buffer ? le16_to_cpu(((acx_ie_generic_t *)buffer)->type) : -1); ++ ++ loc = kmalloc(buflen + 4 + BOGUS_SAFETY_PADDING, GFP_KERNEL); ++ if (!loc) { ++ printk("%s: "FUNC"(): no memory for data buffer\n", devname); ++ goto bad; ++ } ++ ++ /* get context from acx_device */ ++ usbdev = adev->usbdev; ++ ++ /* check which kind of command was issued */ ++ loc->cmd = cpu_to_le16(cmd); ++ loc->status = 0; ++ ++/* NB: buflen == frmlen + 4 ++** ++** Interrogate: write 8 bytes: (cmd,status,rid,frmlen), then ++** read (cmd,status,rid,frmlen,data[frmlen]) back ++** ++** Configure: write (cmd,status,rid,frmlen,data[frmlen]) ++** ++** Possibly bogus special handling of ACX1xx_IE_SCAN_STATUS removed ++*/ ++ ++ /* now write the parameters of the command if needed */ ++ acklen = buflen + 4 + BOGUS_SAFETY_PADDING; ++ blocklen = buflen; ++ if (buffer && buflen) { ++ /* if it's an INTERROGATE command, just pass the length ++ * of parameters to read, as data */ ++ if (cmd == ACX1xx_CMD_INTERROGATE) { ++ blocklen = 4; ++ acklen = buflen + 4; ++ } ++ memcpy(loc->data, buffer, blocklen); ++ } ++ blocklen += 4; /* account for cmd,status */ ++ ++ /* obtain the I/O pipes */ ++ outpipe = usb_sndctrlpipe(usbdev, 0); ++ inpipe = usb_rcvctrlpipe(usbdev, 0); ++ log(L_CTL, "ctrl inpipe=0x%X outpipe=0x%X\n", inpipe, outpipe); ++ log(L_CTL, "sending USB control msg (out) (blocklen=%d)\n", blocklen); ++ if (acx_debug & L_DATA) ++ acx_dump_bytes(loc, blocklen); ++ ++ result = usb_control_msg(usbdev, outpipe, ++ ACX_USB_REQ_CMD, /* request */ ++ USB_TYPE_VENDOR|USB_DIR_OUT, /* requesttype */ ++ 0, /* value */ ++ 0, /* index */ ++ loc, /* dataptr */ ++ blocklen, /* size */ ++ ACX_USB_CTRL_TIMEOUT /* timeout in ms */ ++ ); ++ ++ if (result == -ENODEV) { ++ log(L_CTL, "no device present (unplug?)\n"); ++ goto good; ++ } ++ ++ log(L_CTL, "wrote %d bytes\n", result); ++ if (result < 0) { ++ goto bad; ++ } ++ ++ /* check for device acknowledge */ ++ log(L_CTL, "sending USB control msg (in) (acklen=%d)\n", acklen); ++ loc->status = 0; /* delete old status flag -> set to IDLE */ ++ /* shall we zero out the rest? */ ++ result = usb_control_msg(usbdev, inpipe, ++ ACX_USB_REQ_CMD, /* request */ ++ USB_TYPE_VENDOR|USB_DIR_IN, /* requesttype */ ++ 0, /* value */ ++ 0, /* index */ ++ loc, /* dataptr */ ++ acklen, /* size */ ++ ACX_USB_CTRL_TIMEOUT /* timeout in ms */ ++ ); ++ if (result < 0) { ++ printk("%s: "FUNC"(): USB read error %d\n", devname, result); ++ goto bad; ++ } ++ if (acx_debug & L_CTL) { ++ printk("read %d bytes: ", result); ++ acx_dump_bytes(loc, result); ++ } ++ ++/* ++ check for result==buflen+4? Was seen: ++ ++interrogate(type:ACX100_IE_DOT11_ED_THRESHOLD,len:4) ++issue_cmd(cmd:ACX1xx_CMD_INTERROGATE,buflen:8,type:4111) ++ctrl inpipe=0x80000280 outpipe=0x80000200 ++sending USB control msg (out) (blocklen=8) ++01 00 00 00 0F 10 04 00 ++wrote 8 bytes ++sending USB control msg (in) (acklen=12) sizeof(loc->data ++read 4 bytes <==== MUST BE 12!! ++*/ ++ ++ cmd_status = le16_to_cpu(loc->status); ++ if (cmd_status != 1) { ++ printk("%s: "FUNC"(): cmd_status is not SUCCESS: %d (%s)\n", ++ devname, cmd_status, acx_cmd_status_str(cmd_status)); ++ /* TODO: goto bad; ? */ ++ } ++ if ((cmd == ACX1xx_CMD_INTERROGATE) && buffer && buflen) { ++ memcpy(buffer, loc->data, buflen); ++ log(L_CTL, "response frame: cmd=0x%04X status=%d\n", ++ le16_to_cpu(loc->cmd), ++ cmd_status); ++ } ++good: ++ kfree(loc); ++ FN_EXIT1(OK); ++ return OK; ++bad: ++ /* Give enough info so that callers can avoid ++ ** printing their own diagnostic messages */ ++#if ACX_DEBUG ++ printk("%s: "FUNC"(cmd:%s) FAILED\n", devname, cmdstr); ++#else ++ printk("%s: "FUNC"(cmd:0x%04X) FAILED\n", devname, cmd); ++#endif ++ dump_stack(); ++ kfree(loc); ++ FN_EXIT1(NOT_OK); ++ return NOT_OK; ++} ++ ++ ++/*********************************************************************** ++** acxusb_boot() ++** Inputs: ++** usbdev -> Pointer to kernel's usb_device structure ++** ++** Returns: ++** (int) Errorcode or 0 on success ++** ++** This function triggers the loading of the firmware image from harddisk ++** and then uploads the firmware to the USB device. After uploading the ++** firmware and transmitting the checksum, the device resets and appears ++** as a new device on the USB bus (the device we can finally deal with) ++*/ ++static inline int ++acxusb_fw_needs_padding(firmware_image_t *fw_image, unsigned int usb_maxlen) ++{ ++ unsigned int num_xfers = ((fw_image->size - 1) / usb_maxlen) + 1; ++ ++ return ((num_xfers % 2) == 0); ++} ++ ++static int ++acxusb_boot(struct usb_device *usbdev, int is_tnetw1450, int *radio_type) ++{ ++ char filename[sizeof("tiacx1NNusbcRR")]; ++ ++ firmware_image_t *fw_image = NULL; ++ char *usbbuf; ++ unsigned int offset; ++ unsigned int blk_len, inpipe, outpipe; ++ u32 num_processed; ++ u32 img_checksum, sum; ++ u32 file_size; ++ int result = -EIO; ++ int i; ++ ++ FN_ENTER; ++ ++ /* dump_device(usbdev); */ ++ ++ usbbuf = kmalloc(USB_RWMEM_MAXLEN, GFP_KERNEL); ++ if (!usbbuf) { ++ printk(KERN_ERR "acx: no memory for USB transfer buffer (%d bytes)\n", USB_RWMEM_MAXLEN); ++ result = -ENOMEM; ++ goto end; ++ } ++ if (is_tnetw1450) { ++ /* Obtain the I/O pipes */ ++ outpipe = usb_sndbulkpipe(usbdev, 1); ++ inpipe = usb_rcvbulkpipe(usbdev, 2); ++ ++ printk(KERN_DEBUG "wait for device ready\n"); ++ for (i = 0; i <= 2; i++) { ++ result = usb_bulk_msg(usbdev, inpipe, ++ usbbuf, ++ USB_RWMEM_MAXLEN, ++ &num_processed, ++ 2000 ++ ); ++ ++ if ((*(u32 *)&usbbuf[4] == 0x40000001) ++ && (*(u16 *)&usbbuf[2] == 0x1) ++ && ((*(u16 *)usbbuf & 0x3fff) == 0) ++ && ((*(u16 *)usbbuf & 0xc000) == 0xc000)) ++ break; ++ msleep(10); ++ } ++ if (i == 2) ++ goto fw_end; ++ ++ *radio_type = usbbuf[8]; ++ } else { ++ /* Obtain the I/O pipes */ ++ outpipe = usb_sndctrlpipe(usbdev, 0); ++ inpipe = usb_rcvctrlpipe(usbdev, 0); ++ ++ /* FIXME: shouldn't be hardcoded */ ++ *radio_type = RADIO_MAXIM_0D; ++ } ++ ++ snprintf(filename, sizeof(filename), "tiacx1%02dusbc%02X", ++ is_tnetw1450 * 11, *radio_type); ++ ++ fw_image = acx_s_read_fw(&usbdev->dev, filename, &file_size); ++ if (!fw_image) { ++ result = -EIO; ++ goto end; ++ } ++ log(L_INIT, "firmware size: %d bytes\n", file_size); ++ ++ img_checksum = le32_to_cpu(fw_image->chksum); ++ ++ if (is_tnetw1450) { ++ u8 cmdbuf[20]; ++ const u8 *p; ++ u8 need_padding; ++ u32 tmplen, val; ++ ++ memset(cmdbuf, 0, 16); ++ ++ need_padding = acxusb_fw_needs_padding(fw_image, USB_RWMEM_MAXLEN); ++ tmplen = need_padding ? file_size-4 : file_size-8; ++ *(u16 *)&cmdbuf[0] = 0xc000; ++ *(u16 *)&cmdbuf[2] = 0x000b; ++ *(u32 *)&cmdbuf[4] = tmplen; ++ *(u32 *)&cmdbuf[8] = file_size-8; ++ *(u32 *)&cmdbuf[12] = img_checksum; ++ ++ result = usb_bulk_msg(usbdev, outpipe, cmdbuf, 16, &num_processed, HZ); ++ if (result < 0) ++ goto fw_end; ++ ++ p = (const u8 *)&fw_image->size; ++ ++ /* first calculate checksum for image size part */ ++ sum = p[0]+p[1]+p[2]+p[3]; ++ p += 4; ++ ++ /* now continue checksum for firmware data part */ ++ tmplen = le32_to_cpu(fw_image->size); ++ for (i = 0; i < tmplen /* image size */; i++) { ++ sum += *p++; ++ } ++ ++ if (sum != le32_to_cpu(fw_image->chksum)) { ++ printk("acx: FATAL: firmware upload: " ++ "checksums don't match! " ++ "(0x%08x vs. 0x%08x)\n", ++ sum, fw_image->chksum); ++ goto fw_end; ++ } ++ ++ offset = 8; ++ while (offset < file_size) { ++ blk_len = file_size - offset; ++ if (blk_len > USB_RWMEM_MAXLEN) { ++ blk_len = USB_RWMEM_MAXLEN; ++ } ++ ++ log(L_INIT, "uploading firmware (%d bytes, offset=%d)\n", ++ blk_len, offset); ++ memcpy(usbbuf, ((u8 *)fw_image) + offset, blk_len); ++ ++ p = usbbuf; ++ for (i = 0; i < blk_len; i += 4) { ++ *(u32 *)p = be32_to_cpu(*(u32 *)p); ++ p += 4; ++ } ++ ++ result = usb_bulk_msg(usbdev, outpipe, usbbuf, blk_len, &num_processed, HZ); ++ if ((result < 0) || (num_processed != blk_len)) ++ goto fw_end; ++ offset += blk_len; ++ } ++ if (need_padding) { ++ printk(KERN_DEBUG "send padding\n"); ++ memset(usbbuf, 0, 4); ++ result = usb_bulk_msg(usbdev, outpipe, usbbuf, 4, &num_processed, HZ); ++ if ((result < 0) || (num_processed != 4)) ++ goto fw_end; ++ } ++ printk(KERN_DEBUG "read firmware upload result\n"); ++ memset(cmdbuf, 0, 20); /* additional memset */ ++ result = usb_bulk_msg(usbdev, inpipe, cmdbuf, 20, &num_processed, 2000); ++ if (result < 0) ++ goto fw_end; ++ if (*(u32 *)&cmdbuf[4] == 0x40000003) ++ goto fw_end; ++ if (*(u32 *)&cmdbuf[4]) ++ goto fw_end; ++ if (*(u16 *)&cmdbuf[16] != 1) ++ goto fw_end; ++ ++ val = *(u32 *)&cmdbuf[0]; ++ if ((val & 0x3fff) ++ || ((val & 0xc000) != 0xc000)) ++ goto fw_end; ++ ++ val = *(u32 *)&cmdbuf[8]; ++ if (val & 2) { ++ result = usb_bulk_msg(usbdev, inpipe, cmdbuf, 20, &num_processed, 2000); ++ if (result < 0) ++ goto fw_end; ++ val = *(u32 *)&cmdbuf[8]; ++ } ++ /* yup, no "else" here! */ ++ if (val & 1) { ++ memset(usbbuf, 0, 4); ++ result = usb_bulk_msg(usbdev, outpipe, usbbuf, 4, &num_processed, HZ); ++ if ((result < 0) || (!num_processed)) ++ goto fw_end; ++ } ++ ++ printk("TNETW1450 firmware upload successful!\n"); ++ result = 0; ++ goto end; ++fw_end: ++ result = -EIO; ++ goto end; ++ } else { ++ /* ACX100 USB */ ++ ++ /* now upload the firmware, slice the data into blocks */ ++ offset = 8; ++ while (offset < file_size) { ++ blk_len = file_size - offset; ++ if (blk_len > USB_RWMEM_MAXLEN) { ++ blk_len = USB_RWMEM_MAXLEN; ++ } ++ log(L_INIT, "uploading firmware (%d bytes, offset=%d)\n", ++ blk_len, offset); ++ memcpy(usbbuf, ((u8 *)fw_image) + offset, blk_len); ++ result = usb_control_msg(usbdev, outpipe, ++ ACX_USB_REQ_UPLOAD_FW, ++ USB_TYPE_VENDOR|USB_DIR_OUT, ++ (file_size - 8) & 0xffff, /* value */ ++ (file_size - 8) >> 16, /* index */ ++ usbbuf, /* dataptr */ ++ blk_len, /* size */ ++ 3000 /* timeout in ms */ ++ ); ++ offset += blk_len; ++ if (result < 0) { ++ printk(KERN_ERR "acx: error %d during upload " ++ "of firmware, aborting\n", result); ++ goto end; ++ } ++ } ++ ++ /* finally, send the checksum and reboot the device */ ++ /* does this trigger the reboot? */ ++ result = usb_control_msg(usbdev, outpipe, ++ ACX_USB_REQ_UPLOAD_FW, ++ USB_TYPE_VENDOR|USB_DIR_OUT, ++ img_checksum & 0xffff, /* value */ ++ img_checksum >> 16, /* index */ ++ NULL, /* dataptr */ ++ 0, /* size */ ++ 3000 /* timeout in ms */ ++ ); ++ if (result < 0) { ++ printk(KERN_ERR "acx: error %d during tx of checksum, " ++ "aborting\n", result); ++ goto end; ++ } ++ result = usb_control_msg(usbdev, inpipe, ++ ACX_USB_REQ_ACK_CS, ++ USB_TYPE_VENDOR|USB_DIR_IN, ++ img_checksum & 0xffff, /* value */ ++ img_checksum >> 16, /* index */ ++ usbbuf, /* dataptr */ ++ 8, /* size */ ++ 3000 /* timeout in ms */ ++ ); ++ if (result < 0) { ++ printk(KERN_ERR "acx: error %d during ACK of checksum, " ++ "aborting\n", result); ++ goto end; ++ } ++ if (*usbbuf != 0x10) { ++ printk(KERN_ERR "acx: invalid checksum?\n"); ++ result = -EINVAL; ++ goto end; ++ } ++ result = 0; ++ } ++ ++end: ++ vfree(fw_image); ++ kfree(usbbuf); ++ ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/* FIXME: maybe merge it with usual eeprom reading, into common code? */ ++static void ++acxusb_s_read_eeprom_version(acx_device_t *adev) ++{ ++ u8 eeprom_ver[0x8]; ++ ++ memset(eeprom_ver, 0, sizeof(eeprom_ver)); ++ acx_s_interrogate(adev, &eeprom_ver, ACX1FF_IE_EEPROM_VER); ++ ++ /* FIXME: which one of those values to take? */ ++ adev->eeprom_version = eeprom_ver[5]; ++} ++ ++ ++/* ++ * temporary helper function to at least fill important cfgopt members with ++ * useful replacement values until we figure out how one manages to fetch ++ * the configoption struct in the USB device case... ++ */ ++static int ++acxusb_s_fill_configoption(acx_device_t *adev) ++{ ++ adev->cfgopt_probe_delay = 200; ++ adev->cfgopt_dot11CCAModes = 4; ++ adev->cfgopt_dot11Diversity = 1; ++ adev->cfgopt_dot11ShortPreambleOption = 1; ++ adev->cfgopt_dot11PBCCOption = 1; ++ adev->cfgopt_dot11ChannelAgility = 0; ++ adev->cfgopt_dot11PhyType = 5; ++ adev->cfgopt_dot11TempType = 1; ++ return OK; ++} ++ ++ ++/*********************************************************************** ++** acxusb_e_probe() ++** ++** This function is invoked by the kernel's USB core whenever a new device is ++** attached to the system or the module is loaded. It is presented a usb_device ++** structure from which information regarding the device is obtained and evaluated. ++** In case this driver is able to handle one of the offered devices, it returns ++** a non-null pointer to a driver context and thereby claims the device. ++*/ ++ ++static void ++dummy_netdev_init(struct net_device *ndev) {} ++ ++static int ++acxusb_e_probe(struct usb_interface *intf, const struct usb_device_id *devID) ++{ ++ struct usb_device *usbdev = interface_to_usbdev(intf); ++ acx_device_t *adev = NULL; ++ struct net_device *ndev = NULL; ++ struct usb_config_descriptor *config; ++ struct usb_endpoint_descriptor *epdesc; ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) ++ struct usb_host_endpoint *ep; ++#endif ++ struct usb_interface_descriptor *ifdesc; ++ const char* msg; ++ int numconfigs, numfaces, numep; ++ int result = OK; ++ int i; ++ int radio_type; ++ /* this one needs to be more precise in case there appears a TNETW1450 from the same vendor */ ++ int is_tnetw1450 = (usbdev->descriptor.idVendor != ACX100_VENDOR_ID); ++ ++ FN_ENTER; ++ ++ if (is_tnetw1450) { ++ /* Boot the device (i.e. upload the firmware) */ ++ acxusb_boot(usbdev, is_tnetw1450, &radio_type); ++ ++ /* TNETW1450-based cards will continue right away with ++ * the same USB ID after booting */ ++ } else { ++ /* First check if this is the "unbooted" hardware */ ++ if (usbdev->descriptor.idProduct == ACX100_PRODUCT_ID_UNBOOTED) { ++ ++ /* Boot the device (i.e. upload the firmware) */ ++ acxusb_boot(usbdev, is_tnetw1450, &radio_type); ++ ++ /* DWL-120+ will first boot the firmware, ++ * then later have a *separate* probe() run ++ * since its USB ID will have changed after ++ * firmware boot! ++ * Since the first probe() run has no ++ * other purpose than booting the firmware, ++ * simply return immediately. ++ */ ++ log(L_INIT, "finished booting, returning from probe()\n"); ++ result = OK; /* success */ ++ goto end; ++ } ++ else ++ /* device not unbooted, but invalid USB ID!? */ ++ if (usbdev->descriptor.idProduct != ACX100_PRODUCT_ID_BOOTED) ++ goto end_nodev; ++ } ++ ++/* Ok, so it's our device and it has already booted */ ++ ++ /* Allocate memory for a network device */ ++ ++ ndev = alloc_netdev(sizeof(*adev), "wlan%d", dummy_netdev_init); ++ /* (NB: memsets to 0 entire area) */ ++ if (!ndev) { ++ msg = "acx: no memory for netdev\n"; ++ goto end_nomem; ++ } ++ ++ /* Register the callbacks for the network device functions */ ++ ++ ether_setup(ndev); ++ ndev->open = &acxusb_e_open; ++ ndev->stop = &acxusb_e_close; ++ ndev->hard_start_xmit = (void *)&acx_i_start_xmit; ++ ndev->get_stats = (void *)&acx_e_get_stats; ++#if IW_HANDLER_VERSION <= 5 ++ ndev->get_wireless_stats = (void *)&acx_e_get_wireless_stats; ++#endif ++ ndev->wireless_handlers = (struct iw_handler_def *)&acx_ioctl_handler_def; ++ ndev->set_multicast_list = (void *)&acxusb_i_set_rx_mode; ++#ifdef HAVE_TX_TIMEOUT ++ ndev->tx_timeout = &acxusb_i_tx_timeout; ++ ndev->watchdog_timeo = 4 * HZ; ++#endif ++ ndev->change_mtu = &acx_e_change_mtu; ++ SET_MODULE_OWNER(ndev); ++ ++ /* Setup private driver context */ ++ ++ adev = ndev2adev(ndev); ++ adev->ndev = ndev; ++ ++ adev->dev_type = DEVTYPE_USB; ++ adev->radio_type = radio_type; ++ if (is_tnetw1450) { ++ /* well, actually it's a TNETW1450, but since it ++ * seems to be sufficiently similar to TNETW1130, ++ * I don't want to change large amounts of code now */ ++ adev->chip_type = CHIPTYPE_ACX111; ++ } else { ++ adev->chip_type = CHIPTYPE_ACX100; ++ } ++ ++ adev->usbdev = usbdev; ++ spin_lock_init(&adev->lock); /* initial state: unlocked */ ++ sema_init(&adev->sem, 1); /* initial state: 1 (upped) */ ++ ++ /* Check that this is really the hardware we know about. ++ ** If not sure, at least notify the user that he ++ ** may be in trouble... ++ */ ++ numconfigs = (int)usbdev->descriptor.bNumConfigurations; ++ if (numconfigs != 1) ++ printk("acx: number of configurations is %d, " ++ "this driver only knows how to handle 1, " ++ "be prepared for surprises\n", numconfigs); ++ ++ config = &usbdev->config->desc; ++ numfaces = config->bNumInterfaces; ++ if (numfaces != 1) ++ printk("acx: number of interfaces is %d, " ++ "this driver only knows how to handle 1, " ++ "be prepared for surprises\n", numfaces); ++ ++ ifdesc = &intf->altsetting->desc; ++ numep = ifdesc->bNumEndpoints; ++ log(L_DEBUG, "# of endpoints: %d\n", numep); ++ ++ if (is_tnetw1450) { ++ adev->bulkoutep = 1; ++ adev->bulkinep = 2; ++ } else { ++ /* obtain information about the endpoint ++ ** addresses, begin with some default values ++ */ ++ adev->bulkoutep = 1; ++ adev->bulkinep = 1; ++ for (i = 0; i < numep; i++) { ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) ++ ep = usbdev->ep_in[i]; ++ if (!ep) ++ continue; ++ epdesc = &ep->desc; ++#else ++ epdesc = usb_epnum_to_ep_desc(usbdev, i); ++ if (!epdesc) ++ continue; ++#endif ++ if (epdesc->bmAttributes & USB_ENDPOINT_XFER_BULK) { ++ if (epdesc->bEndpointAddress & 0x80) ++ adev->bulkinep = epdesc->bEndpointAddress & 0xF; ++ else ++ adev->bulkoutep = epdesc->bEndpointAddress & 0xF; ++ } ++ } ++ } ++ log(L_DEBUG, "bulkout ep: 0x%X\n", adev->bulkoutep); ++ log(L_DEBUG, "bulkin ep: 0x%X\n", adev->bulkinep); ++ ++ /* already done by memset: adev->rxtruncsize = 0; */ ++ log(L_DEBUG, "TXBUFSIZE=%d RXBUFSIZE=%d\n", ++ (int) TXBUFSIZE, (int) RXBUFSIZE); ++ ++ /* Allocate the RX/TX containers. */ ++ adev->usb_tx = kmalloc(sizeof(usb_tx_t) * ACX_TX_URB_CNT, GFP_KERNEL); ++ if (!adev->usb_tx) { ++ msg = "acx: no memory for tx container"; ++ goto end_nomem; ++ } ++ adev->usb_rx = kmalloc(sizeof(usb_rx_t) * ACX_RX_URB_CNT, GFP_KERNEL); ++ if (!adev->usb_rx) { ++ msg = "acx: no memory for rx container"; ++ goto end_nomem; ++ } ++ ++ /* Setup URBs for bulk-in/out messages */ ++ for (i = 0; i < ACX_RX_URB_CNT; i++) { ++ adev->usb_rx[i].urb = usb_alloc_urb(0, GFP_KERNEL); ++ if (!adev->usb_rx[i].urb) { ++ msg = "acx: no memory for input URB\n"; ++ goto end_nomem; ++ } ++ adev->usb_rx[i].urb->status = 0; ++ adev->usb_rx[i].adev = adev; ++ adev->usb_rx[i].busy = 0; ++ } ++ ++ for (i = 0; i< ACX_TX_URB_CNT; i++) { ++ adev->usb_tx[i].urb = usb_alloc_urb(0, GFP_KERNEL); ++ if (!adev->usb_tx[i].urb) { ++ msg = "acx: no memory for output URB\n"; ++ goto end_nomem; ++ } ++ adev->usb_tx[i].urb->status = 0; ++ adev->usb_tx[i].adev = adev; ++ adev->usb_tx[i].busy = 0; ++ } ++ adev->tx_free = ACX_TX_URB_CNT; ++ ++ usb_set_intfdata(intf, adev); ++ SET_NETDEV_DEV(ndev, &intf->dev); ++ ++ /* TODO: move all of fw cmds to open()? But then we won't know our MAC addr ++ until ifup (it's available via reading ACX1xx_IE_DOT11_STATION_ID)... */ ++ ++ /* put acx out of sleep mode and initialize it */ ++ acx_s_issue_cmd(adev, ACX1xx_CMD_WAKE, NULL, 0); ++ ++ result = acx_s_init_mac(adev); ++ if (result) ++ goto end; ++ ++ /* TODO: see similar code in pci.c */ ++ acxusb_s_read_eeprom_version(adev); ++ acxusb_s_fill_configoption(adev); ++ acx_s_set_defaults(adev); ++ acx_s_get_firmware_version(adev); ++ acx_display_hardware_details(adev); ++ ++ /* Register the network device */ ++ log(L_INIT, "registering network device\n"); ++ result = register_netdev(ndev); ++ if (result) { ++ msg = "acx: failed to register USB network device " ++ "(error %d)\n"; ++ goto end_nomem; ++ } ++ ++ acx_proc_register_entries(ndev); ++ ++ acx_stop_queue(ndev, "on probe"); ++ acx_carrier_off(ndev, "on probe"); ++ ++ printk("acx: USB module " ACX_RELEASE " loaded successfully\n"); ++ ++#if CMD_DISCOVERY ++ great_inquisitor(adev); ++#endif ++ ++ /* Everything went OK, we are happy now */ ++ result = OK; ++ goto end; ++ ++end_nomem: ++ printk(msg, result); ++ ++ if (ndev) { ++ if (adev->usb_rx) { ++ for (i = 0; i < ACX_RX_URB_CNT; i++) ++ usb_free_urb(adev->usb_rx[i].urb); ++ kfree(adev->usb_rx); ++ } ++ if (adev->usb_tx) { ++ for (i = 0; i < ACX_TX_URB_CNT; i++) ++ usb_free_urb(adev->usb_tx[i].urb); ++ kfree(adev->usb_tx); ++ } ++ free_netdev(ndev); ++ } ++ ++ result = -ENOMEM; ++ goto end; ++ ++end_nodev: ++ /* no device we could handle, return error. */ ++ result = -EIO; ++ ++end: ++ FN_EXIT1(result); ++ return result; ++} ++ ++ ++/*********************************************************************** ++** acxusb_e_disconnect() ++** ++** This function is invoked whenever the user pulls the plug from the USB ++** device or the module is removed from the kernel. In these cases, the ++** network devices have to be taken down and all allocated memory has ++** to be freed. ++*/ ++static void ++acxusb_e_disconnect(struct usb_interface *intf) ++{ ++ acx_device_t *adev = usb_get_intfdata(intf); ++ unsigned long flags; ++ int i; ++ ++ FN_ENTER; ++ ++ /* No WLAN device... no sense */ ++ if (!adev) ++ goto end; ++ ++ /* Unregister network device ++ * ++ * If the interface is up, unregister_netdev() will take ++ * care of calling our close() function, which takes ++ * care of unlinking the urbs, sending the device to ++ * sleep, etc... ++ * This can't be called with sem or lock held because ++ * _close() will try to grab it as well if it's called, ++ * deadlocking the machine. ++ */ ++ unregister_netdev(adev->ndev); ++ ++ acx_sem_lock(adev); ++ acx_lock(adev, flags); ++ /* This device exists no more */ ++ usb_set_intfdata(intf, NULL); ++ acx_proc_unregister_entries(adev->ndev); ++ ++ /* ++ * Here we only free them. _close() took care of ++ * unlinking them. ++ */ ++ for (i = 0; i < ACX_RX_URB_CNT; ++i) { ++ usb_free_urb(adev->usb_rx[i].urb); ++ } ++ for (i = 0; i< ACX_TX_URB_CNT; ++i) { ++ usb_free_urb(adev->usb_tx[i].urb); ++ } ++ ++ /* Freeing containers */ ++ kfree(adev->usb_rx); ++ kfree(adev->usb_tx); ++ ++ acx_unlock(adev, flags); ++ acx_sem_unlock(adev); ++ ++ free_netdev(adev->ndev); ++end: ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acxusb_e_open() ++** This function is called when the user sets up the network interface. ++** It initializes a management timer, sets up the USB card and starts ++** the network tx queue and USB receive. ++*/ ++static int ++acxusb_e_open(struct net_device *ndev) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ unsigned long flags; ++ int i; ++ ++ FN_ENTER; ++ ++ acx_sem_lock(adev); ++ ++ /* put the ACX100 out of sleep mode */ ++ acx_s_issue_cmd(adev, ACX1xx_CMD_WAKE, NULL, 0); ++ ++ acx_init_task_scheduler(adev); ++ ++ init_timer(&adev->mgmt_timer); ++ adev->mgmt_timer.function = acx_i_timer; ++ adev->mgmt_timer.data = (unsigned long)adev; ++ ++ /* acx_s_start needs it */ ++ SET_BIT(adev->dev_state_mask, ACX_STATE_IFACE_UP); ++ acx_s_start(adev); ++ ++ /* don't acx_start_queue() here, we need to associate first */ ++ ++ acx_lock(adev, flags); ++ for (i = 0; i < ACX_RX_URB_CNT; i++) { ++ adev->usb_rx[i].urb->status = 0; ++ } ++ ++ acxusb_l_poll_rx(adev, &adev->usb_rx[0]); ++ ++ acx_unlock(adev, flags); ++ ++ acx_sem_unlock(adev); ++ ++ FN_EXIT0; ++ return 0; ++} ++ ++ ++/*********************************************************************** ++** acxusb_e_close() ++** ++** This function stops the network functionality of the interface (invoked ++** when the user calls ifconfig <wlan> down). The tx queue is halted and ++** the device is marked as down. In case there were any pending USB bulk ++** transfers, these are unlinked (asynchronously). The module in-use count ++** is also decreased in this function. ++*/ ++static int ++acxusb_e_close(struct net_device *ndev) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ unsigned long flags; ++ int i; ++ ++ FN_ENTER; ++ ++#ifdef WE_STILL_DONT_CARE_ABOUT_IT ++ /* Transmit a disassociate frame */ ++ lock ++ acx_l_transmit_disassoc(adev, &client); ++ unlock ++#endif ++ ++ acx_sem_lock(adev); ++ ++ CLEAR_BIT(adev->dev_state_mask, ACX_STATE_IFACE_UP); ++ ++/* Code below is remarkably similar to acxpci_s_down(). Maybe we can merge them? */ ++ ++ /* Make sure we don't get any more rx requests */ ++ acx_s_issue_cmd(adev, ACX1xx_CMD_DISABLE_RX, NULL, 0); ++ acx_s_issue_cmd(adev, ACX1xx_CMD_DISABLE_TX, NULL, 0); ++ ++ /* ++ * We must do FLUSH *without* holding sem to avoid a deadlock. ++ * See pci.c:acxpci_s_down() for deails. ++ */ ++ acx_sem_unlock(adev); ++ FLUSH_SCHEDULED_WORK(); ++ acx_sem_lock(adev); ++ ++ /* Power down the device */ ++ acx_s_issue_cmd(adev, ACX1xx_CMD_SLEEP, NULL, 0); ++ ++ /* Stop the transmit queue, mark the device as DOWN */ ++ acx_lock(adev, flags); ++ acx_stop_queue(ndev, "on ifdown"); ++ acx_set_status(adev, ACX_STATUS_0_STOPPED); ++ /* stop pending rx/tx urb transfers */ ++ for (i = 0; i < ACX_TX_URB_CNT; i++) { ++ acxusb_unlink_urb(adev->usb_tx[i].urb); ++ adev->usb_tx[i].busy = 0; ++ } ++ for (i = 0; i < ACX_RX_URB_CNT; i++) { ++ acxusb_unlink_urb(adev->usb_rx[i].urb); ++ adev->usb_rx[i].busy = 0; ++ } ++ adev->tx_free = ACX_TX_URB_CNT; ++ acx_unlock(adev, flags); ++ ++ /* Must do this outside of lock */ ++ del_timer_sync(&adev->mgmt_timer); ++ ++ acx_sem_unlock(adev); ++ ++ FN_EXIT0; ++ return 0; ++} ++ ++ ++/*********************************************************************** ++** acxusb_l_poll_rx ++** This function (re)initiates a bulk-in USB transfer on a given urb ++*/ ++static void ++acxusb_l_poll_rx(acx_device_t *adev, usb_rx_t* rx) ++{ ++ struct usb_device *usbdev; ++ struct urb *rxurb; ++ int errcode, rxnum; ++ unsigned int inpipe; ++ ++ FN_ENTER; ++ ++ rxurb = rx->urb; ++ usbdev = adev->usbdev; ++ ++ rxnum = rx - adev->usb_rx; ++ ++ inpipe = usb_rcvbulkpipe(usbdev, adev->bulkinep); ++ if (unlikely(rxurb->status == -EINPROGRESS)) { ++ printk(KERN_ERR "acx: error, rx triggered while rx urb in progress\n"); ++ /* FIXME: this is nasty, receive is being cancelled by this code ++ * on the other hand, this should not happen anyway... ++ */ ++ usb_unlink_urb(rxurb); ++ } else ++ if (unlikely(rxurb->status == -ECONNRESET)) { ++ log(L_USBRXTX, "acx_usb: _poll_rx: connection reset\n"); ++ goto end; ++ } ++ rxurb->actual_length = 0; ++ usb_fill_bulk_urb(rxurb, usbdev, inpipe, ++ &rx->bulkin, /* dataptr */ ++ RXBUFSIZE, /* size */ ++ acxusb_i_complete_rx, /* handler */ ++ rx /* handler param */ ++ ); ++ rxurb->transfer_flags = URB_ASYNC_UNLINK; ++ ++ /* ATOMIC: we may be called from complete_rx() usb callback */ ++ errcode = usb_submit_urb(rxurb, GFP_ATOMIC); ++ /* FIXME: evaluate the error code! */ ++ log(L_USBRXTX, "SUBMIT RX (%d) inpipe=0x%X size=%d errcode=%d\n", ++ rxnum, inpipe, (int) RXBUFSIZE, errcode); ++end: ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acxusb_i_complete_rx() ++** Inputs: ++** urb -> pointer to USB request block ++** regs -> pointer to register-buffer for syscalls (see asm/ptrace.h) ++** ++** This function is invoked by USB subsystem whenever a bulk receive ++** request returns. ++** The received data is then committed to the network stack and the next ++** USB receive is triggered. ++*/ ++static void ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) ++acxusb_i_complete_rx(struct urb *urb) ++#else ++acxusb_i_complete_rx(struct urb *urb, struct pt_regs *regs) ++#endif ++{ ++ acx_device_t *adev; ++ rxbuffer_t *ptr; ++ rxbuffer_t *inbuf; ++ usb_rx_t *rx; ++ unsigned long flags; ++ int size, remsize, packetsize, rxnum; ++ ++ FN_ENTER; ++ ++ BUG_ON(!urb->context); ++ ++ rx = (usb_rx_t *)urb->context; ++ adev = rx->adev; ++ ++ acx_lock(adev, flags); ++ ++ /* ++ * Happens on disconnect or close. Don't play with the urb. ++ * Don't resubmit it. It will get unlinked by close() ++ */ ++ if (unlikely(!(adev->dev_state_mask & ACX_STATE_IFACE_UP))) { ++ log(L_USBRXTX, "rx: device is down, not doing anything\n"); ++ goto end_unlock; ++ } ++ ++ inbuf = &rx->bulkin; ++ size = urb->actual_length; ++ remsize = size; ++ rxnum = rx - adev->usb_rx; ++ ++ log(L_USBRXTX, "RETURN RX (%d) status=%d size=%d\n", ++ rxnum, urb->status, size); ++ ++ /* Send the URB that's waiting. */ ++ log(L_USBRXTX, "rxnum=%d, sending=%d\n", rxnum, rxnum^1); ++ acxusb_l_poll_rx(adev, &adev->usb_rx[rxnum^1]); ++ ++ if (unlikely(size > sizeof(rxbuffer_t))) ++ printk("acx_usb: rx too large: %d, please report\n", size); ++ ++ /* check if the transfer was aborted */ ++ switch (urb->status) { ++ case 0: /* No error */ ++ break; ++ case -EOVERFLOW: ++ printk(KERN_ERR "acx: rx data overrun\n"); ++ adev->rxtruncsize = 0; /* Not valid anymore. */ ++ goto end_unlock; ++ case -ECONNRESET: ++ adev->rxtruncsize = 0; ++ goto end_unlock; ++ case -ESHUTDOWN: /* rmmod */ ++ adev->rxtruncsize = 0; ++ goto end_unlock; ++ default: ++ adev->rxtruncsize = 0; ++ adev->stats.rx_errors++; ++ printk("acx: rx error (urb status=%d)\n", urb->status); ++ goto end_unlock; ++ } ++ ++ if (unlikely(!size)) ++ printk("acx: warning, encountered zerolength rx packet\n"); ++ ++ if (urb->transfer_buffer != inbuf) ++ goto end_unlock; ++ ++ /* check if previous frame was truncated ++ ** FIXME: this code can only handle truncation ++ ** of consecutive packets! ++ */ ++ ptr = inbuf; ++ if (adev->rxtruncsize) { ++ int tail_size; ++ ++ ptr = &adev->rxtruncbuf; ++ packetsize = RXBUF_BYTES_USED(ptr); ++ if (acx_debug & L_USBRXTX) { ++ printk("handling truncated frame (truncsize=%d size=%d " ++ "packetsize(from trunc)=%d)\n", ++ adev->rxtruncsize, size, packetsize); ++ acx_dump_bytes(ptr, RXBUF_HDRSIZE); ++ acx_dump_bytes(inbuf, RXBUF_HDRSIZE); ++ } ++ ++ /* bytes needed for rxtruncbuf completion: */ ++ tail_size = packetsize - adev->rxtruncsize; ++ ++ if (size < tail_size) { ++ /* there is not enough data to complete this packet, ++ ** simply append the stuff to the truncation buffer ++ */ ++ memcpy(((char *)ptr) + adev->rxtruncsize, inbuf, size); ++ adev->rxtruncsize += size; ++ remsize = 0; ++ } else { ++ /* ok, this data completes the previously ++ ** truncated packet. copy it into a descriptor ++ ** and give it to the rest of the stack */ ++ ++ /* append tail to previously truncated part ++ ** NB: adev->rxtruncbuf (pointed to by ptr) can't ++ ** overflow because this is already checked before ++ ** truncation buffer was filled. See below, ++ ** "if (packetsize > sizeof(rxbuffer_t))..." code */ ++ memcpy(((char *)ptr) + adev->rxtruncsize, inbuf, tail_size); ++ ++ if (acx_debug & L_USBRXTX) { ++ printk("full trailing packet + 12 bytes:\n"); ++ acx_dump_bytes(inbuf, tail_size + RXBUF_HDRSIZE); ++ } ++ acx_l_process_rxbuf(adev, ptr); ++ adev->rxtruncsize = 0; ++ ptr = (rxbuffer_t *) (((char *)inbuf) + tail_size); ++ remsize -= tail_size; ++ } ++ log(L_USBRXTX, "post-merge size=%d remsize=%d\n", ++ size, remsize); ++ } ++ ++ /* size = USB data block size ++ ** remsize = unprocessed USB bytes left ++ ** ptr = current pos in USB data block ++ */ ++ while (remsize) { ++ if (remsize < RXBUF_HDRSIZE) { ++ printk("acx: truncated rx header (%d bytes)!\n", ++ remsize); ++ if (ACX_DEBUG) ++ acx_dump_bytes(ptr, remsize); ++ break; ++ } ++ ++ packetsize = RXBUF_BYTES_USED(ptr); ++ log(L_USBRXTX, "packet with packetsize=%d\n", packetsize); ++ ++ if (RXBUF_IS_TXSTAT(ptr)) { ++ /* do rate handling */ ++ usb_txstatus_t *stat = (void*)ptr; ++ u16 client_no = (u16)stat->hostdata; ++ ++ log(L_USBRXTX, "tx: stat: mac_cnt_rcvd:%04X " ++ "queue_index:%02X mac_status:%02X hostdata:%08X " ++ "rate:%u ack_failures:%02X rts_failures:%02X " ++ "rts_ok:%02X\n", ++ stat->mac_cnt_rcvd, ++ stat->queue_index, stat->mac_status, stat->hostdata, ++ stat->rate, stat->ack_failures, stat->rts_failures, ++ stat->rts_ok); ++ ++ if (adev->rate_auto && client_no < VEC_SIZE(adev->sta_list)) { ++ client_t *clt = &adev->sta_list[client_no]; ++ u16 cur = stat->hostdata >> 16; ++ ++ if (clt && clt->rate_cur == cur) { ++ acx_l_handle_txrate_auto(adev, clt, ++ cur, /* intended rate */ ++ stat->rate, 0, /* actually used rate */ ++ stat->mac_status, /* error? */ ++ ACX_TX_URB_CNT - adev->tx_free); ++ } ++ } ++ goto next; ++ } ++ ++ if (packetsize > sizeof(rxbuffer_t)) { ++ printk("acx: packet exceeds max wlan " ++ "frame size (%d > %d). size=%d\n", ++ packetsize, (int) sizeof(rxbuffer_t), size); ++ if (ACX_DEBUG) ++ acx_dump_bytes(ptr, 16); ++ /* FIXME: put some real error-handling in here! */ ++ break; ++ } ++ ++ if (packetsize > remsize) { ++ /* frame truncation handling */ ++ if (acx_debug & L_USBRXTX) { ++ printk("need to truncate packet, " ++ "packetsize=%d remsize=%d " ++ "size=%d bytes:", ++ packetsize, remsize, size); ++ acx_dump_bytes(ptr, RXBUF_HDRSIZE); ++ } ++ memcpy(&adev->rxtruncbuf, ptr, remsize); ++ adev->rxtruncsize = remsize; ++ break; ++ } ++ ++ /* packetsize <= remsize */ ++ /* now handle the received data */ ++ acx_l_process_rxbuf(adev, ptr); ++next: ++ ptr = (rxbuffer_t *)(((char *)ptr) + packetsize); ++ remsize -= packetsize; ++ if ((acx_debug & L_USBRXTX) && remsize) { ++ printk("more than one packet in buffer, " ++ "second packet hdr:"); ++ acx_dump_bytes(ptr, RXBUF_HDRSIZE); ++ } ++ } ++ ++end_unlock: ++ acx_unlock(adev, flags); ++/* end: */ ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++** acxusb_i_complete_tx() ++** Inputs: ++** urb -> pointer to USB request block ++** regs -> pointer to register-buffer for syscalls (see asm/ptrace.h) ++** ++** This function is invoked upon termination of a USB transfer. ++*/ ++static void ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 19) ++acxusb_i_complete_tx(struct urb *urb) ++#else ++acxusb_i_complete_tx(struct urb *urb, struct pt_regs *regs) ++#endif ++{ ++ acx_device_t *adev; ++ usb_tx_t *tx; ++ unsigned long flags; ++ int txnum; ++ ++ FN_ENTER; ++ ++ BUG_ON(!urb->context); ++ ++ tx = (usb_tx_t *)urb->context; ++ adev = tx->adev; ++ ++ txnum = tx - adev->usb_tx; ++ ++ acx_lock(adev, flags); ++ ++ /* ++ * If the iface isn't up, we don't have any right ++ * to play with them. The urb may get unlinked. ++ */ ++ if (unlikely(!(adev->dev_state_mask & ACX_STATE_IFACE_UP))) { ++ log(L_USBRXTX, "tx: device is down, not doing anything\n"); ++ goto end_unlock; ++ } ++ ++ log(L_USBRXTX, "RETURN TX (%d): status=%d size=%d\n", ++ txnum, urb->status, urb->actual_length); ++ ++ /* handle USB transfer errors */ ++ switch (urb->status) { ++ case 0: /* No error */ ++ break; ++ case -ESHUTDOWN: ++ goto end_unlock; ++ break; ++ case -ECONNRESET: ++ goto end_unlock; ++ break; ++ /* FIXME: real error-handling code here please */ ++ default: ++ printk(KERN_ERR "acx: tx error, urb status=%d\n", urb->status); ++ /* FIXME: real error-handling code here please */ ++ } ++ ++ /* free the URB and check for more data */ ++ tx->busy = 0; ++ adev->tx_free++; ++ if ((adev->tx_free >= TX_START_QUEUE) ++ && (adev->status == ACX_STATUS_4_ASSOCIATED) ++ && (acx_queue_stopped(adev->ndev)) ++ ) { ++ log(L_BUF, "tx: wake queue (%u free txbufs)\n", ++ adev->tx_free); ++ acx_wake_queue(adev->ndev, NULL); ++ } ++ ++end_unlock: ++ acx_unlock(adev, flags); ++/* end: */ ++ FN_EXIT0; ++} ++ ++ ++/*************************************************************** ++** acxusb_l_alloc_tx ++** Actually returns a usb_tx_t* ptr ++*/ ++tx_t* ++acxusb_l_alloc_tx(acx_device_t *adev) ++{ ++ usb_tx_t *tx; ++ unsigned head; ++ ++ FN_ENTER; ++ ++ head = adev->tx_head; ++ do { ++ head = (head + 1) % ACX_TX_URB_CNT; ++ if (!adev->usb_tx[head].busy) { ++ log(L_USBRXTX, "allocated tx %d\n", head); ++ tx = &adev->usb_tx[head]; ++ tx->busy = 1; ++ adev->tx_free--; ++ /* Keep a few free descs between head and tail of tx ring. ++ ** It is not absolutely needed, just feels safer */ ++ if (adev->tx_free < TX_STOP_QUEUE) { ++ log(L_BUF, "tx: stop queue " ++ "(%u free txbufs)\n", adev->tx_free); ++ acx_stop_queue(adev->ndev, NULL); ++ } ++ goto end; ++ } ++ } while (likely(head!=adev->tx_head)); ++ tx = NULL; ++ printk_ratelimited("acx: tx buffers full\n"); ++end: ++ adev->tx_head = head; ++ FN_EXIT0; ++ return (tx_t*)tx; ++} ++ ++ ++/*************************************************************** ++** Used if alloc_tx()'ed buffer needs to be cancelled without doing tx ++*/ ++void ++acxusb_l_dealloc_tx(tx_t *tx_opaque) ++{ ++ usb_tx_t* tx = (usb_tx_t*)tx_opaque; ++ tx->busy = 0; ++} ++ ++ ++/*************************************************************** ++*/ ++void* ++acxusb_l_get_txbuf(acx_device_t *adev, tx_t* tx_opaque) ++{ ++ usb_tx_t* tx = (usb_tx_t*)tx_opaque; ++ return &tx->bulkout.data; ++} ++ ++ ++/*************************************************************** ++** acxusb_l_tx_data ++** ++** Can be called from IRQ (rx -> (AP bridging or mgmt response) -> tx). ++** Can be called from acx_i_start_xmit (data frames from net core). ++*/ ++void ++acxusb_l_tx_data(acx_device_t *adev, tx_t* tx_opaque, int wlanpkt_len) ++{ ++ struct usb_device *usbdev; ++ struct urb* txurb; ++ usb_tx_t* tx; ++ usb_txbuffer_t* txbuf; ++ client_t *clt; ++ wlan_hdr_t* whdr; ++ unsigned int outpipe; ++ int ucode, txnum; ++ ++ FN_ENTER; ++ ++ tx = ((usb_tx_t *)tx_opaque); ++ txurb = tx->urb; ++ txbuf = &tx->bulkout; ++ whdr = (wlan_hdr_t *)txbuf->data; ++ txnum = tx - adev->usb_tx; ++ ++ log(L_DEBUG, "using buf#%d free=%d len=%d\n", ++ txnum, adev->tx_free, wlanpkt_len); ++ ++ switch (adev->mode) { ++ case ACX_MODE_0_ADHOC: ++ case ACX_MODE_3_AP: ++ clt = acx_l_sta_list_get(adev, whdr->a1); ++ break; ++ case ACX_MODE_2_STA: ++ clt = adev->ap_client; ++ break; ++ default: /* ACX_MODE_OFF, ACX_MODE_MONITOR */ ++ clt = NULL; ++ break; ++ } ++ ++ if (unlikely(clt && !clt->rate_cur)) { ++ printk("acx: driver bug! bad ratemask\n"); ++ goto end; ++ } ++ ++ /* fill the USB transfer header */ ++ txbuf->desc = cpu_to_le16(USB_TXBUF_TXDESC); ++ txbuf->mpdu_len = cpu_to_le16(wlanpkt_len); ++ txbuf->queue_index = 1; ++ if (clt) { ++ txbuf->rate = clt->rate_100; ++ txbuf->hostdata = (clt - adev->sta_list) | (clt->rate_cur << 16); ++ } else { ++ txbuf->rate = adev->rate_bcast100; ++ txbuf->hostdata = ((u16)-1) | (adev->rate_bcast << 16); ++ } ++ txbuf->ctrl1 = DESC_CTL_FIRSTFRAG; ++ if (1 == adev->preamble_cur) ++ SET_BIT(txbuf->ctrl1, DESC_CTL_SHORT_PREAMBLE); ++ txbuf->ctrl2 = 0; ++ txbuf->data_len = cpu_to_le16(wlanpkt_len); ++ ++ if (unlikely(acx_debug & L_DATA)) { ++ printk("dump of bulk out urb:\n"); ++ acx_dump_bytes(txbuf, wlanpkt_len + USB_TXBUF_HDRSIZE); ++ } ++ ++ if (unlikely(txurb->status == -EINPROGRESS)) { ++ printk("acx: trying to submit tx urb while already in progress\n"); ++ } ++ ++ /* now schedule the USB transfer */ ++ usbdev = adev->usbdev; ++ outpipe = usb_sndbulkpipe(usbdev, adev->bulkoutep); ++ ++ usb_fill_bulk_urb(txurb, usbdev, outpipe, ++ txbuf, /* dataptr */ ++ wlanpkt_len + USB_TXBUF_HDRSIZE, /* size */ ++ acxusb_i_complete_tx, /* handler */ ++ tx /* handler param */ ++ ); ++ ++ txurb->transfer_flags = URB_ASYNC_UNLINK|URB_ZERO_PACKET; ++ ucode = usb_submit_urb(txurb, GFP_ATOMIC); ++ log(L_USBRXTX, "SUBMIT TX (%d): outpipe=0x%X buf=%p txsize=%d " ++ "rate=%u errcode=%d\n", txnum, outpipe, txbuf, ++ wlanpkt_len + USB_TXBUF_HDRSIZE, txbuf->rate, ucode); ++ ++ if (unlikely(ucode)) { ++ printk(KERN_ERR "acx: submit_urb() error=%d txsize=%d\n", ++ ucode, wlanpkt_len + USB_TXBUF_HDRSIZE); ++ ++ /* on error, just mark the frame as done and update ++ ** the statistics ++ */ ++ adev->stats.tx_errors++; ++ tx->busy = 0; ++ adev->tx_free++; ++ /* needed? if (adev->tx_free > TX_START_QUEUE) acx_wake_queue(...) */ ++ } ++end: ++ FN_EXIT0; ++} ++ ++ ++/*********************************************************************** ++*/ ++static void ++acxusb_i_set_rx_mode(struct net_device *ndev) ++{ ++} ++ ++ ++/*********************************************************************** ++*/ ++#ifdef HAVE_TX_TIMEOUT ++static void ++acxusb_i_tx_timeout(struct net_device *ndev) ++{ ++ acx_device_t *adev = ndev2adev(ndev); ++ unsigned long flags; ++ int i; ++ ++ FN_ENTER; ++ ++ acx_lock(adev, flags); ++ /* unlink the URBs */ ++ for (i = 0; i < ACX_TX_URB_CNT; i++) { ++ acxusb_unlink_urb(adev->usb_tx[i].urb); ++ adev->usb_tx[i].busy = 0; ++ } ++ adev->tx_free = ACX_TX_URB_CNT; ++ /* TODO: stats update */ ++ acx_unlock(adev, flags); ++ ++ FN_EXIT0; ++} ++#endif ++ ++ ++/*********************************************************************** ++** init_module() ++** ++** This function is invoked upon loading of the kernel module. ++** It registers itself at the kernel's USB subsystem. ++** ++** Returns: Errorcode on failure, 0 on success ++*/ ++int __init ++acxusb_e_init_module(void) ++{ ++ log(L_INIT, "USB module " ACX_RELEASE " initialized, " ++ "probing for devices...\n"); ++ return usb_register(&acxusb_driver); ++} ++ ++ ++ ++/*********************************************************************** ++** cleanup_module() ++** ++** This function is invoked as last step of the module unloading. It simply ++** deregisters this module at the kernel's USB subsystem. ++*/ ++void __exit ++acxusb_e_cleanup_module() ++{ ++ usb_deregister(&acxusb_driver); ++} ++ ++ ++/*********************************************************************** ++** DEBUG STUFF ++*/ ++#if ACX_DEBUG ++ ++#ifdef UNUSED ++static void ++dump_device(struct usb_device *usbdev) ++{ ++ int i; ++ struct usb_config_descriptor *cd; ++ ++ printk("acx device dump:\n"); ++ printk(" devnum: %d\n", usbdev->devnum); ++ printk(" speed: %d\n", usbdev->speed); ++ printk(" tt: 0x%X\n", (unsigned int)(usbdev->tt)); ++ printk(" ttport: %d\n", (unsigned int)(usbdev->ttport)); ++ printk(" toggle[0]: 0x%X toggle[1]: 0x%X\n", (unsigned int)(usbdev->toggle[0]), (unsigned int)(usbdev->toggle[1])); ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 11) ++ /* This saw a change after 2.6.10 */ ++ printk(" ep_in wMaxPacketSize: "); ++ for (i = 0; i < 16; ++i) ++ if (usbdev->ep_in[i] != NULL) ++ printk("%d:%d ", i, usbdev->ep_in[i]->desc.wMaxPacketSize); ++ printk("\n"); ++ printk(" ep_out wMaxPacketSize: "); ++ for (i = 0; i < VEC_SIZE(usbdev->ep_out); ++i) ++ if (usbdev->ep_out[i] != NULL) ++ printk("%d:%d ", i, usbdev->ep_out[i]->desc.wMaxPacketSize); ++ printk("\n"); ++#else ++ printk(" epmaxpacketin: "); ++ for (i = 0; i < 16; i++) ++ printk("%d ", usbdev->epmaxpacketin[i]); ++ printk("\n"); ++ printk(" epmaxpacketout: "); ++ for (i = 0; i < 16; i++) ++ printk("%d ", usbdev->epmaxpacketout[i]); ++ printk("\n"); ++#endif ++ printk(" parent: 0x%X\n", (unsigned int)usbdev->parent); ++ printk(" bus: 0x%X\n", (unsigned int)usbdev->bus); ++#ifdef NO_DATATYPE ++ printk(" configs: "); ++ for (i = 0; i < usbdev->descriptor.bNumConfigurations; i++) ++ printk("0x%X ", usbdev->config[i]); ++ printk("\n"); ++#endif ++ printk(" actconfig: %p\n", usbdev->actconfig); ++ dump_device_descriptor(&usbdev->descriptor); ++ ++ cd = &usbdev->config->desc; ++ dump_config_descriptor(cd); ++} ++ ++ ++/*********************************************************************** ++*/ ++static void ++dump_config_descriptor(struct usb_config_descriptor *cd) ++{ ++ printk("Configuration Descriptor:\n"); ++ if (!cd) { ++ printk("NULL\n"); ++ return; ++ } ++ printk(" bLength: %d (0x%X)\n", cd->bLength, cd->bLength); ++ printk(" bDescriptorType: %d (0x%X)\n", cd->bDescriptorType, cd->bDescriptorType); ++ printk(" bNumInterfaces: %d (0x%X)\n", cd->bNumInterfaces, cd->bNumInterfaces); ++ printk(" bConfigurationValue: %d (0x%X)\n", cd->bConfigurationValue, cd->bConfigurationValue); ++ printk(" iConfiguration: %d (0x%X)\n", cd->iConfiguration, cd->iConfiguration); ++ printk(" bmAttributes: %d (0x%X)\n", cd->bmAttributes, cd->bmAttributes); ++ /* printk(" MaxPower: %d (0x%X)\n", cd->bMaxPower, cd->bMaxPower); */ ++} ++ ++ ++static void ++dump_device_descriptor(struct usb_device_descriptor *dd) ++{ ++ printk("Device Descriptor:\n"); ++ if (!dd) { ++ printk("NULL\n"); ++ return; ++ } ++ printk(" bLength: %d (0x%X)\n", dd->bLength, dd->bLength); ++ printk(" bDescriptortype: %d (0x%X)\n", dd->bDescriptorType, dd->bDescriptorType); ++ printk(" bcdUSB: %d (0x%X)\n", dd->bcdUSB, dd->bcdUSB); ++ printk(" bDeviceClass: %d (0x%X)\n", dd->bDeviceClass, dd->bDeviceClass); ++ printk(" bDeviceSubClass: %d (0x%X)\n", dd->bDeviceSubClass, dd->bDeviceSubClass); ++ printk(" bDeviceProtocol: %d (0x%X)\n", dd->bDeviceProtocol, dd->bDeviceProtocol); ++ printk(" bMaxPacketSize0: %d (0x%X)\n", dd->bMaxPacketSize0, dd->bMaxPacketSize0); ++ printk(" idVendor: %d (0x%X)\n", dd->idVendor, dd->idVendor); ++ printk(" idProduct: %d (0x%X)\n", dd->idProduct, dd->idProduct); ++ printk(" bcdDevice: %d (0x%X)\n", dd->bcdDevice, dd->bcdDevice); ++ printk(" iManufacturer: %d (0x%X)\n", dd->iManufacturer, dd->iManufacturer); ++ printk(" iProduct: %d (0x%X)\n", dd->iProduct, dd->iProduct); ++ printk(" iSerialNumber: %d (0x%X)\n", dd->iSerialNumber, dd->iSerialNumber); ++ printk(" bNumConfigurations: %d (0x%X)\n", dd->bNumConfigurations, dd->bNumConfigurations); ++} ++#endif /* UNUSED */ ++ ++#endif /* ACX_DEBUG */ +Index: linux-2.6.23/drivers/net/wireless/acx/wlan.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.23/drivers/net/wireless/acx/wlan.c 2008-01-20 21:13:40.000000000 +0000 +@@ -0,0 +1,424 @@ ++/*********************************************************************** ++** Copyright (C) 2003 ACX100 Open Source Project ++** ++** The contents of this file are subject to the Mozilla Public ++** License Version 1.1 (the "License"); you may not use this file ++** except in compliance with the License. You may obtain a copy of ++** the License at http://www.mozilla.org/MPL/ ++** ++** Software distributed under the License is distributed on an "AS ++** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or ++** implied. See the License for the specific language governing ++** rights and limitations under the License. ++** ++** Alternatively, the contents of this file may be used under the ++** terms of the GNU Public License version 2 (the "GPL"), in which ++** case the provisions of the GPL are applicable instead of the ++** above. If you wish to allow the use of your version of this file ++** only under the terms of the GPL and not to allow others to use ++** your version of this file under the MPL, indicate your decision ++** by deleting the provisions above and replace them with the notice ++** and other provisions required by the GPL. If you do not delete ++** the provisions above, a recipient may use your version of this ++** file under either the MPL or the GPL. ++** --------------------------------------------------------------------- ++** Inquiries regarding the ACX100 Open Source Project can be ++** made directly to: ++** ++** acx100-users@lists.sf.net ++** http://acx100.sf.net ++** --------------------------------------------------------------------- ++*/ ++ ++/*********************************************************************** ++** This code is based on elements which are ++** Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. ++** info@linux-wlan.com ++** http://www.linux-wlan.com ++*/ ++ ++#include <linux/version.h> ++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2, 6, 18) ++#include <linux/config.h> ++#endif ++#include <linux/types.h> ++#include <linux/if_arp.h> ++#include <linux/wireless.h> ++#include <net/iw_handler.h> ++ ++#include "acx.h" ++ ++ ++/*********************************************************************** ++*/ ++#define LOG_BAD_EID(hdr,len,ie_ptr) acx_log_bad_eid(hdr, len, ((wlan_ie_t*)ie_ptr)) ++ ++#define IE_EID(ie_ptr) (((wlan_ie_t*)(ie_ptr))->eid) ++#define IE_LEN(ie_ptr) (((wlan_ie_t*)(ie_ptr))->len) ++#define OFFSET(hdr,off) (WLAN_HDR_A3_DATAP(hdr) + (off)) ++ ++ ++/*********************************************************************** ++** wlan_mgmt_decode_XXX ++** ++** Given a complete frame in f->hdr, sets the pointers in f to ++** the areas that correspond to the parts of the frame. ++** ++** Assumptions: ++** 1) f->len and f->hdr are already set ++** 2) f->len is the length of the MAC header + data, the FCS ++** is NOT included ++** 3) all members except len and hdr are zero ++** Arguments: ++** f frame structure ++** ++** Returns: ++** nothing ++** ++** Side effects: ++** frame structure members are pointing at their ++** respective portions of the frame buffer. ++*/ ++void ++wlan_mgmt_decode_beacon(wlan_fr_beacon_t * f) ++{ ++ u8 *ie_ptr; ++ u8 *end = (u8*)f->hdr + f->len; ++ ++ f->type = WLAN_FSTYPE_BEACON; ++ ++ /*-- Fixed Fields ----*/ ++ f->ts = (u64 *) OFFSET(f->hdr, WLAN_BEACON_OFF_TS); ++ f->bcn_int = (u16 *) OFFSET(f->hdr, WLAN_BEACON_OFF_BCN_INT); ++ f->cap_info = (u16 *) OFFSET(f->hdr, WLAN_BEACON_OFF_CAPINFO); ++ ++ /*-- Information elements */ ++ ie_ptr = OFFSET(f->hdr, WLAN_BEACON_OFF_SSID); ++ while (ie_ptr < end) { ++ switch (IE_EID(ie_ptr)) { ++ case WLAN_EID_SSID: ++ f->ssid = (wlan_ie_ssid_t *) ie_ptr; ++ break; ++ case WLAN_EID_SUPP_RATES: ++ f->supp_rates = (wlan_ie_supp_rates_t *) ie_ptr; ++ break; ++ case WLAN_EID_EXT_RATES: ++ f->ext_rates = (wlan_ie_supp_rates_t *) ie_ptr; ++ break; ++ case WLAN_EID_FH_PARMS: ++ f->fh_parms = (wlan_ie_fh_parms_t *) ie_ptr; ++ break; ++ case WLAN_EID_DS_PARMS: ++ f->ds_parms = (wlan_ie_ds_parms_t *) ie_ptr; ++ break; ++ case WLAN_EID_CF_PARMS: ++ f->cf_parms = (wlan_ie_cf_parms_t *) ie_ptr; ++ break; ++ case WLAN_EID_IBSS_PARMS: ++ f->ibss_parms = (wlan_ie_ibss_parms_t *) ie_ptr; ++ break; ++ case WLAN_EID_TIM: ++ f->tim = (wlan_ie_tim_t *) ie_ptr; ++ break; ++ case WLAN_EID_ERP_INFO: ++ f->erp = (wlan_ie_erp_t *) ie_ptr; ++ break; ++ ++ case WLAN_EID_COUNTRY: ++ /* was seen: 07 06 47 42 20 01 0D 14 */ ++ case WLAN_EID_PWR_CONSTRAINT: ++ /* was seen by Ashwin Mansinghka <ashwin_man@yahoo.com> from ++ Atheros-based PCI card in AP mode using madwifi drivers: */ ++ /* 20 01 00 */ ++ case WLAN_EID_NONERP: ++ /* was seen from WRT54GS with OpenWrt: 2F 01 07 */ ++ case WLAN_EID_UNKNOWN128: ++ /* was seen by Jacek Jablonski <conexion2000@gmail.com> from Orinoco AP */ ++ /* 80 06 00 60 1D 2C 3B 00 */ ++ case WLAN_EID_UNKNOWN133: ++ /* was seen by David Bronaugh <dbronaugh@linuxboxen.org> from ???? */ ++ /* 85 1E 00 00 84 12 07 00 FF 00 11 00 61 70 63 31 */ ++ /* 63 73 72 30 34 32 00 00 00 00 00 00 00 00 00 25 */ ++ case WLAN_EID_UNKNOWN223: ++ /* was seen by Carlos Martin <carlosmn@gmail.com> from ???? */ ++ /* DF 20 01 1E 04 00 00 00 06 63 09 02 FF 0F 30 30 */ ++ /* 30 42 36 42 33 34 30 39 46 31 00 00 00 00 00 00 00 00 */ ++ case WLAN_EID_GENERIC: ++ /* WPA: hostap code: ++ if (pos[1] >= 4 && ++ pos[2] == 0x00 && pos[3] == 0x50 && ++ pos[4] == 0xf2 && pos[5] == 1) { ++ wpa = pos; ++ wpa_len = pos[1] + 2; ++ } ++ TI x4 mode: seen DD 04 08 00 28 00 ++ (08 00 28 is TI's OUI) ++ last byte is probably 0/1 - disabled/enabled ++ */ ++ case WLAN_EID_RSN: ++ /* hostap does something with it: ++ rsn = pos; ++ rsn_len = pos[1] + 2; ++ */ ++ break; ++ ++ default: ++ LOG_BAD_EID(f->hdr, f->len, ie_ptr); ++ break; ++ } ++ ie_ptr = ie_ptr + 2 + IE_LEN(ie_ptr); ++ } ++} ++ ++ ++#ifdef UNUSED ++void wlan_mgmt_decode_ibssatim(wlan_fr_ibssatim_t * f) ++{ ++ f->type = WLAN_FSTYPE_ATIM; ++ /*-- Fixed Fields ----*/ ++ /*-- Information elements */ ++} ++#endif /* UNUSED */ ++ ++void ++wlan_mgmt_decode_disassoc(wlan_fr_disassoc_t * f) ++{ ++ f->type = WLAN_FSTYPE_DISASSOC; ++ ++ /*-- Fixed Fields ----*/ ++ f->reason = (u16 *) OFFSET(f->hdr, WLAN_DISASSOC_OFF_REASON); ++ ++ /*-- Information elements */ ++} ++ ++ ++void ++wlan_mgmt_decode_assocreq(wlan_fr_assocreq_t * f) ++{ ++ u8 *ie_ptr; ++ u8 *end = (u8*)f->hdr + f->len; ++ ++ ++ f->type = WLAN_FSTYPE_ASSOCREQ; ++ ++ /*-- Fixed Fields ----*/ ++ f->cap_info = (u16 *) OFFSET(f->hdr, WLAN_ASSOCREQ_OFF_CAP_INFO); ++ f->listen_int = (u16 *) OFFSET(f->hdr, WLAN_ASSOCREQ_OFF_LISTEN_INT); ++ ++ /*-- Information elements */ ++ ie_ptr = OFFSET(f->hdr, WLAN_ASSOCREQ_OFF_SSID); ++ while (ie_ptr < end) { ++ switch (IE_EID(ie_ptr)) { ++ case WLAN_EID_SSID: ++ f->ssid = (wlan_ie_ssid_t *) ie_ptr; ++ break; ++ case WLAN_EID_SUPP_RATES: ++ f->supp_rates = (wlan_ie_supp_rates_t *) ie_ptr; ++ break; ++ case WLAN_EID_EXT_RATES: ++ f->ext_rates = (wlan_ie_supp_rates_t *) ie_ptr; ++ break; ++ default: ++ LOG_BAD_EID(f->hdr, f->len, ie_ptr); ++ break; ++ } ++ ie_ptr = ie_ptr + 2 + IE_LEN(ie_ptr); ++ } ++} ++ ++ ++void ++wlan_mgmt_decode_assocresp(wlan_fr_assocresp_t * f) ++{ ++ f->type = WLAN_FSTYPE_ASSOCRESP; ++ ++ /*-- Fixed Fields ----*/ ++ f->cap_info = (u16 *) OFFSET(f->hdr, WLAN_ASSOCRESP_OFF_CAP_INFO); ++ f->status = (u16 *) OFFSET(f->hdr, WLAN_ASSOCRESP_OFF_STATUS); ++ f->aid = (u16 *) OFFSET(f->hdr, WLAN_ASSOCRESP_OFF_AID); ++ ++ /*-- Information elements */ ++ f->supp_rates = (wlan_ie_supp_rates_t *) ++ OFFSET(f->hdr, WLAN_ASSOCRESP_OFF_SUPP_RATES); ++} ++ ++ ++#ifdef UNUSED ++void ++wlan_mgmt_decode_reassocreq(wlan_fr_reassocreq_t * f) ++{ ++ u8 *ie_ptr; ++ u8 *end = (u8*)f->hdr + f->len; ++ ++ f->type = WLAN_FSTYPE_REASSOCREQ; ++ ++ /*-- Fixed Fields ----*/ ++ f->cap_info = (u16 *) OFFSET(f->hdr, WLAN_REASSOCREQ_OFF_CAP_INFO); ++ f->listen_int = (u16 *) OFFSET(f->hdr, WLAN_REASSOCREQ_OFF_LISTEN_INT); ++ f->curr_ap = (u8 *) OFFSET(f->hdr, WLAN_REASSOCREQ_OFF_CURR_AP); ++ ++ /*-- Information elements */ ++ ie_ptr = OFFSET(f->hdr, WLAN_REASSOCREQ_OFF_SSID); ++ while (ie_ptr < end) { ++ switch (IE_EID(ie_ptr)) { ++ case WLAN_EID_SSID: ++ f->ssid = (wlan_ie_ssid_t *) ie_ptr; ++ break; ++ case WLAN_EID_SUPP_RATES: ++ f->supp_rates = (wlan_ie_supp_rates_t *) ie_ptr; ++ break; ++ case WLAN_EID_EXT_RATES: ++ f->ext_rates = (wlan_ie_supp_rates_t *) ie_ptr; ++ break; ++ default: ++ LOG_BAD_EID(f->hdr, f->len, ie_ptr); ++ break; ++ } ++ ie_ptr = ie_ptr + 2 + IE_LEN(ie_ptr); ++ } ++} ++ ++ ++void ++wlan_mgmt_decode_reassocresp(wlan_fr_reassocresp_t * f) ++{ ++ f->type = WLAN_FSTYPE_REASSOCRESP; ++ ++ /*-- Fixed Fields ----*/ ++ f->cap_info = (u16 *) OFFSET(f->hdr, WLAN_REASSOCRESP_OFF_CAP_INFO); ++ f->status = (u16 *) OFFSET(f->hdr, WLAN_REASSOCRESP_OFF_STATUS); ++ f->aid = (u16 *) OFFSET(f->hdr, WLAN_REASSOCRESP_OFF_AID); ++ ++ /*-- Information elements */ ++ f->supp_rates = (wlan_ie_supp_rates_t *) ++ OFFSET(f->hdr, WLAN_REASSOCRESP_OFF_SUPP_RATES); ++} ++ ++ ++void ++wlan_mgmt_decode_probereq(wlan_fr_probereq_t * f) ++{ ++ u8 *ie_ptr; ++ u8 *end = (u8*)f->hdr + f->len; ++ ++ f->type = WLAN_FSTYPE_PROBEREQ; ++ ++ /*-- Fixed Fields ----*/ ++ ++ /*-- Information elements */ ++ ie_ptr = OFFSET(f->hdr, WLAN_PROBEREQ_OFF_SSID); ++ while (ie_ptr < end) { ++ switch (IE_EID(ie_ptr)) { ++ case WLAN_EID_SSID: ++ f->ssid = (wlan_ie_ssid_t *) ie_ptr; ++ break; ++ case WLAN_EID_SUPP_RATES: ++ f->supp_rates = (wlan_ie_supp_rates_t *) ie_ptr; ++ break; ++ case WLAN_EID_EXT_RATES: ++ f->ext_rates = (wlan_ie_supp_rates_t *) ie_ptr; ++ break; ++ default: ++ LOG_BAD_EID(f->hdr, f->len, ie_ptr); ++ break; ++ } ++ ie_ptr = ie_ptr + 2 + IE_LEN(ie_ptr); ++ } ++} ++#endif /* UNUSED */ ++ ++ ++/* TODO: decoding of beacon and proberesp can be merged (similar structure) */ ++void ++wlan_mgmt_decode_proberesp(wlan_fr_proberesp_t * f) ++{ ++ u8 *ie_ptr; ++ u8 *end = (u8*)f->hdr + f->len; ++ ++ f->type = WLAN_FSTYPE_PROBERESP; ++ ++ /*-- Fixed Fields ----*/ ++ f->ts = (u64 *) OFFSET(f->hdr, WLAN_PROBERESP_OFF_TS); ++ f->bcn_int = (u16 *) OFFSET(f->hdr, WLAN_PROBERESP_OFF_BCN_INT); ++ f->cap_info = (u16 *) OFFSET(f->hdr, WLAN_PROBERESP_OFF_CAP_INFO); ++ ++ /*-- Information elements */ ++ ie_ptr = OFFSET(f->hdr, WLAN_PROBERESP_OFF_SSID); ++ while (ie_ptr < end) { ++ switch (IE_EID(ie_ptr)) { ++ case WLAN_EID_SSID: ++ f->ssid = (wlan_ie_ssid_t *) ie_ptr; ++ break; ++ case WLAN_EID_SUPP_RATES: ++ f->supp_rates = (wlan_ie_supp_rates_t *) ie_ptr; ++ break; ++ case WLAN_EID_EXT_RATES: ++ f->ext_rates = (wlan_ie_supp_rates_t *) ie_ptr; ++ break; ++ case WLAN_EID_FH_PARMS: ++ f->fh_parms = (wlan_ie_fh_parms_t *) ie_ptr; ++ break; ++ case WLAN_EID_DS_PARMS: ++ f->ds_parms = (wlan_ie_ds_parms_t *) ie_ptr; ++ break; ++ case WLAN_EID_CF_PARMS: ++ f->cf_parms = (wlan_ie_cf_parms_t *) ie_ptr; ++ break; ++ case WLAN_EID_IBSS_PARMS: ++ f->ibss_parms = (wlan_ie_ibss_parms_t *) ie_ptr; ++ break; ++#ifdef DONT_DO_IT_ADD_REAL_HANDLING_INSTEAD ++ case WLAN_EID_COUNTRY: ++ break; ++ ... ++#endif ++#ifdef SENT_HERE_BY_OPENWRT ++ /* should those be trapped or handled?? */ ++ case WLAN_EID_ERP_INFO: ++ break; ++ case WLAN_EID_NONERP: ++ break; ++ case WLAN_EID_GENERIC: ++ break; ++#endif ++ default: ++ LOG_BAD_EID(f->hdr, f->len, ie_ptr); ++ break; ++ } ++ ++ ie_ptr = ie_ptr + 2 + IE_LEN(ie_ptr); ++ } ++} ++ ++ ++void ++wlan_mgmt_decode_authen(wlan_fr_authen_t * f) ++{ ++ u8 *ie_ptr; ++ u8 *end = (u8*)f->hdr + f->len; ++ ++ f->type = WLAN_FSTYPE_AUTHEN; ++ ++ /*-- Fixed Fields ----*/ ++ f->auth_alg = (u16 *) OFFSET(f->hdr, WLAN_AUTHEN_OFF_AUTH_ALG); ++ f->auth_seq = (u16 *) OFFSET(f->hdr, WLAN_AUTHEN_OFF_AUTH_SEQ); ++ f->status = (u16 *) OFFSET(f->hdr, WLAN_AUTHEN_OFF_STATUS); ++ ++ /*-- Information elements */ ++ ie_ptr = OFFSET(f->hdr, WLAN_AUTHEN_OFF_CHALLENGE); ++ if ((ie_ptr < end) && (IE_EID(ie_ptr) == WLAN_EID_CHALLENGE)) { ++ f->challenge = (wlan_ie_challenge_t *) ie_ptr; ++ } ++} ++ ++ ++void ++wlan_mgmt_decode_deauthen(wlan_fr_deauthen_t * f) ++{ ++ f->type = WLAN_FSTYPE_DEAUTHEN; ++ ++ /*-- Fixed Fields ----*/ ++ f->reason = (u16 *) OFFSET(f->hdr, WLAN_DEAUTHEN_OFF_REASON); ++ ++ /*-- Information elements */ ++} +Index: linux-2.6.23/drivers/net/wireless/acx/wlan_compat.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.23/drivers/net/wireless/acx/wlan_compat.h 2008-01-20 21:13:40.000000000 +0000 +@@ -0,0 +1,260 @@ ++/*********************************************************************** ++** Copyright (C) 2003 ACX100 Open Source Project ++** ++** The contents of this file are subject to the Mozilla Public ++** License Version 1.1 (the "License"); you may not use this file ++** except in compliance with the License. You may obtain a copy of ++** the License at http://www.mozilla.org/MPL/ ++** ++** Software distributed under the License is distributed on an "AS ++** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or ++** implied. See the License for the specific language governing ++** rights and limitations under the License. ++** ++** Alternatively, the contents of this file may be used under the ++** terms of the GNU Public License version 2 (the "GPL"), in which ++** case the provisions of the GPL are applicable instead of the ++** above. If you wish to allow the use of your version of this file ++** only under the terms of the GPL and not to allow others to use ++** your version of this file under the MPL, indicate your decision ++** by deleting the provisions above and replace them with the notice ++** and other provisions required by the GPL. If you do not delete ++** the provisions above, a recipient may use your version of this ++** file under either the MPL or the GPL. ++** --------------------------------------------------------------------- ++** Inquiries regarding the ACX100 Open Source Project can be ++** made directly to: ++** ++** acx100-users@lists.sf.net ++** http://acx100.sf.net ++** --------------------------------------------------------------------- ++*/ ++ ++/*********************************************************************** ++** This code is based on elements which are ++** Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. ++** info@linux-wlan.com ++** http://www.linux-wlan.com ++*/ ++ ++/*=============================================================*/ ++/*------ Establish Platform Identity --------------------------*/ ++/*=============================================================*/ ++/* Key macros: */ ++/* WLAN_CPU_FAMILY */ ++#define WLAN_Ix86 1 ++#define WLAN_PPC 2 ++#define WLAN_Ix96 3 ++#define WLAN_ARM 4 ++#define WLAN_ALPHA 5 ++#define WLAN_MIPS 6 ++#define WLAN_HPPA 7 ++#define WLAN_SPARC 8 ++#define WLAN_SH 9 ++#define WLAN_x86_64 10 ++/* WLAN_CPU_CORE */ ++#define WLAN_I386CORE 1 ++#define WLAN_PPCCORE 2 ++#define WLAN_I296 3 ++#define WLAN_ARMCORE 4 ++#define WLAN_ALPHACORE 5 ++#define WLAN_MIPSCORE 6 ++#define WLAN_HPPACORE 7 ++/* WLAN_CPU_PART */ ++#define WLAN_I386PART 1 ++#define WLAN_MPC860 2 ++#define WLAN_MPC823 3 ++#define WLAN_I296SA 4 ++#define WLAN_PPCPART 5 ++#define WLAN_ARMPART 6 ++#define WLAN_ALPHAPART 7 ++#define WLAN_MIPSPART 8 ++#define WLAN_HPPAPART 9 ++/* WLAN_SYSARCH */ ++#define WLAN_PCAT 1 ++#define WLAN_MBX 2 ++#define WLAN_RPX 3 ++#define WLAN_LWARCH 4 ++#define WLAN_PMAC 5 ++#define WLAN_SKIFF 6 ++#define WLAN_BITSY 7 ++#define WLAN_ALPHAARCH 7 ++#define WLAN_MIPSARCH 9 ++#define WLAN_HPPAARCH 10 ++/* WLAN_HOSTIF (generally set on the command line, not detected) */ ++#define WLAN_PCMCIA 1 ++#define WLAN_ISA 2 ++#define WLAN_PCI 3 ++#define WLAN_USB 4 ++#define WLAN_PLX 5 ++ ++/* Note: the PLX HOSTIF above refers to some vendors implementations for */ ++/* PCI. It's a PLX chip that is a PCI to PCMCIA adapter, but it */ ++/* isn't a real PCMCIA host interface adapter providing all the */ ++/* card&socket services. */ ++ ++#ifdef __powerpc__ ++#ifndef __ppc__ ++#define __ppc__ ++#endif ++#endif ++ ++#if (defined(CONFIG_PPC) || defined(CONFIG_8xx)) ++#ifndef __ppc__ ++#define __ppc__ ++#endif ++#endif ++ ++#if defined(__x86_64__) ++ #define WLAN_CPU_FAMILY WLAN_x86_64 ++ #define WLAN_SYSARCH WLAN_PCAT ++#elif defined(__i386__) || defined(__i486__) || defined(__i586__) || defined(__i686__) ++ #define WLAN_CPU_FAMILY WLAN_Ix86 ++ #define WLAN_CPU_CORE WLAN_I386CORE ++ #define WLAN_CPU_PART WLAN_I386PART ++ #define WLAN_SYSARCH WLAN_PCAT ++#elif defined(__ppc__) ++ #define WLAN_CPU_FAMILY WLAN_PPC ++ #define WLAN_CPU_CORE WLAN_PPCCORE ++ #if defined(CONFIG_MBX) ++ #define WLAN_CPU_PART WLAN_MPC860 ++ #define WLAN_SYSARCH WLAN_MBX ++ #elif defined(CONFIG_RPXLITE) ++ #define WLAN_CPU_PART WLAN_MPC823 ++ #define WLAN_SYSARCH WLAN_RPX ++ #elif defined(CONFIG_RPXCLASSIC) ++ #define WLAN_CPU_PART WLAN_MPC860 ++ #define WLAN_SYSARCH WLAN_RPX ++ #else ++ #define WLAN_CPU_PART WLAN_PPCPART ++ #define WLAN_SYSARCH WLAN_PMAC ++ #endif ++#elif defined(__arm__) ++ #define WLAN_CPU_FAMILY WLAN_ARM ++ #define WLAN_CPU_CORE WLAN_ARMCORE ++ #define WLAN_CPU_PART WLAN_ARM_PART ++ #define WLAN_SYSARCH WLAN_SKIFF ++#elif defined(__alpha__) ++ #define WLAN_CPU_FAMILY WLAN_ALPHA ++ #define WLAN_CPU_CORE WLAN_ALPHACORE ++ #define WLAN_CPU_PART WLAN_ALPHAPART ++ #define WLAN_SYSARCH WLAN_ALPHAARCH ++#elif defined(__mips__) ++ #define WLAN_CPU_FAMILY WLAN_MIPS ++ #define WLAN_CPU_CORE WLAN_MIPSCORE ++ #define WLAN_CPU_PART WLAN_MIPSPART ++ #define WLAN_SYSARCH WLAN_MIPSARCH ++#elif defined(__hppa__) ++ #define WLAN_CPU_FAMILY WLAN_HPPA ++ #define WLAN_CPU_CORE WLAN_HPPACORE ++ #define WLAN_CPU_PART WLAN_HPPAPART ++ #define WLAN_SYSARCH WLAN_HPPAARCH ++#elif defined(__sparc__) ++ #define WLAN_CPU_FAMILY WLAN_SPARC ++ #define WLAN_SYSARCH WLAN_SPARC ++#elif defined(__sh__) ++ #define WLAN_CPU_FAMILY WLAN_SH ++ #define WLAN_SYSARCH WLAN_SHARCH ++ #ifndef __LITTLE_ENDIAN__ ++ #define __LITTLE_ENDIAN__ ++ #endif ++#else ++ #error "No CPU identified!" ++#endif ++ ++/* ++ Some big endian machines implicitly do all I/O in little endian mode. ++ ++ In particular: ++ Linux/PPC on PowerMacs (PCI) ++ Arm/Intel Xscale (PCI) ++ ++ This may also affect PLX boards and other BE &| PPC platforms; ++ as new ones are discovered, add them below. ++*/ ++ ++#if ((WLAN_SYSARCH == WLAN_SKIFF) || (WLAN_SYSARCH == WLAN_PMAC)) ++#define REVERSE_ENDIAN ++#endif ++ ++/*=============================================================*/ ++/*------ Hardware Portability Macros --------------------------*/ ++/*=============================================================*/ ++#if (WLAN_CPU_FAMILY == WLAN_PPC) ++#define wlan_inw(a) in_be16((unsigned short *)((a)+_IO_BASE)) ++#define wlan_inw_le16_to_cpu(a) inw((a)) ++#define wlan_outw(v,a) out_be16((unsigned short *)((a)+_IO_BASE), (v)) ++#define wlan_outw_cpu_to_le16(v,a) outw((v),(a)) ++#else ++#define wlan_inw(a) inw((a)) ++#define wlan_inw_le16_to_cpu(a) __cpu_to_le16(inw((a))) ++#define wlan_outw(v,a) outw((v),(a)) ++#define wlan_outw_cpu_to_le16(v,a) outw(__cpu_to_le16((v)),(a)) ++#endif ++ ++/*=============================================================*/ ++/*------ Bit settings -----------------------------------------*/ ++/*=============================================================*/ ++#define ieee2host16(n) __le16_to_cpu(n) ++#define ieee2host32(n) __le32_to_cpu(n) ++#define host2ieee16(n) __cpu_to_le16(n) ++#define host2ieee32(n) __cpu_to_le32(n) ++ ++/* for constants */ ++#ifdef __LITTLE_ENDIAN ++ #define IEEE16(a,n) a = n, a##i = n, ++#else ++ #ifdef __BIG_ENDIAN ++ /* shifts would produce gcc warnings. Oh well... */ ++ #define IEEE16(a,n) a = n, a##i = ((n&0xff)*256 + ((n&0xff00)/256)), ++ #else ++ #error give me endianness or give me death ++ #endif ++#endif ++ ++/*=============================================================*/ ++/*------ Compiler Portability Macros --------------------------*/ ++/*=============================================================*/ ++#define WLAN_PACKED __attribute__ ((packed)) ++ ++/* Interrupt handler backwards compatibility stuff */ ++#ifndef IRQ_NONE ++#define IRQ_NONE ++#define IRQ_HANDLED ++typedef void irqreturn_t; ++#endif ++ ++#ifndef ARPHRD_IEEE80211_PRISM ++#define ARPHRD_IEEE80211_PRISM 802 ++#endif ++ ++#define ETH_P_80211_RAW (ETH_P_ECONET + 1) ++ ++/*============================================================================* ++ * Constants * ++ *============================================================================*/ ++#define WLAN_IEEE_OUI_LEN 3 ++ ++/*============================================================================* ++ * Types * ++ *============================================================================*/ ++ ++/* local ether header type */ ++typedef struct wlan_ethhdr { ++ u8 daddr[ETH_ALEN]; ++ u8 saddr[ETH_ALEN]; ++ u16 type; ++} WLAN_PACKED wlan_ethhdr_t; ++ ++/* local llc header type */ ++typedef struct wlan_llc { ++ u8 dsap; ++ u8 ssap; ++ u8 ctl; ++} WLAN_PACKED wlan_llc_t; ++ ++/* local snap header type */ ++typedef struct wlan_snap { ++ u8 oui[WLAN_IEEE_OUI_LEN]; ++ u16 type; ++} WLAN_PACKED wlan_snap_t; +Index: linux-2.6.23/drivers/net/wireless/acx/wlan_hdr.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.23/drivers/net/wireless/acx/wlan_hdr.h 2008-01-20 21:13:40.000000000 +0000 +@@ -0,0 +1,497 @@ ++/*********************************************************************** ++** Copyright (C) 2003 ACX100 Open Source Project ++** ++** The contents of this file are subject to the Mozilla Public ++** License Version 1.1 (the "License"); you may not use this file ++** except in compliance with the License. You may obtain a copy of ++** the License at http://www.mozilla.org/MPL/ ++** ++** Software distributed under the License is distributed on an "AS ++** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or ++** implied. See the License for the specific language governing ++** rights and limitations under the License. ++** ++** Alternatively, the contents of this file may be used under the ++** terms of the GNU Public License version 2 (the "GPL"), in which ++** case the provisions of the GPL are applicable instead of the ++** above. If you wish to allow the use of your version of this file ++** only under the terms of the GPL and not to allow others to use ++** your version of this file under the MPL, indicate your decision ++** by deleting the provisions above and replace them with the notice ++** and other provisions required by the GPL. If you do not delete ++** the provisions above, a recipient may use your version of this ++** file under either the MPL or the GPL. ++** --------------------------------------------------------------------- ++** Inquiries regarding the ACX100 Open Source Project can be ++** made directly to: ++** ++** acx100-users@lists.sf.net ++** http://acx100.sf.net ++** --------------------------------------------------------------------- ++*/ ++ ++/*********************************************************************** ++** This code is based on elements which are ++** Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. ++** info@linux-wlan.com ++** http://www.linux-wlan.com ++*/ ++ ++/* mini-doc ++ ++Here are all 11b/11g/11a rates and modulations: ++ ++ 11b 11g 11a ++ --- --- --- ++ 1 |B |B | ++ 2 |Q |Q | ++ 5.5|Cp |C p| ++ 6 | |Od |O ++ 9 | |od |o ++11 |Cp |C p| ++12 | |Od |O ++18 | |od |o ++22 | | p| ++24 | |Od |O ++33 | | p| ++36 | |od |o ++48 | |od |o ++54 | |od |o ++ ++Mandatory: ++ B - DBPSK (Differential Binary Phase Shift Keying) ++ Q - DQPSK (Differential Quaternary Phase Shift Keying) ++ C - CCK (Complementary Code Keying, a form of DSSS ++ (Direct Sequence Spread Spectrum) modulation) ++ O - OFDM (Orthogonal Frequency Division Multiplexing) ++Optional: ++ o - OFDM ++ d - CCK-OFDM (also known as DSSS-OFDM) ++ p - PBCC (Packet Binary Convolutional Coding) ++ ++The term CCK-OFDM may be used interchangeably with DSSS-OFDM ++(the IEEE 802.11g-2003 standard uses the latter terminology). ++In the CCK-OFDM, the PLCP header of the frame uses the CCK form of DSSS, ++while the PLCP payload (the MAC frame) is modulated using OFDM. ++ ++Basically, you must use CCK-OFDM if you have mixed 11b/11g environment, ++or else (pure OFDM) 11b equipment may not realize that AP ++is sending a packet and start sending its own one. ++Sadly, looks like acx111 does not support CCK-OFDM, only pure OFDM. ++ ++Re PBCC: avoid using it. It makes sense only if you have ++TI "11b+" hardware. You _must_ use PBCC in order to reach 22Mbps on it. ++ ++Preambles: ++ ++Long preamble (at 1Mbit rate, takes 144 us): ++ 16 bytes ones ++ 2 bytes 0xF3A0 (lsb sent first) ++PLCP header follows (at 1Mbit also): ++ 1 byte Signal: speed, in 0.1Mbit units, except for: ++ 33Mbit: 33 (instead of 330 - doesn't fit in octet) ++ all CCK-OFDM rates: 30 ++ 1 byte Service ++ 0,1,4: reserved ++ 2: 1=locked clock ++ 3: 1=PBCC ++ 5: Length Extension (PBCC 22,33Mbit (11g only)) <- ++ 6: Length Extension (PBCC 22,33Mbit (11g only)) <- BLACK MAGIC HERE ++ 7: Length Extension <- ++ 2 bytes Length (time needed to tx this frame) ++ a) 5.5 Mbit/s CCK ++ Length = octets*8/5.5, rounded up to integer ++ b) 11 Mbit/s CCK ++ Length = octets*8/11, rounded up to integer ++ Service bit 7: ++ 0 = rounding took less than 8/11 ++ 1 = rounding took more than or equal to 8/11 ++ c) 5.5 Mbit/s PBCC ++ Length = (octets+1)*8/5.5, rounded up to integer ++ d) 11 Mbit/s PBCC ++ Length = (octets+1)*8/11, rounded up to integer ++ Service bit 7: ++ 0 = rounding took less than 8/11 ++ 1 = rounding took more than or equal to 8/11 ++ e) 22 Mbit/s PBCC ++ Length = (octets+1)*8/22, rounded up to integer ++ Service bits 6,7: ++ 00 = rounding took less than 8/22ths ++ 01 = rounding took 8/22...15/22ths ++ 10 = rounding took 16/22ths or more. ++ f) 33 Mbit/s PBCC ++ Length = (octets+1)*8/33, rounded up to integer ++ Service bits 5,6,7: ++ 000 rounding took less than 8/33 ++ 001 rounding took 8/33...15/33 ++ 010 rounding took 16/33...23/33 ++ 011 rounding took 24/33...31/33 ++ 100 rounding took 32/33 or more ++ 2 bytes CRC ++ ++PSDU follows (up to 2346 bytes at selected rate) ++ ++While Signal value alone is not enough to determine rate and modulation, ++Signal+Service is always sufficient. ++ ++Short preamble (at 1Mbit rate, takes 72 us): ++ 7 bytes zeroes ++ 2 bytes 0x05CF (lsb sent first) ++PLCP header follows *at 2Mbit/s*. Format is the same as in long preamble. ++PSDU follows (up to 2346 bytes at selected rate) ++ ++OFDM preamble is completely different, uses OFDM ++modulation from the start and thus easily identifiable. ++Not shown here. ++*/ ++ ++ ++/*********************************************************************** ++** Constants ++*/ ++ ++#define WLAN_HDR_A3_LEN 24 ++#define WLAN_HDR_A4_LEN 30 ++/* IV structure: ++** 3 bytes: Initialization Vector (24 bits) ++** 1 byte: 0..5: padding, must be 0; 6..7: key selector (0-3) ++*/ ++#define WLAN_WEP_IV_LEN 4 ++/* 802.11 says 2312 but looks like 2312 is a max size of _WEPed data_ */ ++#define WLAN_DATA_MAXLEN 2304 ++#define WLAN_WEP_ICV_LEN 4 ++#define WLAN_FCS_LEN 4 ++#define WLAN_A3FR_MAXLEN (WLAN_HDR_A3_LEN + WLAN_DATA_MAXLEN) ++#define WLAN_A4FR_MAXLEN (WLAN_HDR_A4_LEN + WLAN_DATA_MAXLEN) ++#define WLAN_A3FR_MAXLEN_FCS (WLAN_HDR_A3_LEN + WLAN_DATA_MAXLEN + 4) ++#define WLAN_A4FR_MAXLEN_FCS (WLAN_HDR_A4_LEN + WLAN_DATA_MAXLEN + 4) ++#define WLAN_A3FR_MAXLEN_WEP (WLAN_A3FR_MAXLEN + 8) ++#define WLAN_A4FR_MAXLEN_WEP (WLAN_A4FR_MAXLEN + 8) ++#define WLAN_A3FR_MAXLEN_WEP_FCS (WLAN_A3FR_MAXLEN_FCS + 8) ++#define WLAN_A4FR_MAXLEN_WEP_FCS (WLAN_A4FR_MAXLEN_FCS + 8) ++ ++#define WLAN_BSS_TS_LEN 8 ++#define WLAN_SSID_MAXLEN 32 ++#define WLAN_BEACON_FR_MAXLEN (WLAN_HDR_A3_LEN + 334) ++#define WLAN_ATIM_FR_MAXLEN (WLAN_HDR_A3_LEN + 0) ++#define WLAN_DISASSOC_FR_MAXLEN (WLAN_HDR_A3_LEN + 2) ++#define WLAN_ASSOCREQ_FR_MAXLEN (WLAN_HDR_A3_LEN + 48) ++#define WLAN_ASSOCRESP_FR_MAXLEN (WLAN_HDR_A3_LEN + 16) ++#define WLAN_REASSOCREQ_FR_MAXLEN (WLAN_HDR_A3_LEN + 54) ++#define WLAN_REASSOCRESP_FR_MAXLEN (WLAN_HDR_A3_LEN + 16) ++#define WLAN_PROBEREQ_FR_MAXLEN (WLAN_HDR_A3_LEN + 44) ++#define WLAN_PROBERESP_FR_MAXLEN (WLAN_HDR_A3_LEN + 78) ++#define WLAN_AUTHEN_FR_MAXLEN (WLAN_HDR_A3_LEN + 261) ++#define WLAN_DEAUTHEN_FR_MAXLEN (WLAN_HDR_A3_LEN + 2) ++#define WLAN_CHALLENGE_IE_LEN 130 ++#define WLAN_CHALLENGE_LEN 128 ++#define WLAN_WEP_MAXKEYLEN 13 ++#define WLAN_WEP_NKEYS 4 ++ ++/*--- Frame Control Field -------------------------------------*/ ++/* Frame Types */ ++#define WLAN_FTYPE_MGMT 0x00 ++#define WLAN_FTYPE_CTL 0x01 ++#define WLAN_FTYPE_DATA 0x02 ++ ++/* Frame subtypes */ ++/* Management */ ++#define WLAN_FSTYPE_ASSOCREQ 0x00 ++#define WLAN_FSTYPE_ASSOCRESP 0x01 ++#define WLAN_FSTYPE_REASSOCREQ 0x02 ++#define WLAN_FSTYPE_REASSOCRESP 0x03 ++#define WLAN_FSTYPE_PROBEREQ 0x04 ++#define WLAN_FSTYPE_PROBERESP 0x05 ++#define WLAN_FSTYPE_BEACON 0x08 ++#define WLAN_FSTYPE_ATIM 0x09 ++#define WLAN_FSTYPE_DISASSOC 0x0a ++#define WLAN_FSTYPE_AUTHEN 0x0b ++#define WLAN_FSTYPE_DEAUTHEN 0x0c ++ ++/* Control */ ++#define WLAN_FSTYPE_PSPOLL 0x0a ++#define WLAN_FSTYPE_RTS 0x0b ++#define WLAN_FSTYPE_CTS 0x0c ++#define WLAN_FSTYPE_ACK 0x0d ++#define WLAN_FSTYPE_CFEND 0x0e ++#define WLAN_FSTYPE_CFENDCFACK 0x0f ++ ++/* Data */ ++#define WLAN_FSTYPE_DATAONLY 0x00 ++#define WLAN_FSTYPE_DATA_CFACK 0x01 ++#define WLAN_FSTYPE_DATA_CFPOLL 0x02 ++#define WLAN_FSTYPE_DATA_CFACK_CFPOLL 0x03 ++#define WLAN_FSTYPE_NULL 0x04 ++#define WLAN_FSTYPE_CFACK 0x05 ++#define WLAN_FSTYPE_CFPOLL 0x06 ++#define WLAN_FSTYPE_CFACK_CFPOLL 0x07 ++ ++/*--- FC Constants v. 2.0 ------------------------------------*/ ++/* Each constant is defined twice: WF_CONST is in host */ ++/* byteorder, WF_CONSTi is in ieee byteorder. */ ++/* Usage: */ ++/* printf("the frame subtype is %X", WF_FC_FTYPEi & rx.fc); */ ++/* tx.fc = WF_FTYPE_CTLi | WF_FSTYPE_RTSi; */ ++/*------------------------------------------------------------*/ ++ ++enum { ++/*--- Frame Control Field -------------------------------------*/ ++/* Protocol version: always 0 for current 802.11 standards */ ++IEEE16(WF_FC_PVER, 0x0003) ++IEEE16(WF_FC_FTYPE, 0x000c) ++IEEE16(WF_FC_FSTYPE, 0x00f0) ++IEEE16(WF_FC_TODS, 0x0100) ++IEEE16(WF_FC_FROMDS, 0x0200) ++IEEE16(WF_FC_FROMTODS, 0x0300) ++IEEE16(WF_FC_MOREFRAG, 0x0400) ++IEEE16(WF_FC_RETRY, 0x0800) ++/* Indicates PS mode in which STA will be after successful completion ++** of current frame exchange sequence. Always 0 for AP frames */ ++IEEE16(WF_FC_PWRMGT, 0x1000) ++/* What MoreData=1 means: ++** From AP to STA in PS mode: don't sleep yet, I have more frames for you ++** From Contention-Free (CF) Pollable STA in response to a CF-Poll: ++** STA has buffered frames for transmission in response to next CF-Poll ++** Bcast/mcast frames transmitted from AP: ++** when additional bcast/mcast frames remain to be transmitted by AP ++** during this beacon interval ++** In all other cases MoreData=0 */ ++IEEE16(WF_FC_MOREDATA, 0x2000) ++IEEE16(WF_FC_ISWEP, 0x4000) ++IEEE16(WF_FC_ORDER, 0x8000) ++ ++/* Frame Types */ ++IEEE16(WF_FTYPE_MGMT, 0x00) ++IEEE16(WF_FTYPE_CTL, 0x04) ++IEEE16(WF_FTYPE_DATA, 0x08) ++ ++/* Frame subtypes */ ++/* Management */ ++IEEE16(WF_FSTYPE_ASSOCREQ, 0x00) ++IEEE16(WF_FSTYPE_ASSOCRESP, 0x10) ++IEEE16(WF_FSTYPE_REASSOCREQ, 0x20) ++IEEE16(WF_FSTYPE_REASSOCRESP, 0x30) ++IEEE16(WF_FSTYPE_PROBEREQ, 0x40) ++IEEE16(WF_FSTYPE_PROBERESP, 0x50) ++IEEE16(WF_FSTYPE_BEACON, 0x80) ++IEEE16(WF_FSTYPE_ATIM, 0x90) ++IEEE16(WF_FSTYPE_DISASSOC, 0xa0) ++IEEE16(WF_FSTYPE_AUTHEN, 0xb0) ++IEEE16(WF_FSTYPE_DEAUTHEN, 0xc0) ++ ++/* Control */ ++IEEE16(WF_FSTYPE_PSPOLL, 0xa0) ++IEEE16(WF_FSTYPE_RTS, 0xb0) ++IEEE16(WF_FSTYPE_CTS, 0xc0) ++IEEE16(WF_FSTYPE_ACK, 0xd0) ++IEEE16(WF_FSTYPE_CFEND, 0xe0) ++IEEE16(WF_FSTYPE_CFENDCFACK, 0xf0) ++ ++/* Data */ ++IEEE16(WF_FSTYPE_DATAONLY, 0x00) ++IEEE16(WF_FSTYPE_DATA_CFACK, 0x10) ++IEEE16(WF_FSTYPE_DATA_CFPOLL, 0x20) ++IEEE16(WF_FSTYPE_DATA_CFACK_CFPOLL, 0x30) ++IEEE16(WF_FSTYPE_NULL, 0x40) ++IEEE16(WF_FSTYPE_CFACK, 0x50) ++IEEE16(WF_FSTYPE_CFPOLL, 0x60) ++IEEE16(WF_FSTYPE_CFACK_CFPOLL, 0x70) ++}; ++ ++ ++/*********************************************************************** ++** Macros ++*/ ++ ++/*--- Duration Macros ----------------------------------------*/ ++/* Macros to get/set the bitfields of the Duration Field */ ++/* - the duration value is only valid when bit15 is zero */ ++/* - the firmware handles these values, so I'm not going */ ++/* to use these macros right now. */ ++/*------------------------------------------------------------*/ ++ ++/*--- Sequence Control Macros -------------------------------*/ ++/* Macros to get/set the bitfields of the Sequence Control */ ++/* Field. */ ++/*------------------------------------------------------------*/ ++#define WLAN_GET_SEQ_FRGNUM(n) ((u16)(n) & 0x000f) ++#define WLAN_GET_SEQ_SEQNUM(n) (((u16)(n) & 0xfff0) >> 4) ++ ++/*--- Data ptr macro -----------------------------------------*/ ++/* Creates a u8* to the data portion of a frame */ ++/* Assumes you're passing in a ptr to the beginning of the hdr*/ ++/*------------------------------------------------------------*/ ++#define WLAN_HDR_A3_DATAP(p) (((u8*)(p)) + WLAN_HDR_A3_LEN) ++#define WLAN_HDR_A4_DATAP(p) (((u8*)(p)) + WLAN_HDR_A4_LEN) ++ ++ ++/*********************************************************************** ++** Types ++*/ ++ ++/* 802.11 header type ++** ++** Note the following: ++** a1 *always* is receiver's mac or bcast/mcast ++** a2 *always* is transmitter's mac, if a2 exists ++** seq: [0:3] frag#, [4:15] seq# - used for dup detection ++** (dups from retries have same seq#) */ ++typedef struct wlan_hdr { ++ u16 fc; ++ u16 dur; ++ u8 a1[ETH_ALEN]; ++ u8 a2[ETH_ALEN]; ++ u8 a3[ETH_ALEN]; ++ u16 seq; ++ u8 a4[ETH_ALEN]; ++} WLAN_PACKED wlan_hdr_t; ++ ++/* Separate structs for use if frame type is known */ ++typedef struct wlan_hdr_a3 { ++ u16 fc; ++ u16 dur; ++ u8 a1[ETH_ALEN]; ++ u8 a2[ETH_ALEN]; ++ u8 a3[ETH_ALEN]; ++ u16 seq; ++} WLAN_PACKED wlan_hdr_a3_t; ++ ++typedef struct wlan_hdr_mgmt { ++ u16 fc; ++ u16 dur; ++ u8 da[ETH_ALEN]; ++ u8 sa[ETH_ALEN]; ++ u8 bssid[ETH_ALEN]; ++ u16 seq; ++} WLAN_PACKED wlan_hdr_mgmt_t; ++ ++#ifdef NOT_NEEDED_YET ++typedef struct { /* ad-hoc peer->peer (to/from DS = 0/0) */ ++ u16 fc; ++ u16 dur; ++ u8 da[ETH_ALEN]; ++ u8 sa[ETH_ALEN]; ++ u8 bssid[ETH_ALEN]; ++ u16 seq; ++} WLAN_PACKED ibss; ++typedef struct { /* ap->sta (to/from DS = 0/1) */ ++ u16 fc; ++ u16 dur; ++ u8 da[ETH_ALEN]; ++ u8 bssid[ETH_ALEN]; ++ u8 sa[ETH_ALEN]; ++ u16 seq; ++} WLAN_PACKED fromap; ++typedef struct { /* sta->ap (to/from DS = 1/0) */ ++ u16 fc; ++ u16 dur; ++ u8 bssid[ETH_ALEN]; ++ u8 sa[ETH_ALEN]; ++ u8 da[ETH_ALEN]; ++ u16 seq; ++} WLAN_PACKED toap; ++typedef struct { /* wds->wds (to/from DS = 1/1), the only 4addr pkt */ ++ u16 fc; ++ u16 dur; ++ u8 ra[ETH_ALEN]; ++ u8 ta[ETH_ALEN]; ++ u8 da[ETH_ALEN]; ++ u16 seq; ++ u8 sa[ETH_ALEN]; ++} WLAN_PACKED wds; ++typedef struct { /* all management packets */ ++ u16 fc; ++ u16 dur; ++ u8 da[ETH_ALEN]; ++ u8 sa[ETH_ALEN]; ++ u8 bssid[ETH_ALEN]; ++ u16 seq; ++} WLAN_PACKED mgmt; ++typedef struct { /* has no body, just a FCS */ ++ u16 fc; ++ u16 dur; ++ u8 ra[ETH_ALEN]; ++ u8 ta[ETH_ALEN]; ++} WLAN_PACKED rts; ++typedef struct { /* has no body, just a FCS */ ++ u16 fc; ++ u16 dur; ++ u8 ra[ETH_ALEN]; ++} WLAN_PACKED cts; ++typedef struct { /* has no body, just a FCS */ ++ u16 fc; ++ u16 dur; ++ u8 ra[ETH_ALEN]; ++} WLAN_PACKED ack; ++typedef struct { /* has no body, just a FCS */ ++ u16 fc; ++ /* NB: this one holds Assoc ID in dur field: */ ++ u16 aid; ++ u8 bssid[ETH_ALEN]; ++ u8 ta[ETH_ALEN]; ++} WLAN_PACKED pspoll; ++typedef struct { /* has no body, just a FCS */ ++ u16 fc; ++ u16 dur; ++ u8 ra[ETH_ALEN]; ++ u8 bssid[ETH_ALEN]; ++} WLAN_PACKED cfend; ++typedef struct { /* has no body, just a FCS */ ++ u16 fc; ++ u16 dur; ++ u8 ra[ETH_ALEN]; ++ u8 bssid[ETH_ALEN]; ++} WLAN_PACKED cfendcfack; ++#endif ++ ++/* Prism header emulation (monitor mode) */ ++typedef struct wlanitem_u32 { ++ u32 did; ++ u16 status; ++ u16 len; ++ u32 data; ++} WLAN_PACKED wlanitem_u32_t; ++#define WLANITEM_STATUS_data_ok 0 ++#define WLANITEM_STATUS_no_value 1 ++#define WLANITEM_STATUS_invalid_itemname 2 ++#define WLANITEM_STATUS_invalid_itemdata 3 ++#define WLANITEM_STATUS_missing_itemdata 4 ++#define WLANITEM_STATUS_incomplete_itemdata 5 ++#define WLANITEM_STATUS_invalid_msg_did 6 ++#define WLANITEM_STATUS_invalid_mib_did 7 ++#define WLANITEM_STATUS_missing_conv_func 8 ++#define WLANITEM_STATUS_string_too_long 9 ++#define WLANITEM_STATUS_data_out_of_range 10 ++#define WLANITEM_STATUS_string_too_short 11 ++#define WLANITEM_STATUS_missing_valid_func 12 ++#define WLANITEM_STATUS_unknown 13 ++#define WLANITEM_STATUS_invalid_did 14 ++#define WLANITEM_STATUS_missing_print_func 15 ++ ++#define WLAN_DEVNAMELEN_MAX 16 ++typedef struct wlansniffrm { ++ u32 msgcode; ++ u32 msglen; ++ u8 devname[WLAN_DEVNAMELEN_MAX]; ++ wlanitem_u32_t hosttime; ++ wlanitem_u32_t mactime; ++ wlanitem_u32_t channel; ++ wlanitem_u32_t rssi; ++ wlanitem_u32_t sq; ++ wlanitem_u32_t signal; ++ wlanitem_u32_t noise; ++ wlanitem_u32_t rate; ++ wlanitem_u32_t istx; /* tx? 0:no 1:yes */ ++ wlanitem_u32_t frmlen; ++} WLAN_PACKED wlansniffrm_t; ++#define WLANSNIFFFRM 0x0041 ++#define WLANSNIFFFRM_hosttime 0x1041 ++#define WLANSNIFFFRM_mactime 0x2041 ++#define WLANSNIFFFRM_channel 0x3041 ++#define WLANSNIFFFRM_rssi 0x4041 ++#define WLANSNIFFFRM_sq 0x5041 ++#define WLANSNIFFFRM_signal 0x6041 ++#define WLANSNIFFFRM_noise 0x7041 ++#define WLANSNIFFFRM_rate 0x8041 ++#define WLANSNIFFFRM_istx 0x9041 ++#define WLANSNIFFFRM_frmlen 0xA041 +Index: linux-2.6.23/drivers/net/wireless/acx/wlan_mgmt.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.23/drivers/net/wireless/acx/wlan_mgmt.h 2008-01-20 21:13:40.000000000 +0000 +@@ -0,0 +1,582 @@ ++/*********************************************************************** ++** Copyright (C) 2003 ACX100 Open Source Project ++** ++** The contents of this file are subject to the Mozilla Public ++** License Version 1.1 (the "License"); you may not use this file ++** except in compliance with the License. You may obtain a copy of ++** the License at http://www.mozilla.org/MPL/ ++** ++** Software distributed under the License is distributed on an "AS ++** IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or ++** implied. See the License for the specific language governing ++** rights and limitations under the License. ++** ++** Alternatively, the contents of this file may be used under the ++** terms of the GNU Public License version 2 (the "GPL"), in which ++** case the provisions of the GPL are applicable instead of the ++** above. If you wish to allow the use of your version of this file ++** only under the terms of the GPL and not to allow others to use ++** your version of this file under the MPL, indicate your decision ++** by deleting the provisions above and replace them with the notice ++** and other provisions required by the GPL. If you do not delete ++** the provisions above, a recipient may use your version of this ++** file under either the MPL or the GPL. ++** --------------------------------------------------------------------- ++** Inquiries regarding the ACX100 Open Source Project can be ++** made directly to: ++** ++** acx100-users@lists.sf.net ++** http://acx100.sf.net ++** --------------------------------------------------------------------- ++*/ ++ ++/*********************************************************************** ++** This code is based on elements which are ++** Copyright (C) 1999 AbsoluteValue Systems, Inc. All Rights Reserved. ++** info@linux-wlan.com ++** http://www.linux-wlan.com ++*/ ++ ++/*********************************************************************** ++** Constants ++*/ ++ ++/*-- Information Element IDs --------------------*/ ++#define WLAN_EID_SSID 0 ++#define WLAN_EID_SUPP_RATES 1 ++#define WLAN_EID_FH_PARMS 2 ++#define WLAN_EID_DS_PARMS 3 ++#define WLAN_EID_CF_PARMS 4 ++#define WLAN_EID_TIM 5 ++#define WLAN_EID_IBSS_PARMS 6 ++#define WLAN_EID_COUNTRY 7 /* 802.11d */ ++#define WLAN_EID_FH_HOP_PARMS 8 /* 802.11d */ ++#define WLAN_EID_FH_TABLE 9 /* 802.11d */ ++#define WLAN_EID_REQUEST 10 /* 802.11d */ ++/*-- values 11-15 reserved --*/ ++#define WLAN_EID_CHALLENGE 16 ++/*-- values 17-31 reserved for challenge text extension --*/ ++#define WLAN_EID_PWR_CONSTRAINT 32 /* 11h PowerConstraint */ ++#define WLAN_EID_ERP_INFO 42 /* was seen from WRT54GS with OpenWrt */ ++#define WLAN_EID_NONERP 47 /* was seen from WRT54GS with OpenWrt */ ++#define WLAN_EID_RSN 48 ++#define WLAN_EID_EXT_RATES 50 ++#define WLAN_EID_UNKNOWN128 128 ++#define WLAN_EID_UNKNOWN133 133 ++#define WLAN_EID_GENERIC 221 /* was seen from WRT54GS with OpenWrt */ ++#define WLAN_EID_UNKNOWN223 223 ++ ++#if 0 ++#define WLAN_EID_PWR_CAP 33 /* 11h PowerCapability */ ++#define WLAN_EID_TPC_REQUEST 34 /* 11h TPC Request */ ++#define WLAN_EID_TPC_REPORT 35 /* 11h TPC Report */ ++#define WLAN_EID_SUPP_CHANNELS 36 /* 11h Supported Channels */ ++#define WLAN_EID_CHANNEL_SWITCH 37 /* 11h ChannelSwitch */ ++#define WLAN_EID_MEASURE_REQUEST 38 /* 11h MeasurementRequest */ ++#define WLAN_EID_MEASURE_REPORT 39 /* 11h MeasurementReport */ ++#define WLAN_EID_QUIET_ID 40 /* 11h Quiet */ ++#define WLAN_EID_IBSS_DFS_ID 41 /* 11h IBSS_DFS */ ++#endif ++ ++/*-- Reason Codes -------------------------------*/ ++#define WLAN_MGMT_REASON_RSVD 0 ++#define WLAN_MGMT_REASON_UNSPEC 1 ++#define WLAN_MGMT_REASON_PRIOR_AUTH_INVALID 2 ++#define WLAN_MGMT_REASON_DEAUTH_LEAVING 3 ++#define WLAN_MGMT_REASON_DISASSOC_INACTIVE 4 ++#define WLAN_MGMT_REASON_DISASSOC_AP_BUSY 5 ++#define WLAN_MGMT_REASON_CLASS2_NONAUTH 6 ++#define WLAN_MGMT_REASON_CLASS3_NONASSOC 7 ++#define WLAN_MGMT_REASON_DISASSOC_STA_HASLEFT 8 ++#define WLAN_MGMT_REASON_CANT_ASSOC_NONAUTH 9 ++ ++/*-- Status Codes -------------------------------*/ ++#define WLAN_MGMT_STATUS_SUCCESS 0 ++#define WLAN_MGMT_STATUS_UNSPEC_FAILURE 1 ++#define WLAN_MGMT_STATUS_CAPS_UNSUPPORTED 10 ++#define WLAN_MGMT_STATUS_REASSOC_NO_ASSOC 11 ++#define WLAN_MGMT_STATUS_ASSOC_DENIED_UNSPEC 12 ++#define WLAN_MGMT_STATUS_UNSUPPORTED_AUTHALG 13 ++#define WLAN_MGMT_STATUS_RX_AUTH_NOSEQ 14 ++#define WLAN_MGMT_STATUS_CHALLENGE_FAIL 15 ++#define WLAN_MGMT_STATUS_AUTH_TIMEOUT 16 ++#define WLAN_MGMT_STATUS_ASSOC_DENIED_BUSY 17 ++#define WLAN_MGMT_STATUS_ASSOC_DENIED_RATES 18 ++/* p80211b additions */ ++#define WLAN_MGMT_STATUS_ASSOC_DENIED_NOSHORT 19 ++#define WLAN_MGMT_STATUS_ASSOC_DENIED_NOPBCC 20 ++#define WLAN_MGMT_STATUS_ASSOC_DENIED_NOAGILITY 21 ++ ++/*-- Auth Algorithm Field ---------------------------*/ ++#define WLAN_AUTH_ALG_OPENSYSTEM 0 ++#define WLAN_AUTH_ALG_SHAREDKEY 1 ++ ++/*-- Management Frame Field Offsets -------------*/ ++/* Note: Not all fields are listed because of variable lengths */ ++/* Note: These offsets are from the start of the frame data */ ++ ++#define WLAN_BEACON_OFF_TS 0 ++#define WLAN_BEACON_OFF_BCN_INT 8 ++#define WLAN_BEACON_OFF_CAPINFO 10 ++#define WLAN_BEACON_OFF_SSID 12 ++ ++#define WLAN_DISASSOC_OFF_REASON 0 ++ ++#define WLAN_ASSOCREQ_OFF_CAP_INFO 0 ++#define WLAN_ASSOCREQ_OFF_LISTEN_INT 2 ++#define WLAN_ASSOCREQ_OFF_SSID 4 ++ ++#define WLAN_ASSOCRESP_OFF_CAP_INFO 0 ++#define WLAN_ASSOCRESP_OFF_STATUS 2 ++#define WLAN_ASSOCRESP_OFF_AID 4 ++#define WLAN_ASSOCRESP_OFF_SUPP_RATES 6 ++ ++#define WLAN_REASSOCREQ_OFF_CAP_INFO 0 ++#define WLAN_REASSOCREQ_OFF_LISTEN_INT 2 ++#define WLAN_REASSOCREQ_OFF_CURR_AP 4 ++#define WLAN_REASSOCREQ_OFF_SSID 10 ++ ++#define WLAN_REASSOCRESP_OFF_CAP_INFO 0 ++#define WLAN_REASSOCRESP_OFF_STATUS 2 ++#define WLAN_REASSOCRESP_OFF_AID 4 ++#define WLAN_REASSOCRESP_OFF_SUPP_RATES 6 ++ ++#define WLAN_PROBEREQ_OFF_SSID 0 ++ ++#define WLAN_PROBERESP_OFF_TS 0 ++#define WLAN_PROBERESP_OFF_BCN_INT 8 ++#define WLAN_PROBERESP_OFF_CAP_INFO 10 ++#define WLAN_PROBERESP_OFF_SSID 12 ++ ++#define WLAN_AUTHEN_OFF_AUTH_ALG 0 ++#define WLAN_AUTHEN_OFF_AUTH_SEQ 2 ++#define WLAN_AUTHEN_OFF_STATUS 4 ++#define WLAN_AUTHEN_OFF_CHALLENGE 6 ++ ++#define WLAN_DEAUTHEN_OFF_REASON 0 ++ ++enum { ++IEEE16(WF_MGMT_CAP_ESS, 0x0001) ++IEEE16(WF_MGMT_CAP_IBSS, 0x0002) ++/* In (re)assoc request frames by STA: ++** Pollable=0, PollReq=0: STA is not CF-Pollable ++** 0 1: STA is CF-Pollable, not requesting to be placed on the CF-Polling list ++** 1 0: STA is CF-Pollable, requesting to be placed on the CF-Polling list ++** 1 1: STA is CF-Pollable, requesting never to be polled ++** In beacon, proberesp, (re)assoc resp frames by AP: ++** 0 0: No point coordinator at AP ++** 0 1: Point coordinator at AP for delivery only (no polling) ++** 1 0: Point coordinator at AP for delivery and polling ++** 1 1: Reserved */ ++IEEE16(WF_MGMT_CAP_CFPOLLABLE, 0x0004) ++IEEE16(WF_MGMT_CAP_CFPOLLREQ, 0x0008) ++/* 1=non-WEP data frames are disallowed */ ++IEEE16(WF_MGMT_CAP_PRIVACY, 0x0010) ++/* In beacon, proberesp, (re)assocresp by AP/AdHoc: ++** 1=use of shortpre is allowed ("I can receive shortpre") */ ++IEEE16(WF_MGMT_CAP_SHORT, 0x0020) ++IEEE16(WF_MGMT_CAP_PBCC, 0x0040) ++IEEE16(WF_MGMT_CAP_AGILITY, 0x0080) ++/* In (re)assoc request frames by STA: ++** 1=short slot time implemented and enabled ++** NB: AP shall use long slot time beginning at the next Beacon after assoc ++** of STA with this bit set to 0 ++** In beacon, proberesp, (re)assoc resp frames by AP: ++** currently used slot time value: 0/1 - long/short */ ++IEEE16(WF_MGMT_CAP_SHORTSLOT, 0x0400) ++/* In (re)assoc request frames by STA: 1=CCK-OFDM is implemented and enabled ++** In beacon, proberesp, (re)assoc resp frames by AP/AdHoc: ++** 1=CCK-OFDM is allowed */ ++IEEE16(WF_MGMT_CAP_CCKOFDM, 0x2000) ++}; ++ ++ ++/*********************************************************************** ++** Types ++*/ ++ ++/* Information Element types */ ++ ++/* prototype structure, all IEs start with these members */ ++typedef struct wlan_ie { ++ u8 eid; ++ u8 len; ++} WLAN_PACKED wlan_ie_t; ++ ++/*-- Service Set Identity (SSID) -----------------*/ ++typedef struct wlan_ie_ssid { ++ u8 eid; ++ u8 len; ++ u8 ssid[1]; /* may be zero */ ++} WLAN_PACKED wlan_ie_ssid_t; ++ ++/*-- Supported Rates -----------------------------*/ ++typedef struct wlan_ie_supp_rates { ++ u8 eid; ++ u8 len; ++ u8 rates[1]; /* had better be at LEAST one! */ ++} WLAN_PACKED wlan_ie_supp_rates_t; ++ ++/*-- FH Parameter Set ----------------------------*/ ++typedef struct wlan_ie_fh_parms { ++ u8 eid; ++ u8 len; ++ u16 dwell; ++ u8 hopset; ++ u8 hoppattern; ++ u8 hopindex; ++} WLAN_PACKED wlan_ie_fh_parms_t; ++ ++/*-- DS Parameter Set ----------------------------*/ ++typedef struct wlan_ie_ds_parms { ++ u8 eid; ++ u8 len; ++ u8 curr_ch; ++} WLAN_PACKED wlan_ie_ds_parms_t; ++ ++/*-- CF Parameter Set ----------------------------*/ ++typedef struct wlan_ie_cf_parms { ++ u8 eid; ++ u8 len; ++ u8 cfp_cnt; ++ u8 cfp_period; ++ u16 cfp_maxdur; ++ u16 cfp_durremaining; ++} WLAN_PACKED wlan_ie_cf_parms_t; ++ ++/*-- TIM ------------------------------------------*/ ++typedef struct wlan_ie_tim { ++ u8 eid; ++ u8 len; ++ u8 dtim_cnt; ++ u8 dtim_period; ++ u8 bitmap_ctl; ++ u8 virt_bm[1]; ++} WLAN_PACKED wlan_ie_tim_t; ++ ++/*-- IBSS Parameter Set ---------------------------*/ ++typedef struct wlan_ie_ibss_parms { ++ u8 eid; ++ u8 len; ++ u16 atim_win; ++} WLAN_PACKED wlan_ie_ibss_parms_t; ++ ++/*-- Challenge Text ------------------------------*/ ++typedef struct wlan_ie_challenge { ++ u8 eid; ++ u8 len; ++ u8 challenge[1]; ++} WLAN_PACKED wlan_ie_challenge_t; ++ ++/*-- ERP (42) -------------------------------------*/ ++typedef struct wlan_ie_erp { ++ u8 eid; ++ u8 len; ++ /* bit 0:Non ERP present ++ ** 1:Use Protection ++ ** 2:Barker Preamble mode ++ ** 3-7:reserved */ ++ u8 erp; ++} WLAN_PACKED wlan_ie_erp_t; ++ ++/* Types for parsing mgmt frames */ ++ ++/* prototype structure, all mgmt frame types will start with these members */ ++typedef struct wlan_fr_mgmt { ++ u16 type; ++ u16 len; /* DOES NOT include FCS */ ++ wlan_hdr_t *hdr; ++ /* used for target specific data, skb in Linux */ ++ /*-- fixed fields -----------*/ ++ /*-- info elements ----------*/ ++} WLAN_PACKED wlan_fr_mgmt_t; ++ ++/*-- Beacon ---------------------------------------*/ ++typedef struct wlan_fr_beacon { ++ u16 type; ++ u16 len; ++ wlan_hdr_t *hdr; ++ /*-- fixed fields -----------*/ ++ u64 *ts; ++ u16 *bcn_int; ++ u16 *cap_info; ++ /*-- info elements ----------*/ ++ wlan_ie_ssid_t *ssid; ++ wlan_ie_supp_rates_t *supp_rates; ++ wlan_ie_supp_rates_t *ext_rates; ++ wlan_ie_fh_parms_t *fh_parms; ++ wlan_ie_ds_parms_t *ds_parms; ++ wlan_ie_cf_parms_t *cf_parms; ++ wlan_ie_ibss_parms_t *ibss_parms; ++ wlan_ie_tim_t *tim; /* in beacon only, not proberesp */ ++ wlan_ie_erp_t *erp; /* in beacon only, not proberesp */ ++} wlan_fr_beacon_t; ++#define wlan_fr_proberesp wlan_fr_beacon ++#define wlan_fr_proberesp_t wlan_fr_beacon_t ++ ++/*-- IBSS ATIM ------------------------------------*/ ++typedef struct wlan_fr_ibssatim { ++ u16 type; ++ u16 len; ++ wlan_hdr_t *hdr; ++ /*-- fixed fields -----------*/ ++ /*-- info elements ----------*/ ++ /* this frame type has a null body */ ++} wlan_fr_ibssatim_t; ++ ++/*-- Disassociation -------------------------------*/ ++typedef struct wlan_fr_disassoc { ++ u16 type; ++ u16 len; ++ wlan_hdr_t *hdr; ++ /*-- fixed fields -----------*/ ++ u16 *reason; ++ /*-- info elements ----------*/ ++} wlan_fr_disassoc_t; ++ ++/*-- Association Request --------------------------*/ ++typedef struct wlan_fr_assocreq { ++ u16 type; ++ u16 len; ++ wlan_hdr_t *hdr; ++ /*-- fixed fields -----------*/ ++ u16 *cap_info; ++ u16 *listen_int; ++ /*-- info elements ----------*/ ++ wlan_ie_ssid_t *ssid; ++ wlan_ie_supp_rates_t *supp_rates; ++ wlan_ie_supp_rates_t *ext_rates; ++} wlan_fr_assocreq_t; ++ ++/*-- Association Response -------------------------*/ ++typedef struct wlan_fr_assocresp { ++ u16 type; ++ u16 len; ++ wlan_hdr_t *hdr; ++ /*-- fixed fields -----------*/ ++ u16 *cap_info; ++ u16 *status; ++ u16 *aid; ++ /*-- info elements ----------*/ ++ wlan_ie_supp_rates_t *supp_rates; ++ wlan_ie_supp_rates_t *ext_rates; ++} wlan_fr_assocresp_t; ++ ++/*-- Reassociation Request ------------------------*/ ++typedef struct wlan_fr_reassocreq { ++ u16 type; ++ u16 len; ++ wlan_hdr_t *hdr; ++ /*-- fixed fields -----------*/ ++ u16 *cap_info; ++ u16 *listen_int; ++ u8 *curr_ap; ++ /*-- info elements ----------*/ ++ wlan_ie_ssid_t *ssid; ++ wlan_ie_supp_rates_t *supp_rates; ++ wlan_ie_supp_rates_t *ext_rates; ++} wlan_fr_reassocreq_t; ++ ++/*-- Reassociation Response -----------------------*/ ++typedef struct wlan_fr_reassocresp { ++ u16 type; ++ u16 len; ++ wlan_hdr_t *hdr; ++ /*-- fixed fields -----------*/ ++ u16 *cap_info; ++ u16 *status; ++ u16 *aid; ++ /*-- info elements ----------*/ ++ wlan_ie_supp_rates_t *supp_rates; ++ wlan_ie_supp_rates_t *ext_rates; ++} wlan_fr_reassocresp_t; ++ ++/*-- Probe Request --------------------------------*/ ++typedef struct wlan_fr_probereq { ++ u16 type; ++ u16 len; ++ wlan_hdr_t *hdr; ++ /*-- fixed fields -----------*/ ++ /*-- info elements ----------*/ ++ wlan_ie_ssid_t *ssid; ++ wlan_ie_supp_rates_t *supp_rates; ++ wlan_ie_supp_rates_t *ext_rates; ++} wlan_fr_probereq_t; ++ ++/*-- Authentication -------------------------------*/ ++typedef struct wlan_fr_authen { ++ u16 type; ++ u16 len; ++ wlan_hdr_t *hdr; ++ /*-- fixed fields -----------*/ ++ u16 *auth_alg; ++ u16 *auth_seq; ++ u16 *status; ++ /*-- info elements ----------*/ ++ wlan_ie_challenge_t *challenge; ++} wlan_fr_authen_t; ++ ++/*-- Deauthenication -----------------------------*/ ++typedef struct wlan_fr_deauthen { ++ u16 type; ++ u16 len; ++ wlan_hdr_t *hdr; ++ /*-- fixed fields -----------*/ ++ u16 *reason; ++ /*-- info elements ----------*/ ++} wlan_fr_deauthen_t; ++ ++/* Types for building mgmt frames */ ++ ++/* Warning. Several types used in below structs are ++** in fact variable length. Use structs with such fields with caution */ ++typedef struct auth_frame_body { ++ u16 auth_alg; ++ u16 auth_seq; ++ u16 status; ++ wlan_ie_challenge_t challenge; ++} WLAN_PACKED auth_frame_body_t; ++ ++typedef struct assocresp_frame_body { ++ u16 cap_info; ++ u16 status; ++ u16 aid; ++ wlan_ie_supp_rates_t rates; ++} WLAN_PACKED assocresp_frame_body_t; ++ ++typedef struct reassocreq_frame_body { ++ u16 cap_info; ++ u16 listen_int; ++ u8 current_ap[ETH_ALEN]; ++ wlan_ie_ssid_t ssid; ++/* access to this one is disabled since ssid_t is variable length: */ ++ /* wlan_ie_supp_rates_t rates; */ ++} WLAN_PACKED reassocreq_frame_body_t; ++ ++typedef struct reassocresp_frame_body { ++ u16 cap_info; ++ u16 status; ++ u16 aid; ++ wlan_ie_supp_rates_t rates; ++} WLAN_PACKED reassocresp_frame_body_t; ++ ++typedef struct deauthen_frame_body { ++ u16 reason; ++} WLAN_PACKED deauthen_frame_body_t; ++ ++typedef struct disassoc_frame_body { ++ u16 reason; ++} WLAN_PACKED disassoc_frame_body_t; ++ ++typedef struct probereq_frame_body { ++ wlan_ie_ssid_t ssid; ++ wlan_ie_supp_rates_t rates; ++} WLAN_PACKED probereq_frame_body_t; ++ ++typedef struct proberesp_frame_body { ++ u8 timestamp[8]; ++ u16 beacon_int; ++ u16 cap_info; ++ wlan_ie_ssid_t ssid; ++/* access to these is disabled since ssid_t is variable length: */ ++ /* wlan_ie_supp_rates_t rates; */ ++ /* fhps_t fhps; */ ++ /* dsps_t dsps; */ ++ /* cfps_t cfps; */ ++} WLAN_PACKED proberesp_frame_body_t; ++ ++ ++/*********************************************************************** ++** Functions ++*/ ++ ++/* Helpers for parsing mgmt frames */ ++void wlan_mgmt_decode_ibssatim(wlan_fr_ibssatim_t *f); ++void wlan_mgmt_decode_assocreq(wlan_fr_assocreq_t *f); ++void wlan_mgmt_decode_assocresp(wlan_fr_assocresp_t *f); ++void wlan_mgmt_decode_authen(wlan_fr_authen_t *f); ++void wlan_mgmt_decode_beacon(wlan_fr_beacon_t *f); ++void wlan_mgmt_decode_deauthen(wlan_fr_deauthen_t *f); ++void wlan_mgmt_decode_disassoc(wlan_fr_disassoc_t *f); ++void wlan_mgmt_decode_probereq(wlan_fr_probereq_t *f); ++void wlan_mgmt_decode_proberesp(wlan_fr_proberesp_t *f); ++void wlan_mgmt_decode_reassocreq(wlan_fr_reassocreq_t *f); ++void wlan_mgmt_decode_reassocresp(wlan_fr_reassocresp_t *f); ++ ++/* Helpers for building mgmt frames */ ++static inline u8* ++wlan_fill_ie_ssid(u8 *p, int len, const char *ssid) ++{ ++ struct wlan_ie_ssid *ie = (void*)p; ++ ie->eid = WLAN_EID_SSID; ++ ie->len = len; ++ memcpy(ie->ssid, ssid, len); ++ return p + len + 2; ++} ++/* This controls whether we create 802.11g 'ext supported rates' IEs ++** or just create overlong 'supported rates' IEs instead ++** (non-11g compliant) */ ++#define WE_OBEY_802_11G 1 ++static inline u8* ++wlan_fill_ie_rates(u8 *p, int len, const u8 *rates) ++{ ++ struct wlan_ie_supp_rates *ie = (void*)p; ++#if WE_OBEY_802_11G ++ if (len > 8 ) len = 8; ++#endif ++ /* supported rates (1 to 8 octets) */ ++ ie->eid = WLAN_EID_SUPP_RATES; ++ ie->len = len; ++ memcpy(ie->rates, rates, len); ++ return p + len + 2; ++} ++/* This one wouldn't create an IE at all if not needed */ ++static inline u8* ++wlan_fill_ie_rates_ext(u8 *p, int len, const u8 *rates) ++{ ++ struct wlan_ie_supp_rates *ie = (void*)p; ++#if !WE_OBEY_802_11G ++ return p; ++#endif ++ len -= 8; ++ if (len <= 0) return p; ++ /* ext supported rates */ ++ ie->eid = WLAN_EID_EXT_RATES; ++ ie->len = len; ++ memcpy(ie->rates, rates+8, len); ++ return p + len + 2; ++} ++static inline u8* ++wlan_fill_ie_ds_parms(u8 *p, int channel) ++{ ++ struct wlan_ie_ds_parms *ie = (void*)p; ++ ie->eid = WLAN_EID_DS_PARMS; ++ ie->len = 1; ++ ie->curr_ch = channel; ++ return p + sizeof(*ie); ++} ++static inline u8* ++wlan_fill_ie_ibss_parms(u8 *p, int atim_win) ++{ ++ struct wlan_ie_ibss_parms *ie = (void*)p; ++ ie->eid = WLAN_EID_IBSS_PARMS; ++ ie->len = 2; ++ ie->atim_win = atim_win; ++ return p + sizeof(*ie); ++} ++static inline u8* ++wlan_fill_ie_tim(u8 *p, int rem, int period, int bcast, ++ int ofs, int len, const u8 *vbm) ++{ ++ struct wlan_ie_tim *ie = (void*)p; ++ ie->eid = WLAN_EID_TIM; ++ ie->len = len + 3; ++ ie->dtim_cnt = rem; ++ ie->dtim_period = period; ++ ie->bitmap_ctl = ofs | (bcast!=0); ++ if (vbm) ++ memcpy(ie->virt_bm, vbm, len); /* min 1 byte */ ++ else ++ ie->virt_bm[0] = 0; ++ return p + len + 3 + 2; ++} +Index: linux-2.6.23/drivers/net/wireless/Kconfig +=================================================================== +--- linux-2.6.23.orig/drivers/net/wireless/Kconfig 2008-01-20 21:13:17.000000000 +0000 ++++ linux-2.6.23/drivers/net/wireless/Kconfig 2008-01-20 21:15:12.000000000 +0000 +@@ -5,6 +5,36 @@ + menu "Wireless LAN" + depends on !S390 + ++config NET_RADIO ++ bool "Wireless LAN drivers (non-hamradio) & Wireless Extensions" ++ select WIRELESS_EXT ++ ---help--- ++ Support for wireless LANs and everything having to do with radio, ++ but not with amateur radio or FM broadcasting. ++ ++ Saying Y here also enables the Wireless Extensions (creates ++ /proc/net/wireless and enables iwconfig access). The Wireless ++ Extension is a generic API allowing a driver to expose to the user ++ space configuration and statistics specific to common Wireless LANs. ++ The beauty of it is that a single set of tool can support all the ++ variations of Wireless LANs, regardless of their type (as long as ++ the driver supports Wireless Extension). Another advantage is that ++ these parameters may be changed on the fly without restarting the ++ driver (or Linux). If you wish to use Wireless Extensions with ++ wireless PCMCIA (PC-) cards, you need to say Y here; you can fetch ++ the tools from ++ <http://www.hpl.hp.com/personal/Jean_Tourrilhes/Linux/Tools.html>. ++ ++config NET_WIRELESS_RTNETLINK ++ bool "Wireless Extension API over RtNetlink" ++ depends on NET_RADIO ++ ---help--- ++ Support the Wireless Extension API over the RtNetlink socket ++ in addition to the traditional ioctl interface (selected above). ++ ++ For now, few tools use this facility, but it might grow in the ++ future. The only downside is that it adds 4.5 kB to your kernel. ++ + config WLAN_PRE80211 + bool "Wireless LAN (pre-802.11)" + depends on NETDEVICES +@@ -650,6 +680,7 @@ config P54_PCI + + source "drivers/net/wireless/iwlwifi/Kconfig" + source "drivers/net/wireless/hostap/Kconfig" ++source "drivers/net/wireless/acx/Kconfig" + source "drivers/net/wireless/bcm43xx/Kconfig" + source "drivers/net/wireless/b43/Kconfig" + source "drivers/net/wireless/b43legacy/Kconfig" +Index: linux-2.6.23/drivers/net/wireless/Makefile +=================================================================== +--- linux-2.6.23.orig/drivers/net/wireless/Makefile 2008-01-20 21:13:17.000000000 +0000 ++++ linux-2.6.23/drivers/net/wireless/Makefile 2008-01-20 21:13:40.000000000 +0000 +@@ -34,6 +34,8 @@ obj-$(CONFIG_PCMCIA_ATMEL) += atmel + + obj-$(CONFIG_PRISM54) += prism54/ + ++obj-$(CONFIG_ACX) += acx/ ++ + obj-$(CONFIG_HOSTAP) += hostap/ + obj-$(CONFIG_BCM43XX) += bcm43xx/ + obj-$(CONFIG_B43) += b43/ diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/htcuni.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/htcuni.patch new file mode 100644 index 0000000000..8448c4ec06 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/htcuni.patch @@ -0,0 +1,7920 @@ +--- + arch/arm/Kconfig | 2 + arch/arm/mach-pxa/Kconfig | 89 + + arch/arm/mach-pxa/Makefile | 1 + arch/arm/mach-pxa/generic.c | 13 + arch/arm/mach-pxa/htcuniversal/Makefile | 19 + arch/arm/mach-pxa/htcuniversal/htcuniversal.c | 468 +++++ + arch/arm/mach-pxa/htcuniversal/htcuniversal_ak4641.c | 917 +++++++++++ + arch/arm/mach-pxa/htcuniversal/htcuniversal_ak4641.h | 65 + arch/arm/mach-pxa/htcuniversal/htcuniversal_asic3_leds.c | 143 + + arch/arm/mach-pxa/htcuniversal/htcuniversal_bl.c | 61 + arch/arm/mach-pxa/htcuniversal/htcuniversal_bt.c | 135 + + arch/arm/mach-pxa/htcuniversal/htcuniversal_bt.h | 17 + arch/arm/mach-pxa/htcuniversal/htcuniversal_buttons.c | 87 + + arch/arm/mach-pxa/htcuniversal/htcuniversal_core.c | 226 ++ + arch/arm/mach-pxa/htcuniversal/htcuniversal_lcd.c | 212 ++ + arch/arm/mach-pxa/htcuniversal/htcuniversal_phone.c | 167 ++ + arch/arm/mach-pxa/htcuniversal/htcuniversal_phone.h | 16 + arch/arm/mach-pxa/htcuniversal/htcuniversal_pm.c | 69 + arch/arm/mach-pxa/htcuniversal/htcuniversal_power2.c | 97 + + arch/arm/mach-pxa/htcuniversal/htcuniversal_ts2.c | 490 ++++++ + arch/arm/mach-pxa/htcuniversal/htcuniversal_udc.c | 71 + arch/arm/mach-pxa/htcuniversal/tsc2046_ts.h | 20 + drivers/input/keyboard/Kconfig | 7 + drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/asic3_keys.c | 131 + + drivers/leds/Kconfig | 7 + drivers/leds/Makefile | 1 + drivers/leds/leds-asic3.c | 189 ++ + drivers/mfd/Kconfig | 10 + drivers/mfd/Makefile | 2 + drivers/mfd/asic3_base.c | 1208 +++++++++++++++ + drivers/mfd/soc-core.c | 106 + + drivers/mfd/soc-core.h | 30 + drivers/mmc/host/Kconfig | 6 + drivers/mmc/host/Makefile | 1 + drivers/mmc/host/asic3_mmc.c | 900 +++++++++++ + drivers/mmc/host/asic3_mmc.h | 25 + drivers/serial/pxa.c | 22 + include/asm-arm/arch-pxa/clock.h | 27 + include/asm-arm/arch-pxa/htcuniversal-asic.h | 213 ++ + include/asm-arm/arch-pxa/htcuniversal-gpio.h | 220 ++ + include/asm-arm/arch-pxa/htcuniversal-init.h | 14 + include/asm-arm/arch-pxa/htcuniversal.h | 3 + include/asm-arm/arch-pxa/irqs.h | 2 + include/asm-arm/arch-pxa/pxa-pm_ll.h | 6 + include/asm-arm/arch-pxa/pxa-regs.h | 2 + include/asm-arm/arch-pxa/serial.h | 78 + include/asm-arm/hardware/asic3_keys.h | 18 + include/asm-arm/hardware/asic3_leds.h | 34 + include/asm-arm/hardware/ipaq-asic3.h | 602 +++++++ + include/linux/backlight.h | 7 + include/linux/gpiodev.h | 44 + include/linux/input_pda.h | 47 + include/linux/ioport.h | 1 + include/linux/soc/asic3_base.h | 104 + + include/linux/soc/tmio_mmc.h | 17 + 56 files changed, 7469 insertions(+), 1 deletion(-) + +Index: linux-2.6.24/arch/arm/mach-pxa/htcuniversal/Makefile +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/arch/arm/mach-pxa/htcuniversal/Makefile 2008-03-10 16:09:23.000000000 +0000 +@@ -0,0 +1,19 @@ ++# ++# Makefile for HTC Universal ++# ++ ++snd-htcuniversal-ak4641-objs := htcuniversal_ak4641.o ++ ++obj-$(CONFIG_MACH_HTCUNIVERSAL) += htcuniversal.o ++obj-$(CONFIG_HTCUNIVERSAL_CORE) += htcuniversal_core.o ++obj-$(CONFIG_HTCUNIVERSAL_POWER) += htcuniversal_power2.o ++obj-$(CONFIG_HTCUNIVERSAL_LCD) += htcuniversal_lcd.o ++obj-$(CONFIG_HTCUNIVERSAL_BACKLIGHT) += htcuniversal_bl.o ++obj-$(CONFIG_HTCUNIVERSAL_TS2) += htcuniversal_ts2.o ++obj-$(CONFIG_HTCUNIVERSAL_BUTTONS) += htcuniversal_buttons.o ++obj-$(CONFIG_HTCUNIVERSAL_BLUETOOTH) += htcuniversal_bt.o ++obj-$(CONFIG_HTCUNIVERSAL_PHONE) += htcuniversal_phone.o ++obj-$(CONFIG_HTCUNIVERSAL_ASIC3_LEDS) += htcuniversal_asic3_leds.o ++obj-$(CONFIG_HTCUNIVERSAL_UDC) += htcuniversal_udc.o ++ ++obj-$(CONFIG_HTCUNIVERSAL_AK4641) += htcuniversal_ak4641.o +Index: linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal.c 2008-03-10 16:09:23.000000000 +0000 +@@ -0,0 +1,468 @@ ++/* ++ * Hardware definitions for HTC Universal ++ * ++ * Copyright (c) 2006 Oleg Gusev ++ * ++ * Use consistent with the GNU GPL is permitted, ++ * provided that this copyright notice is ++ * preserved in its entirety in all copies and derived works. ++ * ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/platform_device.h> ++#include <linux/irq.h> ++#include <linux/input.h> ++#include <linux/gpio_keys.h> ++#include <linux/soc/asic3_base.h> ++ ++#include <asm/mach-types.h> ++#include <asm/hardware.h> ++#include <asm/setup.h> ++ ++#include <asm/mach/irq.h> ++#include <asm/mach/arch.h> ++ ++#include <asm/arch/bitfield.h> ++#include <asm/arch/pxa-regs.h> ++#include <asm/arch/serial.h> ++#include <asm/arch/pxa27x_keyboard.h> ++#include <asm/arch/pxafb.h> ++#include <asm/arch/irda.h> ++#include <asm/arch/ohci.h> ++ ++#include <asm/arch/htcuniversal.h> ++#include <asm/arch/htcuniversal-gpio.h> ++#include <asm/arch/htcuniversal-init.h> ++#include <asm/arch/htcuniversal-asic.h> ++ ++#include <asm/hardware/ipaq-asic3.h> ++ ++#include "../generic.h" ++ ++#include "htcuniversal_bt.h" ++#include "htcuniversal_phone.h" ++#include "tsc2046_ts.h" ++ ++/* ++ * IRDA ++ */ ++ ++static void htcuniversal_irda_transceiver_mode(struct device *dev, int mode) ++{ ++ /* */ ++} ++ ++static struct pxaficp_platform_data htcuniversal_ficp_platform_data = { ++ .transceiver_cap = IR_SIRMODE | IR_FIRMODE, ++ .transceiver_mode = htcuniversal_irda_transceiver_mode, ++}; ++ ++/* ++ * Bluetooth - Relies on other loadable modules, like ASIC3 and Core, ++ * so make the calls indirectly through pointers. Requires that the ++ * htcuniversal_bt module be loaded before any attempt to use ++ * bluetooth (obviously). ++ */ ++ ++static struct htcuniversal_bt_funcs bt_funcs; ++ ++static void ++htcuniversal_bt_configure( int state ) ++{ ++ if (bt_funcs.configure != NULL) ++ bt_funcs.configure( state ); ++} ++ ++static struct htcuniversal_phone_funcs phone_funcs; ++ ++static void ++htcuniversal_phone_configure( int state ) ++{ ++ if (phone_funcs.configure != NULL) ++ phone_funcs.configure( state ); ++} ++ ++//void htcuniversal_ll_pm_init(void); ++ ++extern struct platform_device htcuniversal_bl; ++static struct platform_device htcuniversal_lcd = { .name = "htcuniversal_lcd", }; ++//static struct platform_device htcuniversal_kbd = { .name = "htcuniversal_kbd", }; ++static struct platform_device htcuniversal_buttons = { .name = "htcuniversal_buttons", }; ++//static struct platform_device htcuniversal_ts = { .name = "htcuniversal_ts", }; ++//static struct platform_device htcuniversal_bt = { .name = "htcuniversal_bt", }; ++//static struct platform_device htcuniversal_phone = { .name = "htcuniversal_phone", }; ++static struct platform_device htcuniversal_power = { .name = "htcuniversal_power", }; ++static struct platform_device htcuniversal_udc = { .name = "htcuniversal_udc", }; ++ ++static struct tsc2046_mach_info htcuniversal_ts_platform_data = { ++ .port = 1, ++ .clock = CKEN_SSP1, ++ .pwrbit_X = 1, ++ .pwrbit_Y = 1, ++ .irq = 0 /* asic3 irq */ ++}; ++ ++static struct platform_device htcuniversal_ts = { ++ .name = "htcuniversal_ts", ++ .dev = { ++ .platform_data = &htcuniversal_ts_platform_data, ++ }, ++}; ++ ++ ++/* Bluetooth */ ++ ++static struct platform_device htcuniversal_bt = { ++ .name = "htcuniversal_bt", ++ .id = -1, ++ .dev = { ++ .platform_data = &bt_funcs, ++ }, ++}; ++ ++static struct platform_device htcuniversal_phone = { ++ .name = "htcuniversal_phone", ++ .id = -1, ++ .dev = { ++ .platform_data = &phone_funcs, ++ }, ++}; ++ ++/* PXA2xx Keys */ ++ ++static struct gpio_keys_button htcuniversal_button_table[] = { ++ { KEY_POWER, GPIO_NR_HTCUNIVERSAL_KEY_ON_N, 1 }, ++}; ++ ++static struct gpio_keys_platform_data htcuniversal_pxa_keys_data = { ++ .buttons = htcuniversal_button_table, ++ .nbuttons = ARRAY_SIZE(htcuniversal_button_table), ++}; ++ ++static struct platform_device htcuniversal_pxa_keys = { ++ .name = "gpio-keys", ++ .dev = { ++ .platform_data = &htcuniversal_pxa_keys_data, ++ }, ++ .id = -1, ++}; ++ ++/**************************************************************** ++ * Keyboard ++ ****************************************************************/ ++ ++static struct pxa27x_keyboard_platform_data htcuniversal_kbd = { ++ .nr_rows = 8, ++ .nr_cols = 8, ++ .keycodes = { ++ { ++ /* row 0 */ ++ KEY_ENTER, ++ KEY_MINUS, ++ KEY_ESC, ++ KEY_1, ++ KEY_TAB, ++ KEY_CAPSLOCK, ++ KEY_LEFTSHIFT, ++ KEY_RIGHTALT, /* Fn */ ++ }, { /* row 1 */ ++ KEY_COMMA, ++ KEY_EQUAL, ++ KEY_F1, ++ KEY_2, ++ KEY_Q, ++ KEY_A, ++ KEY_Z, ++ KEY_LEFTCTRL, ++ }, { /* row 2 */ ++ KEY_UP, ++ KEY_I, ++ KEY_F2, ++ KEY_3, ++ KEY_W, ++ KEY_S, ++ KEY_X, ++ KEY_F6, ++ }, { /* row 3 */ ++ KEY_DOT, ++ KEY_O, ++ KEY_F3, ++ KEY_4, ++ KEY_E, ++ KEY_D, ++ KEY_C, ++ KEY_LEFTALT, ++ }, { /* row 4 */ ++ KEY_F9, ++ KEY_P, ++ KEY_F4, ++ KEY_5, ++ KEY_R, ++ KEY_F, ++ KEY_V, ++ KEY_SPACE, ++ }, { /* row 5 */ ++ KEY_RIGHT, ++ KEY_BACKSPACE, ++ KEY_F5, ++ KEY_6, ++ KEY_T, ++ KEY_G, ++ KEY_B, ++ KEY_F7, ++ }, { /* row 6 */ ++ KEY_F9, ++ KEY_K, ++ KEY_9, ++ KEY_7, ++ KEY_Y, ++ KEY_H, ++ KEY_N, ++ KEY_LEFT, ++ }, { /* row 7 */ ++ KEY_F10, ++ KEY_L, ++ KEY_0, ++ KEY_8, ++ KEY_U, ++ KEY_J, ++ KEY_M, ++ KEY_DOWN, ++ }, ++ }, ++ .gpio_modes = { ++ GPIO_NR_HTCUNIVERSAL_KP_MKIN0_MD, ++ GPIO_NR_HTCUNIVERSAL_KP_MKIN1_MD, ++ GPIO_NR_HTCUNIVERSAL_KP_MKIN2_MD, ++ GPIO_NR_HTCUNIVERSAL_KP_MKIN3_MD, ++ GPIO_NR_HTCUNIVERSAL_KP_MKIN4_MD, ++ GPIO_NR_HTCUNIVERSAL_KP_MKIN5_MD, ++ GPIO_NR_HTCUNIVERSAL_KP_MKIN6_MD, ++ GPIO_NR_HTCUNIVERSAL_KP_MKIN7_MD, ++ GPIO_NR_HTCUNIVERSAL_KP_MKOUT0_MD, ++ GPIO_NR_HTCUNIVERSAL_KP_MKOUT1_MD, ++ GPIO_NR_HTCUNIVERSAL_KP_MKOUT2_MD, ++ GPIO_NR_HTCUNIVERSAL_KP_MKOUT3_MD, ++ GPIO_NR_HTCUNIVERSAL_KP_MKOUT4_MD, ++ GPIO_NR_HTCUNIVERSAL_KP_MKOUT5_MD, ++ GPIO_NR_HTCUNIVERSAL_KP_MKOUT6_MD, ++ GPIO_NR_HTCUNIVERSAL_KP_MKOUT7_MD, ++ }, ++}; ++ ++static struct platform_device htcuniversal_pxa_keyboard = { ++ .name = "pxa27x-keyboard", ++ .id = -1, ++ .dev = { ++ .platform_data = &htcuniversal_kbd, ++ }, ++}; ++/* Core Hardware Functions */ ++ ++struct platform_device htcuniversal_core = { ++ .name = "htcuniversal_core", ++ .id = 0, ++ .dev = { ++ .platform_data = NULL, ++ }, ++}; ++ ++static struct platform_device *devices[] __initdata = { ++ &htcuniversal_core, ++// &htcuniversal_flash, ++ &htcuniversal_pxa_keyboard, ++ &htcuniversal_pxa_keys, ++}; ++ ++static struct platform_device *htcuniversal_asic3_devices[] __initdata = { ++ &htcuniversal_lcd, ++#ifdef CONFIG_HTCUNIVERSAL_BACKLIGHT ++ &htcuniversal_bl, ++#endif ++ &htcuniversal_buttons, ++ &htcuniversal_ts, ++ &htcuniversal_bt, ++ &htcuniversal_phone, ++ &htcuniversal_power, ++ &htcuniversal_udc, ++}; ++ ++static struct asic3_platform_data htcuniversal_asic3_platform_data = { ++ ++ /* Setting ASIC3 GPIO registers to the below initialization states ++ * HTC Universal asic3 information: ++ * http://wiki.xda-developers.com/index.php?pagename=UniversalASIC3 ++ * http://wiki.xda-developers.com/index.php?pagename=ASIC3 ++ * ++ * dir: Direction of the GPIO pin. 0: input, 1: output. ++ * If unknown, set as output to avoid power consuming floating input nodes ++ * init: Initial state of the GPIO bits ++ * ++ * These registers are configured as they are on Wince. ++ */ ++ .gpio_a = { ++ .dir = (1<<GPIOA_LCD_PWR5_ON) | ++ (1<<GPIOA_FLASHLIGHT) | ++ (1<<GPIOA_UNKNOWN9) | ++ (1<<GPIOA_SPK_PWR2_ON) | ++ (1<<GPIOA_UNKNOWN4) | ++ (1<<GPIOA_EARPHONE_PWR_ON)| ++ (1<<GPIOA_AUDIO_PWR_ON) | ++ (1<<GPIOA_SPK_PWR1_ON) | ++ (1<<GPIOA_I2C_EN), ++ .init = (1<<GPIOA_LCD_PWR5_ON) | ++ (1<<GPIOA_I2C_EN), ++ .sleep_out = 0x0000, ++ .batt_fault_out = 0x0000, ++ .alt_function = 0x0000, ++ .sleep_conf = 0x000c, ++ }, ++ .gpio_b = { ++ .dir = 0xc142, ++ .init = 0x8842, // TODO: 0x0900 ++ .sleep_out = 0x0000, ++ .batt_fault_out = 0x0000, ++ .alt_function = 0x0000, ++ .sleep_conf = 0x000c, ++ }, ++ .gpio_c = { ++ .dir = 0xc7e7, ++ .init = 0xc6e0, // TODO: 0x8000 ++ .sleep_out = 0x0000, ++ .batt_fault_out = 0x0000, ++ .alt_function = 0x0007, // GPIOC_LED_RED | GPIOC_LED_GREEN | GPIOC_LED_BLUE ++ .sleep_conf = 0x000c, ++ }, ++ .gpio_d = { ++ .dir = 0xffc0, ++ .init = 0x7840, // TODO: 0x0000 ++ .sleep_out = 0x0000, ++ .batt_fault_out = 0x0000, ++ .alt_function = 0x0000, ++ .sleep_conf = 0x0008, ++ }, ++ .bus_shift = 1, ++ .irq_base = HTCUNIVERSAL_ASIC3_IRQ_BASE, ++ ++ .child_platform_devs = htcuniversal_asic3_devices, ++ .num_child_platform_devs = ARRAY_SIZE(htcuniversal_asic3_devices), ++}; ++ ++static struct resource htcuniversal_asic3_resources[] = { ++ [0] = { ++ .start = HTCUNIVERSAL_ASIC3_GPIO_PHYS, ++ .end = HTCUNIVERSAL_ASIC3_GPIO_PHYS + IPAQ_ASIC3_MAP_SIZE, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = HTCUNIVERSAL_IRQ(ASIC3_EXT_INT), ++ .end = HTCUNIVERSAL_IRQ(ASIC3_EXT_INT), ++ .flags = IORESOURCE_IRQ, ++ }, ++ [2] = { ++ .start = HTCUNIVERSAL_ASIC3_MMC_PHYS, ++ .end = HTCUNIVERSAL_ASIC3_MMC_PHYS + IPAQ_ASIC3_MAP_SIZE, ++ .flags = IORESOURCE_MEM, ++ }, ++ [3] = { ++ .start = HTCUNIVERSAL_IRQ(ASIC3_SDIO_INT_N), ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++struct platform_device htcuniversal_asic3 = { ++ .name = "asic3", ++ .id = 0, ++ .num_resources = ARRAY_SIZE(htcuniversal_asic3_resources), ++ .resource = htcuniversal_asic3_resources, ++ .dev = { .platform_data = &htcuniversal_asic3_platform_data, }, ++}; ++EXPORT_SYMBOL(htcuniversal_asic3); ++ ++static struct pxafb_mode_info htcuniversal_lcd_modes[] = { ++{ ++ .pixclock = 96153, ++ .xres = 480, ++ .yres = 640, ++ .bpp = 16, ++ .hsync_len = 4, ++ .vsync_len = 1, ++ .left_margin = 20, ++ .right_margin = 8, ++ .upper_margin = 7, ++ .lower_margin = 8, ++ ++// .sync = FB_SYNC_HOR_LOW_ACT|FB_SYNC_VERT_LOW_ACT, ++ ++}, ++}; ++ ++static struct pxafb_mach_info sony_acx526akm = { ++ .modes = htcuniversal_lcd_modes, ++ .num_modes = ARRAY_SIZE(htcuniversal_lcd_modes), ++ ++ /* fixme: use constants defined in pxafb.h */ ++ .lccr0 = 0x00000080, ++ .lccr3 = 0x00400000, ++// .lccr4 = 0x80000000, ++}; ++ ++static void __init htcuniversal_init_irq(void) ++{ ++ pxa27x_init_irq(); ++} ++ ++static struct platform_pxa_serial_funcs htcuniversal_pxa_bt_funcs = { ++ .configure = htcuniversal_bt_configure, ++}; ++static struct platform_pxa_serial_funcs htcuniversal_pxa_phone_funcs = { ++ .configure = htcuniversal_phone_configure, ++}; ++ ++/* USB OHCI */ ++ ++static int htcuniversal_ohci_init(struct device *dev) ++{ ++ /* missing GPIO setup here */ ++ ++ /* got the value from wince */ ++ UHCHR=UHCHR_CGR; ++ ++ return 0; ++} ++ ++static struct pxaohci_platform_data htcuniversal_ohci_platform_data = { ++ .port_mode = PMM_PERPORT_MODE, ++ .init = htcuniversal_ohci_init, ++}; ++ ++static void __init htcuniversal_map_io(void) ++{ ++ pxa_map_io(); ++ ++ pxa_set_btuart_info(&htcuniversal_pxa_bt_funcs); ++ pxa_set_ffuart_info(&htcuniversal_pxa_phone_funcs); ++} ++ ++static void __init htcuniversal_init(void) ++{ ++ set_pxa_fb_info(&sony_acx526akm); ++ ++ platform_device_register(&htcuniversal_asic3); ++ platform_add_devices(devices, ARRAY_SIZE(devices) ); ++ pxa_set_ficp_info(&htcuniversal_ficp_platform_data); ++ pxa_set_ohci_info(&htcuniversal_ohci_platform_data); ++} ++ ++MACHINE_START(HTCUNIVERSAL, "HTC Universal") ++ /* Maintainer xanadux.org */ ++ .phys_io = 0x40000000, ++ .io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc, ++ .boot_params = 0xa0000100, ++ .map_io = htcuniversal_map_io, ++ .init_irq = htcuniversal_init_irq, ++ .init_machine = htcuniversal_init, ++ .timer = &pxa_timer, ++MACHINE_END +Index: linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_ak4641.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_ak4641.c 2008-03-10 16:09:23.000000000 +0000 +@@ -0,0 +1,917 @@ ++/* ++ * Audio support for codec Asahi Kasei AK4641 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * Copyright (c) 2006 Giorgio Padrin <giorgio@mandarinlogiq.org> ++ * ++ * History: ++ * ++ * 2006-03 Written -- Giorgio Padrin ++ * 2006-09 Test and debug on machine (HP hx4700) -- Elshin Roman <roxmail@list.ru> ++ * ++ * AK4641 codec device driver ++ * ++ * Copyright (c) 2005 SDG Systems, LLC ++ * ++ * Based on code: ++ * Copyright (c) 2002 Hewlett-Packard Company ++ * Copyright (c) 2000 Nicolas Pitre <nico@cam.org> ++ * Copyright (c) 2000 Lernout & Hauspie Speech Products, N.V. ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License. ++ */ ++ ++#include <sound/driver.h> ++ ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/types.h> ++#include <linux/string.h> ++#include <linux/slab.h> ++#include <linux/errno.h> ++#include <linux/ioctl.h> ++#include <linux/delay.h> ++#include <linux/i2c.h> ++ ++#include <sound/core.h> ++#include <sound/control.h> ++#include <sound/initval.h> ++#include <sound/info.h> ++ ++#include "htcuniversal_ak4641.h" ++ ++/* Registers */ ++#define R_PM1 0x00 ++#define R_PM2 0x01 ++#define R_SEL1 0x02 ++#define R_SEL2 0x03 ++#define R_MODE1 0x04 ++#define R_MODE2 0x05 ++#define R_DAC 0x06 ++#define R_MIC 0x07 ++#define REG_TIMER 0x08 ++#define REG_ALC1 0x09 ++#define REG_ALC2 0x0a ++#define R_PGA 0x0b ++#define R_ATTL 0x0c ++#define R_ATTR 0x0d ++#define REG_VOL 0x0e ++#define R_STATUS 0x0f ++#define REG_EQLO 0x10 ++#define REG_EQMID 0x11 ++#define REG_EQHI 0x12 ++#define REG_BTIF 0x13 ++ ++/* Register flags */ ++/* REG_PWR1 */ ++#define R_PM1_PMADC 0x01 ++#define R_PM1_PMMIC 0x02 ++#define REG_PWR1_PMAUX 0x04 ++#define REG_PWR1_PMMO 0x08 ++#define R_PM1_PMLO 0x10 ++/* unused 0x20 */ ++/* unused 0x40 */ ++#define R_PM1_PMVCM 0x80 ++ ++/* REG_PWR2 */ ++#define R_PM2_PMDAC 0x01 ++/* unused 0x02 */ ++/* unused 0x04 */ ++#define R_PM2_PMMO2 0x08 ++#define REG_PWR2_MCKAC 0x10 ++/* unused 0x20 */ ++/* unused 0x40 */ ++#define R_PM2_MCKPD 0x80 ++ ++/* REG_SEL1 */ ++#define R_SEL1_PSMO2 0x01 ++/* unused 0x02 */ ++/* unused 0x04 */ ++/* unused 0x08 */ ++#define REG_SEL1_MICM 0x10 ++#define REG_SEL1_DACM 0x20 ++#define REG_SEL1_PSMO 0x40 ++#define REG_SEL1_MOGN 0x80 ++ ++/* REG_SEL2 */ ++#define R_SEL2_PSLOR 0x01 ++#define R_SEL2_PSLOL 0x02 ++#define REG_SEL2_AUXSI 0x04 ++/* unused 0x08 */ ++#define REG_SEL2_MICL 0x10 ++#define REG_SEL2_AUXL 0x20 ++/* unused 0x40 */ ++#define R_SEL2_DACL 0x80 ++ ++/* REG_MODE1 */ ++#define REG_MODE1_DIF0 0x01 ++#define REG_MODE1_DIF1 0x02 ++/* unused 0x04 */ ++/* unused 0x08 */ ++/* unused 0x10 */ ++/* unused 0x20 */ ++/* unused 0x40 */ ++/* unused 0x80 */ ++ ++/* REG_MODE2 */ ++/* unused 0x01 */ ++#define REG_MODE2_LOOP 0x02 ++#define REG_MODE2_HPM 0x04 ++/* unused 0x08 */ ++/* unused 0x10 */ ++#define REG_MODE2_MCK0 0x20 ++#define REG_MODE2_MCK1 0x40 ++/* unused 0x80 */ ++ ++/* REG_DAC */ ++#define REG_DAC_DEM0 0x01 ++#define REG_DAC_DEM1 0x02 ++#define REG_DAC_EQ 0x04 ++/* unused 0x08 */ ++#define R_DAC_DATTC 0x10 ++#define R_DAC_SMUTE 0x20 ++#define REG_DAC_TM 0x40 ++/* unused 0x80 */ ++ ++/* REG_MIC */ ++#define R_MIC_MGAIN 0x01 ++#define R_MIC_MSEL 0x02 ++#define R_MIC_MICAD 0x04 ++#define R_MIC_MPWRI 0x08 ++#define R_MIC_MPWRE 0x10 ++#define REG_MIC_AUXAD 0x20 ++/* unused 0x40 */ ++/* unused 0x80 */ ++ ++/* REG_TIMER */ ++ ++#define REG_TIMER_LTM0 0x01 ++#define REG_TIMER_LTM1 0x02 ++#define REG_TIMER_WTM0 0x04 ++#define REG_TIMER_WTM1 0x08 ++#define REG_TIMER_ZTM0 0x10 ++#define REG_TIMER_ZTM1 0x20 ++/* unused 0x40 */ ++/* unused 0x80 */ ++ ++#define REG_ALC1_LMTH 0x01 ++#define REG_ALC1_RATT 0x02 ++#define REG_ALC1_LMAT0 0x04 ++#define REG_ALC1_LMAT1 0x08 ++#define REG_ALC1_ZELM 0x10 ++#define REG_ALC1_ALC1 0x20 ++/* unused 0x40 */ ++/* unused 0x80 */ ++ ++/* REG_ALC2 */ ++ ++/* REG_PGA */ ++ ++/* REG_ATTL */ ++ ++/* REG_ATTR */ ++ ++/* REG_VOL */ ++#define REG_VOL_ATTM 0x80 ++ ++/* REG_STATUS */ ++#define R_STATUS_DTMIC 0x01 ++ ++/* REG_EQ controls use 4 bits for each of 5 EQ levels */ ++ ++/* Bluetooth not yet implemented */ ++#define REG_BTIF_PMAD2 0x01 ++#define REG_BTIF_PMDA2 0x02 ++#define REG_BTIF_PMBIF 0x04 ++#define REG_BTIF_ADC2 0x08 ++#define REG_BTIF_DAC2 0x10 ++#define REG_BTIF_BTFMT0 0x20 ++#define REG_BTIF_BTFMT1 0x40 ++/* unused 0x80 */ ++ ++/* begin {{ I2C }} */ ++ ++static struct i2c_driver snd_ak4641_i2c_driver = { ++ .driver = { ++ .name = "ak4641-i2c" ++ }, ++}; ++ ++static int snd_ak4641_i2c_init(void) ++{ ++ return i2c_add_driver(&snd_ak4641_i2c_driver); ++} ++ ++static void snd_ak4641_i2c_free(void) ++{ ++ i2c_del_driver(&snd_ak4641_i2c_driver); ++} ++ ++static inline int snd_ak4641_i2c_probe(struct snd_ak4641 *ak) ++{ ++ if (ak->i2c_client.adapter == NULL) return -EINVAL; ++ ak->i2c_client.addr = 0x12; ++ if (i2c_smbus_xfer(ak->i2c_client.adapter, ak->i2c_client.addr, ++ 0, 0, 0, I2C_SMBUS_QUICK, NULL) < 0) ++ return -ENODEV; ++ else return 0; ++} ++ ++static int snd_ak4641_i2c_attach(struct snd_ak4641 *ak) ++{ ++ int ret = 0; ++ if ((ret = snd_ak4641_i2c_probe(ak)) < 0) return ret; ++ snprintf(ak->i2c_client.name, sizeof(ak->i2c_client.name), ++ "ak4641-i2c at %d-%04x", ++ i2c_adapter_id(ak->i2c_client.adapter), ak->i2c_client.addr); ++ return i2c_attach_client(&ak->i2c_client); ++} ++ ++static void snd_ak4641_i2c_detach(struct snd_ak4641 *ak) ++{ ++ i2c_detach_client(&ak->i2c_client); ++} ++ ++/* end {{ I2C }} */ ++ ++ ++/* begin {{ Registers & Cache Ops }} */ ++ ++static int snd_ak4641_hwsync(struct snd_ak4641 *ak, int read, u8 reg) ++{ ++ struct i2c_msg msgs[2]; ++ u8 buf[2]; ++ int ret; ++ ++ snd_assert(reg < ARRAY_SIZE(ak->regs), return -EINVAL); ++ ++ /* setup i2c msgs */ ++ msgs[0].addr = ak->i2c_client.addr; ++ msgs[0].flags = 0; ++ msgs[0].buf = buf; ++ if (!read) ++ msgs[0].len = 2; ++ else { ++ msgs[1].flags = I2C_M_RD; ++ msgs[1].addr = msgs[0].addr; ++ msgs[1].buf = msgs[0].buf + 1; ++ msgs[0].len = 1; ++ msgs[1].len = 1; ++ } ++ ++ buf[0] = reg; ++ ++ /* regs[reg] -> buffer, on write */ ++ if (!read) buf[1] = ak->regs[reg]; ++ ++ /* i2c transfer */ ++ ret = i2c_transfer(ak->i2c_client.adapter, msgs, read ? 2 : 1); ++ if (ret != (read ? 2 : 1)) return ret; /* transfer error */ //@@ error ret < 0, or not ? ++ ++ /* regs[reg] <- buffer, on read */ ++ if (read) ak->regs[reg] = buf[1]; ++ ++ return 0; ++} ++ ++static inline int snd_ak4641_hwsync_read(struct snd_ak4641 *ak, u8 reg) ++{ ++ return snd_ak4641_hwsync(ak, 1, reg); ++} ++ ++static inline int snd_ak4641_hwsync_write(struct snd_ak4641 *ak, u8 reg) ++{ ++ return snd_ak4641_hwsync(ak, 0, reg); ++} ++ ++static int snd_ak4641_hwsync_read_all(struct snd_ak4641 *ak) ++{ ++ u8 reg; ++ for (reg = 0; reg < ARRAY_SIZE(ak->regs); reg++) ++ if (snd_ak4641_hwsync_read(ak, reg) < 0) return -1; ++ return 0; ++} ++ ++static int snd_ak4641_hwsync_write_all(struct snd_ak4641 *ak) ++{ ++ u8 reg; ++ for (reg = 0; reg < ARRAY_SIZE(ak->regs); reg++) ++ if (snd_ak4641_hwsync_write(ak, reg) < 0) return -1; ++ return 0; ++} ++ ++static int snd_ak4641_reg_changed(struct snd_ak4641 *ak, u8 reg) ++{ ++ if ((reg != R_PGA && ak->powered_on) || ++ (reg == R_PGA && (ak->regs[R_PM1] & R_PM1_PMMIC))) ++ return snd_ak4641_hwsync_write(ak, reg); ++ return 0; ++} ++ ++/* end {{ Registers & Cache Ops }}*/ ++ ++ ++static inline void snd_ak4641_lock(struct snd_ak4641 *ak) ++{ ++ down(&ak->sem); ++} ++ ++static inline void snd_ak4641_unlock(struct snd_ak4641 *ak) ++{ ++ up(&ak->sem); ++} ++ ++#define WRITE_MASK(i, val, mask) (((i) & ~(mask)) | ((val) & (mask))) ++ ++ ++/* begin {{ Controls }} */ ++ ++#define INV_RANGE(val, mask) \ ++ (~(val) & (mask)) ++ ++/*-begin----------------------------------------------------------*/ ++static int snd_ak4641_actl_playback_volume_info(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; ++ uinfo->count = 2; ++ uinfo->value.integer.min = 0; ++ uinfo->value.integer.max = 0xff; ++ return 0; ++} ++ ++static int snd_ak4641_actl_playback_volume_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_ak4641 *ak = (struct snd_ak4641 *) kcontrol->private_data; ++ ++ snd_ak4641_lock(ak); ++ ucontrol->value.integer.value[0] = INV_RANGE(ak->regs[R_ATTL], 0xff); ++ ucontrol->value.integer.value[1] = INV_RANGE(ak->regs[R_ATTR], 0xff); ++ snd_ak4641_unlock(ak); ++ return 0; ++} ++ ++static int snd_ak4641_actl_playback_volume_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_ak4641 *ak = (struct snd_ak4641 *) kcontrol->private_data; ++ ++ snd_ak4641_lock(ak); ++ ak->regs[R_ATTL] = INV_RANGE(ucontrol->value.integer.value[0], 0xff); ++ ak->regs[R_ATTR] = INV_RANGE(ucontrol->value.integer.value[1], 0xff); ++ snd_ak4641_reg_changed(ak, R_ATTL); ++ snd_ak4641_reg_changed(ak, R_ATTR); ++ snd_ak4641_unlock(ak); ++ return 0; ++} ++/*-end------------------------------------------------------------*/ ++ ++/*-begin----------------------------------------------------------*/ ++static int snd_ak4641_actl_mic_gain_info(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; ++ uinfo->count = 1; ++ uinfo->value.integer.min = 0; ++ uinfo->value.integer.max = 0x7f; ++ return 0; ++} ++ ++static int snd_ak4641_actl_mic_gain_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_ak4641 *ak = (struct snd_ak4641 *) kcontrol->private_data; ++ ++ ucontrol->value.integer.value[0] = ak->regs[R_PGA]; ++ return 0; ++} ++ ++static int snd_ak4641_actl_mic_gain_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_ak4641 *ak = (struct snd_ak4641 *) kcontrol->private_data; ++ ++ snd_ak4641_lock(ak); ++ ak->regs[R_PGA] = ucontrol->value.integer.value[0]; ++ snd_ak4641_reg_changed(ak, R_PGA); ++ snd_ak4641_unlock(ak); ++ return 0; ++} ++/*-end------------------------------------------------------------*/ ++ ++#define ACTL(ctl_name, _name) \ ++static struct snd_kcontrol_new snd_ak4641_actl_ ## ctl_name = \ ++{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = _name, \ ++ .info = snd_ak4641_actl_ ## ctl_name ## _info, \ ++ .get = snd_ak4641_actl_ ## ctl_name ## _get, .put = snd_ak4641_actl_ ## ctl_name ## _put }; ++ ++ACTL(playback_volume, "Master Playback Volume") ++ACTL(mic_gain, "Mic Capture Gain") ++ ++struct snd_ak4641_uctl_bool { ++ int (*get) (struct snd_ak4641 *uda); ++ int (*set) (struct snd_ak4641 *uda, int on); ++}; ++ ++static int snd_ak4641_actl_bool_info(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN; ++ uinfo->count = 1; ++ return 0; ++} ++ ++static int snd_ak4641_actl_bool_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_ak4641 *ak = (struct snd_ak4641 *) kcontrol->private_data; ++ struct snd_ak4641_uctl_bool *uctl = ++ (struct snd_ak4641_uctl_bool *) kcontrol->private_value; ++ ++ ucontrol->value.integer.value[0] = uctl->get(ak); ++ return 0; ++} ++ ++static int snd_ak4641_actl_bool_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct snd_ak4641 *ak = (struct snd_ak4641 *) kcontrol->private_data; ++ struct snd_ak4641_uctl_bool *uctl = ++ (struct snd_ak4641_uctl_bool *) kcontrol->private_value; ++ ++ return uctl->set(ak, ucontrol->value.integer.value[0]); ++} ++ ++/*-begin----------------------------------------------------------*/ ++static int snd_ak4641_uctl_playback_switch_get(struct snd_ak4641 *ak) ++{ ++ return (ak->regs[R_DAC] & R_DAC_SMUTE) == 0x00; ++} ++ ++static int snd_ak4641_uctl_playback_switch_set(struct snd_ak4641 *ak, int on) ++{ ++ snd_ak4641_lock(ak); ++ ak->regs[R_DAC] = WRITE_MASK(ak->regs[R_DAC], ++ on ? 0x00 : R_DAC_SMUTE, R_DAC_SMUTE); ++ snd_ak4641_reg_changed(ak, R_DAC); ++ snd_ak4641_unlock(ak); ++ return 0; ++} ++/*-end------------------------------------------------------------*/ ++ ++/*-begin----------------------------------------------------------*/ ++static int snd_ak4641_uctl_mic_boost_get(struct snd_ak4641 *ak) ++{ ++ return (ak->regs[R_MIC] & R_MIC_MGAIN) == R_MIC_MGAIN; ++} ++ ++static int snd_ak4641_uctl_mic_boost_set(struct snd_ak4641 *ak, int on) ++{ ++ snd_ak4641_lock(ak); ++ ak->regs[R_MIC] = WRITE_MASK(ak->regs[R_MIC], ++ on ? R_MIC_MGAIN : 0x00, R_MIC_MGAIN); ++ snd_ak4641_reg_changed(ak, R_MIC); ++ snd_ak4641_unlock(ak); ++ return 0; ++} ++/*-end------------------------------------------------------------*/ ++ ++/*-begin----------------------------------------------------------*/ ++static int snd_ak4641_uctl_mono_out_get(struct snd_ak4641 *ak) ++{ ++ printk("mono_out status 0x%8.8x -> 0x%8.8x\n",ak->regs[R_SEL1], ak->regs[R_SEL1] & REG_SEL1_PSMO); ++ return (ak->regs[R_SEL1] & REG_SEL1_PSMO) == REG_SEL1_PSMO; ++} ++ ++static int snd_ak4641_uctl_mono_out_set(struct snd_ak4641 *ak, int on) ++{ ++ printk("phone mic enable called. on=%d\n",on); ++ snd_ak4641_lock(ak); ++ ak->regs[R_PM1] = WRITE_MASK(ak->regs[R_PM1], on ? R_PM1_PMMIC : 0x00, R_PM1_PMMIC); ++ ak->regs[R_PM1] = WRITE_MASK(ak->regs[R_PM1], on ? REG_PWR1_PMMO : 0x00, REG_PWR1_PMMO); ++ snd_ak4641_reg_changed(ak, R_PM1); ++ ++ snd_ak4641_hwsync_write(ak, R_PGA); /* mic PGA gain is reset when PMMIC = 0 */ ++ ++ /* internal mic */ ++ ak->regs[R_MIC] = WRITE_MASK(ak->regs[R_MIC], on ? R_MIC_MPWRI : 0x0, R_MIC_MPWRI); ++ ak->regs[R_MIC] = WRITE_MASK(ak->regs[R_MIC], 0x0, R_MIC_MSEL); ++ snd_ak4641_hwsync_write(ak, R_MIC); ++ ++// ak->regs[REG_BTIF] = WRITE_MASK(ak->regs[REG_BTIF], 0x0, REG_BTIF_DAC2); ++// snd_ak4641_hwsync_write(ak, REG_BTIF); ++ /* */ ++// ak->regs[REG_VOL] = WRITE_MASK(ak->regs[REG_VOL], on ? REG_VOL_ATTM : 0x00, REG_VOL_ATTM); ++// ak->regs[R_SEL1] = WRITE_MASK(ak->regs[R_SEL1], on ? REG_SEL1_MOGN : 0x00, REG_SEL1_MOGN); ++ ak->regs[R_SEL1] = WRITE_MASK(ak->regs[R_SEL1], on ? REG_SEL1_MICM : 0x00, REG_SEL1_MICM); ++ ak->regs[R_SEL1] = WRITE_MASK(ak->regs[R_SEL1], on ? REG_SEL1_PSMO : 0x00, REG_SEL1_PSMO); ++ snd_ak4641_reg_changed(ak, R_SEL1); ++ snd_ak4641_unlock(ak); ++ return 0; ++} ++/*-end------------------------------------------------------------*/ ++ ++#define ACTL_BOOL(ctl_name, _name) \ ++static struct snd_ak4641_uctl_bool snd_ak4641_actl_ ## ctl_name ## _pvalue = \ ++{ .get = snd_ak4641_uctl_ ## ctl_name ## _get, \ ++ .set = snd_ak4641_uctl_ ## ctl_name ## _set }; \ ++static struct snd_kcontrol_new snd_ak4641_actl_ ## ctl_name = \ ++{ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = _name, .info = snd_ak4641_actl_bool_info, \ ++ .get = snd_ak4641_actl_bool_get, .put = snd_ak4641_actl_bool_put, \ ++ .private_value = (unsigned long) &snd_ak4641_actl_ ## ctl_name ## _pvalue }; ++ ++ACTL_BOOL(playback_switch, "Master Playback Switch") ++ACTL_BOOL(mic_boost, "Mic Boost (+20dB)") ++ACTL_BOOL(mono_out, "Phone mic enable") ++ ++static void snd_ak4641_headphone_on(struct snd_ak4641 *ak, int on); ++static void snd_ak4641_speaker_on(struct snd_ak4641 *ak, int on); ++static void snd_ak4641_select_mic(struct snd_ak4641 *ak); ++ ++void snd_ak4641_hp_connected(struct snd_ak4641 *ak, int connected) ++{ ++ snd_ak4641_lock(ak); ++ if (connected != ak->hp_connected) { ++ ak->hp_connected = connected; ++ ++ /* headphone or speaker, on playback */ ++ if (ak->playback_on) { ++ if (connected) { ++ snd_ak4641_headphone_on(ak, 1); ++ snd_ak4641_speaker_on(ak, 0); ++ } else { ++ snd_ak4641_speaker_on(ak, 1); ++ snd_ak4641_headphone_on(ak, 0); ++ } ++ } ++ ++ /* headset or internal mic, on capture */ ++ if (ak->capture_on) ++ snd_ak4641_select_mic(ak); ++ } ++ snd_ak4641_unlock(ak); ++} ++ ++/* end {{ Controls }} */ ++ ++ ++/* begin {{ Headphone Detected Notification }} */ ++ ++static void snd_ak4641_hp_detected_w_fn(void *p) ++{ ++ struct snd_ak4641 *ak = (struct snd_ak4641 *)p; ++ ++ snd_ak4641_hp_connected(ak, ak->hp_detected.detected); ++} ++ ++void snd_ak4641_hp_detected(struct snd_ak4641 *ak, int detected) ++{ ++ if (detected != ak->hp_detected.detected) { ++ ak->hp_detected.detected = detected; ++ queue_work(ak->hp_detected.wq, &ak->hp_detected.w); ++ } ++} ++ ++static int snd_ak4641_hp_detected_init(struct snd_ak4641 *ak) ++{ ++ INIT_WORK(&ak->hp_detected.w, snd_ak4641_hp_detected_w_fn); ++ ak->hp_detected.detected = ak->hp_connected; ++ ak->hp_detected.wq = create_singlethread_workqueue("ak4641"); ++ if (ak->hp_detected.wq) return 0; ++ else return -1; ++} ++ ++static void snd_ak4641_hp_detected_free(struct snd_ak4641 *ak) ++{ ++ destroy_workqueue(ak->hp_detected.wq); ++} ++ ++/* end {{ Headphone Detected Notification }} */ ++ ++ ++/* begin {{ Codec Control }} */ ++ ++static void snd_ak4641_headphone_on(struct snd_ak4641 *ak, int on) ++{ ++ if (on) { ++ ak->regs[R_PM1] = WRITE_MASK(ak->regs[R_PM1], R_PM1_PMLO, R_PM1_PMLO); ++ snd_ak4641_hwsync_write(ak, R_PM1); ++ ak->headphone_out_on(1); ++ ak->regs[R_SEL2] = WRITE_MASK(ak->regs[R_SEL2], ++ R_SEL2_PSLOL | R_SEL2_PSLOR, ++ R_SEL2_PSLOL | R_SEL2_PSLOR); ++ snd_ak4641_hwsync_write(ak, R_SEL2); ++ } else { ++ ak->regs[R_SEL2] = WRITE_MASK(ak->regs[R_SEL2], ++ 0x00, R_SEL2_PSLOL | R_SEL2_PSLOR); ++ snd_ak4641_hwsync_write(ak, R_SEL2); ++ ak->headphone_out_on(0); ++ ak->regs[R_PM1] = WRITE_MASK(ak->regs[R_PM1], 0x00, R_PM1_PMLO); ++ snd_ak4641_hwsync_write(ak, R_PM1); ++ } ++} ++ ++static void snd_ak4641_speaker_on(struct snd_ak4641 *ak, int on) ++{ ++ if (on) { ++ ak->regs[R_PM1] = WRITE_MASK(ak->regs[R_PM1], R_PM1_PMLO, R_PM1_PMLO); ++ snd_ak4641_hwsync_write(ak, R_PM1); ++ ak->speaker_out_on(1); ++ ak->regs[R_SEL2] = WRITE_MASK(ak->regs[R_SEL2], ++ R_SEL2_PSLOL | R_SEL2_PSLOR, ++ R_SEL2_PSLOL | R_SEL2_PSLOR); ++ snd_ak4641_hwsync_write(ak, R_SEL2); ++ } else { ++ ak->regs[R_SEL2] = WRITE_MASK(ak->regs[R_SEL2], ++ 0x00, R_SEL2_PSLOL | R_SEL2_PSLOR); ++ snd_ak4641_hwsync_write(ak, R_SEL2); ++ ak->speaker_out_on(0); ++ ak->regs[R_PM1] = WRITE_MASK(ak->regs[R_PM1], 0x00, R_PM1_PMLO); ++ snd_ak4641_hwsync_write(ak, R_PM1); ++ } ++} ++ ++static inline int snd_ak4641_power_on(struct snd_ak4641 *ak) ++{ ++ ak->reset_pin(1); ++ ak->power_on_chip(1); ++ msleep(1); ++ ak->reset_pin(0); ++ ak->powered_on = 1; ++ return 0; ++} ++ ++static inline int snd_ak4641_power_off(struct snd_ak4641 *ak) ++{ ++ ak->powered_on = 0; ++ ak->power_on_chip(0); ++ return 0; ++} ++ ++static inline void snd_ak4641_headphone_out_on(struct snd_ak4641 *ak, int on) ++{ ++ if (ak->headphone_out_on) ak->headphone_out_on(on); ++} ++ ++static inline void snd_ak4641_speaker_out_on(struct snd_ak4641 *ak, int on) ++{ ++ if (ak->speaker_out_on) ak->speaker_out_on(on); ++} ++ ++static int snd_ak4641_playback_on(struct snd_ak4641 *ak) ++{ ++ if (ak->playback_on) return 0; ++ ++ ak->regs[R_PM2] = WRITE_MASK(ak->regs[R_PM2], ++ R_PM2_PMDAC, R_PM2_MCKPD | R_PM2_PMDAC); ++ ak->regs[R_PM1] = WRITE_MASK(ak->regs[R_PM1], R_PM1_PMLO, R_PM1_PMLO); ++ snd_ak4641_hwsync_write(ak, R_PM2); ++ snd_ak4641_hwsync_write(ak, R_PM1); ++ if (ak->hp_connected) snd_ak4641_headphone_on(ak, 1); ++ else snd_ak4641_speaker_on(ak, 1); ++ ++ ak->playback_on = 1; ++ ++ return 0; ++} ++ ++static int snd_ak4641_playback_off(struct snd_ak4641 *ak) ++{ ++ if (!ak->playback_on) return 0; ++ ++ ak->playback_on = 0; ++ ++ if (ak->hp_connected) snd_ak4641_headphone_on(ak, 0); ++ else snd_ak4641_speaker_on(ak, 0); ++ ak->regs[R_PM1] = WRITE_MASK(ak->regs[R_PM1], 0x00, R_PM1_PMLO); ++ ak->regs[R_PM2] = WRITE_MASK(ak->regs[R_PM2], ++ (!ak->capture_on ? R_PM2_MCKPD : 0x00) | R_PM2_PMDAC, ++ R_PM2_MCKPD | R_PM2_PMDAC); ++ snd_ak4641_hwsync_write(ak, R_PM1); ++ snd_ak4641_hwsync_write(ak, R_PM2); ++ ++ return 0; ++} ++ ++static void snd_ak4641_select_mic(struct snd_ak4641 *ak) ++{ ++ int mic = 0; ++ u8 r_mic; ++ ++ if (ak->hp_connected) { ++ /* check headset mic */ ++ ak->regs[R_MIC] = WRITE_MASK(ak->regs[R_MIC], R_MIC_MPWRE, R_MIC_MPWRE); ++ snd_ak4641_hwsync_write(ak, R_MIC); ++ snd_ak4641_hwsync_read(ak, R_STATUS); ++ mic = (ak->regs[R_STATUS] & R_STATUS_DTMIC) == R_STATUS_DTMIC; ++ ++ printk("htcuniversal_ak4641_select_mic: mic=%d\n",mic); ++ ++ r_mic = WRITE_MASK(ak->regs[R_MIC], ++ R_MIC_MSEL | (ak->capture_on ? R_MIC_MPWRE : 0x00), ++ R_MIC_MSEL | R_MIC_MPWRI | R_MIC_MPWRE); ++ } ++ else ++ r_mic = WRITE_MASK(ak->regs[R_MIC], ++ 0x00 | (ak->capture_on ? R_MIC_MPWRI : 0x00), ++ R_MIC_MSEL | R_MIC_MPWRI | R_MIC_MPWRE); ++ ++ if (r_mic != ak->regs[R_MIC]) { ++ ak->regs[R_MIC] = r_mic; ++ snd_ak4641_hwsync_write(ak, R_MIC); ++ } ++} ++ ++static int snd_ak4641_capture_on(struct snd_ak4641 *ak) ++{ ++ if (ak->capture_on) return 0; ++ ++ if (!ak->playback_on) { ++ ak->regs[R_PM2] = WRITE_MASK(ak->regs[R_PM2], 0x00, R_PM2_MCKPD); ++ snd_ak4641_hwsync_write(ak, R_PM2); ++ } ++ ak->regs[R_PM1] = WRITE_MASK(ak->regs[R_PM1], R_PM1_PMMIC | R_PM1_PMADC, ++ R_PM1_PMMIC | R_PM1_PMADC); ++ snd_ak4641_hwsync_write(ak, R_PM1); ++ snd_ak4641_hwsync_write(ak, R_PGA); /* mic PGA gain is reset when PMMIC = 0 */ ++ ++ ak->capture_on = 1; ++ ++ snd_ak4641_select_mic(ak); ++ ++ msleep(47); /* accounts for ADC init cycle, time enough for fs >= 44.1 kHz */ ++ ++ return 0; ++} ++ ++static int snd_ak4641_capture_off(struct snd_ak4641 *ak) ++{ ++ if (!ak->capture_on) return 0; ++ ++ ak->regs[R_MIC] = WRITE_MASK(ak->regs[R_MIC], ++ 0x00, R_MIC_MPWRI | R_MIC_MPWRE | R_MIC_MSEL); ++ ak->regs[R_PM1] = WRITE_MASK(ak->regs[R_PM1], 0x00, R_PM1_PMMIC | R_PM1_PMADC); ++ snd_ak4641_hwsync_write(ak, R_MIC); ++ snd_ak4641_hwsync_write(ak, R_PM1); ++ if (!ak->playback_on) { ++ ak->regs[R_PM2] = WRITE_MASK(ak->regs[R_PM2], R_PM2_MCKPD, R_PM2_MCKPD); ++ snd_ak4641_hwsync_write(ak, R_PM2); ++ } ++ ++ ak->capture_on = 0; ++ ++ return 0; ++} ++ ++int snd_ak4641_open_stream(struct snd_ak4641 *ak, int stream) ++{ ++ snd_ak4641_lock(ak); ++ if (stream == SNDRV_PCM_STREAM_PLAYBACK) { ++ ak->playback_stream_opened = 1; ++ snd_ak4641_playback_on(ak); ++ } else { ++ ak->capture_stream_opened = 1; ++ snd_ak4641_capture_on(ak); ++ } ++ snd_ak4641_unlock(ak); ++ return 0; ++} ++ ++int snd_ak4641_close_stream(struct snd_ak4641 *ak, int stream) ++{ ++ snd_ak4641_lock(ak); ++ if (stream == SNDRV_PCM_STREAM_PLAYBACK) { ++ ak->playback_stream_opened = 0; ++ snd_ak4641_playback_off(ak); ++ } else { ++ ak->capture_stream_opened = 0; ++ snd_ak4641_capture_off(ak); ++ } ++ snd_ak4641_unlock(ak); ++ return 0; ++} ++ ++static int snd_ak4641_init_regs(struct snd_ak4641 *ak) ++{ ++ snd_ak4641_hwsync_read_all(ak); ++ ++ //@@ MEMO: add some configs ++ ++ ak->regs[R_PM1] = WRITE_MASK(ak->regs[R_PM1], R_PM1_PMVCM, R_PM1_PMVCM); ++ ak->regs[R_DAC] = WRITE_MASK(ak->regs[R_DAC], 0x00, R_DAC_DATTC); ++ snd_ak4641_hwsync_write(ak, R_PM1); ++ snd_ak4641_hwsync_write(ak, R_DAC); ++ ++ return 0; ++} ++ ++int snd_ak4641_suspend(struct snd_ak4641 *ak, pm_message_t state) ++{ ++ snd_ak4641_lock(ak); ++ if (ak->playback_on) snd_ak4641_playback_off(ak); ++ if (ak->capture_on) snd_ak4641_capture_off(ak); ++ snd_ak4641_power_off(ak); ++ snd_ak4641_unlock(ak); ++ return 0; ++} ++ ++int snd_ak4641_resume(struct snd_ak4641 *ak) ++{ ++ snd_ak4641_lock(ak); ++ snd_ak4641_power_on(ak); ++ snd_ak4641_hwsync_write_all(ak); ++ if (ak->playback_stream_opened) snd_ak4641_playback_on(ak); ++ if (ak->capture_stream_opened) snd_ak4641_capture_on(ak); ++ snd_ak4641_unlock(ak); ++ return 0; ++} ++ ++static void snd_ak4641_init_ak(struct snd_ak4641 *ak) ++{ ++ init_MUTEX(&ak->sem); ++ ak->i2c_client.driver = &snd_ak4641_i2c_driver; ++} ++ ++int snd_ak4641_activate(struct snd_ak4641 *ak) ++{ ++ int ret = 0; ++ ++ snd_ak4641_init_ak(ak); ++ snd_ak4641_lock(ak); ++ snd_ak4641_power_on(ak); ++ if ((ret = snd_ak4641_i2c_attach(ak)) < 0) ++ goto failed_i2c_attach; ++ snd_ak4641_init_regs(ak); ++ if ((ret = snd_ak4641_hp_detected_init(ak)) < 0) ++ goto failed_hp_detected_init; ++ snd_ak4641_unlock(ak); ++ return 0; ++ ++ failed_hp_detected_init: ++ snd_ak4641_i2c_detach(ak); ++ failed_i2c_attach: ++ snd_ak4641_power_off(ak); ++ snd_ak4641_unlock(ak); ++ return ret; ++} ++ ++void snd_ak4641_deactivate(struct snd_ak4641 *ak) ++{ ++ snd_ak4641_lock(ak); ++ snd_ak4641_hp_detected_free(ak); ++ snd_ak4641_i2c_detach(ak); ++ snd_ak4641_power_off(ak); ++ snd_ak4641_unlock(ak); ++} ++ ++int snd_ak4641_add_mixer_controls(struct snd_ak4641 *ak, struct snd_card *card) ++{ ++ snd_ak4641_lock(ak); ++ snd_ctl_add(card, snd_ctl_new1(&snd_ak4641_actl_playback_volume, ak)); ++ snd_ctl_add(card, snd_ctl_new1(&snd_ak4641_actl_playback_switch, ak)); ++ snd_ctl_add(card, snd_ctl_new1(&snd_ak4641_actl_mic_gain, ak)); ++ snd_ctl_add(card, snd_ctl_new1(&snd_ak4641_actl_mic_boost, ak)); ++ snd_ctl_add(card, snd_ctl_new1(&snd_ak4641_actl_mono_out, ak)); ++ snd_ak4641_unlock(ak); ++ return 0; ++} ++ ++/* end {{ Codec Control }} */ ++ ++ ++/* begin {{ Module }} */ ++ ++static int __init snd_ak4641_module_on_load(void) ++{ ++ snd_ak4641_i2c_init(); ++ return 0; ++} ++ ++static void __exit snd_ak4641_module_on_unload(void) ++{ ++ snd_ak4641_i2c_free(); ++} ++ ++module_init(snd_ak4641_module_on_load); ++module_exit(snd_ak4641_module_on_unload); ++ ++EXPORT_SYMBOL(snd_ak4641_activate); ++EXPORT_SYMBOL(snd_ak4641_deactivate); ++EXPORT_SYMBOL(snd_ak4641_add_mixer_controls); ++EXPORT_SYMBOL(snd_ak4641_open_stream); ++EXPORT_SYMBOL(snd_ak4641_close_stream); ++EXPORT_SYMBOL(snd_ak4641_suspend); ++EXPORT_SYMBOL(snd_ak4641_resume); ++EXPORT_SYMBOL(snd_ak4641_hp_connected); ++EXPORT_SYMBOL(snd_ak4641_hp_detected); ++ ++MODULE_AUTHOR("Giorgio Padrin"); ++MODULE_DESCRIPTION("Audio support for codec Asahi Kasei AK4641"); ++MODULE_LICENSE("GPL"); ++ ++/* end {{ Module }} */ +Index: linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_ak4641.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_ak4641.h 2008-03-10 16:09:23.000000000 +0000 +@@ -0,0 +1,65 @@ ++/* ++ * Audio support for codec Asahi Kasei AK4641 ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * Copyright (c) 2006 Giorgio Padrin <giorgio@mandarinlogiq.org> ++ */ ++ ++#ifndef __SOUND_AK4641_H ++#define __SOUND_AK4641_H ++ ++#include <linux/i2c.h> ++ ++struct snd_ak4641 { ++ struct semaphore sem; ++ ++ u8 regs[0x14]; /* registers cache */ ++ ++ unsigned int ++ powered_on:1, ++ playback_on:1, ++ playback_stream_opened:1, ++ capture_on:1, ++ capture_stream_opened:1; ++ ++ unsigned int ++ hp_connected:1; ++ ++ /* -- configuration (to fill before activation) -- */ ++ void (*power_on_chip)(int on); ++ void (*reset_pin)(int on); ++ void (*headphone_out_on)(int on); ++ void (*speaker_out_on)(int on); ++ ++ struct i2c_client i2c_client; /* to fill .adapter */ ++ /* ----------------------------------------------- */ ++ ++ struct { ++ int detected; ++ struct workqueue_struct *wq; ++ struct work_struct w; ++ } hp_detected; ++}; ++ ++ ++/* Note: opening, closing, suspending and resuming a stream ++ * require the clocks (MCLK and I2S ones) running ++ */ ++ ++/* don't forget to specify I2C adapter in i2c_client field */ ++int snd_ak4641_activate(struct snd_ak4641 *ak); ++ ++void snd_ak4641_deactivate(struct snd_ak4641 *ak); ++int snd_ak4641_add_mixer_controls(struct snd_ak4641 *ak, struct snd_card *card); ++int snd_ak4641_open_stream(struct snd_ak4641 *ak, int stream); ++int snd_ak4641_close_stream(struct snd_ak4641 *ak, int stream); ++int snd_ak4641_suspend(struct snd_ak4641 *ak, pm_message_t state); ++int snd_ak4641_resume(struct snd_ak4641 *ak); ++ ++void snd_ak4641_hp_connected(struct snd_ak4641 *ak, int connected); /* non atomic context */ ++void snd_ak4641_hp_detected(struct snd_ak4641 *ak, int detected); /* atomic context */ ++ ++#endif /* __SOUND_AK4641_H */ +Index: linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_asic3_leds.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_asic3_leds.c 2008-03-10 16:09:23.000000000 +0000 +@@ -0,0 +1,143 @@ ++/* ++ * LEDs support for the HP iPaq hx4700 ++ * ++ * Copyright (c) 2006 Anton Vorontsov <cbou@mail.ru> ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive for ++ * more details. ++ * ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/platform_device.h> ++#include <linux/leds.h> ++#include <linux/soc/asic3_base.h> ++ ++#include <asm/hardware/ipaq-asic3.h> ++#include <asm/mach-types.h> ++#include <asm/hardware/asic3_leds.h> ++#include <asm/arch/htcuniversal-asic.h> ++ ++//FIXME ++//DEFINE_LED_TRIGGER_SHARED_GLOBAL(htcuniversal_radio_trig); ++//EXPORT_LED_TRIGGER_SHARED(htcuniversal_radio_trig); ++ ++static struct asic3_led htcuniversal_leds[] = { ++ { ++ .led_cdev = { ++ .name = "htcuniversal:red", ++ .default_trigger = "htcuniversal-charging", ++ }, ++ .hw_num = 2, ++ ++ }, ++ { ++ .led_cdev = { ++ .name = "htcuniversal:green", ++ .default_trigger = "htcuniversal-chargefull", ++ }, ++ .hw_num = 1, ++ }, ++ { ++ .led_cdev = { ++ .name = "htcuniversal:wifi-bt", ++ .default_trigger = "htcuniversal-radio", ++ }, ++ .hw_num = 0, ++ }, ++ { ++ .led_cdev = { ++ .name = "htcuniversal:phonebuttons", ++ .default_trigger = "htcuniversal-phonebuttons", ++ }, ++ .hw_num = -1, ++ .gpio_num = ('D'-'A')*16+GPIOD_BL_KEYP_PWR_ON, ++ }, ++ { ++ .led_cdev = { ++ .name = "htcuniversal:vibra", ++ .default_trigger = "htcuniversal-vibra", ++ }, ++ .hw_num = -1, ++ .gpio_num = ('D'-'A')*16+GPIOD_VIBRA_PWR_ON, ++ }, ++ { ++ .led_cdev = { ++ .name = "htcuniversal:flashlight1", ++ .default_trigger = "htcuniversal-flashlight1", ++ }, ++ .hw_num = -1, ++ .gpio_num = ('A'-'A')*16+GPIOA_FLASHLIGHT, ++ }, ++ { ++ .led_cdev = { ++ .name = "htcuniversal:kbdbacklight", ++ .default_trigger = "htcuniversal-kbdbacklight", ++ }, ++ .hw_num = -1, ++ .gpio_num = ('D'-'A')*16+GPIOD_BL_KEYB_PWR_ON, ++ }, ++}; ++ ++void htcuniversal_leds_release(struct device *dev) ++{ ++ return; ++} ++ ++static ++struct asic3_leds_machinfo htcuniversal_leds_machinfo = { ++ .num_leds = ARRAY_SIZE(htcuniversal_leds), ++ .leds = htcuniversal_leds, ++ .asic3_pdev = &htcuniversal_asic3, ++}; ++ ++static ++struct platform_device htcuniversal_leds_pdev = { ++ .name = "asic3-leds", ++ .dev = { ++ .platform_data = &htcuniversal_leds_machinfo, ++ .release = htcuniversal_leds_release, ++ }, ++}; ++ ++static ++int __init htcuniversal_leds_init(void) ++{ ++ int ret; ++ printk("htcuniversal LEDs Driver\n"); ++// led_trigger_register_shared("htcuniversal-radio", &htcuniversal_radio_trig); ++ ++ ret = asic3_leds_register(); ++ if (ret) goto asic3_leds_failed; ++ ++ ret = platform_device_register(&htcuniversal_leds_pdev); ++ if (ret) goto platform_device_failed; ++ ++ goto success; ++ ++platform_device_failed: ++ asic3_leds_unregister(); ++asic3_leds_failed: ++// led_trigger_unregister_shared(htcuniversal_radio_trig); ++ printk("htcuniversal LEDs Driver failed to init"); ++success: ++ return ret; ++} ++ ++static ++void __exit htcuniversal_leds_exit(void) ++{ ++// led_trigger_unregister_shared(htcuniversal_radio_trig); ++ platform_device_unregister(&htcuniversal_leds_pdev); ++ asic3_leds_unregister(); ++ return; ++} ++ ++module_init(htcuniversal_leds_init); ++module_exit(htcuniversal_leds_exit); ++ ++MODULE_AUTHOR("Anton Vorontsov <cbou@mail.ru>"); ++MODULE_DESCRIPTION("htcuniversal LEDs driver"); ++MODULE_LICENSE("GPL"); +Index: linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_bl.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_bl.c 2008-03-10 16:09:23.000000000 +0000 +@@ -0,0 +1,61 @@ ++/* ++ * Use consistent with the GNU GPL is permitted, ++ * provided that this copyright notice is ++ * preserved in its entirety in all copies and derived works. ++ * ++ * Copyright (C) 2006 Paul Sokolosvky ++ * Based on code from older versions of htcuniversal_lcd.c ++ * ++ */ ++ ++#include <linux/types.h> ++#include <linux/platform_device.h> ++#include <asm/arch/hardware.h> /* for pxa-regs.h (__REG) */ ++#include <asm/arch/pxa-regs.h> ++#include <asm/mach-types.h> /* machine_is_htcuniversal */ ++//#include <linux/corgi_bl.h> ++#include <linux/backlight.h> ++#include <linux/err.h> ++ ++#include <asm/arch/htcuniversal-gpio.h> ++#include <asm/arch/htcuniversal-asic.h> ++#include <asm/hardware/ipaq-asic3.h> ++#include <linux/soc/asic3_base.h> ++ ++#define HTCUNIVERSAL_MAX_INTENSITY 0xc7 ++ ++static void htcuniversal_set_bl_intensity(int intensity) ++{ ++ PWM_CTRL1 = 1; /* pre-scaler */ ++ PWM_PWDUTY1 = intensity; /* duty cycle */ ++ PWM_PERVAL1 = HTCUNIVERSAL_MAX_INTENSITY+1; /* period */ ++ ++ if (intensity > 0) { ++ pxa_set_cken(CKEN_PWM1, 1); ++ asic3_set_gpio_out_d(&htcuniversal_asic3.dev, ++ (1<<GPIOD_FL_PWR_ON), (1<<GPIOD_FL_PWR_ON)); ++ } else { ++ pxa_set_cken(CKEN_PWM1, 0); ++ asic3_set_gpio_out_d(&htcuniversal_asic3.dev, ++ (1<<GPIOD_FL_PWR_ON), 0); ++ } ++} ++ ++ ++static struct generic_bl_info htcuniversal_bl_machinfo = { ++ .default_intensity = HTCUNIVERSAL_MAX_INTENSITY / 4, ++ .limit_mask = 0xff, ++ .max_intensity = HTCUNIVERSAL_MAX_INTENSITY, ++ .set_bl_intensity = htcuniversal_set_bl_intensity, ++}; ++ ++struct platform_device htcuniversal_bl = { ++ .name = "corgi-bl", ++ .dev = { ++ .platform_data = &htcuniversal_bl_machinfo, ++ }, ++}; ++ ++MODULE_AUTHOR("Paul Sokolovsky <pmiscml@gmail.com>"); ++MODULE_DESCRIPTION("Backlight driver for HTC Universal"); ++MODULE_LICENSE("GPL"); +Index: linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_bt.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_bt.c 2008-03-10 16:09:23.000000000 +0000 +@@ -0,0 +1,135 @@ ++/* Bluetooth interface driver for TI BRF6150 on HX4700 ++ * ++ * Copyright (c) 2005 SDG Systems, LLC ++ * ++ * 2005-04-21 Todd Blumer Created. ++ */ ++ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/delay.h> ++#include <linux/platform_device.h> ++#include <linux/soc/asic3_base.h> ++ ++#include <asm/hardware.h> ++#include <asm/arch/serial.h> ++#include <asm/hardware/ipaq-asic3.h> ++#include <asm/arch/htcuniversal-gpio.h> ++#include <asm/arch/htcuniversal-asic.h> ++ ++#include "htcuniversal_bt.h" ++ ++static uint use_led=1; ++ ++static void ++htcuniversal_bt_configure( int state ) ++{ ++ int tries; ++ ++ printk( KERN_NOTICE "htcuniversal configure bluetooth: %d\n", state ); ++ switch (state) { ++ ++ case PXA_UART_CFG_PRE_STARTUP: ++ break; ++ ++ case PXA_UART_CFG_POST_STARTUP: ++ /* pre-serial-up hardware configuration */ ++ htcuniversal_egpio_enable(1<<EGPIO5_BT_3V3_ON); ++ mdelay(50); ++ asic3_set_gpio_out_c(&htcuniversal_asic3.dev, 1<<GPIOC_BT_PWR_ON, 1<<GPIOC_BT_PWR_ON); ++ mdelay(10); ++ asic3_set_gpio_out_c(&htcuniversal_asic3.dev, 1<<GPIOC_BT_RESET, 0); ++ mdelay(10); ++ asic3_set_gpio_out_c(&htcuniversal_asic3.dev, 1<<GPIOC_BT_RESET, 1<<GPIOC_BT_RESET); ++ mdelay(10); ++ ++ /* ++ * BRF6150's RTS goes low when firmware is ready ++ * so check for CTS=1 (nCTS=0 -> CTS=1). Typical 150ms ++ */ ++ tries = 0; ++ do { ++ mdelay(10); ++ } while ((BTMSR & MSR_CTS) == 0 && tries++ < 50); ++ if (use_led) { ++// htcuniversal_set_led(2, 16, 16); ++ } ++ break; ++ ++ case PXA_UART_CFG_PRE_SHUTDOWN: ++ htcuniversal_egpio_disable(1<<EGPIO5_BT_3V3_ON ); ++ mdelay(50); ++// htcuniversal_clear_led(2); ++ asic3_set_gpio_out_c(&htcuniversal_asic3.dev, 1<<GPIOC_BT_PWR_ON, 0); ++ break; ++ ++ default: ++ break; ++ } ++} ++ ++ ++static int ++htcuniversal_bt_probe( struct platform_device *dev ) ++{ ++ struct htcuniversal_bt_funcs *funcs = dev->dev.platform_data; ++ ++ /* configure bluetooth UART */ ++ pxa_gpio_mode( GPIO_NR_HTCUNIVERSAL_BT_RXD_MD ); ++ pxa_gpio_mode( GPIO_NR_HTCUNIVERSAL_BT_TXD_MD ); ++ pxa_gpio_mode( GPIO_NR_HTCUNIVERSAL_BT_UART_CTS_MD ); ++ pxa_gpio_mode( GPIO_NR_HTCUNIVERSAL_BT_UART_RTS_MD ); ++ ++ funcs->configure = htcuniversal_bt_configure; ++ ++ /* Make sure the LED is off */ ++// htcuniversal_clear_led(2); ++ ++ return 0; ++} ++ ++static int ++htcuniversal_bt_remove( struct platform_device *dev ) ++{ ++ struct htcuniversal_bt_funcs *funcs = dev->dev.platform_data; ++ ++ funcs->configure = NULL; ++ ++ /* Make sure the LED is off */ ++// htcuniversal_clear_led(2); ++ ++ return 0; ++} ++ ++static struct platform_driver bt_driver = { ++ .driver = { ++ .name = "htcuniversal_bt", ++ }, ++ .probe = htcuniversal_bt_probe, ++ .remove = htcuniversal_bt_remove, ++}; ++ ++module_param(use_led, uint, 0); ++ ++static int __init ++htcuniversal_bt_init( void ) ++{ ++ printk(KERN_NOTICE "htcuniversal Bluetooth Driver\n"); ++ return platform_driver_register( &bt_driver ); ++} ++ ++static void __exit ++htcuniversal_bt_exit( void ) ++{ ++ platform_driver_unregister( &bt_driver ); ++} ++ ++module_init( htcuniversal_bt_init ); ++module_exit( htcuniversal_bt_exit ); ++ ++MODULE_AUTHOR("Todd Blumer, SDG Systems, LLC"); ++MODULE_DESCRIPTION("HTC Universal Bluetooth Support Driver"); ++MODULE_LICENSE("GPL"); ++ ++/* vim600: set noexpandtab sw=8 ts=8 :*/ ++ +Index: linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_bt.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_bt.h 2008-03-10 16:09:23.000000000 +0000 +@@ -0,0 +1,17 @@ ++/* ++ * Bluetooth support file for calling bluetooth configuration functions ++ * ++ * Copyright (c) 2005 SDG Systems, LLC ++ * ++ * 2005-06 Todd Blumer Initial Revision ++ */ ++ ++#ifndef _HTCUNIVERSAL_BT_H ++#define _HTCUNIVERSAL_BT_H ++ ++struct htcuniversal_bt_funcs { ++ void (*configure) ( int state ); ++}; ++ ++ ++#endif +Index: linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_buttons.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_buttons.c 2008-03-10 16:09:23.000000000 +0000 +@@ -0,0 +1,87 @@ ++/* ++ * Buttons driver for HTC Universal ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. ++ * ++ * Copyright (C) 2005 Pawel Kolodziejski ++ * Copyright (C) 2003 Joshua Wise ++ * ++ */ ++ ++#include <linux/input.h> ++#include <linux/input_pda.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/interrupt.h> ++#include <linux/irq.h> ++#include <linux/platform_device.h> ++#include <linux/gpio_keys.h> ++#include <linux/soc/asic3_base.h> ++#include <asm/mach-types.h> ++#include <asm/hardware/asic3_keys.h> ++#include <asm/arch/htcuniversal-gpio.h> ++#include <asm/arch/htcuniversal-asic.h> ++ ++static struct asic3_keys_button asic3_buttons[] = { ++//{KEY_SCREEN, ASIC3_GPIOA_IRQ_BASE+GPIOA_COVER_ROTATE_N, 1, "screen_cover", EV_SW}, ++//{KEY_SWITCHVIDEOMODE, ASIC3_GPIOB_IRQ_BASE+GPIOB_CLAMSHELL_N, 1, "clamshell_rotate", EV_SW}, ++//{KEY_KBDILLUMTOGGLE, ASIC3_GPIOB_IRQ_BASE+GPIOB_NIGHT_SENSOR, 1, "night_sensor", EV_SW}, ++{SW_LID, ASIC3_GPIOA_IRQ_BASE+GPIOA_COVER_ROTATE_N, 1, "screen_cover", EV_SW}, ++{SW_TABLET_MODE, ASIC3_GPIOB_IRQ_BASE+GPIOB_CLAMSHELL_N, 1, "clamshell_rotate", EV_SW}, ++//{SW_NIGHT_SENSOR, ASIC3_GPIOB_IRQ_BASE+GPIOB_NIGHT_SENSOR, 1, "night_sensor", EV_SW}, ++{KEY_F10, ASIC3_GPIOA_IRQ_BASE+GPIOA_BUTTON_BACKLIGHT_N, 1, "backlight_button"}, ++{KEY_RECORD, ASIC3_GPIOA_IRQ_BASE+GPIOA_BUTTON_RECORD_N, 1, "record_button"}, ++{KEY_CAMERA, ASIC3_GPIOA_IRQ_BASE+GPIOA_BUTTON_CAMERA_N, 1, "camera_button"}, ++{KEY_VOLUMEDOWN, ASIC3_GPIOA_IRQ_BASE+GPIOA_VOL_UP_N, 1, "volume_slider_down"}, ++{KEY_VOLUMEUP, ASIC3_GPIOA_IRQ_BASE+GPIOA_VOL_DOWN_N, 1, "volume_slider_up"}, ++{KEY_KPENTER, ASIC3_GPIOD_IRQ_BASE+GPIOD_KEY_OK_N, 1, "select"}, ++{KEY_RIGHT, ASIC3_GPIOD_IRQ_BASE+GPIOD_KEY_RIGHT_N, 1, "right"}, ++{KEY_LEFT, ASIC3_GPIOD_IRQ_BASE+GPIOD_KEY_LEFT_N, 1, "left"}, ++{KEY_DOWN, ASIC3_GPIOD_IRQ_BASE+GPIOD_KEY_DOWN_N, 1, "down"}, ++{KEY_UP, ASIC3_GPIOD_IRQ_BASE+GPIOD_KEY_UP_N, 1, "up"}, ++}; ++ ++static struct asic3_keys_platform_data asic3_keys_data = { ++ .buttons = asic3_buttons, ++ .nbuttons = ARRAY_SIZE(asic3_buttons), ++ .asic3_dev = &htcuniversal_asic3.dev, ++}; ++ ++static struct platform_device htcuniversal_keys_asic3 = { ++ .name = "asic3-keys", ++ .dev = { .platform_data = &asic3_keys_data, } ++}; ++ ++static int __init htcuniversal_buttons_probe(struct platform_device *dev) ++{ ++ platform_device_register(&htcuniversal_keys_asic3); ++ return 0; ++} ++ ++static struct platform_driver htcuniversal_buttons_driver = { ++ .driver = { ++ .name = "htcuniversal_buttons", ++ }, ++ .probe = htcuniversal_buttons_probe, ++}; ++ ++static int __init htcuniversal_buttons_init(void) ++{ ++ if (!machine_is_htcuniversal()) ++ return -ENODEV; ++ ++ return platform_driver_register(&htcuniversal_buttons_driver); ++} ++ ++static void __exit htcuniversal_buttons_exit(void) ++{ ++ platform_driver_unregister(&htcuniversal_buttons_driver); ++} ++ ++module_init(htcuniversal_buttons_init); ++module_exit(htcuniversal_buttons_exit); ++ ++MODULE_AUTHOR ("Joshua Wise, Pawel Kolodziejski, Paul Sokolosvky"); ++MODULE_DESCRIPTION ("Buttons support for HTC Universal"); ++MODULE_LICENSE ("GPL"); +Index: linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_core.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_core.c 2008-03-10 16:09:23.000000000 +0000 +@@ -0,0 +1,226 @@ ++/* Core Hardware driver for Hx4700 (Serial, ASIC3, EGPIOs) ++ * ++ * Copyright (c) 2005 SDG Systems, LLC ++ * ++ * 2005-03-29 Todd Blumer Converted basic structure to support hx4700 ++ * 2005-04-30 Todd Blumer Add IRDA code from H2200 ++ */ ++ ++#include <linux/module.h> ++#include <linux/version.h> ++#include <linux/interrupt.h> ++#include <linux/platform_device.h> ++#include <linux/delay.h> ++#include <linux/pm.h> ++#include <linux/irq.h> ++ ++#include <asm/io.h> ++#include <asm/mach/irq.h> ++#include <asm/arch/pxa-regs.h> ++#include <asm/arch/pxa-pm_ll.h> ++#include <asm/arch/htcuniversal-gpio.h> ++#include <asm/arch/htcuniversal-asic.h> ++ ++#include <linux/soc/asic3_base.h> ++#include <asm/hardware/ipaq-asic3.h> ++ ++volatile u_int16_t *egpios; ++u_int16_t egpio_reg; ++ ++static int htc_bootloader = 0; /* Is the stock HTC bootloader installed? */ ++ ++/* ++ * may make sense to put egpios elsewhere, but they're here now ++ * since they share some of the same address space with the TI WLAN ++ * ++ * EGPIO register is write-only ++ */ ++ ++void ++htcuniversal_egpio_enable( u_int16_t bits ) ++{ ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ ++ egpio_reg |= bits; ++ *egpios = egpio_reg; ++ ++ local_irq_restore(flags); ++} ++EXPORT_SYMBOL_GPL(htcuniversal_egpio_enable); ++ ++void ++htcuniversal_egpio_disable( u_int16_t bits ) ++{ ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ ++ egpio_reg &= ~bits; ++ *egpios = egpio_reg; ++ ++ local_irq_restore(flags); ++} ++EXPORT_SYMBOL_GPL(htcuniversal_egpio_disable); ++ ++#ifdef CONFIG_PM ++ ++//void htcuniversal_ll_pm_init(void); ++ ++static int htcuniversal_suspend(struct platform_device *dev, pm_message_t state) ++{ ++ /* Turn off external clocks here, because htcuniversal_power and asic3_mmc ++ * scared to do so to not hurt each other. (-5 mA) */ ++ ++ ++ /* 0x20c2 is HTC clock value ++ * CLOCK_CDEX_SOURCE 2 ++ * CLOCK_CDEX_SPI 0 ++ * CLOCK_CDEX_OWM 0 ++ * ++ * CLOCK_CDEX_PWM0 0 ++ * CLOCK_CDEX_PWM1 0 ++ * CLOCK_CDEX_LED0 1 ++ * CLOCK_CDEX_LED1 1 ++ * ++ * CLOCK_CDEX_LED2 0 ++ * CLOCK_CDEX_SD_HOST 0 ++ * CLOCK_CDEX_SD_BUS 0 ++ * CLOCK_CDEX_SMBUS 0 ++ * ++ * CLOCK_CDEX_CONTROL_CX 0 ++ * CLOCK_CDEX_EX0 1 ++ * CLOCK_CDEX_EX1 0 ++ * */ ++ asic3_set_clock_cdex(&htcuniversal_asic3.dev, 0xffff, CLOCK_CDEX_SOURCE1 ++ |CLOCK_CDEX_LED0 ++ |CLOCK_CDEX_LED1 ++ |CLOCK_CDEX_LED2 ++ |CLOCK_CDEX_EX0 ++ |CLOCK_CDEX_EX1); ++ ++ *egpios = 0; /* turn off all egpio power */ ++ ++ /* Wake up enable. */ ++ PWER = PWER_GPIO0 ++ | PWER_GPIO1 /* reset */ ++ | PWER_GPIO9 /* USB */ ++ | PWER_GPIO10 /* AC on USB */ ++ | PWER_GPIO14 /* ASIC3 mux */ ++ | PWER_RTC; ++ /* Wake up on falling edge. */ ++ PFER = PWER_GPIO0 ++ | PWER_GPIO1 ++ | PWER_GPIO9 ++ | PWER_GPIO10 ++ | PWER_GPIO14; ++ ++ /* Wake up on rising edge. */ ++ PRER = PWER_GPIO0 ++ | PWER_GPIO1 ++ | PWER_GPIO9 ++ | PWER_GPIO10; ++ /* 3.6864 MHz oscillator power-down enable */ ++ PCFR = PCFR_OPDE | PCFR_PI2CEN | PCFR_GPROD | PCFR_GPR_EN; ++ ++ PGSR0 = 0x09088004; ++ PGSR1 = 0x00020002; ++ PGSR2 = 0x8001c000; ++ PGSR3 = 0x00106284; ++ ++ PSLR = 0xcc000000; ++ ++#if 0 ++ /* ++ * If we're using bootldr and not the stock HTC bootloader, ++ * we want to wake up periodically to see if the charge is full while ++ * it is suspended. We do this with the OS timer 4 in the pxa270. ++ */ ++ if (!htc_bootloader) { ++ OMCR4 = 0x4b; /* Periodic, self-resetting, 1-second timer */ ++ OSMR4 = 5; /* Wake up bootldr after x seconds so it can ++ figure out what to do with the LEDs. */ ++ OIER |= 0x10; /* Enable interrupt source for Timer 4 */ ++ OSCR4 = 0; /* This starts the timer */ ++ } ++#endif ++ ++ asic3_set_extcf_select(&htcuniversal_asic3.dev, ASIC3_EXTCF_OWM_EN, 0); ++ ++ return 0; ++} ++ ++static int htcuniversal_resume(struct platform_device *dev) ++{ ++ htcuniversal_egpio_enable(0); ++ ++ return 0; ++} ++#else ++# define htcuniversal_suspend NULL ++# define htcuniversal_resume NULL ++#endif ++ ++static int ++htcuniversal_core_probe( struct platform_device *dev ) ++{ ++ ++ printk( KERN_NOTICE "HTC Universal Core Hardware Driver\n" ); ++ ++ egpios = (volatile u_int16_t *)ioremap_nocache(HTCUNIVERSAL_EGPIO_BASE, sizeof *egpios ); ++ if (!egpios) ++ return -ENODEV; ++ else ++ printk( KERN_NOTICE "HTC Universal Core: egpio at phy=0x%8.8x is at virt=0x%p\n", ++ HTCUNIVERSAL_EGPIO_BASE, egpios ); ++ ++ printk("Using stock HTC first stage bootloader\n"); ++ htc_bootloader = 1; ++ ++// htcuniversal_ll_pm_init(); ++ ++ return 0; ++} ++ ++static int ++htcuniversal_core_remove( struct platform_device *dev ) ++{ ++ ++ if (egpios != NULL) ++ iounmap( (void *)egpios ); ++ ++ return 0; ++} ++ ++static struct platform_driver htcuniversal_core_driver = { ++ .driver = { ++ .name = "htcuniversal_core", ++ }, ++ .probe = htcuniversal_core_probe, ++ .remove = htcuniversal_core_remove, ++ .suspend = htcuniversal_suspend, ++ .resume = htcuniversal_resume, ++}; ++ ++static int __init ++htcuniversal_core_init( void ) ++{ ++ return platform_driver_register( &htcuniversal_core_driver ); ++} ++ ++ ++static void __exit ++htcuniversal_core_exit( void ) ++{ ++ platform_driver_unregister( &htcuniversal_core_driver ); ++} ++ ++module_init( htcuniversal_core_init ); ++module_exit( htcuniversal_core_exit ); ++ ++MODULE_AUTHOR("Todd Blumer, SDG Systems, LLC"); ++MODULE_DESCRIPTION("HTC Universal Core Hardware Driver"); ++MODULE_LICENSE("GPL"); ++ ++/* vim600: set noexpandtab sw=8 ts=8 :*/ +Index: linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_lcd.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_lcd.c 2008-03-10 16:09:23.000000000 +0000 +@@ -0,0 +1,212 @@ ++/* ++ * Use consistent with the GNU GPL is permitted, ++ * provided that this copyright notice is ++ * preserved in its entirety in all copies and derived works. ++ * ++ * History: ++ * ++ * 2004-03-01 Eddi De Pieri Adapted for htcuniversal using h3900_lcd.c ++ * 2004 Shawn Anderson Lcd hacking on htcuniversal ++ * see h3900_lcd.c for more history. ++ * ++ */ ++ ++#include <linux/types.h> ++#include <asm/arch/hardware.h> /* for pxa-regs.h (__REG) */ ++#include <linux/platform_device.h> ++#include <asm/arch/pxa-regs.h> /* LCCR[0,1,2,3]* */ ++#include <asm/arch/bitfield.h> /* for pxa-regs.h (Fld, etc) */ ++#include <asm/arch/pxafb.h> /* pxafb_mach_info, set_pxa_fb_info */ ++#include <asm/mach-types.h> /* machine_is_htcuniversal */ ++#include <linux/lcd.h> /* lcd_device */ ++#include <linux/err.h> ++#include <linux/delay.h> ++ ++#include <asm/arch/htcuniversal-gpio.h> ++#include <asm/arch/htcuniversal-asic.h> ++#include <asm/hardware/ipaq-asic3.h> ++#include <linux/soc/asic3_base.h> ++ ++static int saved_lcdpower=-1; ++ ++static int powerup_lcd(void) ++{ ++ printk( KERN_INFO "htcuniversal powerup_lcd: called\n"); ++ ++ asic3_set_gpio_out_c(&htcuniversal_asic3.dev, 1<<GPIOC_LCD_PWR1_ON, 0); ++ asic3_set_gpio_out_c(&htcuniversal_asic3.dev, 1<<GPIOC_LCD_PWR2_ON, 0); ++ asic3_set_gpio_out_b(&htcuniversal_asic3.dev, 1<<GPIOB_LCD_PWR3_ON, 0); ++ asic3_set_gpio_out_d(&htcuniversal_asic3.dev, 1<<GPIOD_LCD_PWR4_ON, 0); ++ asic3_set_gpio_out_a(&htcuniversal_asic3.dev, 1<<GPIOA_LCD_PWR5_ON, 0); ++#if 1 ++ LCCR4|=LCCR4_PCDDIV; ++#endif ++ pxa_set_cken(CKEN_LCD, 0); ++ ++ mdelay(100); ++ asic3_set_gpio_out_a(&htcuniversal_asic3.dev, 1<<GPIOA_LCD_PWR5_ON, 1<<GPIOA_LCD_PWR5_ON); ++ mdelay(5); ++ asic3_set_gpio_out_b(&htcuniversal_asic3.dev, 1<<GPIOB_LCD_PWR3_ON, 1<<GPIOB_LCD_PWR3_ON); ++ mdelay(2); ++ asic3_set_gpio_out_c(&htcuniversal_asic3.dev, 1<<GPIOC_LCD_PWR1_ON, 1<<GPIOC_LCD_PWR1_ON); ++ mdelay(2); ++ asic3_set_gpio_out_c(&htcuniversal_asic3.dev, 1<<GPIOC_LCD_PWR2_ON, 1<<GPIOC_LCD_PWR2_ON); ++ mdelay(20); ++ asic3_set_gpio_out_d(&htcuniversal_asic3.dev, 1<<GPIOD_LCD_PWR4_ON, 1<<GPIOD_LCD_PWR4_ON); ++ mdelay(1); ++ pxa_set_cken(CKEN_LCD, 1); ++ ++ SET_HTCUNIVERSAL_GPIO(LCD1,1); ++ SET_HTCUNIVERSAL_GPIO(LCD2,1); ++ return 0; ++} ++ ++static int powerdown_lcd(void) ++{ ++ printk( KERN_INFO "htcuniversal powerdown_lcd: called\n"); ++ ++#if 1 ++ asic3_set_gpio_out_c(&htcuniversal_asic3.dev, 1<<GPIOC_LCD_PWR2_ON, 0); ++ mdelay(100); ++ asic3_set_gpio_out_d(&htcuniversal_asic3.dev, 1<<GPIOD_LCD_PWR4_ON, 0); ++ mdelay(10); ++ asic3_set_gpio_out_a(&htcuniversal_asic3.dev, 1<<GPIOA_LCD_PWR5_ON, 0); ++ mdelay(1); ++ asic3_set_gpio_out_b(&htcuniversal_asic3.dev, 1<<GPIOB_LCD_PWR3_ON, 0); ++ mdelay(1); ++ asic3_set_gpio_out_c(&htcuniversal_asic3.dev, 1<<GPIOC_LCD_PWR1_ON, 0); ++ pxa_set_cken(CKEN_LCD, 0); ++ ++ SET_HTCUNIVERSAL_GPIO(LCD1,0); ++ SET_HTCUNIVERSAL_GPIO(LCD2,0); ++#else ++ pxa_set_cken(CKEN_LCD, 0); ++ ++ SET_HTCUNIVERSAL_GPIO(LCD1,0); ++ SET_HTCUNIVERSAL_GPIO(LCD2,0); ++ ++ asic3_set_gpio_out_c(&htcuniversal_asic3.dev, 1<<GPIOC_LCD_PWR2_ON, 0); ++ mdelay(100); ++ asic3_set_gpio_out_d(&htcuniversal_asic3.dev, 1<<GPIOD_LCD_PWR4_ON, 0); ++ mdelay(10); ++ asic3_set_gpio_out_a(&htcuniversal_asic3.dev, 1<<GPIOA_LCD_PWR5_ON, 0); ++ mdelay(1); ++ asic3_set_gpio_out_b(&htcuniversal_asic3.dev, 1<<GPIOB_LCD_PWR3_ON, 0); ++ mdelay(1); ++ asic3_set_gpio_out_c(&htcuniversal_asic3.dev, 1<<GPIOC_LCD_PWR1_ON, 0); ++#endif ++ return 0; ++} ++ ++static int htcuniversal_lcd_set_power(struct lcd_device *lm, int power) ++{ ++ /* Enable or disable power to the LCD (0: on; 4: off) */ ++ ++ if ( power < 1 ) { ++ ++ powerup_lcd(); ++ ++ } else { ++ ++ powerdown_lcd(); ++ ++ } ++ ++ saved_lcdpower=power; ++ ++ return 0; ++} ++ ++static int htcuniversal_lcd_get_power(struct lcd_device *lm) ++{ ++ /* Get the LCD panel power status (0: full on, 1..3: controller ++ * power on, flat panel power off, 4: full off) */ ++ ++ if (saved_lcdpower == -1) ++ { ++ htcuniversal_lcd_set_power(lm, 4); ++ saved_lcdpower=4; ++ } ++ ++ return saved_lcdpower; ++} ++ ++static struct lcd_ops htcuniversal_lcd_properties = ++{ ++ .get_power = htcuniversal_lcd_get_power, ++ .set_power = htcuniversal_lcd_set_power, ++}; ++ ++static struct lcd_device *htcuniversal_lcd_dev; ++ ++static int htcuniversal_lcd_probe(struct platform_device * dev) ++{ ++ htcuniversal_lcd_dev = lcd_device_register("pxa2xx-fb", &dev->dev, NULL, ++ &htcuniversal_lcd_properties); ++ if (IS_ERR(htcuniversal_lcd_dev)) { ++ printk("htcuniversal_lcd_probe: error registering devices\n"); ++ return -1; ++ } ++ ++ return 0; ++} ++ ++static int htcuniversal_lcd_remove(struct platform_device * dev) ++{ ++ htcuniversal_lcd_set_power(htcuniversal_lcd_dev, 4); ++ lcd_device_unregister(htcuniversal_lcd_dev); ++ ++ return 0; ++} ++ ++static int htcuniversal_lcd_suspend(struct platform_device * dev, pm_message_t state) ++{ ++// printk("htcuniversal_lcd_suspend: called.\n"); ++ htcuniversal_lcd_set_power(htcuniversal_lcd_dev, 4); ++ return 0; ++} ++ ++static int htcuniversal_lcd_resume(struct platform_device * dev) ++{ ++// printk("htcuniversal_lcd_resume: called.\n"); ++ ++ /* */ ++#if 1 ++ LCCR4|=LCCR4_PCDDIV; ++#endif ++ ++ htcuniversal_lcd_set_power(htcuniversal_lcd_dev, 0); ++ return 0; ++} ++ ++static struct platform_driver htcuniversal_lcd_driver = { ++ .driver = { ++ .name = "htcuniversal_lcd", ++ }, ++ .probe = htcuniversal_lcd_probe, ++ .remove = htcuniversal_lcd_remove, ++ .suspend = htcuniversal_lcd_suspend, ++ .resume = htcuniversal_lcd_resume, ++}; ++ ++static int htcuniversal_lcd_init(void) ++{ ++ if (!machine_is_htcuniversal()) ++ return -ENODEV; ++ ++ return platform_driver_register(&htcuniversal_lcd_driver); ++} ++ ++static void htcuniversal_lcd_exit(void) ++{ ++ lcd_device_unregister(htcuniversal_lcd_dev); ++ platform_driver_unregister(&htcuniversal_lcd_driver); ++} ++ ++module_init(htcuniversal_lcd_init); ++module_exit(htcuniversal_lcd_exit); ++ ++MODULE_AUTHOR("xanadux.org"); ++MODULE_DESCRIPTION("Framebuffer driver for HTC Universal"); ++MODULE_LICENSE("GPL"); ++ +Index: linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_phone.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_phone.c 2008-03-10 16:09:23.000000000 +0000 +@@ -0,0 +1,167 @@ ++ ++/* Phone interface driver for Qualcomm MSM6250 on HTC Universal ++ * ++ * Copyright (c) 2005 SDG Systems, LLC ++ * ++ * 2005-04-21 Todd Blumer Created. ++ */ ++ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/delay.h> ++#include <linux/platform_device.h> ++#include <linux/soc/asic3_base.h> ++ ++#include <asm/hardware.h> ++#include <asm/arch/serial.h> ++#include <asm/hardware/ipaq-asic3.h> ++#include <asm/arch/htcuniversal-gpio.h> ++#include <asm/arch/htcuniversal-asic.h> ++ ++#include "htcuniversal_phone.h" ++ ++static void phone_reset(void) ++{ ++ asic3_set_gpio_out_b(&htcuniversal_asic3.dev, 1<<GPIOB_BB_RESET2, 0); ++ ++ SET_HTCUNIVERSAL_GPIO(PHONE_RESET,0); ++ asic3_set_gpio_out_d(&htcuniversal_asic3.dev, 1<<GPIOD_BB_RESET1, 0); ++ mdelay(1); ++ asic3_set_gpio_out_d(&htcuniversal_asic3.dev, 1<<GPIOD_BB_RESET1, 1<<GPIOD_BB_RESET1); ++ mdelay(20); ++ SET_HTCUNIVERSAL_GPIO(PHONE_RESET,1); ++ mdelay(200); ++ asic3_set_gpio_out_d(&htcuniversal_asic3.dev, 1<<GPIOD_BB_RESET1, 0); ++} ++ ++static void phone_off(void) ++{ ++ asic3_set_gpio_out_d(&htcuniversal_asic3.dev, 1<<GPIOD_BB_RESET1, 1<<GPIOD_BB_RESET1); ++ mdelay(2000); ++ asic3_set_gpio_out_d(&htcuniversal_asic3.dev, 1<<GPIOD_BB_RESET1, 0); ++ ++ asic3_set_gpio_out_b(&htcuniversal_asic3.dev, 1<<GPIOB_BB_RESET2, 1<<GPIOB_BB_RESET2); ++ SET_HTCUNIVERSAL_GPIO(PHONE_OFF,0); ++} ++ ++static void ++htcuniversal_phone_configure( int state ) ++{ ++ int tries; ++ unsigned short statusb; ++ ++ printk( KERN_NOTICE "htcuniversal configure phone: %d\n", state ); ++ switch (state) { ++ ++ case PXA_UART_CFG_PRE_STARTUP: ++ break; ++ ++ case PXA_UART_CFG_POST_STARTUP: ++ /* pre-serial-up hardware configuration */ ++ ++ SET_HTCUNIVERSAL_GPIO(PHONE_START,0); /* "bootloader" */ ++ SET_HTCUNIVERSAL_GPIO(PHONE_UNKNOWN,0); /* not used */ ++ SET_HTCUNIVERSAL_GPIO(PHONE_OFF,0); /* PHONE_OFF */ ++ ++ phone_reset(); ++ ++ SET_HTCUNIVERSAL_GPIO(PHONE_START,1); /* phone */ ++ ++ phone_reset(); ++ ++ asic3_set_gpio_dir_b(&htcuniversal_asic3.dev, 1<<GPIOB_BB_READY, 0); ++ asic3_set_gpio_dir_b(&htcuniversal_asic3.dev, 1<<GPIOB_BB_UNKNOWN3, 0); ++ ++ /* ++ */ ++ tries = 0; ++ do { ++ mdelay(10); ++ statusb = asic3_get_gpio_status_b( &htcuniversal_asic3.dev ); ++ } while ( (statusb & (1<<GPIOB_UMTS_DCD)) == 0 && tries++ < 200); ++ ++ printk("UMTS_DCD tries=%d of 200\n",tries); ++ ++ tries = 0; ++ do { ++ SET_HTCUNIVERSAL_GPIO(PHONE_OFF,1); ++ mdelay(10); ++ SET_HTCUNIVERSAL_GPIO(PHONE_OFF,0); ++ mdelay(20); ++ statusb = asic3_get_gpio_status_b( &htcuniversal_asic3.dev ); ++ } while ( (statusb & (1<<GPIOB_BB_READY)) == 0 && tries++ < 200); ++ ++ printk("BB_READY tries=%d of 200\n",tries); ++ ++ break; ++ ++ case PXA_UART_CFG_PRE_SHUTDOWN: ++ ++ phone_off(); ++ ++ break; ++ ++ default: ++ break; ++ } ++} ++ ++ ++static int ++htcuniversal_phone_probe( struct platform_device *dev ) ++{ ++ struct htcuniversal_phone_funcs *funcs = dev->dev.platform_data; ++ ++ /* configure phone UART */ ++ pxa_gpio_mode( GPIO_NR_HTCUNIVERSAL_PHONE_RXD_MD ); ++ pxa_gpio_mode( GPIO_NR_HTCUNIVERSAL_PHONE_TXD_MD ); ++ pxa_gpio_mode( GPIO_NR_HTCUNIVERSAL_PHONE_UART_CTS_MD ); ++ pxa_gpio_mode( GPIO_NR_HTCUNIVERSAL_PHONE_UART_RTS_MD ); ++ ++ funcs->configure = htcuniversal_phone_configure; ++ ++ return 0; ++} ++ ++static int ++htcuniversal_phone_remove( struct platform_device *dev ) ++{ ++ struct htcuniversal_phone_funcs *funcs = dev->dev.platform_data; ++ ++ funcs->configure = NULL; ++ ++ asic3_set_gpio_dir_b(&htcuniversal_asic3.dev, 1<<GPIOB_BB_READY, 1<<GPIOB_BB_READY); ++ asic3_set_gpio_dir_b(&htcuniversal_asic3.dev, 1<<GPIOB_BB_UNKNOWN3, 1<<GPIOB_BB_UNKNOWN3); ++ ++ return 0; ++} ++ ++static struct platform_driver phone_driver = { ++ .driver = { ++ .name = "htcuniversal_phone", ++ }, ++ .probe = htcuniversal_phone_probe, ++ .remove = htcuniversal_phone_remove, ++}; ++ ++static int __init ++htcuniversal_phone_init( void ) ++{ ++ printk(KERN_NOTICE "htcuniversal Phone Driver\n"); ++ return platform_driver_register( &phone_driver ); ++} ++ ++static void __exit ++htcuniversal_phone_exit( void ) ++{ ++ platform_driver_unregister( &phone_driver ); ++} ++ ++module_init( htcuniversal_phone_init ); ++module_exit( htcuniversal_phone_exit ); ++ ++MODULE_AUTHOR("Todd Blumer, SDG Systems, LLC"); ++MODULE_DESCRIPTION("HTC Universal Phone Support Driver"); ++MODULE_LICENSE("GPL"); ++ ++/* vim600: set noexpandtab sw=8 ts=8 :*/ +Index: linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_phone.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_phone.h 2008-03-10 16:09:23.000000000 +0000 +@@ -0,0 +1,16 @@ ++/* ++ * Bluetooth support file for calling bluetooth configuration functions ++ * ++ * Copyright (c) 2005 SDG Systems, LLC ++ * ++ * 2005-06 Todd Blumer Initial Revision ++ */ ++ ++#ifndef _HTCUNIVERSAL_PHONE_H ++#define _HTCUNIVERSAL_PHONE_H ++ ++struct htcuniversal_phone_funcs { ++ void (*configure) ( int state ); ++}; ++ ++#endif +Index: linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_pm.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_pm.c 2008-03-10 16:09:23.000000000 +0000 +@@ -0,0 +1,69 @@ ++/* ++ * MyPal 716 power management support for the original HTC IPL in DoC G3 ++ * ++ * Use consistent with the GNU GPL is permitted, provided that this ++ * copyright notice is preserved in its entirety in all copies and ++ * derived works. ++ * ++ * Copyright (C) 2005 Pawel Kolodziejski ++ * ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/device.h> ++#include <linux/pm.h> ++ ++#include <asm/mach-types.h> ++#include <asm/hardware.h> ++#include <asm/arch/pxa-regs.h> ++#include <asm/arch/pxa-pm_ll.h> ++ ++#ifdef CONFIG_PM ++ ++static u32 *addr_a0040000; ++static u32 *addr_a0040004; ++static u32 *addr_a0040008; ++static u32 *addr_a004000c; ++ ++static u32 save_a0040000; ++static u32 save_a0040004; ++static u32 save_a0040008; ++static u32 save_a004000c; ++ ++static void htcuniversal_pxa_ll_pm_suspend(unsigned long resume_addr) ++{ ++ save_a0040000 = *addr_a0040000; ++ save_a0040004 = *addr_a0040004; ++ save_a0040008 = *addr_a0040008; ++ save_a004000c = *addr_a004000c; ++ ++ /* jump to PSPR */ ++ *addr_a0040000 = 0xe3a00101; // mov r0, #0x40000000 ++ *addr_a0040004 = 0xe380060f; // orr r0, r0, #0x0f000000 ++ *addr_a0040008 = 0xe3800008; // orr r0, r0, #8 ++ *addr_a004000c = 0xe590f000; // ldr pc, [r0] ++} ++ ++static void htcuniversal_pxa_ll_pm_resume(void) ++{ ++ *addr_a0040000 = save_a0040000; ++ *addr_a0040004 = save_a0040004; ++ *addr_a0040008 = save_a0040008; ++ *addr_a004000c = save_a004000c; ++} ++ ++static struct pxa_ll_pm_ops htcuniversal_ll_pm_ops = { ++ .suspend = htcuniversal_pxa_ll_pm_suspend, ++ .resume = htcuniversal_pxa_ll_pm_resume, ++}; ++ ++void htcuniversal_ll_pm_init(void) { ++ addr_a0040000 = phys_to_virt(0xa0040000); ++ addr_a0040004 = phys_to_virt(0xa0040004); ++ addr_a0040008 = phys_to_virt(0xa0040008); ++ addr_a004000c = phys_to_virt(0xa004000c); ++ ++ pxa_pm_set_ll_ops(&htcuniversal_ll_pm_ops); ++} ++#endif /* CONFIG_PM */ +Index: linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_power2.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_power2.c 2008-03-10 16:09:23.000000000 +0000 +@@ -0,0 +1,97 @@ ++/* ++ * pda_power driver for HTC Universal ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or (at ++ * your option) any later version. ++ * ++ */ ++ ++#include <linux/platform_device.h> ++#include <linux/module.h> ++#include <linux/pda_power.h> ++#include <linux/soc/asic3_base.h> ++ ++#include <asm/mach-types.h> ++#include <asm/hardware.h> ++#include <asm/arch/htcuniversal-gpio.h> ++#include <asm/arch/htcuniversal-asic.h> ++ ++static void charge_on(int flags) ++{ ++ asic3_set_gpio_out_b(&htcuniversal_asic3.dev, 1<<GPIOB_CHARGE_EN, 0); ++} ++ ++static int ac_on(void) ++{ ++ return (GET_HTCUNIVERSAL_GPIO(POWER_DET) == 0); ++} ++ ++static int usb_on(void) ++{ ++ return (GET_HTCUNIVERSAL_GPIO(USB_DET) == 0); ++} ++ ++static char *supplicants[] = { ++ "ds2760-battery.0", "backup-battery" ++}; ++ ++static struct pda_power_pdata power_pdata = { ++ .is_ac_online = ac_on, ++ .is_usb_online = usb_on, ++ .set_charge = charge_on, ++ .supplied_to = supplicants, ++ .num_supplicants = ARRAY_SIZE(supplicants), ++}; ++ ++static struct resource power_resources[] = { ++ [0] = { ++ .name = "ac", ++ .start = HTCUNIVERSAL_IRQ(POWER_DET), ++ .end = HTCUNIVERSAL_IRQ(POWER_DET), ++ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_LOWEDGE, ++ }, ++ [1] = { ++ .name = "usb", ++ .start = HTCUNIVERSAL_IRQ(USB_DET), ++ .end = HTCUNIVERSAL_IRQ(USB_DET), ++ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE | IORESOURCE_IRQ_LOWEDGE, ++ }, ++}; ++ ++static void dev_release(struct device *dev) ++{ ++ return; ++} ++ ++static struct platform_device power_dev = ++{ ++ .name = "pda-power", ++ .id = -1, ++ .resource = power_resources, ++ .num_resources = ARRAY_SIZE(power_resources), ++ .dev = ++ { ++ .platform_data = &power_pdata, ++ .release = dev_release, ++ }, ++}; ++ ++static int htcuniversal_power_init(void) ++{ ++ return platform_device_register(&power_dev); ++} ++ ++static void htcuniversal_power_exit(void) ++{ ++ platform_device_unregister(&power_dev); ++ ++ return; ++} ++ ++module_init(htcuniversal_power_init); ++module_exit(htcuniversal_power_exit); ++ ++MODULE_DESCRIPTION("Power driver for HTC Universal"); ++MODULE_LICENSE("GPL"); +Index: linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_ts2.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_ts2.c 2008-03-10 16:09:23.000000000 +0000 +@@ -0,0 +1,490 @@ ++/* Touch screen driver for the TI something-or-other ++ * ++ * Copyright © 2005 SDG Systems, LLC ++ * ++ * Based on code that was based on the SAMCOP driver. ++ * Copyright © 2003, 2004 Compaq Computer Corporation. ++ * ++ * Use consistent with the GNU GPL is permitted, ++ * provided that this copyright notice is ++ * preserved in its entirety in all copies and derived works. ++ * ++ * COMPAQ COMPUTER CORPORATION MAKES NO WARRANTIES, EXPRESSED OR IMPLIED, ++ * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS ++ * FITNESS FOR ANY PARTICULAR PURPOSE. ++ * ++ * Author: Keith Packard <keith.packard@hp.com> ++ * May 2003 ++ * ++ * Updates: ++ * ++ * 2004-02-11 Michael Opdenacker Renamed names from samcop to shamcop, ++ * Goal:support HAMCOP and SAMCOP. ++ * 2004-02-14 Michael Opdenacker Temporary fix for device id handling ++ * ++ * 2005-02-18 Aric Blumer Converted basic structure to support hx4700 ++ * ++ * 2005-06-07 Aric Blumer Added tssim device handling so we can ++ * hook in the fbvncserver. ++ */ ++ ++#include <linux/module.h> ++#include <linux/version.h> ++ ++#include <linux/init.h> ++#include <linux/fs.h> ++#include <linux/cdev.h> ++#include <linux/interrupt.h> ++#include <linux/sched.h> ++#include <linux/pm.h> ++#include <linux/delay.h> ++#include <linux/input.h> ++#include <linux/platform_device.h> ++#include <linux/irq.h> ++ ++#include <asm/arch/hardware.h> ++#include <asm/mach/irq.h> ++#include <asm/io.h> ++ ++/* remove me */ ++#include <asm/arch/pxa-regs.h> ++#include <asm/arch/htcuniversal-gpio.h> ++#include <asm/arch/htcuniversal-asic.h> ++#include <asm/mach-types.h> ++ ++#include <asm/hardware/ipaq-asic3.h> ++#include <linux/soc/asic3_base.h> ++ ++ ++#include "tsc2046_ts.h" ++ ++enum touchscreen_state { ++ STATE_WAIT_FOR_TOUCH, /* Waiting for a PEN interrupt */ ++ STATE_SAMPLING /* Actively sampling ADC */ ++}; ++ ++struct touchscreen_data { ++ enum touchscreen_state state; ++ struct timer_list timer; ++ int irq; ++ struct input_dev *input; ++ /* */ ++ int port; ++ int clock; ++ int pwrbit_X; ++ int pwrbit_Y; ++ int (*pen_down)(void); ++}; ++ ++static unsigned long poll_sample_time = 10; /* Sample every 10 milliseconds */ ++ ++static struct touchscreen_data *ts_data; ++ ++static int irqblock; ++ ++module_param(poll_sample_time, ulong, 0644); ++MODULE_PARM_DESC(poll_sample_time, "Poll sample time"); ++ ++static inline void ++report_touchpanel(struct touchscreen_data *ts, int pressure, int x, int y) ++{ ++ input_report_abs(ts->input, ABS_PRESSURE, pressure); ++ input_report_abs(ts->input, ABS_X, x); ++ input_report_abs(ts->input, ABS_Y, y); ++ input_sync(ts->input); ++} ++ ++static void start_read(struct touchscreen_data *touch); ++ ++static irqreturn_t ++pen_isr(int irq, void *irq_desc) ++{ ++ struct touchscreen_data *ts = ts_data; ++ ++ if(irq == ts->irq /* && !irqblock */) { ++ irqblock = 1; ++ ++ /* ++ * Disable the pen interrupt. It's reenabled when the user lifts the ++ * pen. ++ */ ++ disable_irq(ts->irq); ++ ++ if (ts->state == STATE_WAIT_FOR_TOUCH) { ++ ts->state = STATE_SAMPLING; ++ start_read(ts); ++ } else { ++ /* Shouldn't happen */ ++ printk(KERN_ERR "Unexpected ts interrupt\n"); ++ } ++ ++ } ++ return IRQ_HANDLED; ++} ++ ++static void ++ssp_init(int port, int clock) ++{ ++ ++ pxa_set_cken(clock, 0); ++ ++ pxa_gpio_mode(GPIO_NR_HTCUNIVERSAL_TOUCHSCREEN_SPI_CLK_MD); ++ pxa_gpio_mode(GPIO_NR_HTCUNIVERSAL_TOUCHSCREEN_SPI_FRM_MD); ++ pxa_gpio_mode(GPIO_NR_HTCUNIVERSAL_TOUCHSCREEN_SPI_DO_MD); ++ pxa_gpio_mode(GPIO_NR_HTCUNIVERSAL_TOUCHSCREEN_SPI_DI_MD); ++ ++ SET_HTCUNIVERSAL_GPIO(SPI_FRM,1); ++ ++ /* *** Set up the SPI Registers *** */ ++ SSCR0_P(port) = ++ SSCR0_EDSS /* Extended Data Size Select */ ++ | SSCR0_SerClkDiv(7) /* Serial Clock Rate */ ++ /* Synchronous Serial Enable (Disable for now) */ ++ | SSCR0_Motorola /* Motorola SPI Interface */ ++ | SSCR0_DataSize(8) /* Data Size Select (24-bit) */ ++ ; ++ SSCR1_P(port) = 0; ++ SSPSP_P(port) = 0; ++ ++ /* Clear the Status */ ++ SSSR_P(port) = SSSR_P(port) & 0x00fcfffc; ++ ++ /* Now enable it */ ++ SSCR0_P(port) = ++ SSCR0_EDSS /* Extended Data Size Select */ ++ | SSCR0_SerClkDiv(7) /* Serial Clock Rate */ ++ | SSCR0_SSE /* Synchronous Serial Enable */ ++ | SSCR0_Motorola /* Motorola SPI Interface */ ++ | SSCR0_DataSize(8) /* Data Size Select (24-bit) */ ++ ; ++ ++ pxa_set_cken(clock, 1); ++} ++ ++static void ++start_read(struct touchscreen_data *touch) ++{ ++ unsigned long inc = (poll_sample_time * HZ) / 1000; ++ int i; ++ ++ /* Write here to the serial port. We request X and Y only for now. ++ * Then we have to wait for poll_sample_time before we read out the serial ++ * port. Then, when we read it out, we check to see if the pen is still ++ * down. If so, then we issue another request here. ++ */ ++#define TS_SAMPLES 7 ++ ++ /* ++ * We do four samples for each, and throw out the highest and lowest, then ++ * average the other two. ++ */ ++ ++ for(i = 0; i < TS_SAMPLES; i++) { ++ while(!(SSSR_P(touch->port) & SSSR_TNF)) ++ ; ++ /* It's not full. Write the command for X */ ++ SSDR_P(touch->port) = (TSC2046_SAMPLE_X|(touch->pwrbit_X))<<16; ++ } ++ ++ for(i = 0; i < TS_SAMPLES; i++) { ++ while(!(SSSR_P(touch->port) & SSSR_TNF)) ++ ; ++ /* It's not full. Write the command for Y */ ++ SSDR_P(touch->port) = (TSC2046_SAMPLE_Y|(touch->pwrbit_Y))<<16; ++ } ++ ++ /* ++ * Enable the timer. We should get an interrupt, but we want keep a timer ++ * to ensure that we can detect missing data ++ */ ++ mod_timer(&touch->timer, jiffies + inc); ++} ++ ++static void ++ts_timer_callback(unsigned long data) ++{ ++ struct touchscreen_data *ts = (struct touchscreen_data *)data; ++ int x, a[TS_SAMPLES], y; ++ static int oldx, oldy; ++ int ssrval; ++ ++ /* ++ * Check here to see if there is anything in the SPI FIFO. If so, ++ * return it if there has been a change. If not, then we have a ++ * timeout. Generate an erro somehow. ++ */ ++ ssrval = SSSR_P(ts->port); ++ ++ if(ssrval & SSSR_RNE) { /* Look at Rx Not Empty bit */ ++ int number_of_entries_in_fifo; ++ ++ /* The FIFO is not emtpy. Good! Now make sure there are at least two ++ * entries. (Should be two exactly.) */ ++ ++ number_of_entries_in_fifo = ((ssrval >> 12) & 0xf) + 1; ++ ++ if(number_of_entries_in_fifo < TS_SAMPLES * 2) { ++ /* Not ready yet. Come back later. */ ++ unsigned long inc = (poll_sample_time * HZ) / 1000; ++ mod_timer(&ts->timer, jiffies + inc); ++ return; ++ } ++ ++ if(number_of_entries_in_fifo == TS_SAMPLES * 2) { ++ int i, j; ++ ++ for(i = 0; i < TS_SAMPLES; i++) { ++ a[i] = SSDR_P(ts->port); ++ } ++ /* Sort them (bubble) */ ++ for(j = TS_SAMPLES - 1; j > 0; j--) { ++ for(i = 0; i < j; i++) { ++ if(a[i] > a[i + 1]) { ++ int tmp; ++ tmp = a[i+1]; ++ a[i+1] = a[i]; ++ a[i] = tmp; ++ } ++ } ++ } ++ ++ /* Take the average of the middle two */ ++ /* x = (a[TS_SAMPLES/2 - 1] + a[TS_SAMPLES/2] + a[TS_SAMPLES/2+1] + a[TS_SAMPLES/2+2]) >> 2; */ ++ x = a[TS_SAMPLES/2]; ++ ++ for(i = 0; i < TS_SAMPLES; i++) { ++ a[i] = SSDR_P(ts->port); ++ } ++ /* Sort them (bubble) */ ++ for(j = TS_SAMPLES - 1; j > 0; j--) { ++ for(i = 0; i < j; i++) { ++ if(a[i] > a[i + 1]) { ++ int tmp; ++ tmp = a[i+1]; ++ a[i+1] = a[i]; ++ a[i] = tmp; ++ } ++ } ++ } ++ ++ ++ /* Take the average of the middle two */ ++ /* y = (a[TS_SAMPLES/2 - 1] + a[TS_SAMPLES/2] + a[TS_SAMPLES/2+1] + a[TS_SAMPLES/2+2]) >> 2; */ ++ y = a[TS_SAMPLES/2]; ++ } else { ++ /* We have an error! Too many entries. */ ++ printk(KERN_ERR "TS: Expected %d entries. Got %d\n", TS_SAMPLES*2, number_of_entries_in_fifo); ++ /* Try to clear the FIFO */ ++ while(number_of_entries_in_fifo--) { ++ (void)SSDR_P(ts->port); ++ } ++ ++ if (ts->pen_down()) ++ start_read(ts); ++ ++ return; ++ } ++ } else { ++ /* Not ready yet. Come back later. */ ++ unsigned long inc = (poll_sample_time * HZ) / 1000; ++ mod_timer(&ts->timer, jiffies + inc); ++ return; ++ } ++ ++ /* ++ * Now we check to see if the pen is still down. If it is, then call ++ * start_read(). ++ */ ++ if (ts->pen_down()) ++ { ++ /* Still down */ ++ if(oldx != x || oldy != y) { ++ oldx = x; ++ oldy = y; ++ report_touchpanel(ts, 1, x, y); ++ } ++ start_read(ts); ++ } else { ++ /* Up */ ++ report_touchpanel(ts, 0, 0, 0); ++ irqblock = 0; ++ ts->state = STATE_WAIT_FOR_TOUCH; ++ /* Re-enable pen down interrupt */ ++ enable_irq(ts->irq); ++ } ++} ++ ++static int pen_down(void) ++{ ++ return ( asic3_get_gpio_status_a( &htcuniversal_asic3.dev ) & (1<<GPIOA_TOUCHSCREEN_N)) == 0 ; ++} ++ ++static int ++ts_probe (struct platform_device *dev) ++{ ++ int retval; ++ struct touchscreen_data *ts; ++ struct tsc2046_mach_info *mach = dev->dev.platform_data; ++ ++ printk("htcuniversal: ts_probe\n"); ++ ++ ts = ts_data = kmalloc (sizeof (*ts), GFP_KERNEL); ++ if (ts == NULL) { ++ printk( KERN_NOTICE "htcuniversal_ts: unable to allocate memory\n" ); ++ return -ENOMEM; ++ } ++ memset (ts, 0, sizeof (*ts)); ++ ++ ts->input = input_allocate_device(); ++ if (ts->input == NULL) { ++ printk( KERN_NOTICE "htcuniversal_ts: unable to allocation touchscreen input\n" ); ++ kfree(ts); ++ return -ENOMEM; ++ } ++ ts->input->evbit[0] = BIT(EV_ABS); ++ ts->input->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_PRESSURE); ++ ts->input->absmin[ABS_X] = 0; ++ ts->input->absmax[ABS_X] = 32767; ++ ts->input->absmin[ABS_Y] = 0; ++ ts->input->absmax[ABS_Y] = 32767; ++ ts->input->absmin[ABS_PRESSURE] = 0; ++ ts->input->absmax[ABS_PRESSURE] = 1; ++ ++ ts->input->name = "htcuniversal_ts"; ++ ts->input->phys = "touchscreen/htcuniversal_ts"; ++ ts->input->private = ts; ++ ++ input_register_device(ts->input); ++ ++ ts->timer.function = ts_timer_callback; ++ ts->timer.data = (unsigned long)ts; ++ ts->state = STATE_WAIT_FOR_TOUCH; ++ init_timer (&ts->timer); ++ ++ platform_set_drvdata(dev, ts); ++ ++ ts->port=-1; ++ ++ if (mach) { ++ ts->port = mach->port; ++ ts->clock = mach->clock; ++ ts->pwrbit_X = mach->pwrbit_X; ++ ts->pwrbit_Y = mach->pwrbit_Y; ++ ++ /* static irq */ ++ if (mach->irq) ++ ts->irq = mach->irq; ++ ++ if (mach->pen_down) ++ ts->pen_down=mach->pen_down; ++ } ++ ++ if (ts->port == -1) ++ { ++ printk("tsc2046: your device is not supported by this driver\n"); ++ return -ENODEV; ++ } ++ ++ /* *** Initialize the SSP interface *** */ ++ ssp_init(ts->port, ts->clock); ++ ++ while(!(SSSR_P(ts->port) & SSSR_TNF)) ++ ; ++ SSDR_P(ts->port) = (TSC2046_SAMPLE_X|(ts->pwrbit_X))<<16; ++ ++ for(retval = 0; retval < 100; retval++) { ++ if(SSSR_P(ts->port) & SSSR_RNE) { ++ while(SSSR_P(ts->port) & SSSR_RNE) { ++ (void)SSDR_P(ts->port); ++ } ++ break; ++ } ++ mdelay(1); ++ } ++ ++ if (machine_is_htcuniversal() ) ++ { ++ ts->irq = asic3_irq_base( &htcuniversal_asic3.dev ) + ASIC3_GPIOA_IRQ_BASE + GPIOA_TOUCHSCREEN_N; ++ ts->pen_down=pen_down; ++ } ++ ++ retval = request_irq(ts->irq, pen_isr, IRQF_DISABLED, "tsc2046_ts", ts); ++ if(retval) { ++ printk("Unable to get interrupt\n"); ++ input_unregister_device (ts->input); ++ return -ENODEV; ++ } ++ set_irq_type(ts->irq, IRQ_TYPE_EDGE_FALLING); ++ ++ return 0; ++} ++ ++static int ++ts_remove (struct platform_device *dev) ++{ ++ struct touchscreen_data *ts = platform_get_drvdata(dev); ++ ++ input_unregister_device (ts->input); ++ del_timer_sync (&ts->timer); ++ free_irq (ts->irq, ts); ++ pxa_set_cken(ts->clock, 0); ++ ++ kfree(ts); ++ return 0; ++} ++ ++static int ++ts_suspend (struct platform_device *dev, pm_message_t state) ++{ ++ struct touchscreen_data *ts = platform_get_drvdata(dev); ++ ++ disable_irq(ts->irq); ++ ++ printk("htcuniversal_ts2_suspend: called.\n"); ++ return 0; ++} ++ ++static int ++ts_resume (struct platform_device *dev) ++{ ++ struct touchscreen_data *ts = platform_get_drvdata(dev); ++ ++ ts->state = STATE_WAIT_FOR_TOUCH; ++ ssp_init(ts->port, ts->clock); ++ enable_irq(ts->irq); ++ ++ printk("htcuniversal_ts2_resume: called.\n"); ++ return 0; ++} ++ ++static struct platform_driver ts_driver = { ++ .probe = ts_probe, ++ .remove = ts_remove, ++ .suspend = ts_suspend, ++ .resume = ts_resume, ++ .driver = { ++ .name = "htcuniversal_ts", ++ }, ++}; ++ ++ ++static int ++ts_module_init (void) ++{ ++ printk(KERN_NOTICE "HTC Universal Touch Screen Driver\n"); ++ ++ return platform_driver_register(&ts_driver); ++} ++ ++static void ++ts_module_cleanup (void) ++{ ++ platform_driver_unregister (&ts_driver); ++} ++ ++module_init(ts_module_init); ++module_exit(ts_module_cleanup); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Aric Blumer, SDG Systems, LLC"); ++MODULE_DESCRIPTION("HTC Universal Touch Screen Driver"); +Index: linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_udc.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/arch/arm/mach-pxa/htcuniversal/htcuniversal_udc.c 2008-03-10 16:09:23.000000000 +0000 +@@ -0,0 +1,71 @@ ++ ++/* ++ * ++ * htcuniversal_udc.c: ++ * htcuniversal specific code for the pxa27x usb device controller. ++ * ++ * Use consistent with the GNU GPL is permitted. ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/platform_device.h> ++#include <asm/arch/hardware.h> ++#include <asm/arch/pxa-regs.h> ++#include <asm/arch/udc.h> ++#include <linux/soc/asic3_base.h> ++#include <asm/arch/htcuniversal-gpio.h> ++#include <asm/arch/htcuniversal-asic.h> ++ ++static void htcuniversal_udc_command(int cmd) ++{ ++ switch (cmd) { ++ case PXA2XX_UDC_CMD_DISCONNECT: ++ asic3_set_gpio_out_b(&htcuniversal_asic3.dev, ++ 1<<GPIOB_USB_PUEN, 0); ++// SET_HTCUNIVERSAL_GPIO(USB_PUEN,0); ++ break; ++ case PXA2XX_UDC_CMD_CONNECT: ++ asic3_set_gpio_out_b(&htcuniversal_asic3.dev, ++ 1<<GPIOB_USB_PUEN, 1<<GPIOB_USB_PUEN); ++// SET_HTCUNIVERSAL_GPIO(USB_PUEN,1); ++ break; ++ default: ++ printk("_udc_control: unknown command!\n"); ++ break; ++ } ++} ++ ++static int htcuniversal_udc_is_connected(void) ++{ ++ return (GET_HTCUNIVERSAL_GPIO(USB_DET) != 0); ++} ++ ++static struct pxa2xx_udc_mach_info htcuniversal_udc_info __initdata = { ++ .udc_is_connected = htcuniversal_udc_is_connected, ++ .udc_command = htcuniversal_udc_command, ++}; ++ ++static int htcuniversal_udc_probe(struct platform_device * dev) ++{ ++ asic3_set_gpio_dir_b(&htcuniversal_asic3.dev, 1<<GPIOB_USB_PUEN, 1<<GPIOB_USB_PUEN); ++ ++ pxa_set_udc_info(&htcuniversal_udc_info); ++ return 0; ++} ++ ++static struct platform_driver htcuniversal_udc_driver = { ++ .driver = { ++ .name = "htcuniversal_udc", ++ }, ++ .probe = htcuniversal_udc_probe, ++}; ++ ++static int __init htcuniversal_udc_init(void) ++{ ++ return platform_driver_register(&htcuniversal_udc_driver); ++} ++ ++module_init(htcuniversal_udc_init); ++MODULE_LICENSE("GPL"); +Index: linux-2.6.24/arch/arm/mach-pxa/htcuniversal/tsc2046_ts.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/arch/arm/mach-pxa/htcuniversal/tsc2046_ts.h 2008-03-10 16:09:23.000000000 +0000 +@@ -0,0 +1,20 @@ ++/* ++ * temporary TSC2046 touchscreen hack ++ */ ++ ++#ifndef _TSC2046_TS_H ++#define _TSC2046_TS_H ++ ++struct tsc2046_mach_info { ++ int port; ++ int clock; ++ int pwrbit_X; ++ int pwrbit_Y; ++ int irq; ++ int (*pen_down)(void); ++}; ++ ++#define TSC2046_SAMPLE_X 0xd0 ++#define TSC2046_SAMPLE_Y 0x90 ++ ++#endif +Index: linux-2.6.24/arch/arm/mach-pxa/Kconfig +=================================================================== +--- linux-2.6.24.orig/arch/arm/mach-pxa/Kconfig 2008-03-10 16:08:01.000000000 +0000 ++++ linux-2.6.24/arch/arm/mach-pxa/Kconfig 2008-03-10 16:09:23.000000000 +0000 +@@ -92,6 +92,14 @@ + bool "Sharp PXA270 models (SL-Cxx00)" + select PXA27x + ++config MACH_HTCUNIVERSAL ++ bool "HTC Universal" ++ select PXA27x ++ help ++ Say Y here if you intend to run this kernel on a ++ HTC Universal. Currently there is only basic support ++ for this PDA. ++ + endchoice + + endif +@@ -111,6 +119,86 @@ + + endif + ++if MACH_HTCUNIVERSAL ++ ++menu "HTC Universal support" ++ ++config HTCUNIVERSAL_CORE ++ tristate "HTC Universal core" ++ depends on MACH_HTCUNIVERSAL ++ help ++ This selection enables HTC Universal core support. ++ ++config HTCUNIVERSAL_UDC ++ bool "USB Device Controller support" ++ depends on MACH_HTCUNIVERSAL && HTC_ASIC3 && USB_PXA27X ++ help ++ Enables HTC Universal specific USB detection ++ ++config HTCUNIVERSAL_POWER ++ tristate "HTC Universal power" ++ depends on MACH_HTCUNIVERSAL && HTC_ASIC3 ++ help ++ This selection enables HTC Universal power monitoring ++ hardware support (through ASIC3). ++ ++config HTCUNIVERSAL_BACKLIGHT ++ bool "HTC Universal Backlight" ++ depends on MACH_HTCUNIVERSAL && HTC_ASIC3 && BACKLIGHT_CLASS_DEVICE ++ help ++ This driver provides support for changing power and brightness ++ on HTC Universal LCD backlight. ++ ++config HTCUNIVERSAL_LCD ++ tristate "HTC Universal LCD" ++ depends on MACH_HTCUNIVERSAL && HTC_ASIC3 && LCD_CLASS_DEVICE ++ help ++ This driver provides support for changing power and brightness ++ on HTC Universal LCD display. ++ ++config HTCUNIVERSAL_TS2 ++ tristate "HTC Universal Touchscreen (old)" ++ depends on MACH_HTCUNIVERSAL && HTC_ASIC3 ++ help ++ Enable support for the HTC Universal Touchscreen Panel. ++ ++config HTCUNIVERSAL_BUTTONS ++ tristate "HTC Universal buttons support" ++ depends on MACH_HTCUNIVERSAL && HTC_ASIC3 ++ ++config HTCUNIVERSAL_BLUETOOTH ++ tristate "HTC Universal Bluetooth" ++ depends on MACH_HTCUNIVERSAL && HTCUNIVERSAL_CORE && HTC_ASIC3 ++ help ++ Enables support for the TI BRF6150 Bluetooth Module ++ in the HTC Universal. ++ ++config HTCUNIVERSAL_ASIC3_LEDS ++ tristate "HTC Universal ASIC3 LED support" ++ select LEDS_ASIC3 ++ depends on MACH_HTCUNIVERSAL && HTCUNIVERSAL_CORE && HTC_ASIC3 ++ ---help--- ++ Support for right (colors red+green+(amber)) and left (green+blue) led ++ Off/on hook keys LED backlight ++ Keyboard backlight ++ Vibra ++ Flashlight ++ ++config HTCUNIVERSAL_PHONE ++ tristate "HTC Universal Phone" ++ depends on MACH_HTCUNIVERSAL && HTCUNIVERSAL_CORE && HTC_ASIC3 ++ help ++ Enables support for the Qualcomm MSM6520 Phone Module ++ in the HTC Universal. ++ ++config HTCUNIVERSAL_AK4641 ++ depends on SND && I2C ++ tristate "AK4641 chipset support" ++ ++endmenu ++ ++endif ++ + endmenu + + config MACH_POODLE +@@ -196,4 +284,3 @@ + depends on (PXA25x || PXA27x) && INPUT + + endif +- +Index: linux-2.6.24/arch/arm/mach-pxa/Makefile +=================================================================== +--- linux-2.6.24.orig/arch/arm/mach-pxa/Makefile 2008-03-10 16:08:01.000000000 +0000 ++++ linux-2.6.24/arch/arm/mach-pxa/Makefile 2008-03-10 16:09:23.000000000 +0000 +@@ -23,6 +23,7 @@ + obj-$(CONFIG_MACH_TOSA) += tosa.o + obj-$(CONFIG_MACH_EM_X270) += em-x270.o + obj-$(CONFIG_MACH_HX2750) += hx2750.o hx2750_test.o ++obj-$(CONFIG_MACH_HTCUNIVERSAL) += htcuniversal/ + + ifeq ($(CONFIG_MACH_ZYLONITE),y) + obj-y += zylonite.o +Index: linux-2.6.24/drivers/leds/Kconfig +=================================================================== +--- linux-2.6.24.orig/drivers/leds/Kconfig 2008-01-24 22:58:37.000000000 +0000 ++++ linux-2.6.24/drivers/leds/Kconfig 2008-03-10 16:09:23.000000000 +0000 +@@ -114,6 +114,13 @@ + help + This option enables support for the CM-X270 LEDs. + ++config LEDS_ASIC3 ++ tristate "LED Support for the HTC ASIC3 chip" ++ depends on LEDS_CLASS && HTC_ASIC3 ++ help ++ This option enables support for the LEDs connected to the ++ HTC ASIC3 chip. ++ + comment "LED Triggers" + + config LEDS_TRIGGERS +Index: linux-2.6.24/drivers/leds/leds-asic3.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/drivers/leds/leds-asic3.c 2008-03-10 16:09:23.000000000 +0000 +@@ -0,0 +1,189 @@ ++/* ++ * LEDs support for HTC ASIC3 devices. ++ * ++ * Copyright (c) 2006 Anton Vorontsov <cbou@mail.ru> ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive for ++ * more details. ++ * ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/platform_device.h> ++#include <linux/leds.h> ++#include "leds.h" ++ ++#include <asm/hardware/ipaq-asic3.h> ++#include <linux/soc/asic3_base.h> ++#include <asm/mach-types.h> ++#include <asm/hardware/asic3_leds.h> ++ ++#ifdef DEBUG ++#define dbg(msg, ...) printk(msg, __VA_ARGS__) ++#else ++#define dbg(msg, ...) ++#endif ++ ++static ++void asic3_leds_set(struct led_classdev *led_cdev, enum led_brightness b) ++{ ++ struct asic3_led *led = container_of(led_cdev, struct asic3_led, ++ led_cdev); ++ struct asic3_leds_machinfo *machinfo = led->machinfo; ++ struct device *asic3_dev = &machinfo->asic3_pdev->dev; ++ ++ dbg("%s:%s %d(%d)-%s %d\n", __FILE__, __FUNCTION__, led->hw_num, ++ led->gpio_num, led->led_cdev.name, b); ++ ++ if (led->hw_num == -1) { ++ asic3_gpio_set_value(asic3_dev, led->gpio_num, b); ++ return; ++ } ++ ++ if (b == LED_OFF) { ++ asic3_set_led(asic3_dev, led->hw_num, 0, 16, 6); ++ asic3_set_gpio_out_c(asic3_dev, led->hw_num, 0); ++ } ++ else { ++ asic3_set_gpio_out_c(asic3_dev, led->hw_num, led->hw_num); ++ #ifdef CONFIG_LEDS_TRIGGER_HWTIMER ++ if (led_cdev->trigger && led_cdev->trigger->is_led_supported && ++ (led_cdev->trigger->is_led_supported(led_cdev) & ++ LED_SUPPORTS_HWTIMER)) { ++ struct hwtimer_data *td = led_cdev->trigger_data; ++ if (!td) return; ++ asic3_set_led(asic3_dev, led->hw_num, td->delay_on/8, ++ (td->delay_on + td->delay_off)/8, 6); ++ } ++ else ++ #endif ++ asic3_set_led(asic3_dev, led->hw_num, 16, 16, 6); ++ } ++ ++ return; ++} ++ ++static ++int asic3_leds_probe(struct platform_device *pdev) ++{ ++ struct asic3_leds_machinfo *machinfo = pdev->dev.platform_data; ++ struct asic3_led *leds = machinfo->leds; ++ int ret, i = 0; ++ ++ dbg("%s:%s\n", __FILE__, __FUNCTION__); ++ ++ // Turn on clocks early, for the case if trigger would enable ++ // led immediately after led_classdev_register(). ++ asic3_set_clock_cdex(&machinfo->asic3_pdev->dev, ++ CLOCK_CDEX_LED0 | CLOCK_CDEX_LED1 | CLOCK_CDEX_LED2, ++ CLOCK_CDEX_LED0 | CLOCK_CDEX_LED1 | CLOCK_CDEX_LED2); ++ ++ for (i = 0; i < machinfo->num_leds; i++) { ++ leds[i].machinfo = machinfo; ++ leds[i].led_cdev.brightness_set = asic3_leds_set; ++ ret = led_classdev_register(&pdev->dev, &leds[i].led_cdev); ++ if (ret) { ++ printk(KERN_ERR "Error: can't register %s led\n", ++ leds[i].led_cdev.name); ++ goto out_err; ++ } ++ } ++ ++ return 0; ++ ++out_err: ++ while (--i >= 0) led_classdev_unregister(&leds[i].led_cdev); ++ ++ asic3_set_clock_cdex(&machinfo->asic3_pdev->dev, ++ CLOCK_CDEX_LED0 | CLOCK_CDEX_LED1 | CLOCK_CDEX_LED2, ++ 0 | 0 | 0); ++ ++ return ret; ++} ++ ++static ++int asic3_leds_remove(struct platform_device *pdev) ++{ ++ struct asic3_leds_machinfo *machinfo = pdev->dev.platform_data; ++ struct asic3_led *leds = machinfo->leds; ++ int i = 0; ++ ++ dbg("%s:%s\n", __FILE__, __FUNCTION__); ++ ++ for (i = 0; i < machinfo->num_leds; i++) ++ led_classdev_unregister(&leds[i].led_cdev); ++ ++ asic3_set_clock_cdex(&machinfo->asic3_pdev->dev, ++ CLOCK_CDEX_LED0 | CLOCK_CDEX_LED1 | CLOCK_CDEX_LED2, ++ 0 | 0 | 0); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++ ++static ++int asic3_leds_suspend(struct platform_device *pdev, pm_message_t state) ++{ ++ struct asic3_leds_machinfo *machinfo = pdev->dev.platform_data; ++ struct asic3_led *leds = machinfo->leds; ++ int i = 0; ++ ++ dbg("%s:%s\n", __FILE__, __FUNCTION__); ++ ++ for (i = 0; i < machinfo->num_leds; i++) ++ led_classdev_suspend(&leds[i].led_cdev); ++ ++ return 0; ++} ++ ++static ++int asic3_leds_resume(struct platform_device *pdev) ++{ ++ struct asic3_leds_machinfo *machinfo = pdev->dev.platform_data; ++ struct asic3_led *leds = machinfo->leds; ++ int i = 0; ++ ++ dbg("%s:%s\n", __FILE__, __FUNCTION__); ++ ++ for (i = 0; i < machinfo->num_leds; i++) ++ led_classdev_resume(&leds[i].led_cdev); ++ ++ return 0; ++} ++ ++#endif ++ ++static ++struct platform_driver asic3_leds_driver = { ++ .probe = asic3_leds_probe, ++ .remove = asic3_leds_remove, ++#ifdef CONFIG_PM ++ .suspend = asic3_leds_suspend, ++ .resume = asic3_leds_resume, ++#endif ++ .driver = { ++ .name = "asic3-leds", ++ }, ++}; ++ ++int asic3_leds_register(void) ++{ ++ dbg("%s:%s\n", __FILE__, __FUNCTION__); ++ return platform_driver_register(&asic3_leds_driver); ++} ++ ++void asic3_leds_unregister(void) ++{ ++ platform_driver_unregister(&asic3_leds_driver); ++ return; ++} ++ ++EXPORT_SYMBOL_GPL(asic3_leds_register); ++EXPORT_SYMBOL_GPL(asic3_leds_unregister); ++ ++MODULE_AUTHOR("Anton Vorontsov <cbou@mail.ru>"); ++MODULE_DESCRIPTION("HTC ASIC3 LEDs driver"); ++MODULE_LICENSE("GPL"); +Index: linux-2.6.24/drivers/mfd/Kconfig +=================================================================== +--- linux-2.6.24.orig/drivers/mfd/Kconfig 2008-03-10 16:07:51.000000000 +0000 ++++ linux-2.6.24/drivers/mfd/Kconfig 2008-03-10 16:09:23.000000000 +0000 +@@ -21,6 +21,16 @@ + help + Support for TI TSC2101 Touchscreen and Audio Codec + ++config HTC_ASIC3 ++ tristate "HTC ASIC3 (iPAQ h1900/h3900/h4000/hx4700/rx3000) support" ++ ++config HTC_ASIC3_DS1WM ++ bool "Support HTC ASIC3 builtin DS1WM block" ++ help ++ Choose Y here if you want to include support for ASIC3's builtin ++ W1 controller. Some devices do not use it, and yet other have ++ separate DS1WM controller. For them, choose N. ++ + endmenu + + menu "Multimedia Capabilities Port drivers" +Index: linux-2.6.24/drivers/mfd/Makefile +=================================================================== +--- linux-2.6.24.orig/drivers/mfd/Makefile 2008-03-10 16:07:51.000000000 +0000 ++++ linux-2.6.24/drivers/mfd/Makefile 2008-03-10 16:09:23.000000000 +0000 +@@ -2,6 +2,8 @@ + # Makefile for multifunction miscellaneous devices + # + ++obj-$(CONFIG_HTC_ASIC3) += asic3_base.o soc-core.o ++ + obj-$(CONFIG_MFD_SM501) += sm501.o + + obj-$(CONFIG_MCP) += mcp-core.o +Index: linux-2.6.24/drivers/mfd/asic3_base.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/drivers/mfd/asic3_base.c 2008-03-10 16:09:23.000000000 +0000 +@@ -0,0 +1,1208 @@ ++/* ++ * Driver interface to HTC "ASIC3" ++ * ++ * Copyright 2001 Compaq Computer Corporation. ++ * Copyright 2004-2005 Phil Blundell ++ * ++ * 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. ++ * ++ * COMPAQ COMPUTER CORPORATION MAKES NO WARRANTIES, EXPRESSED OR IMPLIED, ++ * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS ++ * FITNESS FOR ANY PARTICULAR PURPOSE. ++ * ++ * Author: Andrew Christian ++ * <Andrew.Christian@compaq.com> ++ * October 2001 ++ */ ++ ++#include <linux/module.h> ++#include <linux/version.h> ++#include <linux/platform_device.h> ++#include <linux/delay.h> ++#include <linux/init.h> ++#include <linux/irq.h> ++#include <linux/clk.h> ++#include <linux/ds1wm.h> ++#include <asm/arch/clock.h> ++ ++#include <asm/hardware.h> ++#include <asm/irq.h> ++#include <asm/io.h> ++ ++#include <asm/hardware/ipaq-asic3.h> ++#include <linux/soc/asic3_base.h> ++#include <linux/soc/tmio_mmc.h> ++#include "soc-core.h" ++ ++ ++struct asic3_data { ++ void *mapping; ++ unsigned int bus_shift; ++ int irq_base; ++ int irq_nr; ++ ++ u16 irq_bothedge[4]; ++ struct device *dev; ++ ++ struct platform_device *mmc_dev; ++}; ++ ++static DEFINE_SPINLOCK(asic3_gpio_lock); ++ ++static int asic3_remove(struct platform_device *dev); ++ ++static inline unsigned long asic3_address(struct device *dev, ++ unsigned int reg) ++{ ++ struct asic3_data *adata; ++ ++ adata = (struct asic3_data *)dev->driver_data; ++ ++ return (unsigned long)adata->mapping + (reg >> (2 - adata->bus_shift)); ++} ++ ++void asic3_write_register(struct device *dev, unsigned int reg, u32 value) ++{ ++ __raw_writew(value, asic3_address(dev, reg)); ++} ++EXPORT_SYMBOL(asic3_write_register); ++ ++u32 asic3_read_register(struct device *dev, unsigned int reg) ++{ ++ return __raw_readw(asic3_address(dev, reg)); ++} ++EXPORT_SYMBOL(asic3_read_register); ++ ++static inline void __asic3_write_register(struct asic3_data *asic, ++ unsigned int reg, u32 value) ++{ ++ __raw_writew(value, (unsigned long)asic->mapping ++ + (reg >> (2 - asic->bus_shift))); ++} ++ ++static inline u32 __asic3_read_register(struct asic3_data *asic, ++ unsigned int reg) ++{ ++ return __raw_readw((unsigned long)asic->mapping ++ + (reg >> (2 - asic->bus_shift))); ++} ++ ++#define ASIC3_GPIO_FN(get_fn_name, set_fn_name, REG) \ ++u32 get_fn_name(struct device *dev) \ ++{ \ ++ return asic3_read_register(dev, REG); \ ++} \ ++EXPORT_SYMBOL(get_fn_name); \ ++ \ ++void set_fn_name(struct device *dev, u32 bits, u32 val) \ ++{ \ ++ unsigned long flags; \ ++ \ ++ spin_lock_irqsave(&asic3_gpio_lock, flags); \ ++ val |= (asic3_read_register(dev, REG) & ~bits); \ ++ asic3_write_register(dev, REG, val); \ ++ spin_unlock_irqrestore(&asic3_gpio_lock, flags); \ ++} \ ++EXPORT_SYMBOL(set_fn_name); ++ ++#define ASIC3_GPIO_REGISTER(ACTION, action, fn, FN) \ ++ ASIC3_GPIO_FN(asic3_get_gpio_ ## action ## _ ## fn , \ ++ asic3_set_gpio_ ## action ## _ ## fn , \ ++ _IPAQ_ASIC3_GPIO_ ## FN ## _Base \ ++ + _IPAQ_ASIC3_GPIO_ ## ACTION ) ++ ++#define ASIC3_GPIO_FUNCTIONS(fn, FN) \ ++ ASIC3_GPIO_REGISTER(Direction, dir, fn, FN) \ ++ ASIC3_GPIO_REGISTER(Out, out, fn, FN) \ ++ ASIC3_GPIO_REGISTER(SleepMask, sleepmask, fn, FN) \ ++ ASIC3_GPIO_REGISTER(SleepOut, sleepout, fn, FN) \ ++ ASIC3_GPIO_REGISTER(BattFaultOut, battfaultout, fn, FN) \ ++ ASIC3_GPIO_REGISTER(AltFunction, alt_fn, fn, FN) \ ++ ASIC3_GPIO_REGISTER(SleepConf, sleepconf, fn, FN) \ ++ ASIC3_GPIO_REGISTER(Status, status, fn, FN) ++ ++#if 0 ++ ASIC3_GPIO_REGISTER(Mask, mask, fn, FN) ++ ASIC3_GPIO_REGISTER(TriggerType, trigtype, fn, FN) ++ ASIC3_GPIO_REGISTER(EdgeTrigger, rising, fn, FN) ++ ASIC3_GPIO_REGISTER(LevelTrigger, triglevel, fn, FN) ++ ASIC3_GPIO_REGISTER(IntStatus, intstatus, fn, FN) ++#endif ++ ++ASIC3_GPIO_FUNCTIONS(a, A) ++ASIC3_GPIO_FUNCTIONS(b, B) ++ASIC3_GPIO_FUNCTIONS(c, C) ++ASIC3_GPIO_FUNCTIONS(d, D) ++ ++int asic3_gpio_get_value(struct device *dev, unsigned gpio) ++{ ++ u32 mask = ASIC3_GPIO_bit(gpio); ++ printk("%s(%d)\n", __FUNCTION__, gpio); ++ switch (gpio >> 4) { ++ case _IPAQ_ASIC3_GPIO_BANK_A: ++ return asic3_get_gpio_status_a(dev) & mask; ++ case _IPAQ_ASIC3_GPIO_BANK_B: ++ return asic3_get_gpio_status_b(dev) & mask; ++ case _IPAQ_ASIC3_GPIO_BANK_C: ++ return asic3_get_gpio_status_c(dev) & mask; ++ case _IPAQ_ASIC3_GPIO_BANK_D: ++ return asic3_get_gpio_status_d(dev) & mask; ++ } ++ ++ printk(KERN_ERR "%s: invalid GPIO value 0x%x", __FUNCTION__, gpio); ++ return 0; ++} ++EXPORT_SYMBOL(asic3_gpio_get_value); ++ ++void asic3_gpio_set_value(struct device *dev, unsigned gpio, int val) ++{ ++ u32 mask = ASIC3_GPIO_bit(gpio); ++ u32 bitval = 0; ++ if (val) bitval = mask; ++ printk("%s(%d, %d)\n", __FUNCTION__, gpio, val); ++ ++ switch (gpio >> 4) { ++ case _IPAQ_ASIC3_GPIO_BANK_A: ++ asic3_set_gpio_out_a(dev, mask, bitval); ++ return; ++ case _IPAQ_ASIC3_GPIO_BANK_B: ++ asic3_set_gpio_out_b(dev, mask, bitval); ++ return; ++ case _IPAQ_ASIC3_GPIO_BANK_C: ++ asic3_set_gpio_out_c(dev, mask, bitval); ++ return; ++ case _IPAQ_ASIC3_GPIO_BANK_D: ++ asic3_set_gpio_out_d(dev, mask, bitval); ++ return; ++ } ++ ++ printk(KERN_ERR "%s: invalid GPIO value 0x%x", __FUNCTION__, gpio); ++} ++EXPORT_SYMBOL(asic3_gpio_set_value); ++ ++int asic3_irq_base(struct device *dev) ++{ ++ struct asic3_data *asic = dev->driver_data; ++ ++ return asic->irq_base; ++} ++EXPORT_SYMBOL(asic3_irq_base); ++ ++static int asic3_gpio_to_irq(struct device *dev, unsigned gpio) ++{ ++ struct asic3_data *asic = dev->driver_data; ++ printk("%s(%d)\n", __FUNCTION__, gpio); ++ ++ return asic->irq_base + gpio; ++} ++ ++void asic3_set_led(struct device *dev, int led_num, int duty_time, ++ int cycle_time, int timebase) ++{ ++ struct asic3_data *asic = dev->driver_data; ++ unsigned int led_base; ++ ++ /* it's a macro thing: see #define _IPAQ_ASIC_LED_0_Base for why you ++ * can't substitute led_num in the macros below... ++ */ ++ ++ switch (led_num) { ++ case 0: ++ led_base = _IPAQ_ASIC3_LED_0_Base; ++ break; ++ case 1: ++ led_base = _IPAQ_ASIC3_LED_1_Base; ++ break; ++ case 2: ++ led_base = _IPAQ_ASIC3_LED_2_Base; ++ break; ++ default: ++ printk(KERN_ERR "%s: invalid led number %d", __FUNCTION__, ++ led_num); ++ return; ++ } ++ ++ __asic3_write_register(asic, led_base + _IPAQ_ASIC3_LED_TimeBase, ++ timebase | LED_EN); ++ __asic3_write_register(asic, led_base + _IPAQ_ASIC3_LED_PeriodTime, ++ cycle_time); ++ __asic3_write_register(asic, led_base + _IPAQ_ASIC3_LED_DutyTime, ++ 0); ++ udelay(20); /* asic voodoo - possibly need a whole duty cycle? */ ++ __asic3_write_register(asic, led_base + _IPAQ_ASIC3_LED_DutyTime, ++ duty_time); ++} ++EXPORT_SYMBOL(asic3_set_led); ++ ++void asic3_set_clock_sel(struct device *dev, u32 bits, u32 val) ++{ ++ struct asic3_data *asic = dev->driver_data; ++ unsigned long flags; ++ u32 v; ++ ++ spin_lock_irqsave(&asic3_gpio_lock, flags); ++ v = __asic3_read_register(asic, IPAQ_ASIC3_OFFSET(CLOCK, SEL)); ++ v = (v & ~bits) | val; ++ __asic3_write_register(asic, IPAQ_ASIC3_OFFSET(CLOCK, SEL), v); ++ spin_unlock_irqrestore(&asic3_gpio_lock, flags); ++} ++EXPORT_SYMBOL(asic3_set_clock_sel); ++ ++void asic3_set_clock_cdex(struct device *dev, u32 bits, u32 val) ++{ ++ struct asic3_data *asic = dev->driver_data; ++ unsigned long flags; ++ u32 v; ++ ++ spin_lock_irqsave(&asic3_gpio_lock, flags); ++ v = __asic3_read_register(asic, IPAQ_ASIC3_OFFSET(CLOCK, CDEX)); ++ v = (v & ~bits) | val; ++ __asic3_write_register(asic, IPAQ_ASIC3_OFFSET(CLOCK, CDEX), v); ++ spin_unlock_irqrestore(&asic3_gpio_lock, flags); ++} ++EXPORT_SYMBOL(asic3_set_clock_cdex); ++ ++static void asic3_clock_cdex_enable(struct clk *clk) ++{ ++ struct asic3_data *asic = (struct asic3_data *)clk->parent->ctrlbit; ++ unsigned long flags, val; ++ ++ local_irq_save(flags); ++ ++ val = __asic3_read_register(asic, IPAQ_ASIC3_OFFSET(CLOCK, CDEX)); ++ val |= clk->ctrlbit; ++ __asic3_write_register(asic, IPAQ_ASIC3_OFFSET(CLOCK, CDEX), val); ++ ++ local_irq_restore(flags); ++} ++ ++static void asic3_clock_cdex_disable(struct clk *clk) ++{ ++ struct asic3_data *asic = (struct asic3_data *)clk->parent->ctrlbit; ++ unsigned long flags, val; ++ ++ local_irq_save(flags); ++ ++ val = __asic3_read_register(asic, IPAQ_ASIC3_OFFSET(CLOCK, CDEX)); ++ val &= ~clk->ctrlbit; ++ __asic3_write_register(asic, IPAQ_ASIC3_OFFSET(CLOCK, CDEX), val); ++ ++ local_irq_restore(flags); ++} ++ ++/* base clocks */ ++ ++static struct clk clk_g = { ++ .name = "gclk", ++ .rate = 0, ++ .parent = NULL, ++}; ++ ++/* clock definitions */ ++ ++static struct clk asic3_clocks[] = { ++ { ++ .name = "spi", ++ .id = -1, ++ .parent = &clk_g, ++ .enable = asic3_clock_cdex_enable, ++ .disable = asic3_clock_cdex_disable, ++ .ctrlbit = CLOCK_CDEX_SPI, ++ }, ++#ifdef CONFIG_HTC_ASIC3_DS1WM ++ { ++ .name = "ds1wm", ++ .id = -1, ++ .rate = 5000000, ++ .parent = &clk_g, ++ .enable = asic3_clock_cdex_enable, ++ .disable = asic3_clock_cdex_disable, ++ .ctrlbit = CLOCK_CDEX_OWM, ++ }, ++#endif ++ { ++ .name = "pwm0", ++ .id = -1, ++ .parent = &clk_g, ++ .enable = asic3_clock_cdex_enable, ++ .disable = asic3_clock_cdex_disable, ++ .ctrlbit = CLOCK_CDEX_PWM0, ++ }, ++ { ++ .name = "pwm1", ++ .id = -1, ++ .parent = &clk_g, ++ .enable = asic3_clock_cdex_enable, ++ .disable = asic3_clock_cdex_disable, ++ .ctrlbit = CLOCK_CDEX_PWM1, ++ }, ++ { ++ .name = "led0", ++ .id = -1, ++ .parent = &clk_g, ++ .enable = asic3_clock_cdex_enable, ++ .disable = asic3_clock_cdex_disable, ++ .ctrlbit = CLOCK_CDEX_LED0, ++ }, ++ { ++ .name = "led1", ++ .id = -1, ++ .parent = &clk_g, ++ .enable = asic3_clock_cdex_enable, ++ .disable = asic3_clock_cdex_disable, ++ .ctrlbit = CLOCK_CDEX_LED1, ++ }, ++ { ++ .name = "led2", ++ .id = -1, ++ .parent = &clk_g, ++ .enable = asic3_clock_cdex_enable, ++ .disable = asic3_clock_cdex_disable, ++ .ctrlbit = CLOCK_CDEX_LED2, ++ }, ++ { ++ .name = "smbus", ++ .id = -1, ++ .parent = &clk_g, ++ .enable = asic3_clock_cdex_enable, ++ .disable = asic3_clock_cdex_disable, ++ .ctrlbit = CLOCK_CDEX_SMBUS, ++ }, ++ { ++ .name = "ex0", ++ .id = -1, ++ .parent = &clk_g, ++ .enable = asic3_clock_cdex_enable, ++ .disable = asic3_clock_cdex_disable, ++ .ctrlbit = CLOCK_CDEX_EX0, ++ }, ++ { ++ .name = "ex1", ++ .id = -1, ++ .parent = &clk_g, ++ .enable = asic3_clock_cdex_enable, ++ .disable = asic3_clock_cdex_disable, ++ .ctrlbit = CLOCK_CDEX_EX1, ++ }, ++}; ++ ++void asic3_set_extcf_select(struct device *dev, u32 bits, u32 val) ++{ ++ struct asic3_data *asic = dev->driver_data; ++ unsigned long flags; ++ u32 v; ++ ++ spin_lock_irqsave(&asic3_gpio_lock, flags); ++ v = __asic3_read_register(asic, IPAQ_ASIC3_OFFSET(EXTCF, Select)); ++ v = (v & ~bits) | val; ++ __asic3_write_register(asic, IPAQ_ASIC3_OFFSET(EXTCF, Select), v); ++ spin_unlock_irqrestore(&asic3_gpio_lock, flags); ++} ++EXPORT_SYMBOL(asic3_set_extcf_select); ++ ++void asic3_set_extcf_reset(struct device *dev, u32 bits, u32 val) ++{ ++ struct asic3_data *asic = dev->driver_data; ++ unsigned long flags; ++ u32 v; ++ ++ spin_lock_irqsave(&asic3_gpio_lock, flags); ++ v = __asic3_read_register(asic, IPAQ_ASIC3_OFFSET(EXTCF, Reset)); ++ v = (v & ~bits) | val; ++ __asic3_write_register(asic, IPAQ_ASIC3_OFFSET(EXTCF, Reset), v); ++ spin_unlock_irqrestore(&asic3_gpio_lock, flags); ++} ++EXPORT_SYMBOL(asic3_set_extcf_reset); ++ ++void asic3_set_sdhwctrl(struct device *dev, u32 bits, u32 val) ++{ ++ struct asic3_data *asic = dev->driver_data; ++ unsigned long flags; ++ u32 v; ++ ++ spin_lock_irqsave (&asic3_gpio_lock, flags); ++ v = __asic3_read_register(asic, IPAQ_ASIC3_OFFSET(SDHWCTRL, SDConf)); ++ v = (v & ~bits) | val; ++ __asic3_write_register(asic, IPAQ_ASIC3_OFFSET(SDHWCTRL, SDConf), v); ++ spin_unlock_irqrestore(&asic3_gpio_lock, flags); ++} ++EXPORT_SYMBOL(asic3_set_sdhwctrl); ++ ++ ++#define MAX_ASIC_ISR_LOOPS 20 ++#define _IPAQ_ASIC3_GPIO_Base_INCR \ ++ (_IPAQ_ASIC3_GPIO_B_Base - _IPAQ_ASIC3_GPIO_A_Base) ++ ++static inline void asic3_irq_flip_edge(struct asic3_data *asic, ++ u32 base, int bit) ++{ ++ u16 edge = __asic3_read_register(asic, ++ base + _IPAQ_ASIC3_GPIO_EdgeTrigger); ++ edge ^= bit; ++ __asic3_write_register(asic, ++ base + _IPAQ_ASIC3_GPIO_EdgeTrigger, edge); ++} ++ ++static void asic3_irq_demux(unsigned int irq, struct irq_desc *desc) ++{ ++ int iter; ++ struct asic3_data *asic; ++ ++ /* Acknowledge the parrent (i.e. CPU's) IRQ */ ++ desc->chip->ack(irq); ++ ++ asic = desc->handler_data; ++ ++ /* printk( KERN_NOTICE "asic3_irq_demux: irq=%d\n", irq ); */ ++ for (iter = 0 ; iter < MAX_ASIC_ISR_LOOPS; iter++) { ++ u32 status; ++ int bank; ++ ++ status = __asic3_read_register(asic, ++ IPAQ_ASIC3_OFFSET(INTR, PIntStat)); ++ /* Check all ten register bits */ ++ if ((status & 0x3ff) == 0) ++ break; ++ ++ /* Handle GPIO IRQs */ ++ for (bank = 0; bank < 4; bank++) { ++ if (status & (1 << bank)) { ++ unsigned long base, i, istat; ++ ++ base = _IPAQ_ASIC3_GPIO_A_Base ++ + bank * _IPAQ_ASIC3_GPIO_Base_INCR; ++ istat = __asic3_read_register(asic, ++ base + _IPAQ_ASIC3_GPIO_IntStatus); ++ /* IntStatus is write 0 to clear */ ++ /* XXX could miss interrupts! */ ++ __asic3_write_register(asic, ++ base + _IPAQ_ASIC3_GPIO_IntStatus, 0); ++ ++ for (i = 0; i < 16; i++) { ++ int bit = (1 << i); ++ unsigned int irqnr; ++ if (!(istat & bit)) ++ continue; ++ ++ irqnr = asic->irq_base ++ + (16 * bank) + i; ++ desc = irq_desc + irqnr; ++ desc->handle_irq(irqnr, desc); ++ if (asic->irq_bothedge[bank] & bit) { ++ asic3_irq_flip_edge(asic, base, ++ bit); ++ } ++ } ++ } ++ } ++ ++ /* Handle remaining IRQs in the status register */ ++ { ++ int i; ++ ++ for (i = ASIC3_LED0_IRQ; i <= ASIC3_OWM_IRQ; i++) { ++ /* They start at bit 4 and go up */ ++ if (status & (1 << (i - ASIC3_LED0_IRQ + 4))) { ++ desc = irq_desc + asic->irq_base + i; ++ desc->handle_irq(asic->irq_base + i, ++ desc); ++ } ++ } ++ } ++ ++ } ++ ++ if (iter >= MAX_ASIC_ISR_LOOPS) ++ printk(KERN_ERR "%s: interrupt processing overrun\n", ++ __FUNCTION__); ++} ++ ++static inline int asic3_irq_to_bank(struct asic3_data *asic, int irq) ++{ ++ int n; ++ ++ n = (irq - asic->irq_base) >> 4; ++ ++ return (n * (_IPAQ_ASIC3_GPIO_B_Base - _IPAQ_ASIC3_GPIO_A_Base)); ++} ++ ++static inline int asic3_irq_to_index(struct asic3_data *asic, int irq) ++{ ++ return (irq - asic->irq_base) & 15; ++} ++ ++static void asic3_mask_gpio_irq(unsigned int irq) ++{ ++ struct asic3_data *asic = get_irq_chip_data(irq); ++ u32 val, bank, index; ++ unsigned long flags; ++ ++ bank = asic3_irq_to_bank(asic, irq); ++ index = asic3_irq_to_index(asic, irq); ++ ++ spin_lock_irqsave(&asic3_gpio_lock, flags); ++ val = __asic3_read_register(asic, bank + _IPAQ_ASIC3_GPIO_Mask); ++ val |= 1 << index; ++ __asic3_write_register(asic, bank + _IPAQ_ASIC3_GPIO_Mask, val); ++ spin_unlock_irqrestore(&asic3_gpio_lock, flags); ++} ++ ++static void asic3_mask_irq(unsigned int irq) ++{ ++ struct asic3_data *asic = get_irq_chip_data(irq); ++ int regval; ++ ++ if (irq < ASIC3_NR_GPIO_IRQS) { ++ printk(KERN_ERR "asic3_base: gpio mask attempt, irq %d\n", ++ irq); ++ return; ++ } ++ ++ regval = __asic3_read_register(asic, ++ _IPAQ_ASIC3_INTR_Base + _IPAQ_ASIC3_INTR_IntMask); ++ ++ switch (irq - asic->irq_base) { ++ case ASIC3_LED0_IRQ: ++ __asic3_write_register(asic, ++ _IPAQ_ASIC3_INTR_Base + _IPAQ_ASIC3_INTR_IntMask, ++ regval & ~ASIC3_INTMASK_MASK0); ++ break; ++ case ASIC3_LED1_IRQ: ++ __asic3_write_register(asic, ++ _IPAQ_ASIC3_INTR_Base + _IPAQ_ASIC3_INTR_IntMask, ++ regval & ~ASIC3_INTMASK_MASK1); ++ break; ++ case ASIC3_LED2_IRQ: ++ __asic3_write_register(asic, ++ _IPAQ_ASIC3_INTR_Base + _IPAQ_ASIC3_INTR_IntMask, ++ regval & ~ASIC3_INTMASK_MASK2); ++ break; ++ case ASIC3_SPI_IRQ: ++ __asic3_write_register(asic, ++ _IPAQ_ASIC3_INTR_Base + _IPAQ_ASIC3_INTR_IntMask, ++ regval & ~ASIC3_INTMASK_MASK3); ++ break; ++ case ASIC3_SMBUS_IRQ: ++ __asic3_write_register(asic, ++ _IPAQ_ASIC3_INTR_Base + _IPAQ_ASIC3_INTR_IntMask, ++ regval & ~ASIC3_INTMASK_MASK4); ++ break; ++ case ASIC3_OWM_IRQ: ++ __asic3_write_register(asic, ++ _IPAQ_ASIC3_INTR_Base + _IPAQ_ASIC3_INTR_IntMask, ++ regval & ~ASIC3_INTMASK_MASK5); ++ break; ++ default: ++ printk(KERN_ERR "asic3_base: bad non-gpio irq %d\n", irq); ++ break; ++ } ++} ++ ++static void asic3_unmask_gpio_irq(unsigned int irq) ++{ ++ struct asic3_data *asic = get_irq_chip_data(irq); ++ u32 val, bank, index; ++ unsigned long flags; ++ ++ bank = asic3_irq_to_bank(asic, irq); ++ index = asic3_irq_to_index(asic, irq); ++ ++ spin_lock_irqsave(&asic3_gpio_lock, flags); ++ val = __asic3_read_register(asic, bank + _IPAQ_ASIC3_GPIO_Mask); ++ val &= ~(1 << index); ++ __asic3_write_register(asic, bank + _IPAQ_ASIC3_GPIO_Mask, val); ++ spin_unlock_irqrestore(&asic3_gpio_lock, flags); ++} ++ ++static void asic3_unmask_irq(unsigned int irq) ++{ ++ struct asic3_data *asic = get_irq_chip_data(irq); ++ int regval; ++ ++ if (irq < ASIC3_NR_GPIO_IRQS) { ++ printk(KERN_ERR "asic3_base: gpio unmask attempt, irq %d\n", ++ irq); ++ return; ++ } ++ ++ regval = __asic3_read_register(asic, ++ _IPAQ_ASIC3_INTR_Base + _IPAQ_ASIC3_INTR_IntMask); ++ ++ switch (irq - asic->irq_base) { ++ case ASIC3_LED0_IRQ: ++ __asic3_write_register(asic, ++ _IPAQ_ASIC3_INTR_Base + _IPAQ_ASIC3_INTR_IntMask, ++ regval | ASIC3_INTMASK_MASK0); ++ break; ++ case ASIC3_LED1_IRQ: ++ __asic3_write_register(asic, ++ _IPAQ_ASIC3_INTR_Base + _IPAQ_ASIC3_INTR_IntMask, ++ regval | ASIC3_INTMASK_MASK1); ++ break; ++ case ASIC3_LED2_IRQ: ++ __asic3_write_register(asic, ++ _IPAQ_ASIC3_INTR_Base + _IPAQ_ASIC3_INTR_IntMask, ++ regval | ASIC3_INTMASK_MASK2); ++ break; ++ case ASIC3_SPI_IRQ: ++ __asic3_write_register(asic, ++ _IPAQ_ASIC3_INTR_Base + _IPAQ_ASIC3_INTR_IntMask, ++ regval | ASIC3_INTMASK_MASK3); ++ break; ++ case ASIC3_SMBUS_IRQ: ++ __asic3_write_register(asic, ++ _IPAQ_ASIC3_INTR_Base + _IPAQ_ASIC3_INTR_IntMask, ++ regval | ASIC3_INTMASK_MASK4); ++ break; ++ case ASIC3_OWM_IRQ: ++ __asic3_write_register(asic, ++ _IPAQ_ASIC3_INTR_Base + _IPAQ_ASIC3_INTR_IntMask, ++ regval | ASIC3_INTMASK_MASK5); ++ break; ++ default: ++ printk(KERN_ERR "asic3_base: bad non-gpio irq %d\n", irq); ++ break; ++ } ++} ++ ++static int asic3_gpio_irq_type(unsigned int irq, unsigned int type) ++{ ++ struct asic3_data *asic = get_irq_chip_data(irq); ++ u32 bank, index; ++ unsigned long flags; ++ u16 trigger, level, edge, bit; ++ ++ bank = asic3_irq_to_bank(asic, irq); ++ index = asic3_irq_to_index(asic, irq); ++ bit = 1<<index; ++ ++ spin_lock_irqsave(&asic3_gpio_lock, flags); ++ level = __asic3_read_register(asic, ++ bank + _IPAQ_ASIC3_GPIO_LevelTrigger); ++ edge = __asic3_read_register(asic, ++ bank + _IPAQ_ASIC3_GPIO_EdgeTrigger); ++ trigger = __asic3_read_register(asic, ++ bank + _IPAQ_ASIC3_GPIO_TriggerType); ++ asic->irq_bothedge[(irq - asic->irq_base) >> 4] &= ~bit; ++ ++ if (type == IRQT_RISING) { ++ trigger |= bit; ++ edge |= bit; ++ } else if (type == IRQT_FALLING) { ++ trigger |= bit; ++ edge &= ~bit; ++ } else if (type == IRQT_BOTHEDGE) { ++ trigger |= bit; ++ if (asic3_gpio_get_value(asic->dev, irq - asic->irq_base)) ++ edge &= ~bit; ++ else ++ edge |= bit; ++ asic->irq_bothedge[(irq - asic->irq_base) >> 4] |= bit; ++ } else if (type == IRQT_LOW) { ++ trigger &= ~bit; ++ level &= ~bit; ++ } else if (type == IRQT_HIGH) { ++ trigger &= ~bit; ++ level |= bit; ++ } else { ++ /* ++ * if type == IRQT_NOEDGE, we should mask interrupts, but ++ * be careful to not unmask them if mask was also called. ++ * Probably need internal state for mask. ++ */ ++ printk(KERN_NOTICE "asic3: irq type not changed.\n"); ++ } ++ __asic3_write_register(asic, bank + _IPAQ_ASIC3_GPIO_LevelTrigger, ++ level); ++ __asic3_write_register(asic, bank + _IPAQ_ASIC3_GPIO_EdgeTrigger, ++ edge); ++ __asic3_write_register(asic, bank + _IPAQ_ASIC3_GPIO_TriggerType, ++ trigger); ++ spin_unlock_irqrestore(&asic3_gpio_lock, flags); ++ return 0; ++} ++ ++static struct irq_chip asic3_gpio_irq_chip = { ++ .name = "ASIC3-GPIO", ++ .ack = asic3_mask_gpio_irq, ++ .mask = asic3_mask_gpio_irq, ++ .unmask = asic3_unmask_gpio_irq, ++ .set_type = asic3_gpio_irq_type, ++}; ++ ++static struct irq_chip asic3_irq_chip = { ++ .name = "ASIC3", ++ .ack = asic3_mask_irq, ++ .mask = asic3_mask_irq, ++ .unmask = asic3_unmask_irq, ++}; ++ ++static void asic3_release(struct device *dev) ++{ ++ struct platform_device *sdev = to_platform_device(dev); ++ ++ kfree(sdev->resource); ++ kfree(sdev); ++} ++ ++int asic3_register_mmc(struct device *dev) ++{ ++ struct platform_device *sdev = kzalloc(sizeof(*sdev), GFP_KERNEL); ++ struct tmio_mmc_hwconfig *mmc_config = kmalloc(sizeof(*mmc_config), ++ GFP_KERNEL); ++ struct platform_device *pdev = to_platform_device(dev); ++ struct asic3_data *asic = dev->driver_data; ++ struct asic3_platform_data *asic3_pdata = dev->platform_data; ++ struct resource *res; ++ int rc; ++ ++ if (sdev == NULL || mmc_config == NULL) ++ return -ENOMEM; ++ ++ if (asic3_pdata->tmio_mmc_hwconfig) { ++ memcpy(mmc_config, asic3_pdata->tmio_mmc_hwconfig, ++ sizeof(*mmc_config)); ++ } else { ++ memset(mmc_config, 0, sizeof(*mmc_config)); ++ } ++ mmc_config->address_shift = asic->bus_shift; ++ ++ sdev->id = -1; ++ sdev->name = "asic3_mmc"; ++ sdev->dev.parent = dev; ++ sdev->num_resources = 2; ++ sdev->dev.platform_data = mmc_config; ++ sdev->dev.release = asic3_release; ++ ++ res = kzalloc(sdev->num_resources * sizeof(struct resource), ++ GFP_KERNEL); ++ if (res == NULL) { ++ kfree(sdev); ++ kfree(mmc_config); ++ return -ENOMEM; ++ } ++ sdev->resource = res; ++ ++ res[0].start = pdev->resource[2].start; ++ res[0].end = pdev->resource[2].end; ++ res[0].flags = IORESOURCE_MEM; ++ res[1].start = res[1].end = pdev->resource[3].start; ++ res[1].flags = IORESOURCE_IRQ; ++ ++ rc = platform_device_register(sdev); ++ if (rc) { ++ printk(KERN_ERR "asic3_base: " ++ "Could not register asic3_mmc device\n"); ++ kfree(res); ++ kfree(sdev); ++ return rc; ++ } ++ ++ asic->mmc_dev = sdev; ++ ++ return 0; ++} ++EXPORT_SYMBOL(asic3_register_mmc); ++ ++int asic3_unregister_mmc(struct device *dev) ++{ ++ struct asic3_data *asic = dev->driver_data; ++ platform_device_unregister(asic->mmc_dev); ++ asic->mmc_dev = 0; ++ ++ return 0; ++} ++EXPORT_SYMBOL(asic3_unregister_mmc); ++ ++#ifdef CONFIG_HTC_ASIC3_DS1WM ++/* ++ * DS1WM subdevice ++ */ ++ ++static void asic3_ds1wm_enable(struct platform_device *ds1wm_dev) ++{ ++ struct device *dev = ds1wm_dev->dev.parent; ++ ++ /* Turn on external clocks and the OWM clock */ ++ asic3_set_clock_cdex(dev, ++ CLOCK_CDEX_EX0 | CLOCK_CDEX_EX1 | CLOCK_CDEX_OWM, ++ CLOCK_CDEX_EX0 | CLOCK_CDEX_EX1 | CLOCK_CDEX_OWM); ++ ++ mdelay(1); ++ ++ asic3_set_extcf_reset(dev, ASIC3_EXTCF_OWM_RESET, ++ ASIC3_EXTCF_OWM_RESET); ++ mdelay(1); ++ asic3_set_extcf_reset(dev, ASIC3_EXTCF_OWM_RESET, 0); ++ mdelay(1); ++ ++ /* Clear OWM_SMB, set OWM_EN */ ++ asic3_set_extcf_select(dev, ++ ASIC3_EXTCF_OWM_SMB | ASIC3_EXTCF_OWM_EN, ++ 0 | ASIC3_EXTCF_OWM_EN); ++ ++ mdelay(1); ++} ++ ++static void asic3_ds1wm_disable(struct platform_device *ds1wm_dev) ++{ ++ struct device *dev = ds1wm_dev->dev.parent; ++ ++ asic3_set_extcf_select(dev, ++ ASIC3_EXTCF_OWM_SMB | ASIC3_EXTCF_OWM_EN, ++ 0 | 0); ++ ++ asic3_set_clock_cdex(dev, ++ CLOCK_CDEX_EX0 | CLOCK_CDEX_EX1 | CLOCK_CDEX_OWM, ++ CLOCK_CDEX_EX0 | CLOCK_CDEX_EX1 | 0); ++} ++ ++ ++static struct resource asic3_ds1wm_resources[] = { ++ { ++ .start = _IPAQ_ASIC3_OWM_Base, ++ .end = _IPAQ_ASIC3_OWM_Base + 0x14 - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .start = ASIC3_OWM_IRQ, ++ .end = ASIC3_OWM_IRQ, ++ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE | ++ IORESOURCE_IRQ_SOC_SUBDEVICE, ++ }, ++}; ++ ++static struct ds1wm_platform_data ds1wm_pd = { ++ .enable = asic3_ds1wm_enable, ++ .disable = asic3_ds1wm_disable, ++}; ++#endif ++ ++static struct soc_device_data asic3_blocks[] = { ++#ifdef CONFIG_HTC_ASIC3_DS1WM ++ { ++ .name = "ds1wm", ++ .res = asic3_ds1wm_resources, ++ .num_resources = ARRAY_SIZE(asic3_ds1wm_resources), ++ .hwconfig = &ds1wm_pd, ++ }, ++#endif ++}; ++ ++static int asic3_probe(struct platform_device *pdev) ++{ ++ struct asic3_platform_data *pdata = pdev->dev.platform_data; ++ struct asic3_data *asic; ++ struct device *dev = &pdev->dev; ++ unsigned long clksel; ++ int i, rc; ++ ++ asic = kzalloc(sizeof(struct asic3_data), GFP_KERNEL); ++ if (!asic) ++ return -ENOMEM; ++ ++ platform_set_drvdata(pdev, asic); ++ asic->dev = &pdev->dev; ++ ++ asic->mapping = ioremap(pdev->resource[0].start, IPAQ_ASIC3_MAP_SIZE); ++ if (!asic->mapping) { ++ printk(KERN_ERR "asic3: couldn't ioremap ASIC3\n"); ++ kfree (asic); ++ return -ENOMEM; ++ } ++ ++ if (pdata && pdata->bus_shift) ++ asic->bus_shift = pdata->bus_shift; ++ else ++ asic->bus_shift = 2; ++ ++ /* XXX: should get correct SD clock values from pdata struct */ ++ clksel = 0; ++ __asic3_write_register(asic, IPAQ_ASIC3_OFFSET(CLOCK, SEL), clksel); ++ ++ /* Register ASIC3's clocks. */ ++ clk_g.ctrlbit = (int)asic; ++ ++ if (clk_register(&clk_g) < 0) ++ printk(KERN_ERR "asic3: failed to register ASIC3 gclk\n"); ++ ++ for (i = 0; i < ARRAY_SIZE(asic3_clocks); i++) { ++ rc = clk_register(&asic3_clocks[i]); ++ if (rc < 0) ++ printk(KERN_ERR "asic3: " ++ "failed to register clock %s (%d)\n", ++ asic3_clocks[i].name, rc); ++ } ++ ++ __asic3_write_register(asic, IPAQ_ASIC3_GPIO_OFFSET(A, Mask), 0xffff); ++ __asic3_write_register(asic, IPAQ_ASIC3_GPIO_OFFSET(B, Mask), 0xffff); ++ __asic3_write_register(asic, IPAQ_ASIC3_GPIO_OFFSET(C, Mask), 0xffff); ++ __asic3_write_register(asic, IPAQ_ASIC3_GPIO_OFFSET(D, Mask), 0xffff); ++ ++ asic3_set_gpio_sleepmask_a(dev, 0xffff, 0xffff); ++ asic3_set_gpio_sleepmask_b(dev, 0xffff, 0xffff); ++ asic3_set_gpio_sleepmask_c(dev, 0xffff, 0xffff); ++ asic3_set_gpio_sleepmask_d(dev, 0xffff, 0xffff); ++ ++ if (pdata) { ++ asic3_set_gpio_out_a(dev, 0xffff, pdata->gpio_a.init); ++ asic3_set_gpio_out_b(dev, 0xffff, pdata->gpio_b.init); ++ asic3_set_gpio_out_c(dev, 0xffff, pdata->gpio_c.init); ++ asic3_set_gpio_out_d(dev, 0xffff, pdata->gpio_d.init); ++ ++ asic3_set_gpio_dir_a(dev, 0xffff, pdata->gpio_a.dir); ++ asic3_set_gpio_dir_b(dev, 0xffff, pdata->gpio_b.dir); ++ asic3_set_gpio_dir_c(dev, 0xffff, pdata->gpio_c.dir); ++ asic3_set_gpio_dir_d(dev, 0xffff, pdata->gpio_d.dir); ++ ++ asic3_set_gpio_sleepmask_a(dev, 0xffff, ++ pdata->gpio_a.sleep_mask); ++ asic3_set_gpio_sleepmask_b(dev, 0xffff, ++ pdata->gpio_b.sleep_mask); ++ asic3_set_gpio_sleepmask_c(dev, 0xffff, ++ pdata->gpio_c.sleep_mask); ++ asic3_set_gpio_sleepmask_d(dev, 0xffff, ++ pdata->gpio_d.sleep_mask); ++ ++ asic3_set_gpio_sleepout_a(dev, 0xffff, ++ pdata->gpio_a.sleep_out); ++ asic3_set_gpio_sleepout_b(dev, 0xffff, ++ pdata->gpio_b.sleep_out); ++ asic3_set_gpio_sleepout_c(dev, 0xffff, ++ pdata->gpio_c.sleep_out); ++ asic3_set_gpio_sleepout_d(dev, 0xffff, ++ pdata->gpio_d.sleep_out); ++ ++ asic3_set_gpio_battfaultout_a(dev, 0xffff, ++ pdata->gpio_a.batt_fault_out); ++ asic3_set_gpio_battfaultout_b(dev, 0xffff, ++ pdata->gpio_b.batt_fault_out); ++ asic3_set_gpio_battfaultout_c(dev, 0xffff, ++ pdata->gpio_c.batt_fault_out); ++ asic3_set_gpio_battfaultout_d(dev, 0xffff, ++ pdata->gpio_d.batt_fault_out); ++ ++ asic3_set_gpio_sleepconf_a(dev, 0xffff, ++ pdata->gpio_a.sleep_conf); ++ asic3_set_gpio_sleepconf_b(dev, 0xffff, ++ pdata->gpio_b.sleep_conf); ++ asic3_set_gpio_sleepconf_c(dev, 0xffff, ++ pdata->gpio_c.sleep_conf); ++ asic3_set_gpio_sleepconf_d(dev, 0xffff, ++ pdata->gpio_d.sleep_conf); ++ ++ asic3_set_gpio_alt_fn_a(dev, 0xffff, ++ pdata->gpio_a.alt_function); ++ asic3_set_gpio_alt_fn_b(dev, 0xffff, ++ pdata->gpio_b.alt_function); ++ asic3_set_gpio_alt_fn_c(dev, 0xffff, ++ pdata->gpio_c.alt_function); ++ asic3_set_gpio_alt_fn_d(dev, 0xffff, ++ pdata->gpio_d.alt_function); ++ } ++ ++ asic->irq_nr = -1; ++ asic->irq_base = -1; ++ ++ if (pdev->num_resources > 1) ++ asic->irq_nr = pdev->resource[1].start; ++ ++ if (asic->irq_nr != -1) { ++ unsigned int i; ++ ++ if (!pdata->irq_base) { ++ printk(KERN_ERR "asic3: IRQ base not specified\n"); ++ asic3_remove(pdev); ++ return -EINVAL; ++ } ++ ++ asic->irq_base = pdata->irq_base; ++ ++ /* turn on clock to IRQ controller */ ++ clksel |= CLOCK_SEL_CX; ++ __asic3_write_register(asic, IPAQ_ASIC3_OFFSET(CLOCK, SEL), ++ clksel); ++ ++ printk(KERN_INFO "asic3: using irq %d-%d on irq %d\n", ++ asic->irq_base, asic->irq_base + ASIC3_NR_IRQS - 1, ++ asic->irq_nr); ++ ++ for (i = 0 ; i < ASIC3_NR_IRQS ; i++) { ++ int irq = i + asic->irq_base; ++ if (i < ASIC3_NR_GPIO_IRQS) { ++ set_irq_chip(irq, &asic3_gpio_irq_chip); ++ set_irq_chip_data(irq, asic); ++ set_irq_handler(irq, handle_level_irq); ++ set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); ++ } else { ++ /* The remaining IRQs are not GPIO */ ++ set_irq_chip(irq, &asic3_irq_chip); ++ set_irq_chip_data(irq, asic); ++ set_irq_handler(irq, handle_level_irq); ++ set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); ++ } ++ } ++ ++ __asic3_write_register(asic, IPAQ_ASIC3_OFFSET(INTR, IntMask), ++ ASIC3_INTMASK_GINTMASK); ++ ++ set_irq_chained_handler(asic->irq_nr, asic3_irq_demux); ++ set_irq_type(asic->irq_nr, IRQT_RISING); ++ set_irq_data(asic->irq_nr, asic); ++ } ++ ++#ifdef CONFIG_HTC_ASIC3_DS1WM ++ ds1wm_pd.bus_shift = asic->bus_shift; ++#endif ++ ++ pdata->gpiodev_ops.get = asic3_gpio_get_value; ++ pdata->gpiodev_ops.set = asic3_gpio_set_value; ++ pdata->gpiodev_ops.to_irq = asic3_gpio_to_irq; ++ ++ soc_add_devices(pdev, asic3_blocks, ARRAY_SIZE(asic3_blocks), ++ &pdev->resource[0], ++ asic->bus_shift - ASIC3_DEFAULT_ADDR_SHIFT, ++ asic->irq_base); ++ ++ if (pdev->num_resources > 2) { ++ int rc; ++ rc = asic3_register_mmc(dev); ++ if (rc) { ++ asic3_remove(pdev); ++ return rc; ++ } ++ } ++ ++ if (pdata && pdata->num_child_platform_devs != 0) ++ platform_add_devices(pdata->child_platform_devs, ++ pdata->num_child_platform_devs); ++ ++ return 0; ++} ++ ++static int asic3_remove(struct platform_device *pdev) ++{ ++ struct asic3_platform_data *pdata = pdev->dev.platform_data; ++ struct asic3_data *asic = platform_get_drvdata(pdev); ++ int i; ++ ++ if (pdata && pdata->num_child_platform_devs != 0) { ++ for (i = 0; i < pdata->num_child_platform_devs; i++) { ++ platform_device_unregister( ++ pdata->child_platform_devs[i]); ++ } ++ } ++ ++ if (asic->irq_nr != -1) { ++ unsigned int i; ++ ++ for (i = 0 ; i < ASIC3_NR_IRQS ; i++) { ++ int irq = i + asic->irq_base; ++ set_irq_flags(irq, 0); ++ set_irq_handler (irq, NULL); ++ set_irq_chip (irq, NULL); ++ set_irq_chip_data(irq, NULL); ++ } ++ ++ set_irq_chained_handler(asic->irq_nr, NULL); ++ } ++ ++ if (asic->mmc_dev) ++ asic3_unregister_mmc(&pdev->dev); ++ ++ for (i = 0; i < ARRAY_SIZE(asic3_clocks); i++) ++ clk_unregister(&asic3_clocks[i]); ++ clk_unregister(&clk_g); ++ ++ __asic3_write_register(asic, IPAQ_ASIC3_OFFSET(CLOCK, SEL), 0); ++ __asic3_write_register(asic, IPAQ_ASIC3_OFFSET(INTR, IntMask), 0); ++ ++ iounmap(asic->mapping); ++ ++ kfree(asic); ++ ++ return 0; ++} ++ ++static void asic3_shutdown(struct platform_device *pdev) ++{ ++} ++ ++#define ASIC3_SUSPEND_CDEX_MASK \ ++ (CLOCK_CDEX_LED0 | CLOCK_CDEX_LED1 | CLOCK_CDEX_LED2) ++static unsigned short suspend_cdex; ++ ++static int asic3_suspend(struct platform_device *pdev, pm_message_t state) ++{ ++ struct asic3_data *asic = platform_get_drvdata(pdev); ++ suspend_cdex = __asic3_read_register(asic, ++ _IPAQ_ASIC3_CLOCK_Base + _IPAQ_ASIC3_CLOCK_CDEX); ++ /* The LEDs are still active during suspend */ ++ __asic3_write_register(asic, ++ _IPAQ_ASIC3_CLOCK_Base + _IPAQ_ASIC3_CLOCK_CDEX, ++ suspend_cdex & ASIC3_SUSPEND_CDEX_MASK); ++ return 0; ++} ++ ++static int asic3_resume(struct platform_device *pdev) ++{ ++ struct asic3_data *asic = platform_get_drvdata(pdev); ++ unsigned short intmask; ++ ++ __asic3_write_register(asic, IPAQ_ASIC3_OFFSET(CLOCK, CDEX), ++ suspend_cdex); ++ ++ if (asic->irq_nr != -1) { ++ /* Toggle the interrupt mask to try to get ASIC3 to show ++ * the CPU an interrupt edge. For more details see the ++ * kernel-discuss thread around 13 June 2005 with the ++ * subject "asic3 suspend / resume". */ ++ intmask = __asic3_read_register(asic, ++ IPAQ_ASIC3_OFFSET(INTR, IntMask)); ++ __asic3_write_register(asic, IPAQ_ASIC3_OFFSET(INTR, IntMask), ++ intmask & ~ASIC3_INTMASK_GINTMASK); ++ mdelay(1); ++ __asic3_write_register(asic, IPAQ_ASIC3_OFFSET(INTR, IntMask), ++ intmask | ASIC3_INTMASK_GINTMASK); ++ } ++ ++ return 0; ++} ++ ++static struct platform_driver asic3_device_driver = { ++ .driver = { ++ .name = "asic3", ++ }, ++ .probe = asic3_probe, ++ .remove = asic3_remove, ++ .suspend = asic3_suspend, ++ .resume = asic3_resume, ++ .shutdown = asic3_shutdown, ++}; ++ ++static int __init asic3_base_init(void) ++{ ++ int retval = 0; ++ retval = platform_driver_register(&asic3_device_driver); ++ return retval; ++} ++ ++static void __exit asic3_base_exit(void) ++{ ++ platform_driver_unregister(&asic3_device_driver); ++} ++ ++#ifdef MODULE ++module_init(asic3_base_init); ++#else /* start early for dependencies */ ++subsys_initcall(asic3_base_init); ++#endif ++module_exit(asic3_base_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Phil Blundell <pb@handhelds.org>"); ++MODULE_DESCRIPTION("Core driver for HTC ASIC3"); ++MODULE_SUPPORTED_DEVICE("asic3"); +Index: linux-2.6.24/drivers/mfd/soc-core.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/drivers/mfd/soc-core.c 2008-03-10 16:09:23.000000000 +0000 +@@ -0,0 +1,106 @@ ++/* ++ * drivers/soc/soc-core.c ++ * ++ * core SoC support ++ * Copyright (c) 2006 Ian Molton ++ * ++ * 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 file contains functionality used by many SoC type devices. ++ * ++ * Created: 2006-11-28 ++ * ++ */ ++ ++#include <linux/ioport.h> ++#include <linux/slab.h> ++#include <linux/kernel.h> ++#include <linux/platform_device.h> ++#include "soc-core.h" ++ ++void soc_free_devices(struct platform_device *devices, int nr_devs) ++{ ++ struct platform_device *dev = devices; ++ int i; ++ ++ for (i = 0; i < nr_devs; i++) { ++ struct resource *res = dev->resource; ++ platform_device_unregister(dev++); ++ kfree(res); ++ } ++ kfree(devices); ++} ++EXPORT_SYMBOL_GPL(soc_free_devices); ++ ++#define SIGNED_SHIFT(val, shift) ((shift) >= 0 ? ((val) << (shift)) : ((val) >> -(shift))) ++ ++struct platform_device *soc_add_devices(struct platform_device *dev, ++ struct soc_device_data *soc, int nr_devs, ++ struct resource *mem, ++ int relative_addr_shift, int irq_base) ++{ ++ struct platform_device *devices; ++ int i, r, base; ++ ++ devices = kzalloc(nr_devs * sizeof(struct platform_device), GFP_KERNEL); ++ if (!devices) ++ return NULL; ++ ++ for (i = 0; i < nr_devs; i++) { ++ struct platform_device *sdev = &devices[i]; ++ struct soc_device_data *blk = &soc[i]; ++ struct resource *res; ++ ++ sdev->id = -1; ++ sdev->name = blk->name; ++ ++ sdev->dev.parent = &dev->dev; ++ sdev->dev.platform_data = (void *)blk->hwconfig; ++ sdev->num_resources = blk->num_resources; ++ ++ /* Allocate space for the subdevice resources */ ++ res = kzalloc (blk->num_resources * sizeof (struct resource), GFP_KERNEL); ++ if (!res) ++ goto fail; ++ ++ for (r = 0 ; r < blk->num_resources ; r++) { ++ res[r].name = blk->res[r].name; // Fixme - should copy ++ ++ /* Find out base to use */ ++ base = 0; ++ if (blk->res[r].flags & IORESOURCE_MEM) { ++ base = mem->start; ++ } else if ((blk->res[r].flags & IORESOURCE_IRQ) && ++ (blk->res[r].flags & IORESOURCE_IRQ_MFD_SUBDEVICE)) { ++ base = irq_base; ++ } ++ ++ /* Adjust resource */ ++ if (blk->res[r].flags & IORESOURCE_MEM) { ++ res[r].parent = mem; ++ res[r].start = base + SIGNED_SHIFT(blk->res[r].start, relative_addr_shift); ++ res[r].end = base + SIGNED_SHIFT(blk->res[r].end, relative_addr_shift); ++ } else { ++ res[r].start = base + blk->res[r].start; ++ res[r].end = base + blk->res[r].end; ++ } ++ res[r].flags = blk->res[r].flags; ++ } ++ ++ sdev->resource = res; ++ if (platform_device_register(sdev)) { ++ kfree(res); ++ goto fail; ++ } ++ ++ printk(KERN_INFO "SoC: registering %s\n", blk->name); ++ } ++ return devices; ++ ++fail: ++ soc_free_devices(devices, i + 1); ++ return NULL; ++} ++EXPORT_SYMBOL_GPL(soc_add_devices); +Index: linux-2.6.24/drivers/mfd/soc-core.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/drivers/mfd/soc-core.h 2008-03-10 16:09:23.000000000 +0000 +@@ -0,0 +1,30 @@ ++/* ++ * drivers/soc/soc-core.h ++ * ++ * core SoC support ++ * Copyright (c) 2006 Ian Molton ++ * ++ * 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 file contains prototypes for the functions in soc-core.c ++ * ++ * Created: 2006-11-28 ++ * ++ */ ++ ++struct soc_device_data { ++ char *name; ++ struct resource *res; ++ int num_resources; ++ void *hwconfig; /* platform_data to pass to the subdevice */ ++}; ++ ++struct platform_device *soc_add_devices(struct platform_device *dev, ++ struct soc_device_data *soc, int n_devs, ++ struct resource *mem, ++ int relative_addr_shift, int irq_base); ++ ++void soc_free_devices(struct platform_device *devices, int nr_devs); ++ +Index: linux-2.6.24/include/asm-arm/arch-pxa/clock.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/include/asm-arm/arch-pxa/clock.h 2008-03-10 16:09:23.000000000 +0000 +@@ -0,0 +1,27 @@ ++/* ++ * linux/include/asm-arm/arch-pxa/clock.h ++ * ++ * Copyright (C) 2006 Erik Hovland ++ * ++ * 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. ++ */ ++ ++struct clk { ++ struct list_head node; ++ struct module *owner; ++ struct clk *parent; ++ const char *name; ++ int id; ++ unsigned int enabled; ++ unsigned long rate; ++ unsigned long ctrlbit; ++ ++ void (*enable)(struct clk *); ++ void (*disable)(struct clk *); ++}; ++ ++ ++extern int clk_register(struct clk *clk); ++extern void clk_unregister(struct clk *clk); +Index: linux-2.6.24/include/asm-arm/arch-pxa/htcuniversal-asic.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/include/asm-arm/arch-pxa/htcuniversal-asic.h 2008-03-10 16:09:23.000000000 +0000 +@@ -0,0 +1,213 @@ ++/* ++ * include/asm/arm/arch-pxa/htcuniversal-asic.h ++ * ++ * Authors: Giuseppe Zompatori <giuseppe_zompatori@yahoo.it> ++ * ++ * based on previews work, see below: ++ * ++ * include/asm/arm/arch-pxa/hx4700-asic.h ++ * Copyright (c) 2004 SDG Systems, LLC ++ * ++ */ ++ ++#ifndef _HTCUNIVERSAL_ASIC_H_ ++#define _HTCUNIVERSAL_ASIC_H_ ++ ++#include <asm/hardware/ipaq-asic3.h> ++ ++/* ASIC3 */ ++ ++#define HTCUNIVERSAL_ASIC3_GPIO_PHYS PXA_CS4_PHYS ++#define HTCUNIVERSAL_ASIC3_MMC_PHYS PXA_CS3_PHYS ++ ++/* TODO: some information is missing here */ ++ ++/* ASIC3 GPIO A bank */ ++ ++#define GPIOA_I2C_EN 0 /* Output */ ++#define GPIOA_SPK_PWR1_ON 1 /* Output */ ++#define GPIOA_AUDIO_PWR_ON 2 /* Output */ ++#define GPIOA_EARPHONE_PWR_ON 3 /* Output */ ++ ++#define GPIOA_UNKNOWN4 4 /* Output */ ++#define GPIOA_BUTTON_BACKLIGHT_N 5 /* Input */ ++#define GPIOA_SPK_PWR2_ON 6 /* Output */ ++#define GPIOA_BUTTON_RECORD_N 7 /* Input */ ++ ++#define GPIOA_BUTTON_CAMERA_N 8 /* Input */ ++#define GPIOA_UNKNOWN9 9 /* Output */ ++#define GPIOA_FLASHLIGHT 10 /* Output */ ++#define GPIOA_COVER_ROTATE_N 11 /* Input */ ++ ++#define GPIOA_TOUCHSCREEN_N 12 /* Input */ ++#define GPIOA_VOL_UP_N 13 /* Input */ ++#define GPIOA_VOL_DOWN_N 14 /* Input */ ++#define GPIOA_LCD_PWR5_ON 15 /* Output */ ++ ++/* ASIC3 GPIO B bank */ ++ ++#define GPIOB_BB_READY 0 /* Input */ ++#define GPIOB_CODEC_PDN 1 /* Output */ ++#define GPIOB_UNKNOWN2 2 /* Input */ ++#define GPIOB_BB_UNKNOWN3 3 /* Input */ ++ ++#define GPIOB_BT_IRQ 4 /* Input */ ++#define GPIOB_CLAMSHELL_N 5 /* Input */ ++#define GPIOB_LCD_PWR3_ON 6 /* Output */ ++#define GPIOB_BB_ALERT 7 /* Input */ ++ ++#define GPIOB_BB_RESET2 8 /* Output */ ++#define GPIOB_EARPHONE_N 9 /* Input */ ++#define GPIOB_MICRECORD_N 10 /* Input */ ++#define GPIOB_NIGHT_SENSOR 11 /* Input */ ++ ++#define GPIOB_UMTS_DCD 12 /* Input */ ++#define GPIOB_UNKNOWN13 13 /* Input */ ++#define GPIOB_CHARGE_EN 14 /* Output */ ++#define GPIOB_USB_PUEN 15 /* Output */ ++ ++/* ASIC3 GPIO C bank */ ++ ++#define GPIOC_LED_BTWIFI 0 /* Output */ ++#define GPIOC_LED_RED 1 /* Output */ ++#define GPIOC_LED_GREEN 2 /* Output */ ++#define GPIOC_BOARDID3 3 /* Input */ ++ ++#define GPIOC_WIFI_IRQ_N 4 /* Input */ ++#define GPIOC_WIFI_RESET 5 /* Output */ ++#define GPIOC_WIFI_PWR1_ON 6 /* Output */ ++#define GPIOC_BT_RESET 7 /* Output */ ++ ++#define GPIOC_UNKNOWN8 8 /* Output */ ++#define GPIOC_LCD_PWR1_ON 9 /* Output */ ++#define GPIOC_LCD_PWR2_ON 10 /* Output */ ++#define GPIOC_BOARDID2 11 /* Input */ ++ ++#define GPIOC_BOARDID1 12 /* Input */ ++#define GPIOC_BOARDID0 13 /* Input */ ++#define GPIOC_BT_PWR_ON 14 /* Output */ ++#define GPIOC_CHARGE_ON 15 /* Output */ ++ ++/* ASIC3 GPIO D bank */ ++ ++#define GPIOD_KEY_OK_N 0 /* Input */ ++#define GPIOD_KEY_RIGHT_N 1 /* Input */ ++#define GPIOD_KEY_LEFT_N 2 /* Input */ ++#define GPIOD_KEY_DOWN_N 3 /* Input */ ++ ++#define GPIOD_KEY_UP_N 4 /* Input */ ++#define GPIOD_SDIO_DET 5 /* Input */ ++#define GPIOD_WIFI_PWR2_ON 6 /* Output */ ++#define GPIOD_HW_REBOOT 7 /* Output */ ++ ++#define GPIOD_BB_RESET1 8 /* Output */ ++#define GPIOD_UNKNOWN9 9 /* Output */ ++#define GPIOD_VIBRA_PWR_ON 10 /* Output */ ++#define GPIOD_WIFI_PWR3_ON 11 /* Output */ ++ ++#define GPIOD_FL_PWR_ON 12 /* Output */ ++#define GPIOD_LCD_PWR4_ON 13 /* Output */ ++#define GPIOD_BL_KEYP_PWR_ON 14 /* Output */ ++#define GPIOD_BL_KEYB_PWR_ON 15 /* Output */ ++ ++extern struct platform_device htcuniversal_asic3; ++ ++/* ASIC3 GPIO A bank */ ++ ++#define GPIO_I2C_EN 0*16+GPIOA_I2C_EN ++#define GPIO_SPK_PWR1_ON 0*16+GPIOA_SPK_PWR1_ON ++#define GPIO_AUDIO_PWR_ON 0*16+GPIOA_AUDIO_PWR_ON ++#define GPIO_EARPHONE_PWR_ON 0*16+GPIOA_EARPHONE_PWR_ON ++ ++#define GPIO_UNKNOWNA4 0*16+GPIOA_UNKNOWN4 ++#define GPIO_BUTTON_BACKLIGHT_N 0*16+GPIOA_BUTTON_BACKLIGHT_N ++#define GPIO_SPK_PWR2_ON 0*16+GPIOA_SPK_PWR2_ON ++#define GPIO_BUTTON_RECORD_N 0*16+GPIOA_BUTTON_RECORD_N ++ ++#define GPIO_BUTTON_CAMERA_N 0*16+GPIOA_BUTTON_CAMERA_N ++#define GPIO_UNKNOWNA9 0*16+GPIOA_UNKNOWN9 ++#define GPIO_FLASHLIGHT 0*16+GPIOA_FLASHLIGHT ++#define GPIO_COVER_ROTATE_N 0*16+GPIOA_COVER_ROTATE_N ++ ++#define GPIO_TOUCHSCREEN_N 0*16+GPIOA_TOUCHSCREEN_N ++#define GPIO_VOL_UP_N 0*16+GPIOA_VOL_UP_N ++#define GPIO_VOL_DOWN_N 0*16+GPIOA_VOL_DOWN_N ++#define GPIO_LCD_PWR5_ON 0*16+GPIOA_LCD_PWR5_ON ++ ++/* ASIC3 GPIO B bank */ ++ ++#define GPIO_BB_READY 1*16+GPIOB_BB_READY ++#define GPIO_CODEC_PDN 1*16+GPIOB_CODEC_PDN ++#define GPIO_UNKNOWNB2 1*16+GPIOB_UNKNOWN2 ++#define GPIO_BB_UNKNOWN3 1*16+GPIOB_BB_UNKNOWN3 ++ ++#define GPIO_BT_IRQ 1*16+GPIOB_BT_IRQ ++#define GPIO_CLAMSHELL_N 1*16+GPIOB_CLAMSHELL_N ++#define GPIO_LCD_PWR3_ON 1*16+GPIOB_LCD_PWR3_ON ++#define GPIO_BB_ALERT 1*16+GPIOB_BB_ALERT ++ ++#define GPIO_BB_RESET2 1*16+GPIOB_BB_RESET2 ++#define GPIO_EARPHONE_N 1*16+GPIOB_EARPHONE_N ++#define GPIO_MICRECORD_N 1*16+GPIOB_MICRECORD_N ++#define GPIO_NIGHT_SENSOR 1*16+GPIOB_NIGHT_SENSOR ++ ++#define GPIO_UMTS_DCD 1*16+GPIOB_UMTS_DCD ++#define GPIO_UNKNOWNB13 1*16+GPIOB_UNKNOWN13 ++#define GPIO_CHARGE_EN 1*16+GPIOB_CHARGE_EN ++#define GPIO_USB_PUEN 1*16+GPIOB_USB_PUEN ++ ++/* ASIC3 GPIO C bank */ ++ ++#define GPIO_LED_BTWIFI 2*16+GPIOC_LED_BTWIFI ++#define GPIO_LED_RED 2*16+GPIOC_LED_RED ++#define GPIO_LED_GREEN 2*16+GPIOC_LED_GREEN ++#define GPIO_BOARDID3 2*16+GPIOC_BOARDID3 ++ ++#define GPIO_WIFI_IRQ_N 2*16+GPIOC_WIFI_IRQ_N ++#define GPIO_WIFI_RESET 2*16+GPIOC_WIFI_RESET ++#define GPIO_WIFI_PWR1_ON 2*16+GPIOC_WIFI_PWR1_ON ++#define GPIO_BT_RESET 2*16+GPIOC_BT_RESET ++ ++#define GPIO_UNKNOWNC8 2*16+GPIOC_UNKNOWN8 ++#define GPIO_LCD_PWR1_ON 2*16+GPIOC_LCD_PWR1_ON ++#define GPIO_LCD_PWR2_ON 2*16+GPIOC_LCD_PWR2_ON ++#define GPIO_BOARDID2 2*16+GPIOC_BOARDID2 ++ ++#define GPIO_BOARDID1 2*16+GPIOC_BOARDID1 ++#define GPIO_BOARDID0 2*16+GPIOC_BOARDID0 ++#define GPIO_BT_PWR_ON 2*16+GPIOC_BT_PWR_ON ++#define GPIO_CHARGE_ON 2*16+GPIOC_CHARGE_ON ++ ++/* ASIC3 GPIO D bank */ ++ ++#define GPIO_KEY_OK_N 3*16+GPIOD_KEY_OK_N ++#define GPIO_KEY_RIGHT_N 3*16+GPIOD_KEY_RIGHT_N ++#define GPIO_KEY_LEFT_N 3*16+GPIOD_KEY_LEFT_N ++#define GPIO_KEY_DOWN_N 3*16+GPIOD_KEY_DOWN_N ++ ++#define GPIO_KEY_UP_N 3*16+GPIOD_KEY_UP_N ++#define GPIO_SDIO_DET 3*16+GPIOD_SDIO_DET ++#define GPIO_WIFI_PWR2_ON 3*16+GPIOD_WIFI_PWR2_ON ++#define GPIO_HW_REBOOT 3*16+GPIOD_HW_REBOOT ++ ++#define GPIO_BB_RESET1 3*16+GPIOD_BB_RESET1 ++#define GPIO_UNKNOWND9 3*16+GPIOD_UNKNOWN9 ++#define GPIO_VIBRA_PWR_ON 3*16+GPIOD_VIBRA_PWR_ON ++#define GPIO_WIFI_PWR3_ON 3*16+GPIOD_WIFI_PWR3_ON ++ ++#define GPIO_FL_PWR_ON 3*16+GPIOD_FL_PWR_ON ++#define GPIO_LCD_PWR4_ON 3*16+GPIOD_LCD_PWR4_ON ++#define GPIO_BL_KEYP_PWR_ON 3*16+GPIOD_BL_KEYP_PWR_ON ++#define GPIO_BL_KEYB_PWR_ON 3*16+GPIOD_BL_KEYB_PWR_ON ++ ++#define HTCUNIVERSAL_EGPIO_BASE PXA_CS2_PHYS+0x02000000 ++ ++#define EGPIO4_ON 4 /* something */ ++#define EGPIO5_BT_3V3_ON 5 /* Bluetooth related */ ++#define EGPIO6_WIFI_ON 6 /* WLAN related*/ ++ ++extern void htcuniversal_egpio_enable( u_int16_t bits ); ++extern void htcuniversal_egpio_disable( u_int16_t bits ); ++ ++#endif /* _HTCUNIVERSAL_ASIC_H_ */ ++ +Index: linux-2.6.24/include/asm-arm/arch-pxa/htcuniversal-gpio.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/include/asm-arm/arch-pxa/htcuniversal-gpio.h 2008-03-10 16:09:23.000000000 +0000 +@@ -0,0 +1,220 @@ ++/* ++ * include/asm-arm/arch-pxa/htcuniversal-gpio.h ++ * History: ++ * ++ * 2004-12-10 Michael Opdenacker. Wrote down GPIO settings as identified by Jamey Hicks. ++ * Reused the h2200-gpio.h file as a template. ++ */ ++ ++#ifndef _HTCUNIVERSAL_GPIO_H_ ++#define _HTCUNIVERSAL_GPIO_H_ ++ ++#include <asm/arch/pxa-regs.h> ++ ++#define GET_HTCUNIVERSAL_GPIO(gpio) \ ++ (GPLR(GPIO_NR_HTCUNIVERSAL_ ## gpio) & GPIO_bit(GPIO_NR_HTCUNIVERSAL_ ## gpio)) ++ ++#define SET_HTCUNIVERSAL_GPIO(gpio, setp) \ ++do { \ ++if (setp) \ ++ GPSR(GPIO_NR_HTCUNIVERSAL_ ## gpio) = GPIO_bit(GPIO_NR_HTCUNIVERSAL_ ## gpio); \ ++else \ ++ GPCR(GPIO_NR_HTCUNIVERSAL_ ## gpio) = GPIO_bit(GPIO_NR_HTCUNIVERSAL_ ## gpio); \ ++} while (0) ++ ++#define SET_HTCUNIVERSAL_GPIO_N(gpio, setp) \ ++do { \ ++if (setp) \ ++ GPCR(GPIO_NR_HTCUNIVERSAL_ ## gpio ## _N) = GPIO_bit(GPIO_NR_HTCUNIVERSAL_ ## gpio ## _N); \ ++else \ ++ GPSR(GPIO_NR_HTCUNIVERSAL_ ## gpio ## _N) = GPIO_bit(GPIO_NR_HTCUNIVERSAL_ ## gpio ## _N); \ ++} while (0) ++ ++#define HTCUNIVERSAL_IRQ(gpio) \ ++ IRQ_GPIO(GPIO_NR_HTCUNIVERSAL_ ## gpio) ++ ++#define GPIO_NR_HTCUNIVERSAL_KEY_ON_N 0 ++#define GPIO_NR_HTCUNIVERSAL_GP_RST_N 1 ++ ++#define GPIO_NR_HTCUNIVERSAL_USB_DET 9 ++#define GPIO_NR_HTCUNIVERSAL_POWER_DET 10 ++ ++#define GPIO_NR_HTCUNIVERSAL_CIF_DD7 12 ++#define GPIO_NR_HTCUNIVERSAL_ASIC3_SDIO_INT_N 13 ++#define GPIO_NR_HTCUNIVERSAL_ASIC3_EXT_INT 14 ++#define GPIO_NR_HTCUNIVERSAL_CS1_N 15 ++ ++#define GPIO_NR_HTCUNIVERSAL_CIF_DD6 17 ++#define GPIO_NR_HTCUNIVERSAL_RDY 18 ++ ++#define GPIO_NR_HTCUNIVERSAL_PHONE_START 19 ++ ++#define GPIO_NR_HTCUNIVERSAL_KP_MKOUT7 22 ++#define GPIO_NR_HTCUNIVERSAL_SPI_CLK 23 ++#define GPIO_NR_HTCUNIVERSAL_SPI_FRM 24 ++#define GPIO_NR_HTCUNIVERSAL_SPI_DO 25 ++#define GPIO_NR_HTCUNIVERSAL_SPI_DI 26 ++ ++#define GPIO_NR_HTCUNIVERSAL_CODEC_ON 27 ++#define GPIO_NR_HTCUNIVERSAL_I2S_BCK 28 ++#define GPIO_NR_HTCUNIVERSAL_I2S_DIN 29 ++#define GPIO_NR_HTCUNIVERSAL_I2S_DOUT 30 ++#define GPIO_NR_HTCUNIVERSAL_I2S_SYNC 31 ++ ++#define GPIO_NR_HTCUNIVERSAL_RS232_ON 32 ++#define GPIO_NR_HTCUNIVERSAL_CS5_N 33 ++ ++#define GPIO_NR_HTCUNIVERSAL_PHONE_RXD 34 ++#define GPIO_NR_HTCUNIVERSAL_PHONE_UART_CTS 35 ++#define GPIO_NR_HTCUNIVERSAL_KP_MKIN7 36 ++#define GPIO_NR_HTCUNIVERSAL_KP_MKIN3 37 ++#define GPIO_NR_HTCUNIVERSAL_KP_MKIN4 38 ++#define GPIO_NR_HTCUNIVERSAL_PHONE_TXD 39 ++#define GPIO_NR_HTCUNIVERSAL_KP_MKOUT6 40 ++#define GPIO_NR_HTCUNIVERSAL_PHONE_UART_RTS 41 ++#define GPIO_NR_HTCUNIVERSAL_BT_RXD 42 ++#define GPIO_NR_HTCUNIVERSAL_BT_TXD 43 ++#define GPIO_NR_HTCUNIVERSAL_BT_UART_CTS 44 ++#define GPIO_NR_HTCUNIVERSAL_BT_UART_RTS 45 ++ ++#define GPIO_NR_HTCUNIVERSAL_SIR_RXD 42 ++#define GPIO_NR_HTCUNIVERSAL_SIR_TXD 43 ++ ++#define GPIO_NR_HTCUNIVERSAL_POE_N 48 ++#define GPIO_NR_HTCUNIVERSAL_PWE_N 49 ++#define GPIO_NR_HTCUNIVERSAL_CIF_DD3 50 ++#define GPIO_NR_HTCUNIVERSAL_CIF_DD2 51 ++#define GPIO_NR_HTCUNIVERSAL_CIF_DD4 52 ++ ++#define GPIO_NR_HTCUNIVERSAL_CIF_MCLK 53 ++#define GPIO_NR_HTCUNIVERSAL_CIF_PCLK 54 ++#define GPIO_NR_HTCUNIVERSAL_CIF_DD1 55 ++ ++#define GPIO_NR_HTCUNIVERSAL_LDD0 58 ++#define GPIO_NR_HTCUNIVERSAL_LDD1 59 ++#define GPIO_NR_HTCUNIVERSAL_LDD2 60 ++#define GPIO_NR_HTCUNIVERSAL_LDD3 61 ++#define GPIO_NR_HTCUNIVERSAL_LDD4 62 ++#define GPIO_NR_HTCUNIVERSAL_LDD5 63 ++#define GPIO_NR_HTCUNIVERSAL_LDD6 64 ++#define GPIO_NR_HTCUNIVERSAL_LDD7 65 ++#define GPIO_NR_HTCUNIVERSAL_LDD8 66 ++#define GPIO_NR_HTCUNIVERSAL_LDD9 67 ++#define GPIO_NR_HTCUNIVERSAL_LDD10 68 ++#define GPIO_NR_HTCUNIVERSAL_LDD11 69 ++#define GPIO_NR_HTCUNIVERSAL_LDD12 70 ++#define GPIO_NR_HTCUNIVERSAL_LDD13 71 ++#define GPIO_NR_HTCUNIVERSAL_LDD14 72 ++#define GPIO_NR_HTCUNIVERSAL_LDD15 73 ++ ++#define GPIO_NR_HTCUNIVERSAL_LFCLK_RD 74 ++#define GPIO_NR_HTCUNIVERSAL_LFCLK_A0 75 ++#define GPIO_NR_HTCUNIVERSAL_LFCLK_WR 76 ++#define GPIO_NR_HTCUNIVERSAL_LBIAS 77 ++ ++#define GPIO_NR_HTCUNIVERSAL_CS2_N 78 ++#define GPIO_NR_HTCUNIVERSAL_CS3_N 79 ++#define GPIO_NR_HTCUNIVERSAL_CS4_N 80 ++#define GPIO_NR_HTCUNIVERSAL_CIF_DD0 81 ++#define GPIO_NR_HTCUNIVERSAL_CIF_DD5 82 ++ ++#define GPIO_NR_HTCUNIVERSAL_CIF_LV 84 ++#define GPIO_NR_HTCUNIVERSAL_CIF_FV 85 ++ ++#define GPIO_NR_HTCUNIVERSAL_LCD1 86 ++#define GPIO_NR_HTCUNIVERSAL_LCD2 87 ++ ++#define GPIO_NR_HTCUNIVERSAL_KP_MKIN5 90 ++#define GPIO_NR_HTCUNIVERSAL_KP_MKIN6 91 ++ ++#define GPIO_NR_HTCUNIVERSAL_DREQ1 97 ++ ++#define GPIO_NR_HTCUNIVERSAL_PHONE_RESET 98 ++ ++#define GPIO_NR_HTCUNIVERSAL_KP_MKIN0 100 ++#define GPIO_NR_HTCUNIVERSAL_KP_MKIN1 101 ++#define GPIO_NR_HTCUNIVERSAL_KP_MKIN2 102 ++#define GPIO_NR_HTCUNIVERSAL_KP_MKOUT0 103 ++#define GPIO_NR_HTCUNIVERSAL_KP_MKOUT1 104 ++#define GPIO_NR_HTCUNIVERSAL_KP_MKOUT2 105 ++#define GPIO_NR_HTCUNIVERSAL_KP_MKOUT3 106 ++#define GPIO_NR_HTCUNIVERSAL_KP_MKOUT4 107 ++#define GPIO_NR_HTCUNIVERSAL_KP_MKOUT5 108 ++ ++#define GPIO_NR_HTCUNIVERSAL_PHONE_UNKNOWN 109 ++#define GPIO_NR_HTCUNIVERSAL_PHONE_OFF 110 ++ ++#define GPIO_NR_HTCUNIVERSAL_USB_PUEN 112 ++#define GPIO_NR_HTCUNIVERSAL_I2S_SYSCLK 113 ++ ++#define GPIO_NR_HTCUNIVERSAL_PWM_OUT1 115 ++ ++#define GPIO_NR_HTCUNIVERSAL_I2C_SCL 117 ++#define GPIO_NR_HTCUNIVERSAL_I2C_SDA 118 ++ ++#if 0 ++#define GPIO_NR_HTCUNIVERSAL_CPU_BATT_FAULT_N ++#define GPIO_NR_HTCUNIVERSAL_ASIC3_RESET_N ++#define GPIO_NR_HTCUNIVERSAL_CHARGE_EN_N ++#define GPIO_NR_HTCUNIVERSAL_FLASH_VPEN ++#define GPIO_NR_HTCUNIVERSAL_BATT_OFF ++#define GPIO_NR_HTCUNIVERSAL_USB_CHARGE_RATE ++#define GPIO_NR_HTCUNIVERSAL_BL_DETECT_N ++#define GPIO_NR_HTCUNIVERSAL_CPU_HW_RESET_N ++#endif ++ ++ ++#define GPIO_NR_HTCUNIVERSAL_TOUCHSCREEN_SPI_CLK_MD (23 | GPIO_ALT_FN_2_OUT) ++#define GPIO_NR_HTCUNIVERSAL_TOUCHSCREEN_SPI_FRM_MD (24 | GPIO_ALT_FN_2_OUT) ++#define GPIO_NR_HTCUNIVERSAL_TOUCHSCREEN_SPI_DO_MD (25 | GPIO_ALT_FN_2_OUT) ++#define GPIO_NR_HTCUNIVERSAL_TOUCHSCREEN_SPI_DI_MD (26 | GPIO_ALT_FN_1_IN) ++ ++#define GPIO_NR_HTCUNIVERSAL_I2S_BCK_MD (28 | GPIO_ALT_FN_1_OUT) ++#define GPIO_NR_HTCUNIVERSAL_I2S_DIN_MD (29 | GPIO_ALT_FN_2_IN) ++#define GPIO_NR_HTCUNIVERSAL_I2S_DOUT_MD (30 | GPIO_ALT_FN_1_OUT) ++#define GPIO_NR_HTCUNIVERSAL_I2S_SYNC_MD (31 | GPIO_ALT_FN_1_OUT) ++ ++#define GPIO_NR_HTCUNIVERSAL_PHONE_RXD_MD (34 | GPIO_ALT_FN_1_IN) ++#define GPIO_NR_HTCUNIVERSAL_PHONE_UART_CTS_MD (35 | GPIO_ALT_FN_1_IN) ++ ++#define GPIO_NR_HTCUNIVERSAL_PHONE_TXD_MD (39 | GPIO_ALT_FN_2_OUT) ++#define GPIO_NR_HTCUNIVERSAL_PHONE_UART_RTS_MD (41 | GPIO_ALT_FN_2_OUT) ++ ++#define GPIO_NR_HTCUNIVERSAL_BT_RXD_MD (42 | GPIO_ALT_FN_1_IN) ++#define GPIO_NR_HTCUNIVERSAL_BT_TXD_MD (43 | GPIO_ALT_FN_2_OUT) ++#define GPIO_NR_HTCUNIVERSAL_BT_UART_CTS_MD (44 | GPIO_ALT_FN_1_IN) ++#define GPIO_NR_HTCUNIVERSAL_BT_UART_RTS_MD (45 | GPIO_ALT_FN_2_OUT) ++ ++#define GPIO_NR_HTCUNIVERSAL_SIR_RXD_MD (46 | GPIO_ALT_FN_2_IN) ++#define GPIO_NR_HTCUNIVERSAL_SIR_TXD_MD (47 | GPIO_ALT_FN_1_OUT) ++ ++#define GPIO_NR_HTCUNIVERSAL_POE_N_MD (48 | GPIO_ALT_FN_2_OUT | GPIO_DFLT_HIGH) ++#define GPIO_NR_HTCUNIVERSAL_PWE_N_MD (49 | GPIO_ALT_FN_2_OUT | GPIO_DFLT_HIGH) ++ ++#define GPIO_NR_HTCUNIVERSAL_KP_MKIN0_MD (GPIO_NR_HTCUNIVERSAL_KP_MKIN0 | GPIO_ALT_FN_1_IN) ++#define GPIO_NR_HTCUNIVERSAL_KP_MKIN1_MD (GPIO_NR_HTCUNIVERSAL_KP_MKIN1 | GPIO_ALT_FN_1_IN) ++#define GPIO_NR_HTCUNIVERSAL_KP_MKIN2_MD (GPIO_NR_HTCUNIVERSAL_KP_MKIN2 | GPIO_ALT_FN_1_IN) ++#define GPIO_NR_HTCUNIVERSAL_KP_MKIN3_MD (GPIO_NR_HTCUNIVERSAL_KP_MKIN3 | GPIO_ALT_FN_3_IN) ++#define GPIO_NR_HTCUNIVERSAL_KP_MKIN4_MD (GPIO_NR_HTCUNIVERSAL_KP_MKIN4 | GPIO_ALT_FN_2_IN) ++#define GPIO_NR_HTCUNIVERSAL_KP_MKIN5_MD (GPIO_NR_HTCUNIVERSAL_KP_MKIN5 | GPIO_ALT_FN_1_IN) ++#define GPIO_NR_HTCUNIVERSAL_KP_MKIN6_MD (GPIO_NR_HTCUNIVERSAL_KP_MKIN6 | GPIO_ALT_FN_1_IN) ++#define GPIO_NR_HTCUNIVERSAL_KP_MKIN7_MD (GPIO_NR_HTCUNIVERSAL_KP_MKIN7 | GPIO_ALT_FN_3_IN) ++ ++#define GPIO_NR_HTCUNIVERSAL_KP_MKOUT0_MD (GPIO_NR_HTCUNIVERSAL_KP_MKOUT0 | GPIO_ALT_FN_2_OUT) ++#define GPIO_NR_HTCUNIVERSAL_KP_MKOUT1_MD (GPIO_NR_HTCUNIVERSAL_KP_MKOUT1 | GPIO_ALT_FN_2_OUT) ++#define GPIO_NR_HTCUNIVERSAL_KP_MKOUT2_MD (GPIO_NR_HTCUNIVERSAL_KP_MKOUT2 | GPIO_ALT_FN_2_OUT) ++#define GPIO_NR_HTCUNIVERSAL_KP_MKOUT3_MD (GPIO_NR_HTCUNIVERSAL_KP_MKOUT3 | GPIO_ALT_FN_2_OUT) ++#define GPIO_NR_HTCUNIVERSAL_KP_MKOUT4_MD (GPIO_NR_HTCUNIVERSAL_KP_MKOUT4 | GPIO_ALT_FN_2_OUT) ++#define GPIO_NR_HTCUNIVERSAL_KP_MKOUT5_MD (GPIO_NR_HTCUNIVERSAL_KP_MKOUT5 | GPIO_ALT_FN_2_OUT) ++#define GPIO_NR_HTCUNIVERSAL_KP_MKOUT6_MD (GPIO_NR_HTCUNIVERSAL_KP_MKOUT6 | GPIO_ALT_FN_1_OUT) ++#define GPIO_NR_HTCUNIVERSAL_KP_MKOUT7_MD (GPIO_NR_HTCUNIVERSAL_KP_MKOUT7 | GPIO_ALT_FN_1_OUT) ++ ++ ++#define GPIO_NR_HTCUNIVERSAL_I2S_SYSCLK_MD (113 | GPIO_ALT_FN_1_OUT) ++ ++#define GPIO_NR_HTCUNIVERSAL_PWM1OUT_MD (115 | GPIO_ALT_FN_3_OUT) ++ ++#define GPIO_NR_HTCUNIVERSAL_I2C_SCL_MD (117 | GPIO_ALT_FN_1_OUT) ++#define GPIO_NR_HTCUNIVERSAL_I2C_SDA_MD (118 | GPIO_ALT_FN_1_OUT) ++ ++#endif /* _HTCUNIVERSAL_GPIO_H */ +Index: linux-2.6.24/include/asm-arm/arch-pxa/htcuniversal-init.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/include/asm-arm/arch-pxa/htcuniversal-init.h 2008-03-10 16:09:23.000000000 +0000 +@@ -0,0 +1,14 @@ ++/* ++ * include/asm/arm/arch-pxa/htcuniversal-init.h ++ * Copyright (c) 2004 SDG Systems, LLC ++ */ ++ ++#ifndef _HTCUNIVERSAL_INIT_H_ ++#define _HTCUNIVERSAL_INIT_H_ ++ ++/* htcuniversal initialization data should be found here ++ * See -init.h files from other devices for details ++ */ ++ ++#endif /* _HTCUNIVERSAL_INIT_H_ */ ++ +Index: linux-2.6.24/include/asm-arm/arch-pxa/htcuniversal.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/include/asm-arm/arch-pxa/htcuniversal.h 2008-03-10 16:09:23.000000000 +0000 +@@ -0,0 +1,3 @@ ++#include <asm/arch/irqs.h> ++ ++#define HTCUNIVERSAL_ASIC3_IRQ_BASE IRQ_BOARD_START +Index: linux-2.6.24/include/asm-arm/arch-pxa/pxa-pm_ll.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/include/asm-arm/arch-pxa/pxa-pm_ll.h 2008-03-10 16:09:23.000000000 +0000 +@@ -0,0 +1,6 @@ ++struct pxa_ll_pm_ops { ++ void (*suspend)(unsigned long); ++ void (*resume)(void); ++}; ++ ++extern struct pxa_ll_pm_ops *pxa_pm_set_ll_ops(struct pxa_ll_pm_ops *new_ops); +Index: linux-2.6.24/include/asm-arm/hardware/asic3_keys.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/include/asm-arm/hardware/asic3_keys.h 2008-03-10 16:09:23.000000000 +0000 +@@ -0,0 +1,18 @@ ++#include <linux/input.h> ++ ++struct asic3_keys_button { ++ /* Configuration parameters */ ++ int keycode; ++ int gpio; ++ int active_low; ++ char *desc; ++ int type; ++ /* Internal state vars - add below */ ++}; ++ ++struct asic3_keys_platform_data { ++ struct asic3_keys_button *buttons; ++ int nbuttons; ++ struct input_dev *input; ++ struct device *asic3_dev; ++}; +Index: linux-2.6.24/include/asm-arm/hardware/asic3_leds.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/include/asm-arm/hardware/asic3_leds.h 2008-03-10 16:09:23.000000000 +0000 +@@ -0,0 +1,34 @@ ++/* ++ * LEDs support for HTC ASIC3 devices. ++ * ++ * Copyright (c) 2006 Anton Vorontsov <cbou@mail.ru> ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive for ++ * more details. ++ * ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/platform_device.h> ++#include <linux/leds.h> ++ ++struct asic3_leds_machinfo; ++ ++struct asic3_led { ++ struct led_classdev led_cdev; ++ int hw_num; /* Number of "hardware-accelerated" led */ ++ int gpio_num; /* Number of GPIO if hw_num == -1 */ ++ struct asic3_leds_machinfo *machinfo; ++}; ++ ++struct asic3_leds_machinfo { ++ int num_leds; ++ struct asic3_led *leds; ++ struct platform_device *asic3_pdev; ++}; ++ ++extern int asic3_leds_register(void); ++extern void asic3_leds_unregister(void); ++ +Index: linux-2.6.24/include/asm-arm/hardware/ipaq-asic3.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/include/asm-arm/hardware/ipaq-asic3.h 2008-03-10 16:09:23.000000000 +0000 +@@ -0,0 +1,602 @@ ++/* ++ * ++ * Definitions for the HTC ASIC3 chip found in several handheld devices ++ * ++ * Copyright 2001 Compaq Computer Corporation. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * COMPAQ COMPUTER CORPORATION MAKES NO WARRANTIES, EXPRESSED OR IMPLIED, ++ * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS ++ * FITNESS FOR ANY PARTICULAR PURPOSE. ++ * ++ * Author: Andrew Christian ++ * ++ */ ++ ++#ifndef IPAQ_ASIC3_H ++#define IPAQ_ASIC3_H ++ ++/****************************************************/ ++/* IPAQ, ASIC #3, replaces ASIC #1 */ ++ ++#define IPAQ_ASIC3_OFFSET(x,y) (_IPAQ_ASIC3_ ## x ## _Base + _IPAQ_ASIC3_ ## x ## _ ## y) ++#define IPAQ_ASIC3_GPIO_OFFSET(x,y) (_IPAQ_ASIC3_GPIO_ ## x ## _Base + _IPAQ_ASIC3_GPIO_ ## y) ++ ++ ++/* All offsets below are specified with the following address bus shift */ ++#define ASIC3_DEFAULT_ADDR_SHIFT 2 ++ ++#define _IPAQ_ASIC3_GPIO_A_Base 0x0000 ++#define _IPAQ_ASIC3_GPIO_B_Base 0x0100 ++#define _IPAQ_ASIC3_GPIO_C_Base 0x0200 ++#define _IPAQ_ASIC3_GPIO_D_Base 0x0300 ++ ++#define _IPAQ_ASIC3_GPIO_Mask 0x00 /* R/W 0:don't mask, 1:mask interrupt */ ++#define _IPAQ_ASIC3_GPIO_Direction 0x04 /* R/W 0:input, 1:output */ ++#define _IPAQ_ASIC3_GPIO_Out 0x08 /* R/W 0:output low, 1:output high */ ++#define _IPAQ_ASIC3_GPIO_TriggerType 0x0c /* R/W 0:level, 1:edge */ ++#define _IPAQ_ASIC3_GPIO_EdgeTrigger 0x10 /* R/W 0:falling, 1:rising */ ++#define _IPAQ_ASIC3_GPIO_LevelTrigger 0x14 /* R/W 0:low, 1:high level detect */ ++#define _IPAQ_ASIC3_GPIO_SleepMask 0x18 /* R/W 0:don't mask, 1:mask trigger in sleep mode */ ++#define _IPAQ_ASIC3_GPIO_SleepOut 0x1c /* R/W level 0:low, 1:high in sleep mode */ ++#define _IPAQ_ASIC3_GPIO_BattFaultOut 0x20 /* R/W level 0:low, 1:high in batt_fault */ ++#define _IPAQ_ASIC3_GPIO_IntStatus 0x24 /* R/W 0:none, 1:detect */ ++#define _IPAQ_ASIC3_GPIO_AltFunction 0x28 /* R/W 0:normal control 1:LED register control */ ++#define _IPAQ_ASIC3_GPIO_SleepConf 0x2c /* R/W bit 1: autosleep 0: disable gposlpout in normal mode, enable gposlpout in sleep mode */ ++#define _IPAQ_ASIC3_GPIO_Status 0x30 /* R Pin status */ ++ ++#define IPAQ_ASIC3_GPIO_A_MASK(_b) IPAQ_ASIC3_GPIO( _b, u16, A, Mask ) ++#define IPAQ_ASIC3_GPIO_A_DIR(_b) IPAQ_ASIC3_GPIO( _b, u16, A, Direction ) ++#define IPAQ_ASIC3_GPIO_A_OUT(_b) IPAQ_ASIC3_GPIO( _b, u16, A, Out ) ++#define IPAQ_ASIC3_GPIO_A_LEVELTRI(_b) IPAQ_ASIC3_GPIO( _b, u16, A, TriggerType ) ++#define IPAQ_ASIC3_GPIO_A_RISING(_b) IPAQ_ASIC3_GPIO( _b, u16, A, EdgeTrigger ) ++#define IPAQ_ASIC3_GPIO_A_LEVEL(_b) IPAQ_ASIC3_GPIO( _b, u16, A, LevelTrigger ) ++#define IPAQ_ASIC3_GPIO_A_SLEEP_MASK(_b) IPAQ_ASIC3_GPIO( _b, u16, A, SleepMask ) ++#define IPAQ_ASIC3_GPIO_A_SLEEP_OUT(_b) IPAQ_ASIC3_GPIO( _b, u16, A, SleepOut ) ++#define IPAQ_ASIC3_GPIO_A_BATT_FAULT_OUT(_b) IPAQ_ASIC3_GPIO( _b, u16, A, BattFaultOut ) ++#define IPAQ_ASIC3_GPIO_A_INT_STATUS(_b) IPAQ_ASIC3_GPIO( _b, u16, A, IntStatus ) ++#define IPAQ_ASIC3_GPIO_A_ALT_FUNCTION(_b) IPAQ_ASIC3_GPIO( _b, u16, A, AltFunction ) ++#define IPAQ_ASIC3_GPIO_A_SLEEP_CONF(_b) IPAQ_ASIC3_GPIO( _b, u16, A, SleepConf ) ++#define IPAQ_ASIC3_GPIO_A_STATUS(_b) IPAQ_ASIC3_GPIO( _b, u16, A, Status ) ++ ++#define IPAQ_ASIC3_GPIO_B_MASK(_b) IPAQ_ASIC3_GPIO( _b, u16, B, Mask ) ++#define IPAQ_ASIC3_GPIO_B_DIR(_b) IPAQ_ASIC3_GPIO( _b, u16, B, Direction ) ++#define IPAQ_ASIC3_GPIO_B_OUT(_b) IPAQ_ASIC3_GPIO( _b, u16, B, Out ) ++#define IPAQ_ASIC3_GPIO_B_LEVELTRI(_b) IPAQ_ASIC3_GPIO( _b, u16, B, TriggerType ) ++#define IPAQ_ASIC3_GPIO_B_RISING(_b) IPAQ_ASIC3_GPIO( _b, u16, B, EdgeTrigger ) ++#define IPAQ_ASIC3_GPIO_B_LEVEL(_b) IPAQ_ASIC3_GPIO( _b, u16, B, LevelTrigger ) ++#define IPAQ_ASIC3_GPIO_B_SLEEP_MASK(_b) IPAQ_ASIC3_GPIO( _b, u16, B, SleepMask ) ++#define IPAQ_ASIC3_GPIO_B_SLEEP_OUT(_b) IPAQ_ASIC3_GPIO( _b, u16, B, SleepOut ) ++#define IPAQ_ASIC3_GPIO_B_BATT_FAULT_OUT(_b) IPAQ_ASIC3_GPIO( _b, u16, B, BattFaultOut ) ++#define IPAQ_ASIC3_GPIO_B_INT_STATUS(_b) IPAQ_ASIC3_GPIO( _b, u16, B, IntStatus ) ++#define IPAQ_ASIC3_GPIO_B_ALT_FUNCTION(_b) IPAQ_ASIC3_GPIO( _b, u16, B, AltFunction ) ++#define IPAQ_ASIC3_GPIO_B_SLEEP_CONF(_b) IPAQ_ASIC3_GPIO( _b, u16, B, SleepConf ) ++#define IPAQ_ASIC3_GPIO_B_STATUS(_b) IPAQ_ASIC3_GPIO( _b, u16, B, Status ) ++ ++#define IPAQ_ASIC3_GPIO_C_MASK(_b) IPAQ_ASIC3_GPIO( _b, u16, C, Mask ) ++#define IPAQ_ASIC3_GPIO_C_DIR(_b) IPAQ_ASIC3_GPIO( _b, u16, C, Direction ) ++#define IPAQ_ASIC3_GPIO_C_OUT(_b) IPAQ_ASIC3_GPIO( _b, u16, C, Out ) ++#define IPAQ_ASIC3_GPIO_C_LEVELTRI(_b) IPAQ_ASIC3_GPIO( _b, u16, C, TriggerType ) ++#define IPAQ_ASIC3_GPIO_C_RISING(_b) IPAQ_ASIC3_GPIO( _b, u16, C, EdgeTrigger ) ++#define IPAQ_ASIC3_GPIO_C_LEVEL(_b) IPAQ_ASIC3_GPIO( _b, u16, C, LevelTrigger ) ++#define IPAQ_ASIC3_GPIO_C_SLEEP_MASK(_b) IPAQ_ASIC3_GPIO( _b, u16, C, SleepMask ) ++#define IPAQ_ASIC3_GPIO_C_SLEEP_OUT(_b) IPAQ_ASIC3_GPIO( _b, u16, C, SleepOut ) ++#define IPAQ_ASIC3_GPIO_C_BATT_FAULT_OUT(_b) IPAQ_ASIC3_GPIO( _b, u16, C, BattFaultOut ) ++#define IPAQ_ASIC3_GPIO_C_INT_STATUS(_b) IPAQ_ASIC3_GPIO( _b, u16, C, IntStatus ) ++#define IPAQ_ASIC3_GPIO_C_ALT_FUNCTION(_b) IPAQ_ASIC3_GPIO( _b, u16, C, AltFunction ) ++#define IPAQ_ASIC3_GPIO_C_SLEEP_CONF(_b) IPAQ_ASIC3_GPIO( _b, u16, C, SleepConf ) ++#define IPAQ_ASIC3_GPIO_C_STATUS(_b) IPAQ_ASIC3_GPIO( _b, u16, C, Status ) ++ ++#define IPAQ_ASIC3_GPIO_D_MASK(_b) IPAQ_ASIC3_GPIO( _b, u16, D, Mask ) ++#define IPAQ_ASIC3_GPIO_D_DIR(_b) IPAQ_ASIC3_GPIO( _b, u16, D, Direction ) ++#define IPAQ_ASIC3_GPIO_D_OUT(_b) IPAQ_ASIC3_GPIO( _b, u16, D, Out ) ++#define IPAQ_ASIC3_GPIO_D_LEVELTRI(_b) IPAQ_ASIC3_GPIO( _b, u16, D, TriggerType ) ++#define IPAQ_ASIC3_GPIO_D_RISING(_b) IPAQ_ASIC3_GPIO( _b, u16, D, EdgeTrigger ) ++#define IPAQ_ASIC3_GPIO_D_LEVEL(_b) IPAQ_ASIC3_GPIO( _b, u16, D, LevelTrigger ) ++#define IPAQ_ASIC3_GPIO_D_SLEEP_MASK(_b) IPAQ_ASIC3_GPIO( _b, u16, D, SleepMask ) ++#define IPAQ_ASIC3_GPIO_D_SLEEP_OUT(_b) IPAQ_ASIC3_GPIO( _b, u16, D, SleepOut ) ++#define IPAQ_ASIC3_GPIO_D_BATT_FAULT_OUT(_b) IPAQ_ASIC3_GPIO( _b, u16, D, BattFaultOut ) ++#define IPAQ_ASIC3_GPIO_D_INT_STATUS(_b) IPAQ_ASIC3_GPIO( _b, u16, D, IntStatus ) ++#define IPAQ_ASIC3_GPIO_D_ALT_FUNCTION(_b) IPAQ_ASIC3_GPIO( _b, u16, D, AltFunction ) ++#define IPAQ_ASIC3_GPIO_D_SLEEP_CONF(_b) IPAQ_ASIC3_GPIO( _b, u16, D, SleepConf ) ++#define IPAQ_ASIC3_GPIO_D_STATUS(_b) IPAQ_ASIC3_GPIO( _b, u16, D, Status ) ++ ++#define _IPAQ_ASIC3_SPI_Base 0x0400 ++#define _IPAQ_ASIC3_SPI_Control 0x0000 ++#define _IPAQ_ASIC3_SPI_TxData 0x0004 ++#define _IPAQ_ASIC3_SPI_RxData 0x0008 ++#define _IPAQ_ASIC3_SPI_Int 0x000c ++#define _IPAQ_ASIC3_SPI_Status 0x0010 ++ ++#define IPAQ_ASIC3_SPI_Control(_b) IPAQ_ASIC3( _b, u16, SPI, Control ) ++#define IPAQ_ASIC3_SPI_TxData(_b) IPAQ_ASIC3( _b, u16, SPI, TxData ) ++#define IPAQ_ASIC3_SPI_RxData(_b) IPAQ_ASIC3( _b, u16, SPI, RxData ) ++#define IPAQ_ASIC3_SPI_Int(_b) IPAQ_ASIC3( _b, u16, SPI, Int ) ++#define IPAQ_ASIC3_SPI_Status(_b) IPAQ_ASIC3( _b, u16, SPI, Status ) ++ ++#define SPI_CONTROL_SPR(clk) ((clk) & 0x0f) /* Clock rate */ ++ ++#define _IPAQ_ASIC3_PWM_0_Base 0x0500 ++#define _IPAQ_ASIC3_PWM_1_Base 0x0600 ++#define _IPAQ_ASIC3_PWM_TimeBase 0x0000 ++#define _IPAQ_ASIC3_PWM_PeriodTime 0x0004 ++#define _IPAQ_ASIC3_PWM_DutyTime 0x0008 ++ ++#define IPAQ_ASIC3_PWM_TimeBase(_b, x) IPAQ_ASIC3_N( _b, u16, PWM, x, TimeBase ) ++#define IPAQ_ASIC3_PWM_PeriodTime(_b, x) IPAQ_ASIC3_N( _b, u16, PWM, x, PeriodTime ) ++#define IPAQ_ASIC3_PWM_DutyTime(_b, x) IPAQ_ASIC3_N( _b, u16, PWM, x, DutyTime ) ++ ++#define PWM_TIMEBASE_VALUE(x) ((x)&0xf) /* Low 4 bits sets time base */ ++#define PWM_TIMEBASE_ENABLE (1 << 4) /* Enable clock */ ++ ++#define _IPAQ_ASIC3_LED_0_Base 0x0700 ++#define _IPAQ_ASIC3_LED_1_Base 0x0800 ++#define _IPAQ_ASIC3_LED_2_Base 0x0900 ++#define _IPAQ_ASIC3_LED_TimeBase 0x0000 /* R/W 7 bits */ ++#define _IPAQ_ASIC3_LED_PeriodTime 0x0004 /* R/W 12 bits */ ++#define _IPAQ_ASIC3_LED_DutyTime 0x0008 /* R/W 12 bits */ ++#define _IPAQ_ASIC3_LED_AutoStopCount 0x000c /* R/W 16 bits */ ++ ++#define IPAQ_ASIC3_LED_TimeBase(_b, x) IPAQ_ASIC3_N( _b, u8, LED, x, TimeBase ) ++#define IPAQ_ASIC3_LED_PeriodTime(_b, x) IPAQ_ASIC3_N( _b, u16, LED, x, PeriodTime ) ++#define IPAQ_ASIC3_LED_DutyTime(_b, x) IPAQ_ASIC3_N( _b, u16, LED, x, DutyTime ) ++#define IPAQ_ASIC3_LED_AutoStopCount(_b, x) IPAQ_ASIC3_N( _b, u16, LED, x, AutoStopCount ) ++ ++/* LED TimeBase bits - match ASIC2 */ ++#define LED_TBS 0x0f /* Low 4 bits sets time base, max = 13 */ ++ /* Note: max = 5 on hx4700 */ ++ /* 0: maximum time base */ ++ /* 1: maximum time base / 2 */ ++ /* n: maximum time base / 2^n */ ++ ++#define LED_EN (1 << 4) /* LED ON/OFF 0:off, 1:on */ ++#define LED_AUTOSTOP (1 << 5) /* LED ON/OFF auto stop set 0:disable, 1:enable */ ++#define LED_ALWAYS (1 << 6) /* LED Interrupt Mask 0:No mask, 1:mask */ ++ ++#define _IPAQ_ASIC3_CLOCK_Base 0x0A00 ++#define _IPAQ_ASIC3_CLOCK_CDEX 0x00 ++#define _IPAQ_ASIC3_CLOCK_SEL 0x04 ++ ++#define IPAQ_ASIC3_CLOCK_CDEX(_b) IPAQ_ASIC3( _b, u16, CLOCK, CDEX ) ++#define IPAQ_ASIC3_CLOCK_SEL(_b) IPAQ_ASIC3( _b, u16, CLOCK, SEL ) ++ ++#define CLOCK_CDEX_SOURCE (1 << 0) /* 2 bits */ ++#define CLOCK_CDEX_SOURCE0 (1 << 0) ++#define CLOCK_CDEX_SOURCE1 (1 << 1) ++#define CLOCK_CDEX_SPI (1 << 2) ++#define CLOCK_CDEX_OWM (1 << 3) ++#define CLOCK_CDEX_PWM0 (1 << 4) ++#define CLOCK_CDEX_PWM1 (1 << 5) ++#define CLOCK_CDEX_LED0 (1 << 6) ++#define CLOCK_CDEX_LED1 (1 << 7) ++#define CLOCK_CDEX_LED2 (1 << 8) ++ ++#define CLOCK_CDEX_SD_HOST (1 << 9) /* R/W: SD host clock source 24.576M/12.288M */ ++#define CLOCK_CDEX_SD_BUS (1 << 10) /* R/W: SD bus clock source control 24.576M/12.288M */ ++#define CLOCK_CDEX_SMBUS (1 << 11) ++#define CLOCK_CDEX_CONTROL_CX (1 << 12) ++ ++#define CLOCK_CDEX_EX0 (1 << 13) /* R/W: 32.768 kHz crystal */ ++#define CLOCK_CDEX_EX1 (1 << 14) /* R/W: 24.576 MHz crystal */ ++ ++#define CLOCK_SEL_SD_HCLK_SEL (1 << 0) /* R/W: SDIO host clock select - 1: 24.576 Mhz, 0: 12.288 MHz */ ++#define CLOCK_SEL_SD_BCLK_SEL (1 << 1) /* R/W: SDIO bus clock select - 1: 24.576 MHz, 0: 12.288 MHz */ ++#define CLOCK_SEL_CX (1 << 2) /* R/W: INT clock source control (32.768 kHz) */ ++ ++ ++#define _IPAQ_ASIC3_INTR_Base 0x0B00 ++ ++#define _IPAQ_ASIC3_INTR_IntMask 0x00 /* Interrupt mask control */ ++#define _IPAQ_ASIC3_INTR_PIntStat 0x04 /* Peripheral interrupt status */ ++#define _IPAQ_ASIC3_INTR_IntCPS 0x08 /* Interrupt timer clock pre-scale */ ++#define _IPAQ_ASIC3_INTR_IntTBS 0x0c /* Interrupt timer set */ ++ ++#define IPAQ_ASIC3_INTR_IntMask(_b) IPAQ_ASIC3( _b, u8, INTR, IntMask ) ++#define IPAQ_ASIC3_INTR_PIntStat(_b) IPAQ_ASIC3( _b, u8, INTR, PIntStat ) ++#define IPAQ_ASIC3_INTR_IntCPS(_b) IPAQ_ASIC3( _b, u8, INTR, IntCPS ) ++#define IPAQ_ASIC3_INTR_IntTBS(_b) IPAQ_ASIC3( _b, u16, INTR, IntTBS ) ++ ++#define ASIC3_INTMASK_GINTMASK (1 << 0) /* Global interrupt mask 1:enable */ ++#define ASIC3_INTMASK_GINTEL (1 << 1) /* 1: rising edge, 0: hi level */ ++#define ASIC3_INTMASK_MASK0 (1 << 2) ++#define ASIC3_INTMASK_MASK1 (1 << 3) ++#define ASIC3_INTMASK_MASK2 (1 << 4) ++#define ASIC3_INTMASK_MASK3 (1 << 5) ++#define ASIC3_INTMASK_MASK4 (1 << 6) ++#define ASIC3_INTMASK_MASK5 (1 << 7) ++ ++#define ASIC3_INTR_PERIPHERAL_A (1 << 0) ++#define ASIC3_INTR_PERIPHERAL_B (1 << 1) ++#define ASIC3_INTR_PERIPHERAL_C (1 << 2) ++#define ASIC3_INTR_PERIPHERAL_D (1 << 3) ++#define ASIC3_INTR_LED0 (1 << 4) ++#define ASIC3_INTR_LED1 (1 << 5) ++#define ASIC3_INTR_LED2 (1 << 6) ++#define ASIC3_INTR_SPI (1 << 7) ++#define ASIC3_INTR_SMBUS (1 << 8) ++#define ASIC3_INTR_OWM (1 << 9) ++ ++#define ASIC3_INTR_CPS(x) ((x)&0x0f) /* 4 bits, max 14 */ ++#define ASIC3_INTR_CPS_SET ( 1 << 4 ) /* Time base enable */ ++ ++ ++/* Basic control of the SD ASIC */ ++#define _IPAQ_ASIC3_SDHWCTRL_Base 0x0E00 ++ ++#define _IPAQ_ASIC3_SDHWCTRL_SDConf 0x00 ++#define IPAQ_ASIC3_SDHWCTRL_SDConf(_b) IPAQ_ASIC3( _b, u8, SDHWCTRL, SDConf ) ++ ++#define ASIC3_SDHWCTRL_SUSPEND (1 << 0) /* 1=suspend all SD operations */ ++#define ASIC3_SDHWCTRL_CLKSEL (1 << 1) /* 1=SDICK, 0=HCLK */ ++#define ASIC3_SDHWCTRL_PCLR (1 << 2) /* All registers of SDIO cleared */ ++#define ASIC3_SDHWCTRL_LEVCD (1 << 3) /* Level of SD card detection: 1:high, 0:low */ ++#define ASIC3_SDHWCTRL_LEVWP (1 << 4) /* Level of SD card write protection: 1=low, 0=high */ ++#define ASIC3_SDHWCTRL_SDLED (1 << 5) /* SD card LED signal 1=enable, 0=disable */ ++#define ASIC3_SDHWCTRL_SDPWR (1 << 6) /* SD card power supply control 1=enable */ ++ ++ ++/* This is a pointer to an array of 12 u32 values - but only the lower 2 bytes matter */ ++/* Use it as "IPAQ_ASIC3_HWPROTECT_ARRAY[x]" */ ++ ++#define _IPAQ_ASIC3_HWPROTECT_Base 0x1000 ++#define IPAQ_ASIC3_HWPROTECT_ARRAY ((volatile u32*)(_IPAQ_ASIC3_Base + _IPAQ_ASIC3_HWPROTECT_Base)) ++#define HWPROTECT_ARRAY_LEN 12 ++#define HWPROTECT_ARRAY_VALUES {0x4854,0x432d,0x5344,0x494f,0x2050,0x2f4e,0x3a33,0x3048,0x3830,0x3032,0x382d,0x3030} ++ ++ ++#define _IPAQ_ASIC3_EXTCF_Base 0x1100 ++ ++#define _IPAQ_ASIC3_EXTCF_Select 0x00 ++#define _IPAQ_ASIC3_EXTCF_Reset 0x04 ++ ++#define IPAQ_ASIC3_EXTCF_Select(_b) IPAQ_ASIC3( _b, u16, EXTCF, Select ) ++#define IPAQ_ASIC3_EXTCF_Reset(_b) IPAQ_ASIC3( _b, u16, EXTCF, Reset ) ++ ++#define ASIC3_EXTCF_SMOD0 (1 << 0) /* slot number of mode 0 */ ++#define ASIC3_EXTCF_SMOD1 (1 << 1) /* slot number of mode 1 */ ++#define ASIC3_EXTCF_SMOD2 (1 << 2) /* slot number of mode 2 */ ++#define ASIC3_EXTCF_OWM_EN (1 << 4) /* enable onewire module */ ++#define ASIC3_EXTCF_OWM_SMB (1 << 5) /* OWM bus selection */ ++#define ASIC3_EXTCF_OWM_RESET (1 << 6) /* undocumented, used by OWM and CF */ ++#define ASIC3_EXTCF_CF0_SLEEP_MODE (1 << 7) /* CF0 sleep state control */ ++#define ASIC3_EXTCF_CF1_SLEEP_MODE (1 << 8) /* CF1 sleep state control */ ++#define ASIC3_EXTCF_CF0_PWAIT_EN (1 << 10) /* CF0 PWAIT_n control */ ++#define ASIC3_EXTCF_CF1_PWAIT_EN (1 << 11) /* CF1 PWAIT_n control */ ++#define ASIC3_EXTCF_CF0_BUF_EN (1 << 12) /* CF0 buffer control */ ++#define ASIC3_EXTCF_CF1_BUF_EN (1 << 13) /* CF1 buffer control */ ++#define ASIC3_EXTCF_SD_MEM_ENABLE (1 << 14) ++#define ASIC3_EXTCF_CF_SLEEP (1 << 15) /* CF sleep mode control */ ++ ++/***************************************************************************** ++ * The Onewire interface registers ++ * ++ * OWM_CMD ++ * OWM_DAT ++ * OWM_INTR ++ * OWM_INTEN ++ * OWM_CLKDIV ++ * ++ *****************************************************************************/ ++ ++#define _IPAQ_ASIC3_OWM_Base 0xC00 ++ ++#define _IPAQ_ASIC3_OWM_CMD 0x00 ++#define _IPAQ_ASIC3_OWM_DAT 0x04 ++#define _IPAQ_ASIC3_OWM_INTR 0x08 ++#define _IPAQ_ASIC3_OWM_INTEN 0x0C ++#define _IPAQ_ASIC3_OWM_CLKDIV 0x10 ++ ++#define ASIC3_OWM_CMD_ONEWR (1 << 0) ++#define ASIC3_OWM_CMD_SRA (1 << 1) ++#define ASIC3_OWM_CMD_DQO (1 << 2) ++#define ASIC3_OWM_CMD_DQI (1 << 3) ++ ++#define ASIC3_OWM_INTR_PD (1 << 0) ++#define ASIC3_OWM_INTR_PDR (1 << 1) ++#define ASIC3_OWM_INTR_TBE (1 << 2) ++#define ASIC3_OWM_INTR_TEMP (1 << 3) ++#define ASIC3_OWM_INTR_RBF (1 << 4) ++ ++#define ASIC3_OWM_INTEN_EPD (1 << 0) ++#define ASIC3_OWM_INTEN_IAS (1 << 1) ++#define ASIC3_OWM_INTEN_ETBE (1 << 2) ++#define ASIC3_OWM_INTEN_ETMT (1 << 3) ++#define ASIC3_OWM_INTEN_ERBF (1 << 4) ++ ++#define ASIC3_OWM_CLKDIV_PRE (3 << 0) /* two bits wide at bit position 0 */ ++#define ASIC3_OWM_CLKDIV_DIV (7 << 2) /* 3 bits wide at bit position 2 */ ++ ++ ++/***************************************************************************** ++ * The SD configuration registers are at a completely different location ++ * in memory. They are divided into three sets of registers: ++ * ++ * SD_CONFIG Core configuration register ++ * SD_CTRL Control registers for SD operations ++ * SDIO_CTRL Control registers for SDIO operations ++ * ++ *****************************************************************************/ ++ ++#define IPAQ_ASIC3_SD_CONFIG(_b, s,x) \ ++ (*((volatile s *) ((_b) + _IPAQ_ASIC3_SD_CONFIG_Base + (_IPAQ_ASIC3_SD_CONFIG_ ## x)))) ++ ++#define _IPAQ_ASIC3_SD_CONFIG_Base 0x0400 // Assumes 32 bit addressing ++ ++#define _IPAQ_ASIC3_SD_CONFIG_Command 0x08 /* R/W: Command */ ++#define _IPAQ_ASIC3_SD_CONFIG_Addr0 0x20 /* [9:31] SD Control Register Base Address */ ++#define _IPAQ_ASIC3_SD_CONFIG_Addr1 0x24 /* [9:31] SD Control Register Base Address */ ++#define _IPAQ_ASIC3_SD_CONFIG_IntPin 0x78 /* R/O: interrupt assigned to pin */ ++#define _IPAQ_ASIC3_SD_CONFIG_ClkStop 0x80 /* Set to 0x1f to clock SD controller, 0 otherwise. */ ++ /* at 0x82 - Gated Clock Control */ ++#define _IPAQ_ASIC3_SD_CONFIG_ClockMode 0x84 /* Control clock of SD controller */ ++#define _IPAQ_ASIC3_SD_CONFIG_SDHC_PinStatus 0x88 /* R/0: read status of SD pins */ ++#define _IPAQ_ASIC3_SD_CONFIG_SDHC_Power1 0x90 /* Power1 - manual power control */ ++ /* Power2 is at 0x92 - auto power up after card inserted */ ++#define _IPAQ_ASIC3_SD_CONFIG_SDHC_Power3 0x94 /* auto power down when card removed */ ++#define _IPAQ_ASIC3_SD_CONFIG_SDHC_CardDetect 0x98 /* */ ++#define _IPAQ_ASIC3_SD_CONFIG_SDHC_Slot 0xA0 /* R/O: define support slot number */ ++#define _IPAQ_ASIC3_SD_CONFIG_SDHC_ExtGateClk1 0x1E0 /* Could be used for gated clock (don't use) */ ++#define _IPAQ_ASIC3_SD_CONFIG_SDHC_ExtGateClk2 0x1E2 /* Could be used for gated clock (don't use) */ ++#define _IPAQ_ASIC3_SD_CONFIG_SDHC_GPIO_OutAndEnable 0x1E8 /* GPIO Output Reg. , at 0x1EA - GPIO Output Enable Reg. */ ++#define _IPAQ_ASIC3_SD_CONFIG_SDHC_GPIO_Status 0x1EC /* GPIO Status Reg. */ ++#define _IPAQ_ASIC3_SD_CONFIG_SDHC_ExtGateClk3 0x1F0 /* Bit 1: double buffer/single buffer */ ++ ++#define IPAQ_ASIC3_SD_CONFIG_Command(_b) IPAQ_ASIC3_SD_CONFIG(_b, u16, Command ) ++#define IPAQ_ASIC3_SD_CONFIG_Addr0(_b) IPAQ_ASIC3_SD_CONFIG(_b, u16, Addr0 ) ++#define IPAQ_ASIC3_SD_CONFIG_Addr1(_b) IPAQ_ASIC3_SD_CONFIG(_b, u16, Addr1 ) ++#define IPAQ_ASIC3_SD_CONFIG_IntPin(_b) IPAQ_ASIC3_SD_CONFIG(_b, u8, IntPin ) ++#define IPAQ_ASIC3_SD_CONFIG_ClkStop(_b) IPAQ_ASIC3_SD_CONFIG(_b, u8, ClkStop ) ++#define IPAQ_ASIC3_SD_CONFIG_ClockMode(_b) IPAQ_ASIC3_SD_CONFIG(_b, u8, ClockMode ) ++#define IPAQ_ASIC3_SD_CONFIG_SDHC_PinStatus(_b) IPAQ_ASIC3_SD_CONFIG(_b, u16, SDHC_PinStatus ) ++#define IPAQ_ASIC3_SD_CONFIG_SDHC_Power1(_b) IPAQ_ASIC3_SD_CONFIG(_b, u16, SDHC_Power1 ) ++#define IPAQ_ASIC3_SD_CONFIG_SDHC_Power3(_b) IPAQ_ASIC3_SD_CONFIG(_b, u16, SDHC_Power3 ) ++#define IPAQ_ASIC3_SD_CONFIG_SDHC_CardDetect(_b) IPAQ_ASIC3_SD_CONFIG(_b, u16, SDHC_CardDetect ) ++#define IPAQ_ASIC3_SD_CONFIG_SDHC_Slot(_b) IPAQ_ASIC3_SD_CONFIG(_b, u16, SDHC_Slot ) ++#define IPAQ_ASIC3_SD_CONFIG_SDHC_ExtGateClk1(_b) IPAQ_ASIC3_SD_CONFIG(_b, u16, SDHC_ExtGateClk1 ) ++#define IPAQ_ASIC3_SD_CONFIG_SDHC_ExtGateClk3(_b) IPAQ_ASIC3_SD_CONFIG(_b, u16, SDHC_ExtGateClk3 ) ++ ++#define SD_CONFIG_ ++ ++#define SD_CONFIG_COMMAND_MAE (1<<1) /* Memory access enable (set to 1 to access SD Controller) */ ++ ++#define SD_CONFIG_CLK_ENABLE_ALL 0x1f ++ ++#define SD_CONFIG_POWER1_PC_33V 0x0200 /* Set for 3.3 volts */ ++#define SD_CONFIG_POWER1_PC_OFF 0x0000 /* Turn off power */ ++ ++#define SD_CONFIG_CARDDETECTMODE_CLK ((x)&0x3) /* two bits - number of cycles for card detection */ ++ ++ ++#define _IPAQ_ASIC3_SD_CTRL_Base 0x1000 ++ ++#define IPAQ_ASIC3_SD(_b, s,x) \ ++ (*((volatile s *) ((_b) + _IPAQ_ASIC3_SD_CTRL_Base + (_IPAQ_ASIC3_SD_CTRL_ ## x)))) ++ ++#define _IPAQ_ASIC3_SD_CTRL_Cmd 0x00 ++#define _IPAQ_ASIC3_SD_CTRL_Arg0 0x08 ++#define _IPAQ_ASIC3_SD_CTRL_Arg1 0x0C ++#define _IPAQ_ASIC3_SD_CTRL_StopInternal 0x10 ++#define _IPAQ_ASIC3_SD_CTRL_TransferSectorCount 0x14 ++#define _IPAQ_ASIC3_SD_CTRL_Response0 0x18 ++#define _IPAQ_ASIC3_SD_CTRL_Response1 0x1C ++#define _IPAQ_ASIC3_SD_CTRL_Response2 0x20 ++#define _IPAQ_ASIC3_SD_CTRL_Response3 0x24 ++#define _IPAQ_ASIC3_SD_CTRL_Response4 0x28 ++#define _IPAQ_ASIC3_SD_CTRL_Response5 0x2C ++#define _IPAQ_ASIC3_SD_CTRL_Response6 0x30 ++#define _IPAQ_ASIC3_SD_CTRL_Response7 0x34 ++#define _IPAQ_ASIC3_SD_CTRL_CardStatus 0x38 ++#define _IPAQ_ASIC3_SD_CTRL_BufferCtrl 0x3C ++#define _IPAQ_ASIC3_SD_CTRL_IntMaskCard 0x40 ++#define _IPAQ_ASIC3_SD_CTRL_IntMaskBuffer 0x44 ++#define _IPAQ_ASIC3_SD_CTRL_CardClockCtrl 0x48 ++#define _IPAQ_ASIC3_SD_CTRL_MemCardXferDataLen 0x4C ++#define _IPAQ_ASIC3_SD_CTRL_MemCardOptionSetup 0x50 ++#define _IPAQ_ASIC3_SD_CTRL_ErrorStatus0 0x58 ++#define _IPAQ_ASIC3_SD_CTRL_ErrorStatus1 0x5C ++#define _IPAQ_ASIC3_SD_CTRL_DataPort 0x60 ++#define _IPAQ_ASIC3_SD_CTRL_TransactionCtrl 0x68 ++#define _IPAQ_ASIC3_SD_CTRL_SoftwareReset 0x1C0 ++ ++#define IPAQ_ASIC3_SD_CTRL_Cmd(_b) IPAQ_ASIC3_SD( _b, u16, Cmd ) /* */ ++#define IPAQ_ASIC3_SD_CTRL_Arg0(_b) IPAQ_ASIC3_SD( _b, u16, Arg0 ) /* */ ++#define IPAQ_ASIC3_SD_CTRL_Arg1(_b) IPAQ_ASIC3_SD( _b, u16, Arg1 ) /* */ ++#define IPAQ_ASIC3_SD_CTRL_StopInternal(_b) IPAQ_ASIC3_SD( _b, u16, StopInternal ) /* */ ++#define IPAQ_ASIC3_SD_CTRL_TransferSectorCount(_b) IPAQ_ASIC3_SD( _b, u16, TransferSectorCount ) /* */ ++#define IPAQ_ASIC3_SD_CTRL_Response0(_b) IPAQ_ASIC3_SD( _b, u16, Response0 ) /* */ ++#define IPAQ_ASIC3_SD_CTRL_Response1(_b) IPAQ_ASIC3_SD( _b, u16, Response1 ) /* */ ++#define IPAQ_ASIC3_SD_CTRL_Response2(_b) IPAQ_ASIC3_SD( _b, u16, Response2 ) /* */ ++#define IPAQ_ASIC3_SD_CTRL_Response3(_b) IPAQ_ASIC3_SD( _b, u16, Response3 ) /* */ ++#define IPAQ_ASIC3_SD_CTRL_Response4(_b) IPAQ_ASIC3_SD( _b, u16, Response4 ) /* */ ++#define IPAQ_ASIC3_SD_CTRL_Response5(_b) IPAQ_ASIC3_SD( _b, u16, Response5 ) /* */ ++#define IPAQ_ASIC3_SD_CTRL_Response6(_b) IPAQ_ASIC3_SD( _b, u16, Response6 ) /* */ ++#define IPAQ_ASIC3_SD_CTRL_Response7(_b) IPAQ_ASIC3_SD( _b, u16, Response7 ) /* */ ++#define IPAQ_ASIC3_SD_CTRL_CardStatus(_b) IPAQ_ASIC3_SD( _b, u16, CardStatus ) /* */ ++#define IPAQ_ASIC3_SD_CTRL_BufferCtrl(_b) IPAQ_ASIC3_SD( _b, u16, BufferCtrl ) /* and error status*/ ++#define IPAQ_ASIC3_SD_CTRL_IntMaskCard(_b) IPAQ_ASIC3_SD( _b, u16, IntMaskCard ) /* */ ++#define IPAQ_ASIC3_SD_CTRL_IntMaskBuffer(_b) IPAQ_ASIC3_SD( _b, u16, IntMaskBuffer ) /* */ ++#define IPAQ_ASIC3_SD_CTRL_CardClockCtrl(_b) IPAQ_ASIC3_SD( _b, u16, CardClockCtrl ) /* */ ++#define IPAQ_ASIC3_SD_CTRL_MemCardXferDataLen(_b) IPAQ_ASIC3_SD( _b, u16, MemCardXferDataLen ) /* */ ++#define IPAQ_ASIC3_SD_CTRL_MemCardOptionSetup(_b) IPAQ_ASIC3_SD( _b, u16, MemCardOptionSetup ) /* */ ++#define IPAQ_ASIC3_SD_CTRL_ErrorStatus0(_b) IPAQ_ASIC3_SD( _b, u16, ErrorStatus0 ) /* */ ++#define IPAQ_ASIC3_SD_CTRL_ErrorStatus1(_b) IPAQ_ASIC3_SD( _b, u16, ErrorStatus1 ) /* */ ++#define IPAQ_ASIC3_SD_CTRL_DataPort(_b) IPAQ_ASIC3_SD( _b, u16, DataPort ) /* */ ++#define IPAQ_ASIC3_SD_CTRL_TransactionCtrl(_b) IPAQ_ASIC3_SD( _b, u16, TransactionCtrl ) /* */ ++#define IPAQ_ASIC3_SD_CTRL_SoftwareReset(_b) IPAQ_ASIC3_SD( _b, u16, SoftwareReset ) /* */ ++ ++#define SD_CTRL_SOFTWARE_RESET_CLEAR (1<<0) ++ ++#define SD_CTRL_TRANSACTIONCONTROL_SET (1<<8) // 0x0100 ++ ++#define SD_CTRL_CARDCLOCKCONTROL_FOR_SD_CARD (1<<15)// 0x8000 ++#define SD_CTRL_CARDCLOCKCONTROL_ENABLE_CLOCK (1<<8) // 0x0100 ++#define SD_CTRL_CARDCLOCKCONTROL_CLK_DIV_512 (1<<7) // 0x0080 ++#define SD_CTRL_CARDCLOCKCONTROL_CLK_DIV_256 (1<<6) // 0x0040 ++#define SD_CTRL_CARDCLOCKCONTROL_CLK_DIV_128 (1<<5) // 0x0020 ++#define SD_CTRL_CARDCLOCKCONTROL_CLK_DIV_64 (1<<4) // 0x0010 ++#define SD_CTRL_CARDCLOCKCONTROL_CLK_DIV_32 (1<<3) // 0x0008 ++#define SD_CTRL_CARDCLOCKCONTROL_CLK_DIV_16 (1<<2) // 0x0004 ++#define SD_CTRL_CARDCLOCKCONTROL_CLK_DIV_8 (1<<1) // 0x0002 ++#define SD_CTRL_CARDCLOCKCONTROL_CLK_DIV_4 (1<<0) // 0x0001 ++#define SD_CTRL_CARDCLOCKCONTROL_CLK_DIV_2 (0<<0) // 0x0000 ++ ++#define MEM_CARD_OPTION_REQUIRED 0x000e ++#define MEM_CARD_OPTION_DATA_RESPONSE_TIMEOUT(x) (((x)&0x0f)<<4) /* Four bits */ ++#define MEM_CARD_OPTION_C2_MODULE_NOT_PRESENT (1<<14) // 0x4000 ++#define MEM_CARD_OPTION_DATA_XFR_WIDTH_1 (1<<15) // 0x8000 ++#define MEM_CARD_OPTION_DATA_XFR_WIDTH_4 (0<<15) //~0x8000 ++ ++#define SD_CTRL_COMMAND_INDEX(x) ((x)&0x3f) /* 0=CMD0, 1=CMD1, ..., 63=CMD63 */ ++#define SD_CTRL_COMMAND_TYPE_CMD (0 << 6) ++#define SD_CTRL_COMMAND_TYPE_ACMD (1 << 6) ++#define SD_CTRL_COMMAND_TYPE_AUTHENTICATION (2 << 6) ++#define SD_CTRL_COMMAND_RESPONSE_TYPE_NORMAL (0 << 8) ++#define SD_CTRL_COMMAND_RESPONSE_TYPE_EXT_R1 (4 << 8) ++#define SD_CTRL_COMMAND_RESPONSE_TYPE_EXT_R1B (5 << 8) ++#define SD_CTRL_COMMAND_RESPONSE_TYPE_EXT_R2 (6 << 8) ++#define SD_CTRL_COMMAND_RESPONSE_TYPE_EXT_R3 (7 << 8) ++#define SD_CTRL_COMMAND_DATA_PRESENT (1 << 11) ++#define SD_CTRL_COMMAND_TRANSFER_READ (1 << 12) ++#define SD_CTRL_COMMAND_TRANSFER_WRITE (0 << 12) ++#define SD_CTRL_COMMAND_MULTI_BLOCK (1 << 13) ++#define SD_CTRL_COMMAND_SECURITY_CMD (1 << 14) ++ ++#define SD_CTRL_STOP_INTERNAL_ISSSUE_CMD12 (1 << 0) ++#define SD_CTRL_STOP_INTERNAL_AUTO_ISSUE_CMD12 (1 << 8) ++ ++#define SD_CTRL_CARDSTATUS_RESPONSE_END (1 << 0) ++#define SD_CTRL_CARDSTATUS_RW_END (1 << 2) ++#define SD_CTRL_CARDSTATUS_CARD_REMOVED_0 (1 << 3) ++#define SD_CTRL_CARDSTATUS_CARD_INSERTED_0 (1 << 4) ++#define SD_CTRL_CARDSTATUS_SIGNAL_STATE_PRESENT_0 (1 << 5) ++#define SD_CTRL_CARDSTATUS_WRITE_PROTECT (1 << 7) ++#define SD_CTRL_CARDSTATUS_CARD_REMOVED_3 (1 << 8) ++#define SD_CTRL_CARDSTATUS_CARD_INSERTED_3 (1 << 9) ++#define SD_CTRL_CARDSTATUS_SIGNAL_STATE_PRESENT_3 (1 << 10) ++ ++#define SD_CTRL_BUFFERSTATUS_CMD_INDEX_ERROR (1 << 0) // 0x0001 ++#define SD_CTRL_BUFFERSTATUS_CRC_ERROR (1 << 1) // 0x0002 ++#define SD_CTRL_BUFFERSTATUS_STOP_BIT_END_ERROR (1 << 2) // 0x0004 ++#define SD_CTRL_BUFFERSTATUS_DATA_TIMEOUT (1 << 3) // 0x0008 ++#define SD_CTRL_BUFFERSTATUS_BUFFER_OVERFLOW (1 << 4) // 0x0010 ++#define SD_CTRL_BUFFERSTATUS_BUFFER_UNDERFLOW (1 << 5) // 0x0020 ++#define SD_CTRL_BUFFERSTATUS_CMD_TIMEOUT (1 << 6) // 0x0040 ++#define SD_CTRL_BUFFERSTATUS_UNK7 (1 << 7) // 0x0080 ++#define SD_CTRL_BUFFERSTATUS_BUFFER_READ_ENABLE (1 << 8) // 0x0100 ++#define SD_CTRL_BUFFERSTATUS_BUFFER_WRITE_ENABLE (1 << 9) // 0x0200 ++#define SD_CTRL_BUFFERSTATUS_ILLEGAL_FUNCTION (1 << 13)// 0x2000 ++#define SD_CTRL_BUFFERSTATUS_CMD_BUSY (1 << 14)// 0x4000 ++#define SD_CTRL_BUFFERSTATUS_ILLEGAL_ACCESS (1 << 15)// 0x8000 ++ ++#define SD_CTRL_INTMASKCARD_RESPONSE_END (1 << 0) // 0x0001 ++#define SD_CTRL_INTMASKCARD_RW_END (1 << 2) // 0x0004 ++#define SD_CTRL_INTMASKCARD_CARD_REMOVED_0 (1 << 3) // 0x0008 ++#define SD_CTRL_INTMASKCARD_CARD_INSERTED_0 (1 << 4) // 0x0010 ++#define SD_CTRL_INTMASKCARD_SIGNAL_STATE_PRESENT_0 (1 << 5) // 0x0020 ++#define SD_CTRL_INTMASKCARD_UNK6 (1 << 6) // 0x0040 ++#define SD_CTRL_INTMASKCARD_WRITE_PROTECT (1 << 7) // 0x0080 ++#define SD_CTRL_INTMASKCARD_CARD_REMOVED_3 (1 << 8) // 0x0100 ++#define SD_CTRL_INTMASKCARD_CARD_INSERTED_3 (1 << 9) // 0x0200 ++#define SD_CTRL_INTMASKCARD_SIGNAL_STATE_PRESENT_3 (1 << 10)// 0x0400 ++ ++#define SD_CTRL_INTMASKBUFFER_CMD_INDEX_ERROR (1 << 0) // 0x0001 ++#define SD_CTRL_INTMASKBUFFER_CRC_ERROR (1 << 1) // 0x0002 ++#define SD_CTRL_INTMASKBUFFER_STOP_BIT_END_ERROR (1 << 2) // 0x0004 ++#define SD_CTRL_INTMASKBUFFER_DATA_TIMEOUT (1 << 3) // 0x0008 ++#define SD_CTRL_INTMASKBUFFER_BUFFER_OVERFLOW (1 << 4) // 0x0010 ++#define SD_CTRL_INTMASKBUFFER_BUFFER_UNDERFLOW (1 << 5) // 0x0020 ++#define SD_CTRL_INTMASKBUFFER_CMD_TIMEOUT (1 << 6) // 0x0040 ++#define SD_CTRL_INTMASKBUFFER_UNK7 (1 << 7) // 0x0080 ++#define SD_CTRL_INTMASKBUFFER_BUFFER_READ_ENABLE (1 << 8) // 0x0100 ++#define SD_CTRL_INTMASKBUFFER_BUFFER_WRITE_ENABLE (1 << 9) // 0x0200 ++#define SD_CTRL_INTMASKBUFFER_ILLEGAL_FUNCTION (1 << 13)// 0x2000 ++#define SD_CTRL_INTMASKBUFFER_CMD_BUSY (1 << 14)// 0x4000 ++#define SD_CTRL_INTMASKBUFFER_ILLEGAL_ACCESS (1 << 15)// 0x8000 ++ ++#define SD_CTRL_DETAIL0_RESPONSE_CMD_ERROR (1 << 0) // 0x0001 ++#define SD_CTRL_DETAIL0_END_BIT_ERROR_FOR_RESPONSE_NON_CMD12 (1 << 2) // 0x0004 ++#define SD_CTRL_DETAIL0_END_BIT_ERROR_FOR_RESPONSE_CMD12 (1 << 3) // 0x0008 ++#define SD_CTRL_DETAIL0_END_BIT_ERROR_FOR_READ_DATA (1 << 4) // 0x0010 ++#define SD_CTRL_DETAIL0_END_BIT_ERROR_FOR_WRITE_CRC_STATUS (1 << 5) // 0x0020 ++#define SD_CTRL_DETAIL0_CRC_ERROR_FOR_RESPONSE_NON_CMD12 (1 << 8) // 0x0100 ++#define SD_CTRL_DETAIL0_CRC_ERROR_FOR_RESPONSE_CMD12 (1 << 9) // 0x0200 ++#define SD_CTRL_DETAIL0_CRC_ERROR_FOR_READ_DATA (1 << 10)// 0x0400 ++#define SD_CTRL_DETAIL0_CRC_ERROR_FOR_WRITE_CMD (1 << 11)// 0x0800 ++ ++#define SD_CTRL_DETAIL1_NO_CMD_RESPONSE (1 << 0) // 0x0001 ++#define SD_CTRL_DETAIL1_TIMEOUT_READ_DATA (1 << 4) // 0x0010 ++#define SD_CTRL_DETAIL1_TIMEOUT_CRS_STATUS (1 << 5) // 0x0020 ++#define SD_CTRL_DETAIL1_TIMEOUT_CRC_BUSY (1 << 6) // 0x0040 ++ ++#define _IPAQ_ASIC3_SDIO_CTRL_Base 0x1200 ++ ++#define IPAQ_ASIC3_SDIO(_b, s,x) \ ++ (*((volatile s *) ((_b) + _IPAQ_ASIC3_SDIO_CTRL_Base + (_IPAQ_ASIC3_SDIO_CTRL_ ## x)))) ++ ++#define _IPAQ_ASIC3_SDIO_CTRL_Cmd 0x00 ++#define _IPAQ_ASIC3_SDIO_CTRL_CardPortSel 0x04 ++#define _IPAQ_ASIC3_SDIO_CTRL_Arg0 0x08 ++#define _IPAQ_ASIC3_SDIO_CTRL_Arg1 0x0C ++#define _IPAQ_ASIC3_SDIO_CTRL_TransferBlockCount 0x14 ++#define _IPAQ_ASIC3_SDIO_CTRL_Response0 0x18 ++#define _IPAQ_ASIC3_SDIO_CTRL_Response1 0x1C ++#define _IPAQ_ASIC3_SDIO_CTRL_Response2 0x20 ++#define _IPAQ_ASIC3_SDIO_CTRL_Response3 0x24 ++#define _IPAQ_ASIC3_SDIO_CTRL_Response4 0x28 ++#define _IPAQ_ASIC3_SDIO_CTRL_Response5 0x2C ++#define _IPAQ_ASIC3_SDIO_CTRL_Response6 0x30 ++#define _IPAQ_ASIC3_SDIO_CTRL_Response7 0x34 ++#define _IPAQ_ASIC3_SDIO_CTRL_CardStatus 0x38 ++#define _IPAQ_ASIC3_SDIO_CTRL_BufferCtrl 0x3C ++#define _IPAQ_ASIC3_SDIO_CTRL_IntMaskCard 0x40 ++#define _IPAQ_ASIC3_SDIO_CTRL_IntMaskBuffer 0x44 ++#define _IPAQ_ASIC3_SDIO_CTRL_CardXferDataLen 0x4C ++#define _IPAQ_ASIC3_SDIO_CTRL_CardOptionSetup 0x50 ++#define _IPAQ_ASIC3_SDIO_CTRL_ErrorStatus0 0x54 ++#define _IPAQ_ASIC3_SDIO_CTRL_ErrorStatus1 0x58 ++#define _IPAQ_ASIC3_SDIO_CTRL_DataPort 0x60 ++#define _IPAQ_ASIC3_SDIO_CTRL_TransactionCtrl 0x68 ++#define _IPAQ_ASIC3_SDIO_CTRL_CardIntCtrl 0x6C ++#define _IPAQ_ASIC3_SDIO_CTRL_ClocknWaitCtrl 0x70 ++#define _IPAQ_ASIC3_SDIO_CTRL_HostInformation 0x74 ++#define _IPAQ_ASIC3_SDIO_CTRL_ErrorCtrl 0x78 ++#define _IPAQ_ASIC3_SDIO_CTRL_LEDCtrl 0x7C ++#define _IPAQ_ASIC3_SDIO_CTRL_SoftwareReset 0x1C0 ++ ++#define IPAQ_ASIC3_SDIO_CTRL_Cmd(_b) IPAQ_ASIC3_SDIO( _b, u16, Cmd ) /* */ ++#define IPAQ_ASIC3_SDIO_CTRL_CardPortSel(_b) IPAQ_ASIC3_SDIO( _b, u16, CardPortSel ) /* */ ++#define IPAQ_ASIC3_SDIO_CTRL_Arg0(_b) IPAQ_ASIC3_SDIO( _b, u16, Arg0 ) /* */ ++#define IPAQ_ASIC3_SDIO_CTRL_Arg1(_b) IPAQ_ASIC3_SDIO( _b, u16, Arg1 ) /* */ ++#define IPAQ_ASIC3_SDIO_CTRL_TransferBlockCount(_b) IPAQ_ASIC3_SDIO( _b, u16, TransferBlockCount ) /* */ ++#define IPAQ_ASIC3_SDIO_CTRL_Response0(_b) IPAQ_ASIC3_SDIO( _b, u16, Response0 ) /* */ ++#define IPAQ_ASIC3_SDIO_CTRL_Response1(_b) IPAQ_ASIC3_SDIO( _b, u16, Response1 ) /* */ ++#define IPAQ_ASIC3_SDIO_CTRL_Response2(_b) IPAQ_ASIC3_SDIO( _b, u16, Response2 ) /* */ ++#define IPAQ_ASIC3_SDIO_CTRL_Response3(_b) IPAQ_ASIC3_SDIO( _b, u16, Response3 ) /* */ ++#define IPAQ_ASIC3_SDIO_CTRL_Response4(_b) IPAQ_ASIC3_SDIO( _b, u16, Response4 ) /* */ ++#define IPAQ_ASIC3_SDIO_CTRL_Response5(_b) IPAQ_ASIC3_SDIO( _b, u16, Response5 ) /* */ ++#define IPAQ_ASIC3_SDIO_CTRL_Response6(_b) IPAQ_ASIC3_SDIO( _b, u16, Response6 ) /* */ ++#define IPAQ_ASIC3_SDIO_CTRL_Response7(_b) IPAQ_ASIC3_SDIO( _b, u16, Response7 ) /* */ ++#define IPAQ_ASIC3_SDIO_CTRL_CardStatus(_b) IPAQ_ASIC3_SDIO( _b, u16, CardStatus ) /* */ ++#define IPAQ_ASIC3_SDIO_CTRL_BufferCtrl(_b) IPAQ_ASIC3_SDIO( _b, u16, BufferCtrl ) /* and error status*/ ++#define IPAQ_ASIC3_SDIO_CTRL_IntMaskCard(_b) IPAQ_ASIC3_SDIO( _b, u16, IntMaskCard ) /* */ ++#define IPAQ_ASIC3_SDIO_CTRL_IntMaskBuffer(_b) IPAQ_ASIC3_SDIO( _b, u16, IntMaskBuffer ) /* */ ++#define IPAQ_ASIC3_SDIO_CTRL_CardXferDataLen(_b) IPAQ_ASIC3_SDIO( _b, u16, CardXferDataLen ) /* */ ++#define IPAQ_ASIC3_SDIO_CTRL_CardOptionSetup(_b) IPAQ_ASIC3_SDIO( _b, u16, CardOptionSetup ) /* */ ++#define IPAQ_ASIC3_SDIO_CTRL_ErrorStatus0(_b) IPAQ_ASIC3_SDIO( _b, u16, ErrorStatus0 ) /* */ ++#define IPAQ_ASIC3_SDIO_CTRL_ErrorStatus1(_b) IPAQ_ASIC3_SDIO( _b, u16, ErrorStatus1 ) /* */ ++#define IPAQ_ASIC3_SDIO_CTRL_DataPort(_b) IPAQ_ASIC3_SDIO( _b, u16, DataPort ) /* */ ++#define IPAQ_ASIC3_SDIO_CTRL_TransactionCtrl(_b) IPAQ_ASIC3_SDIO( _b, u16, TransactionCtrl ) /* */ ++#define IPAQ_ASIC3_SDIO_CTRL_CardIntCtrl(_b) IPAQ_ASIC3_SDIO( _b, u16, CardIntCtrl ) /* */ ++#define IPAQ_ASIC3_SDIO_CTRL_ClocknWaitCtrl(_b) IPAQ_ASIC3_SDIO( _b, u16, ClocknWaitCtrl ) /* */ ++#define IPAQ_ASIC3_SDIO_CTRL_HostInformation(_b) IPAQ_ASIC3_SDIO( _b, u16, HostInformation ) /* */ ++#define IPAQ_ASIC3_SDIO_CTRL_ErrorCtrl(_b) IPAQ_ASIC3_SDIO( _b, u16, ErrorCtrl ) /* */ ++#define IPAQ_ASIC3_SDIO_CTRL_LEDCtrl(_b) IPAQ_ASIC3_SDIO( _b, u16, LEDCtrl ) /* */ ++#define IPAQ_ASIC3_SDIO_CTRL_SoftwareReset(_b) IPAQ_ASIC3_SDIO( _b, u16, SoftwareReset ) /* */ ++ ++#define IPAQ_ASIC3_MAP_SIZE 0x2000 ++ ++#endif +Index: linux-2.6.24/include/linux/gpiodev.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/include/linux/gpiodev.h 2008-03-10 16:09:23.000000000 +0000 +@@ -0,0 +1,44 @@ ++#ifndef __GPIODEV_H ++#define __GPIODEV_H ++ ++#include <linux/device.h> ++#include <linux/platform_device.h> ++#include <asm/gpio.h> ++ ++/* Interface */ ++ ++/* This structure must be first member of device platform_data structure ++ of a device which provides gpiodev interface. All method pointers ++ must be non-NULL, so stubs must be used for non-implemented ones. */ ++struct gpiodev_ops { ++ int (*get)(struct device *this, unsigned gpio_no); ++ void (*set)(struct device *this, unsigned gpio_no, int val); ++ int (*to_irq)(struct device *this, unsigned gpio_no); ++}; ++ ++/* Generalized GPIO structure */ ++ ++struct gpio { ++ struct device *gpio_dev; ++ unsigned gpio_no; ++}; ++ ++/* API functions */ ++ ++static inline int gpiodev_get_value(struct gpio *gpio) ++{ ++ struct gpiodev_ops *ops = gpio->gpio_dev->platform_data; ++ return ops->get(gpio->gpio_dev, gpio->gpio_no); ++} ++static inline void gpiodev_set_value(struct gpio *gpio, int val) ++{ ++ struct gpiodev_ops *ops = gpio->gpio_dev->platform_data; ++ ops->set(gpio->gpio_dev, gpio->gpio_no, val); ++} ++static inline int gpiodev_to_irq(struct gpio *gpio) ++{ ++ struct gpiodev_ops *ops = gpio->gpio_dev->platform_data; ++ return ops->to_irq(gpio->gpio_dev, gpio->gpio_no); ++} ++ ++#endif /* __GPIODEV_H */ +Index: linux-2.6.24/include/linux/input_pda.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/include/linux/input_pda.h 2008-03-10 16:09:23.000000000 +0000 +@@ -0,0 +1,47 @@ ++#ifndef _INPUT_PDA_H ++#define _INPUT_PDA_H ++ ++/* ++ * This is temporary virtual button key codes map ++ * for keyboardless handheld computers. ++ * Its purpose is to provide map common to all devices ++ * and known to work with current software and its bugs ++ * and misfeatures. Once issues with the software are ++ * solved, codes from input.h will be used directly ++ * (missing key definitions will be added). ++ */ ++ ++/* Some directly usable keycodes: ++KEY_POWER - Power/suspend button ++KEY_ENTER - Enter/Action/Central button on joypad ++KEY_UP ++KEY_DOWN ++KEY_LEFT ++KEY_RIGHT ++*/ ++ ++/* XXX Instead of using any values in include/linux/input.h, we have to use ++ use values < 128 due to some munging that kdrive does to get keystrokes. ++ When kdrive gets its key events from evdev instead of the console, ++ we should be able to switch to using input.h values and get rid of ++ xmodmap. */ ++ ++#define _KEY_APP1 KEY_F9 // xmodmap sees 67 + 8 = 75 ++#define _KEY_APP2 KEY_F10 // xmodmap 76 ++#define _KEY_APP3 KEY_F11 // xmodmap 95 ++#define _KEY_APP4 KEY_F12 // xmodmap 96 ++ ++#define _KEY_RECORD KEY_RO ++ ++/* It is highly recommended to use exactly 4 codes above for ++ 4 buttons the device has. This will ensure that console and ++ framebuffer applications (e.g. games) will work ok on all ++ devices. If you'd like more distinguishable names, following ++ convenience defines are provided, suiting many devices. */ ++ ++#define _KEY_CALENDAR _KEY_APP1 ++#define _KEY_CONTACTS _KEY_APP2 ++#define _KEY_MAIL _KEY_APP3 ++#define _KEY_HOMEPAGE _KEY_APP4 ++ ++#endif +Index: linux-2.6.24/include/linux/soc/asic3_base.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/include/linux/soc/asic3_base.h 2008-03-10 16:09:23.000000000 +0000 +@@ -0,0 +1,104 @@ ++#include <asm/types.h> ++#include <linux/gpiodev.h> ++ ++/* Private API - for ASIC3 devices internal use only */ ++#define HDR_IPAQ_ASIC3_ACTION(ACTION,action,fn,FN) \ ++u32 asic3_get_gpio_ ## action ## _ ## fn (struct device *dev); \ ++void asic3_set_gpio_ ## action ## _ ## fn (struct device *dev, u32 bits, u32 val); ++ ++#define HDR_IPAQ_ASIC3_FN(fn,FN) \ ++ HDR_IPAQ_ASIC3_ACTION ( MASK,mask,fn,FN) \ ++ HDR_IPAQ_ASIC3_ACTION ( DIR, dir, fn, FN) \ ++ HDR_IPAQ_ASIC3_ACTION ( OUT, out, fn, FN) \ ++ HDR_IPAQ_ASIC3_ACTION ( LEVELTRI, trigtype, fn, FN) \ ++ HDR_IPAQ_ASIC3_ACTION ( RISING, rising, fn, FN) \ ++ HDR_IPAQ_ASIC3_ACTION ( LEVEL, triglevel, fn, FN) \ ++ HDR_IPAQ_ASIC3_ACTION ( SLEEP_MASK, sleepmask, fn, FN) \ ++ HDR_IPAQ_ASIC3_ACTION ( SLEEP_OUT, sleepout, fn, FN) \ ++ HDR_IPAQ_ASIC3_ACTION ( BATT_FAULT_OUT, battfaultout, fn, FN) \ ++ HDR_IPAQ_ASIC3_ACTION ( INT_STATUS, intstatus, fn, FN) \ ++ HDR_IPAQ_ASIC3_ACTION ( ALT_FUNCTION, alt_fn, fn, FN) \ ++ HDR_IPAQ_ASIC3_ACTION ( SLEEP_CONF, sleepconf, fn, FN) \ ++ HDR_IPAQ_ASIC3_ACTION ( STATUS, status, fn, FN) ++ ++/* Public API */ ++ ++#define ASIC3_GPIOA_IRQ_BASE 0 ++#define ASIC3_GPIOB_IRQ_BASE 16 ++#define ASIC3_GPIOC_IRQ_BASE 32 ++#define ASIC3_GPIOD_IRQ_BASE 48 ++#define ASIC3_LED0_IRQ 64 ++#define ASIC3_LED1_IRQ 65 ++#define ASIC3_LED2_IRQ 66 ++#define ASIC3_SPI_IRQ 67 ++#define ASIC3_SMBUS_IRQ 68 ++#define ASIC3_OWM_IRQ 69 ++ ++#define ASIC3_NR_GPIO_IRQS 64 /* 16 bits each GPIO A...D banks */ ++#define ASIC3_NR_IRQS (ASIC3_OWM_IRQ + 1) ++ ++extern int asic3_irq_base(struct device *dev); ++ ++extern void asic3_write_register(struct device *dev, unsigned int reg, ++ u32 value); ++extern u32 asic3_read_register(struct device *dev, unsigned int reg); ++ ++/* old clock api */ ++extern void asic3_set_clock_sel(struct device *dev, u32 bits, u32 val); ++extern u32 asic3_get_clock_cdex(struct device *dev); ++extern void asic3_set_clock_cdex(struct device *dev, u32 bits, u32 val); ++ ++extern void asic3_set_extcf_select(struct device *dev, u32 bits, u32 val); ++extern void asic3_set_extcf_reset(struct device *dev, u32 bits, u32 val); ++extern void asic3_set_sdhwctrl(struct device *dev, u32 bits, u32 val); ++ ++extern void asic3_set_led(struct device *dev, int led_num, int duty_time, ++ int cycle_time, int timebase); ++ ++extern int asic3_register_mmc(struct device *dev); ++extern int asic3_unregister_mmc(struct device *dev); ++ ++/* Accessors for GPIO banks */ ++HDR_IPAQ_ASIC3_FN(a, A) ++HDR_IPAQ_ASIC3_FN(b, B) ++HDR_IPAQ_ASIC3_FN(c, C) ++HDR_IPAQ_ASIC3_FN(d, D) ++ ++#define _IPAQ_ASIC3_GPIO_BANK_A 0 ++#define _IPAQ_ASIC3_GPIO_BANK_B 1 ++#define _IPAQ_ASIC3_GPIO_BANK_C 2 ++#define _IPAQ_ASIC3_GPIO_BANK_D 3 ++ ++#define ASIC3_GPIO_bit(gpio) (1 << (gpio & 0xf)) ++ ++extern int asic3_get_gpio_bit(struct device *dev, int gpio); ++extern void asic3_set_gpio_bit(struct device *dev, int gpio, int val); ++extern int asic3_gpio_get_value(struct device *dev, unsigned gpio); ++extern void asic3_gpio_set_value(struct device *dev, unsigned gpio, int val); ++ ++ ++struct tmio_mmc_hwconfig; ++ ++struct asic3_platform_data ++{ ++ // Must be first member ++ struct gpiodev_ops gpiodev_ops; ++ ++ struct { ++ u32 dir; ++ u32 init; ++ u32 sleep_mask; ++ u32 sleep_out; ++ u32 batt_fault_out; ++ u32 sleep_conf; ++ u32 alt_function; ++ } gpio_a, gpio_b, gpio_c, gpio_d; ++ ++ int irq_base; ++ unsigned int bus_shift; ++ ++ struct platform_device **child_platform_devs; ++ int num_child_platform_devs; ++ ++ struct tmio_mmc_hwconfig *tmio_mmc_hwconfig; ++}; +Index: linux-2.6.24/include/linux/soc/tmio_mmc.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/include/linux/soc/tmio_mmc.h 2008-03-10 16:09:23.000000000 +0000 +@@ -0,0 +1,17 @@ ++#include <linux/platform_device.h> ++ ++#define MMC_CLOCK_DISABLED 0 ++#define MMC_CLOCK_ENABLED 1 ++ ++#define TMIO_WP_ALWAYS_RW ((void*)-1) ++ ++struct tmio_mmc_hwconfig { ++ void (*hwinit)(struct platform_device *sdev); ++ void (*set_mmc_clock)(struct platform_device *sdev, int state); ++ ++ /* NULL - use ASIC3 signal, ++ TMIO_WP_ALWAYS_RW - assume always R/W (e.g. miniSD) ++ otherwise - machine-specific handler */ ++ int (*mmc_get_ro)(struct platform_device *pdev); ++ short address_shift; ++}; +Index: linux-2.6.24/include/asm-arm/arch-pxa/pxa-regs.h +=================================================================== +--- linux-2.6.24.orig/include/asm-arm/arch-pxa/pxa-regs.h 2008-03-10 16:07:59.000000000 +0000 ++++ linux-2.6.24/include/asm-arm/arch-pxa/pxa-regs.h 2008-03-10 16:09:23.000000000 +0000 +@@ -2058,6 +2058,8 @@ + #define LDCMD_SOFINT (1 << 22) + #define LDCMD_EOFINT (1 << 21) + ++#define LCCR4_13M_PCD_EN (1<<25) /* 13M PCD enable */ ++#define LCCR4_PCDDIV (1<<31) /* PCD selection */ + + #define LCCR5_SOFM1 (1<<0) /* Start Of Frame Mask for Overlay 1 (channel 1) */ + #define LCCR5_SOFM2 (1<<1) /* Start Of Frame Mask for Overlay 2 (channel 2) */ +Index: linux-2.6.24/drivers/mmc/host/Makefile +=================================================================== +--- linux-2.6.24.orig/drivers/mmc/host/Makefile 2008-01-24 22:58:37.000000000 +0000 ++++ linux-2.6.24/drivers/mmc/host/Makefile 2008-03-10 16:09:23.000000000 +0000 +@@ -13,6 +13,7 @@ + obj-$(CONFIG_MMC_RICOH_MMC) += ricoh_mmc.o + obj-$(CONFIG_MMC_WBSD) += wbsd.o + obj-$(CONFIG_MMC_AU1X) += au1xmmc.o ++obj-$(CONFIG_MMC_ASIC3) += asic3_mmc.o + obj-$(CONFIG_MMC_OMAP) += omap.o + obj-$(CONFIG_MMC_AT91) += at91_mci.o + obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o +Index: linux-2.6.24/drivers/mmc/host/asic3_mmc.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/drivers/mmc/host/asic3_mmc.c 2008-03-10 16:09:23.000000000 +0000 +@@ -0,0 +1,900 @@ ++/* Note that this driver can likely be merged into the tmio driver, so ++ * consider this code temporary. It works, though. ++ */ ++/* ++ * linux/drivers/mmc/asic3_mmc.c ++ * ++ * Copyright (c) 2005 SDG Systems, LLC ++ * ++ * based on tmio_mmc.c ++ * Copyright (C) 2004 Ian Molton ++ * ++ * Refactored to support all ASIC3 devices, 2006 Paul Sokolovsky ++ * ++ * 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. ++ * ++ * Driver for the SD / SDIO cell found in: ++ * ++ * TC6393XB ++ * ++ * This driver draws mainly on scattered spec sheets, Reverse engineering ++ * of the toshiba e800 SD driver and some parts of the 2.4 ASIC3 driver (4 bit ++ * support). ++ * ++ * Supports MMC 1 bit transfers and SD 1 and 4 bit modes. ++ * ++ * TODO: ++ * Eliminate FIXMEs ++ * SDIO support ++ * Power management ++ * Handle MMC errors (at all) ++ * ++ */ ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/init.h> ++#include <linux/ioport.h> ++#include <linux/platform_device.h> ++#include <linux/interrupt.h> ++#include <linux/blkdev.h> ++#include <linux/delay.h> ++#include <linux/err.h> ++#include <linux/mmc/mmc.h> ++#include <linux/mmc/host.h> ++#include <linux/mmc/card.h> ++//#include <linux/mmc/protocol.h> ++#include <linux/mmc/sd.h> ++#include <linux/scatterlist.h> ++//#include <linux/soc-old.h> ++#include <linux/soc/asic3_base.h> ++#include <linux/soc/tmio_mmc.h> ++ ++#include <asm/io.h> ++#include <asm/irq.h> ++#include <asm/mach/irq.h> ++#include <linux/clk.h> ++#include <asm/mach-types.h> ++ ++#include <asm/hardware/ipaq-asic3.h> ++#include "asic3_mmc.h" ++ ++struct asic3_mmc_host { ++ void *ctl_base; ++ struct device *asic3_dev; /* asic3 device */ ++ struct tmio_mmc_hwconfig *hwconfig; /* HW config data/handlers, guaranteed != NULL */ ++ unsigned long bus_shift; ++ struct mmc_command *cmd; ++ struct mmc_request *mrq; ++ struct mmc_data *data; ++ struct mmc_host *mmc; ++ int irq; ++ unsigned short clock_for_sd; ++ ++ /* I/O related stuff */ ++ struct scatterlist *sg_ptr; ++ unsigned int sg_len; ++ unsigned int sg_off; ++}; ++ ++static void ++mmc_finish_request(struct asic3_mmc_host *host) ++{ ++ struct mmc_request *mrq = host->mrq; ++ ++ /* Write something to end the command */ ++ host->mrq = NULL; ++ host->cmd = NULL; ++ host->data = NULL; ++ ++ mmc_request_done(host->mmc, mrq); ++} ++ ++ ++#define ASIC3_MMC_REG(host, block, reg) (*((volatile u16 *) ((host->ctl_base) + ((_IPAQ_ASIC3_## block ## _Base + _IPAQ_ASIC3_ ## block ## _ ## reg) >> (2 - host->bus_shift))) )) ++ ++static void ++mmc_start_command(struct asic3_mmc_host *host, struct mmc_command *cmd) ++{ ++ struct mmc_data *data = host->data; ++ int c = cmd->opcode; ++ ++ DBG("[1;33mOpcode: %d[0m, base: %p\n", cmd->opcode, host->ctl_base); ++ ++ if(cmd->opcode == MMC_STOP_TRANSMISSION) { ++ ASIC3_MMC_REG(host, SD_CTRL, StopInternal) = SD_CTRL_STOP_INTERNAL_ISSSUE_CMD12; ++ cmd->resp[0] = cmd->opcode; ++ cmd->resp[1] = 0; ++ cmd->resp[2] = 0; ++ cmd->resp[3] = 0; ++ cmd->resp[4] = 0; ++ return; ++ } ++ ++ switch(cmd->flags & 0x1f) { ++ case MMC_RSP_NONE: c |= SD_CTRL_COMMAND_RESPONSE_TYPE_NORMAL; break; ++ case MMC_RSP_R1: c |= SD_CTRL_COMMAND_RESPONSE_TYPE_EXT_R1; break; ++ case MMC_RSP_R1B: c |= SD_CTRL_COMMAND_RESPONSE_TYPE_EXT_R1B; break; ++ case MMC_RSP_R2: c |= SD_CTRL_COMMAND_RESPONSE_TYPE_EXT_R2; break; ++ case MMC_RSP_R3: c |= SD_CTRL_COMMAND_RESPONSE_TYPE_EXT_R3; break; ++ default: ++ DBG("Unknown response type %d\n", cmd->flags & 0x1f); ++ break; ++ } ++ ++ host->cmd = cmd; ++ ++ if(cmd->opcode == MMC_APP_CMD) { ++ c |= APP_CMD; ++ } ++ if (cmd->opcode == MMC_GO_IDLE_STATE) { ++ c |= (3 << 8); /* This was removed from ipaq-asic3.h for some reason */ ++ } ++ if(data) { ++ c |= SD_CTRL_COMMAND_DATA_PRESENT; ++ if(data->blocks > 1) { ++ ASIC3_MMC_REG(host, SD_CTRL, StopInternal) = SD_CTRL_STOP_INTERNAL_AUTO_ISSUE_CMD12; ++ c |= SD_CTRL_COMMAND_MULTI_BLOCK; ++ } ++ if(data->flags & MMC_DATA_READ) { ++ c |= SD_CTRL_COMMAND_TRANSFER_READ; ++ } ++ /* MMC_DATA_WRITE does not require a bit to be set */ ++ } ++ ++ /* Enable the command and data interrupts */ ++ ASIC3_MMC_REG(host, SD_CTRL, IntMaskCard) = ~( ++ SD_CTRL_INTMASKCARD_RESPONSE_END ++ | SD_CTRL_INTMASKCARD_RW_END ++ | SD_CTRL_INTMASKCARD_CARD_REMOVED_0 ++ | SD_CTRL_INTMASKCARD_CARD_INSERTED_0 ++#if 0 ++ | SD_CTRL_INTMASKCARD_CARD_REMOVED_3 ++ | SD_CTRL_INTMASKCARD_CARD_INSERTED_3 ++#endif ++ ); ++ ++ ASIC3_MMC_REG(host, SD_CTRL, IntMaskBuffer) = ~( ++ SD_CTRL_INTMASKBUFFER_UNK7 ++ | SD_CTRL_INTMASKBUFFER_CMD_BUSY ++#if 0 ++ | SD_CTRL_INTMASKBUFFER_CMD_INDEX_ERROR ++ | SD_CTRL_INTMASKBUFFER_CRC_ERROR ++ | SD_CTRL_INTMASKBUFFER_STOP_BIT_END_ERROR ++ | SD_CTRL_INTMASKBUFFER_DATA_TIMEOUT ++ | SD_CTRL_INTMASKBUFFER_BUFFER_OVERFLOW ++ | SD_CTRL_INTMASKBUFFER_BUFFER_UNDERFLOW ++ | SD_CTRL_INTMASKBUFFER_CMD_TIMEOUT ++ | SD_CTRL_INTMASKBUFFER_BUFFER_READ_ENABLE ++ | SD_CTRL_INTMASKBUFFER_BUFFER_WRITE_ENABLE ++ | SD_CTRL_INTMASKBUFFER_ILLEGAL_ACCESS ++#endif ++ ); ++ ++ /* Send the command */ ++ ASIC3_MMC_REG(host, SD_CTRL, Arg1) = cmd->arg >> 16; ++ ASIC3_MMC_REG(host, SD_CTRL, Arg0) = cmd->arg & 0xffff; ++ ASIC3_MMC_REG(host, SD_CTRL, Cmd) = c; ++} ++ ++/* This chip always returns (at least?) as much data as you ask for. I'm ++ * unsure what happens if you ask for less than a block. This should be looked ++ * into to ensure that a funny length read doesnt mess up the controller data ++ * state machine. ++ * ++ * Aric: Statement above may not apply to ASIC3. ++ * ++ * FIXME - this chip cannot do 1 and 2 byte data requests in 4 bit mode ++ * ++ * Aric: Statement above may not apply to ASIC3. ++ */ ++ ++static struct tasklet_struct mmc_data_read_tasklet; ++ ++static void ++mmc_data_transfer(unsigned long h) ++{ ++ struct asic3_mmc_host *host = (struct asic3_mmc_host *)h; ++ struct mmc_data *data = host->data; ++ unsigned short *buf; ++ int count; ++ /* unsigned long flags; */ ++ ++ if(!data){ ++ printk(KERN_WARNING DRIVER_NAME ": Spurious Data IRQ\n"); ++ return; ++ } ++ ++ /* local_irq_save(flags); */ ++ /* buf = kmap_atomic(host->sg_ptr->page, KM_BIO_SRC_IRQ); */ ++ buf = kmap(host->sg_ptr->page); ++ buf += host->sg_ptr->offset/2 + host->sg_off/2; ++ ++ /* ++ * Ensure we dont read more than one block. The chip will interrupt us ++ * When the next block is available. ++ */ ++ count = host->sg_ptr->length - host->sg_off; ++ if(count > data->blksz) { ++ count = data->blksz; ++ } ++ ++ DBG("count: %08x, page: %p, offset: %08x flags %08x\n", ++ count, host->sg_ptr->page, host->sg_off, data->flags); ++ ++ host->sg_off += count; ++ ++ /* Transfer the data */ ++ if(data->flags & MMC_DATA_READ) { ++ while(count > 0) { ++ /* Read two bytes from SD/MMC controller. */ ++ *buf = ASIC3_MMC_REG(host, SD_CTRL, DataPort); ++ buf++; ++ count -= 2; ++ } ++ //flush_dcache_page(host->sg_ptr->page); ++ } else { ++ while(count > 0) { ++ /* Write two bytes to SD/MMC controller. */ ++ ASIC3_MMC_REG(host, SD_CTRL, DataPort) = *buf; ++ buf++; ++ count -= 2; ++ } ++ } ++ ++ /* kunmap_atomic(host->sg_ptr->page, KM_BIO_SRC_IRQ); */ ++ kunmap(host->sg_ptr->page); ++ /* local_irq_restore(flags); */ ++ if(host->sg_off == host->sg_ptr->length) { ++ host->sg_ptr++; ++ host->sg_off = 0; ++ --host->sg_len; ++ } ++ ++ return; ++} ++ ++static void ++mmc_data_end_irq(struct asic3_mmc_host *host) ++{ ++ struct mmc_data *data = host->data; ++ ++ host->data = NULL; ++ ++ if(!data){ ++ printk(KERN_WARNING DRIVER_NAME ": Spurious data end IRQ\n"); ++ return; ++ } ++ ++ if (data->error == MMC_ERR_NONE) { ++ data->bytes_xfered = data->blocks * data->blksz; ++ } else { ++ data->bytes_xfered = 0; ++ } ++ ++ DBG("Completed data request\n"); ++ ++ ASIC3_MMC_REG(host, SD_CTRL, StopInternal) = 0; ++ ++ /* Make sure read enable interrupt and write enable interrupt are disabled */ ++ if(data->flags & MMC_DATA_READ) { ++ ASIC3_MMC_REG(host, SD_CTRL, IntMaskBuffer) |= SD_CTRL_INTMASKBUFFER_BUFFER_READ_ENABLE; ++ } else { ++ ASIC3_MMC_REG(host, SD_CTRL, IntMaskBuffer) |= SD_CTRL_INTMASKBUFFER_BUFFER_WRITE_ENABLE; ++ } ++ ++ mmc_finish_request(host); ++} ++ ++static void ++mmc_cmd_irq(struct asic3_mmc_host *host, unsigned int buffer_stat) ++{ ++ struct mmc_command *cmd = host->cmd; ++ u8 *buf = (u8 *)cmd->resp; ++ u16 data; ++ ++ if(!host->cmd) { ++ printk(KERN_WARNING DRIVER_NAME ": Spurious CMD irq\n"); ++ return; ++ } ++ ++ host->cmd = NULL; ++ if(cmd->flags & MMC_RSP_PRESENT && cmd->flags & MMC_RSP_136) { ++ /* R2 */ ++ buf[12] = 0xff; ++ data = ASIC3_MMC_REG(host, SD_CTRL, Response0); ++ buf[13] = data & 0xff; ++ buf[14] = data >> 8; ++ data = ASIC3_MMC_REG(host, SD_CTRL, Response1); ++ buf[15] = data & 0xff; ++ buf[8] = data >> 8; ++ data = ASIC3_MMC_REG(host, SD_CTRL, Response2); ++ buf[9] = data & 0xff; ++ buf[10] = data >> 8; ++ data = ASIC3_MMC_REG(host, SD_CTRL, Response3); ++ buf[11] = data & 0xff; ++ buf[4] = data >> 8; ++ data = ASIC3_MMC_REG(host, SD_CTRL, Response4); ++ buf[5] = data & 0xff; ++ buf[6] = data >> 8; ++ data = ASIC3_MMC_REG(host, SD_CTRL, Response5); ++ buf[7] = data & 0xff; ++ buf[0] = data >> 8; ++ data = ASIC3_MMC_REG(host, SD_CTRL, Response6); ++ buf[1] = data & 0xff; ++ buf[2] = data >> 8; ++ data = ASIC3_MMC_REG(host, SD_CTRL, Response7); ++ buf[3] = data & 0xff; ++ } else if(cmd->flags & MMC_RSP_PRESENT) { ++ /* R1, R1B, R3 */ ++ data = ASIC3_MMC_REG(host, SD_CTRL, Response0); ++ buf[0] = data & 0xff; ++ buf[1] = data >> 8; ++ data = ASIC3_MMC_REG(host, SD_CTRL, Response1); ++ buf[2] = data & 0xff; ++ buf[3] = data >> 8; ++ } ++ DBG("Response: %08x %08x %08x %08x\n", cmd->resp[0], cmd->resp[1], cmd->resp[2], cmd->resp[3]); ++ ++ if(buffer_stat & SD_CTRL_BUFFERSTATUS_CMD_TIMEOUT) { ++ cmd->error = MMC_ERR_TIMEOUT; ++ } else if((buffer_stat & SD_CTRL_BUFFERSTATUS_CRC_ERROR) && (cmd->flags & MMC_RSP_CRC)) { ++ cmd->error = MMC_ERR_BADCRC; ++ } else if(buffer_stat & ++ ( ++ SD_CTRL_BUFFERSTATUS_ILLEGAL_ACCESS ++ | SD_CTRL_BUFFERSTATUS_CMD_INDEX_ERROR ++ | SD_CTRL_BUFFERSTATUS_STOP_BIT_END_ERROR ++ | SD_CTRL_BUFFERSTATUS_BUFFER_OVERFLOW ++ | SD_CTRL_BUFFERSTATUS_BUFFER_UNDERFLOW ++ | SD_CTRL_BUFFERSTATUS_DATA_TIMEOUT ++ ) ++ ) { ++ DBG("Buffer status ERROR 0x%04x - inside check buffer\n", buffer_stat); ++ DBG("detail0 error status 0x%04x\n", ASIC3_MMC_REG(host, SD_CTRL, ErrorStatus0)); ++ DBG("detail1 error status 0x%04x\n", ASIC3_MMC_REG(host, SD_CTRL, ErrorStatus1)); ++ cmd->error = MMC_ERR_FAILED; ++ } ++ ++ if(cmd->error == MMC_ERR_NONE) { ++ switch (cmd->opcode) { ++ case SD_APP_SET_BUS_WIDTH: ++ if(cmd->arg == SD_BUS_WIDTH_4) { ++ host->clock_for_sd = SD_CTRL_CARDCLOCKCONTROL_FOR_SD_CARD; ++ ASIC3_MMC_REG(host, SD_CTRL, MemCardOptionSetup) = ++ MEM_CARD_OPTION_REQUIRED ++ | MEM_CARD_OPTION_DATA_RESPONSE_TIMEOUT(14) ++ | MEM_CARD_OPTION_C2_MODULE_NOT_PRESENT ++ | MEM_CARD_OPTION_DATA_XFR_WIDTH_4; ++ } else { ++ host->clock_for_sd = 0; ++ ASIC3_MMC_REG(host, SD_CTRL, MemCardOptionSetup) = ++ MEM_CARD_OPTION_REQUIRED ++ | MEM_CARD_OPTION_DATA_RESPONSE_TIMEOUT(14) ++ | MEM_CARD_OPTION_C2_MODULE_NOT_PRESENT ++ | MEM_CARD_OPTION_DATA_XFR_WIDTH_1; ++ } ++ break; ++ case MMC_SELECT_CARD: ++ if((cmd->arg >> 16) == 0) { ++ /* We have been deselected. */ ++ ASIC3_MMC_REG(host, SD_CTRL, MemCardOptionSetup) = ++ MEM_CARD_OPTION_REQUIRED ++ | MEM_CARD_OPTION_DATA_RESPONSE_TIMEOUT(14) ++ | MEM_CARD_OPTION_C2_MODULE_NOT_PRESENT ++ | MEM_CARD_OPTION_DATA_XFR_WIDTH_1; ++ } ++ } ++ } ++ ++ /* ++ * If there is data to handle we enable data IRQs here, and we will ++ * ultimatley finish the request in the mmc_data_end_irq handler. ++ */ ++ if(host->data && (cmd->error == MMC_ERR_NONE)){ ++ if(host->data->flags & MMC_DATA_READ) { ++ /* Enable the read enable interrupt */ ++ ASIC3_MMC_REG(host, SD_CTRL, IntMaskBuffer) &= ++ ~SD_CTRL_INTMASKBUFFER_BUFFER_READ_ENABLE; ++ } else { ++ /* Enable the write enable interrupt */ ++ ASIC3_MMC_REG(host, SD_CTRL, IntMaskBuffer) &= ++ ~SD_CTRL_INTMASKBUFFER_BUFFER_WRITE_ENABLE; ++ } ++ } else { ++ /* There's no data, or we encountered an error, so finish now. */ ++ mmc_finish_request(host); ++ } ++ ++ return; ++} ++ ++static void hwinit2_irqsafe(struct asic3_mmc_host *host); ++ ++static irqreturn_t ++mmc_irq(int irq, void *irq_desc) ++{ ++ struct asic3_mmc_host *host; ++ unsigned int breg, bmask, bstatus, creg, cmask, cstatus; ++ ++ host = irq_desc; ++ ++ /* asic3 bstatus has errors */ ++ bstatus = ASIC3_MMC_REG(host, SD_CTRL, BufferCtrl); ++ bmask = ASIC3_MMC_REG(host, SD_CTRL, IntMaskBuffer); ++ cstatus = ASIC3_MMC_REG(host, SD_CTRL, CardStatus); ++ cmask = ASIC3_MMC_REG(host, SD_CTRL, IntMaskCard); ++ breg = bstatus & ~bmask & ~DONT_CARE_BUFFER_BITS; ++ creg = cstatus & ~cmask & ~DONT_CARE_CARD_BITS; ++ ++ if (!breg && !creg) { ++ /* This occurs sometimes for no known reason. It doesn't hurt ++ * anything, so I don't print it. */ ++ ASIC3_MMC_REG(host, SD_CTRL, IntMaskBuffer) &= ~breg; ++ ASIC3_MMC_REG(host, SD_CTRL, IntMaskCard) &= ~creg; ++ goto out; ++ } ++ ++ while (breg || creg) { ++ ++ /* XXX TODO: Need to handle errors in breg here. */ ++ ++ /* ++ * Card insert/remove. The mmc controlling code is stateless. That ++ * is, it doesn't care if it was an insert or a remove. It treats ++ * both the same. ++ */ ++ /* XXX Asic3 has _3 versions of these status bits, too, for a second slot, perhaps? */ ++ if (creg & (SD_CTRL_CARDSTATUS_CARD_INSERTED_0 | SD_CTRL_CARDSTATUS_CARD_REMOVED_0)) { ++ ASIC3_MMC_REG(host, SD_CTRL, CardStatus) &= ++ ~(SD_CTRL_CARDSTATUS_CARD_REMOVED_0 | SD_CTRL_CARDSTATUS_CARD_INSERTED_0); ++ if(creg & SD_CTRL_CARDSTATUS_CARD_INSERTED_0) { ++ hwinit2_irqsafe(host); ++ } ++ mmc_detect_change(host->mmc,1); ++ } ++ ++ /* Command completion */ ++ if (creg & SD_CTRL_CARDSTATUS_RESPONSE_END) { ++ ASIC3_MMC_REG(host, SD_CTRL, CardStatus) &= ++ ~(SD_CTRL_CARDSTATUS_RESPONSE_END); ++ mmc_cmd_irq(host, bstatus); ++ } ++ ++ /* Data transfer */ ++ if (breg & (SD_CTRL_BUFFERSTATUS_BUFFER_READ_ENABLE | SD_CTRL_BUFFERSTATUS_BUFFER_WRITE_ENABLE)) { ++ ASIC3_MMC_REG(host, SD_CTRL, BufferCtrl) &= ++ ~(SD_CTRL_BUFFERSTATUS_BUFFER_WRITE_ENABLE | SD_CTRL_BUFFERSTATUS_BUFFER_READ_ENABLE); ++ tasklet_schedule(&mmc_data_read_tasklet); ++ } ++ ++ /* Data transfer completion */ ++ if (creg & SD_CTRL_CARDSTATUS_RW_END) { ++ ASIC3_MMC_REG(host, SD_CTRL, CardStatus) &= ~(SD_CTRL_CARDSTATUS_RW_END); ++ mmc_data_end_irq(host); ++ } ++ ++ /* Check status - keep going until we've handled it all */ ++ bstatus = ASIC3_MMC_REG(host, SD_CTRL, BufferCtrl); ++ bmask = ASIC3_MMC_REG(host, SD_CTRL, IntMaskBuffer); ++ cstatus = ASIC3_MMC_REG(host, SD_CTRL, CardStatus); ++ cmask = ASIC3_MMC_REG(host, SD_CTRL, IntMaskCard); ++ breg = bstatus & ~bmask & ~DONT_CARE_BUFFER_BITS; ++ creg = cstatus & ~cmask & ~DONT_CARE_CARD_BITS; ++ } ++ ++out: ++ /* Ensure all interrupt sources are cleared */ ++ ASIC3_MMC_REG(host, SD_CTRL, BufferCtrl) = 0; ++ ASIC3_MMC_REG(host, SD_CTRL, CardStatus) = 0; ++ return IRQ_HANDLED; ++} ++ ++static void ++mmc_start_data(struct asic3_mmc_host *host, struct mmc_data *data) ++{ ++ DBG("setup data transfer: blocksize %08x nr_blocks %d, page: %08x, offset: %08x\n", data->blksz, ++ data->blocks, (int)data->sg->page, data->sg->offset); ++ ++ host->sg_len = data->sg_len; ++ host->sg_ptr = data->sg; ++ host->sg_off = 0; ++ host->data = data; ++ ++ /* Set transfer length and blocksize */ ++ ASIC3_MMC_REG(host, SD_CTRL, TransferSectorCount) = data->blocks; ++ ASIC3_MMC_REG(host, SD_CTRL, MemCardXferDataLen) = data->blksz; ++} ++ ++/* Process requests from the MMC layer */ ++static void ++mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) ++{ ++ struct asic3_mmc_host *host = mmc_priv(mmc); ++ ++ WARN_ON(host->mrq != NULL); ++ ++ host->mrq = mrq; ++ ++ /* If we're performing a data request we need to setup some ++ extra information */ ++ if(mrq->data) { ++ mmc_start_data(host, mrq->data); ++ } ++ ++ mmc_start_command(host, mrq->cmd); ++} ++ ++/* Set MMC clock / power. ++ * Note: This controller uses a simple divider scheme therefore it cannot run ++ * a MMC card at full speed (20MHz). The max clock is 24MHz on SD, but as MMC ++ * wont run that fast, it has to be clocked at 12MHz which is the next slowest ++ * setting. This is likely not an issue because we are doing single 16-bit ++ * writes for data I/O. ++ */ ++static void ++mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ++{ ++ struct asic3_mmc_host *host = mmc_priv(mmc); ++ u32 clk = 0; ++ ++ DBG("clock %uHz busmode %u powermode %u Vdd %u\n", ++ ios->clock, ios->bus_mode, ios->power_mode, ios->vdd); ++ ++ if (ios->clock) { ++ clk = 0x80; /* slowest by default */ ++ if(ios->clock >= 24000000 / 256) clk >>= 1; ++ if(ios->clock >= 24000000 / 128) clk >>= 1; ++ if(ios->clock >= 24000000 / 64) clk >>= 1; ++ if(ios->clock >= 24000000 / 32) clk >>= 1; ++ if(ios->clock >= 24000000 / 16) clk >>= 1; ++ if(ios->clock >= 24000000 / 8) clk >>= 1; ++ if(ios->clock >= 24000000 / 4) clk >>= 1; ++ if(ios->clock >= 24000000 / 2) clk >>= 1; ++ if(ios->clock >= 24000000 / 1) clk >>= 1; ++ if(clk == 0) { /* For fastest speed we disable the divider. */ ++ ASIC3_MMC_REG(host, SD_CONFIG, ClockMode) = 0; ++ } else { ++ ASIC3_MMC_REG(host, SD_CONFIG, ClockMode) = 1; ++ } ++ ASIC3_MMC_REG(host, SD_CTRL, CardClockCtrl) = 0; ++ ASIC3_MMC_REG(host, SD_CTRL, CardClockCtrl) = ++ host->clock_for_sd ++ | SD_CTRL_CARDCLOCKCONTROL_ENABLE_CLOCK ++ | clk; ++ msleep(10); ++ } else { ++ ASIC3_MMC_REG(host, SD_CTRL, CardClockCtrl) = 0; ++ } ++ ++ switch (ios->power_mode) { ++ case MMC_POWER_OFF: ++ ASIC3_MMC_REG(host, SD_CONFIG, SDHC_Power1) = 0; ++ msleep(1); ++ break; ++ case MMC_POWER_UP: ++ break; ++ case MMC_POWER_ON: ++ ASIC3_MMC_REG(host, SD_CONFIG, SDHC_Power1) = SD_CONFIG_POWER1_PC_33V; ++ msleep(20); ++ break; ++ } ++} ++ ++static int ++mmc_get_ro(struct mmc_host *mmc) ++{ ++ struct asic3_mmc_host *host = mmc_priv(mmc); ++ ++ /* Call custom handler for RO status */ ++ if(host->hwconfig->mmc_get_ro) { ++ /* Special case for cards w/o WP lock (like miniSD) */ ++ if (host->hwconfig->mmc_get_ro == (void*)-1) { ++ return 0; ++ } else { ++ struct platform_device *pdev = to_platform_device(mmc_dev(mmc)); ++ return host->hwconfig->mmc_get_ro(pdev); ++ } ++ } ++ ++ /* WRITE_PROTECT is active low */ ++ return (ASIC3_MMC_REG(host, SD_CTRL, CardStatus) & SD_CTRL_CARDSTATUS_WRITE_PROTECT)?0:1; ++} ++ ++static struct mmc_host_ops mmc_ops = { ++ .request = mmc_request, ++ .set_ios = mmc_set_ios, ++ .get_ro = mmc_get_ro, ++}; ++ ++static void ++hwinit2_irqsafe(struct asic3_mmc_host *host) ++{ ++ ASIC3_MMC_REG(host, SD_CONFIG, Addr1) = 0x0000; ++ ASIC3_MMC_REG(host, SD_CONFIG, Addr0) = 0x0800; ++ ++ ASIC3_MMC_REG(host, SD_CONFIG, ClkStop) = SD_CONFIG_CLKSTOP_ENABLE_ALL; ++ ASIC3_MMC_REG(host, SD_CONFIG, SDHC_CardDetect) = 2; ++ ASIC3_MMC_REG(host, SD_CONFIG, Command) = SD_CONFIG_COMMAND_MAE; ++ ++ ASIC3_MMC_REG(host, SD_CTRL, SoftwareReset) = 0; /* reset on */ ++ mdelay(2); ++ ++ ASIC3_MMC_REG(host, SD_CTRL, SoftwareReset) = 1; /* reset off */ ++ mdelay(2); ++ ++ ASIC3_MMC_REG(host, SD_CTRL, MemCardOptionSetup) = ++ MEM_CARD_OPTION_REQUIRED ++ | MEM_CARD_OPTION_DATA_RESPONSE_TIMEOUT(14) ++ | MEM_CARD_OPTION_C2_MODULE_NOT_PRESENT ++ | MEM_CARD_OPTION_DATA_XFR_WIDTH_1 ++ ; ++ host->clock_for_sd = 0; ++ ++ ASIC3_MMC_REG(host, SD_CTRL, CardClockCtrl) = 0; ++ ASIC3_MMC_REG(host, SD_CTRL, CardStatus) = 0; ++ ASIC3_MMC_REG(host, SD_CTRL, BufferCtrl) = 0; ++ ASIC3_MMC_REG(host, SD_CTRL, ErrorStatus0) = 0; ++ ASIC3_MMC_REG(host, SD_CTRL, ErrorStatus1) = 0; ++ ASIC3_MMC_REG(host, SD_CTRL, StopInternal) = 0; ++ ++ ASIC3_MMC_REG(host, SDIO_CTRL, ClocknWaitCtrl) = 0x100; ++ /* *((unsigned short *)(((char *)host->ctl_base) + 0x938)) = 0x100; */ ++ ++ ASIC3_MMC_REG(host, SD_CONFIG, ClockMode) = 0; ++ ASIC3_MMC_REG(host, SD_CTRL, CardClockCtrl) = 0; ++ ++ mdelay(1); ++ ++ ++ ASIC3_MMC_REG(host, SD_CTRL, IntMaskCard) = ~( ++ SD_CTRL_INTMASKCARD_RESPONSE_END ++ | SD_CTRL_INTMASKCARD_RW_END ++ | SD_CTRL_INTMASKCARD_CARD_REMOVED_0 ++ | SD_CTRL_INTMASKCARD_CARD_INSERTED_0 ++#if 0 ++ | SD_CTRL_INTMASKCARD_CARD_REMOVED_3 ++ | SD_CTRL_INTMASKCARD_CARD_INSERTED_3 ++#endif ++ ) ++ ; /* check */ ++ ASIC3_MMC_REG(host, SD_CTRL, IntMaskBuffer) = 0xffff; /* IRQs off */ ++ ++ /* ++ * ASIC3_MMC_REG(host, SD_CTRL, TransactionCtrl) = SD_CTRL_TRANSACTIONCONTROL_SET; ++ * Wince has 0x1000 ++ */ ++ /* ASIC3_MMC_REG(host, SD_CTRL, TransactionCtrl) = 0x1000; */ ++ ++ ++ asic3_set_sdhwctrl(host->asic3_dev, ASIC3_SDHWCTRL_SDPWR, ASIC3_SDHWCTRL_SDPWR); /* turn on power at controller(?) */ ++ ++} ++ ++static void ++hwinit(struct asic3_mmc_host *host, struct platform_device *pdev) ++{ ++ /* Call custom handler for enabling clock (if needed) */ ++ if(host->hwconfig->set_mmc_clock) ++ host->hwconfig->set_mmc_clock(pdev, MMC_CLOCK_ENABLED); ++ ++ /* Not sure if it must be done bit by bit, but leaving as-is */ ++ asic3_set_sdhwctrl(host->asic3_dev, ASIC3_SDHWCTRL_LEVCD, ASIC3_SDHWCTRL_LEVCD); ++ asic3_set_sdhwctrl(host->asic3_dev, ASIC3_SDHWCTRL_LEVWP, ASIC3_SDHWCTRL_LEVWP); ++ asic3_set_sdhwctrl(host->asic3_dev, ASIC3_SDHWCTRL_SUSPEND, 0); ++ asic3_set_sdhwctrl(host->asic3_dev, ASIC3_SDHWCTRL_PCLR, 0); ++ ++ asic3_set_clock_cdex (host->asic3_dev, ++ CLOCK_CDEX_EX1 | CLOCK_CDEX_EX0, CLOCK_CDEX_EX1 | CLOCK_CDEX_EX0); ++ msleep(1); ++ ++ asic3_set_clock_sel (host->asic3_dev, ++ CLOCK_SEL_SD_HCLK_SEL | CLOCK_SEL_SD_BCLK_SEL, ++ CLOCK_SEL_SD_HCLK_SEL | 0); /* ? */ ++ ++ asic3_set_clock_cdex (host->asic3_dev, ++ CLOCK_CDEX_SD_HOST | CLOCK_CDEX_SD_BUS, ++ CLOCK_CDEX_SD_HOST | CLOCK_CDEX_SD_BUS); ++ msleep(1); ++ ++ asic3_set_extcf_select(host->asic3_dev, ASIC3_EXTCF_SD_MEM_ENABLE, ASIC3_EXTCF_SD_MEM_ENABLE); ++ ++ /* Long Delay */ ++ if( !machine_is_h4700()) ++ msleep(500); ++ ++ hwinit2_irqsafe(host); ++} ++ ++#ifdef CONFIG_PM ++static int ++mmc_suspend(struct platform_device *pdev, pm_message_t state) ++{ ++ struct mmc_host *mmc = platform_get_drvdata(pdev); ++ struct asic3_mmc_host *host = mmc_priv(mmc); ++ int ret; ++ ++ ret = mmc_suspend_host(mmc, state); ++ ++ if (ret) { ++ printk(KERN_ERR DRIVER_NAME ": Could not suspend MMC host, hardware not suspended"); ++ return ret; ++ } ++ ++ /* disable the card insert / remove interrupt while sleeping */ ++ ASIC3_MMC_REG(host, SD_CTRL, IntMaskCard) = ~( ++ SD_CTRL_INTMASKCARD_RESPONSE_END ++ | SD_CTRL_INTMASKCARD_RW_END); ++ ++ /* disable clock */ ++ ASIC3_MMC_REG(host, SD_CTRL, CardClockCtrl) = 0; ++ ASIC3_MMC_REG(host, SD_CONFIG, ClkStop) = 0; ++ ++ /* power down */ ++ ASIC3_MMC_REG(host, SD_CONFIG, SDHC_Power1) = 0; ++ ++ asic3_set_clock_cdex (host->asic3_dev, ++ CLOCK_CDEX_SD_HOST | CLOCK_CDEX_SD_BUS, 0); ++ ++ /* disable core clock */ ++ if(host->hwconfig->set_mmc_clock) ++ host->hwconfig->set_mmc_clock(pdev, MMC_CLOCK_DISABLED); ++ ++ /* Put in suspend mode */ ++ asic3_set_sdhwctrl(host->asic3_dev, ASIC3_SDHWCTRL_SUSPEND, ASIC3_SDHWCTRL_SUSPEND); ++ return 0; ++} ++ ++static int ++mmc_resume(struct platform_device *pdev) ++{ ++ struct mmc_host *mmc = platform_get_drvdata(pdev); ++ struct asic3_mmc_host *host = mmc_priv(mmc); ++ ++ printk(KERN_INFO "%s: starting resume\n", DRIVER_NAME); ++ ++ asic3_set_sdhwctrl(host->asic3_dev, ASIC3_SDHWCTRL_SUSPEND, 0); ++ hwinit(host, pdev); ++ ++ /* re-enable card remove / insert interrupt */ ++ ASIC3_MMC_REG(host, SD_CTRL, IntMaskCard) = ~( ++ SD_CTRL_INTMASKCARD_RESPONSE_END ++ | SD_CTRL_INTMASKCARD_RW_END ++ | SD_CTRL_INTMASKCARD_CARD_REMOVED_0 ++ | SD_CTRL_INTMASKCARD_CARD_INSERTED_0 ); ++ ++ mmc_resume_host(mmc); ++ ++ printk(KERN_INFO "%s: finished resume\n", DRIVER_NAME); ++ return 0; ++} ++#endif ++ ++static int ++mmc_probe(struct platform_device *pdev) ++{ ++ struct mmc_host *mmc; ++ struct asic3_mmc_host *host = NULL; ++ int retval = 0; ++ struct tmio_mmc_hwconfig *mmc_config = (struct tmio_mmc_hwconfig *)pdev->dev.platform_data; ++ ++ /* bus_shift is mandatory */ ++ if (!mmc_config) { ++ printk(KERN_ERR DRIVER_NAME ": Invalid configuration\n"); ++ return -EINVAL; ++ } ++ ++ mmc = mmc_alloc_host(sizeof(struct asic3_mmc_host) + 128, &pdev->dev); ++ if (!mmc) { ++ retval = -ENOMEM; ++ goto exceptional_return; ++ } ++ ++ host = mmc_priv(mmc); ++ host->mmc = mmc; ++ platform_set_drvdata(pdev, mmc); ++ ++ host->ctl_base = 0; ++ host->hwconfig = mmc_config; ++ host->bus_shift = mmc_config->address_shift; ++ host->asic3_dev = pdev->dev.parent; ++ host->clock_for_sd = 0; ++ ++ tasklet_init(&mmc_data_read_tasklet, mmc_data_transfer, (unsigned long)host); ++ ++ host->ctl_base = ioremap_nocache ((unsigned long)pdev->resource[0].start, pdev->resource[0].end - pdev->resource[0].start); ++ if(!host->ctl_base){ ++ printk(KERN_ERR DRIVER_NAME ": Could not map ASIC3 SD controller\n"); ++ retval = -ENODEV; ++ goto exceptional_return; ++ } ++ ++ printk(DRIVER_NAME ": ASIC3 MMC/SD Driver, controller at 0x%lx\n", (unsigned long)pdev->resource[0].start); ++ ++ mmc->ops = &mmc_ops; ++ mmc->caps = MMC_CAP_4_BIT_DATA; ++ mmc->f_min = 46875; /* ARIC: not sure what these should be */ ++ mmc->f_max = 24000000; /* ARIC: not sure what these should be */ ++ mmc->ocr_avail = MMC_VDD_32_33; ++ ++ hwinit(host, pdev); ++ ++ ++ host->irq = pdev->resource[1].start; ++ ++ retval = request_irq(host->irq, mmc_irq, 0, DRIVER_NAME, host); ++ if(retval) { ++ printk(KERN_ERR DRIVER_NAME ": Unable to get interrupt\n"); ++ retval = -ENODEV; ++ goto exceptional_return; ++ } ++ set_irq_type(host->irq, IRQT_FALLING); ++ ++ mmc_add_host(mmc); ++ ++#ifdef CONFIG_PM ++ // resume_timer.function = resume_timer_callback; ++ // resume_timer.data = 0; ++ // init_timer(&resume_timer); ++#endif ++ ++ return 0; ++ ++exceptional_return: ++ if (mmc) { ++ mmc_free_host(mmc); ++ } ++ if(host && host->ctl_base) iounmap(host->ctl_base); ++ return retval; ++} ++ ++static int ++mmc_remove(struct platform_device *pdev) ++{ ++ struct mmc_host *mmc = platform_get_drvdata(pdev); ++ ++ platform_set_drvdata(pdev, NULL); ++ ++ if (mmc) { ++ struct asic3_mmc_host *host = mmc_priv(mmc); ++ mmc_remove_host(mmc); ++ free_irq(host->irq, host); ++ /* FIXME - we might want to consider stopping the chip here... */ ++ iounmap(host->ctl_base); ++ mmc_free_host(mmc); /* FIXME - why does this call hang? */ ++ } ++ return 0; ++} ++ ++/* ------------------- device registration ----------------------- */ ++ ++static struct platform_driver mmc_asic3_driver = { ++ .driver = { ++ .name = DRIVER_NAME, ++ }, ++ .probe = mmc_probe, ++ .remove = mmc_remove, ++#ifdef CONFIG_PM ++ .suspend = mmc_suspend, ++ .resume = mmc_resume, ++#endif ++}; ++ ++static int __init mmc_init(void) ++{ ++ return platform_driver_register(&mmc_asic3_driver); ++} ++ ++static void __exit mmc_exit(void) ++{ ++ platform_driver_unregister(&mmc_asic3_driver); ++} ++ ++late_initcall(mmc_init); ++module_exit(mmc_exit); ++ ++MODULE_DESCRIPTION("HTC ASIC3 SD/MMC driver"); ++MODULE_AUTHOR("Aric Blumer, SDG Systems, LLC"); ++MODULE_LICENSE("GPL"); ++ +Index: linux-2.6.24/drivers/mmc/host/asic3_mmc.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/drivers/mmc/host/asic3_mmc.h 2008-03-10 16:09:23.000000000 +0000 +@@ -0,0 +1,25 @@ ++#ifndef __ASIC3_MMC_H ++#define __ASIC3_MMC_H ++ ++#define DRIVER_NAME "asic3_mmc" ++ ++#ifdef CONFIG_MMC_DEBUG ++#define DBG(x...) printk(DRIVER_NAME ": " x) ++#else ++#define DBG(x...) do { } while (0) ++#endif ++ ++/* Response types */ ++#define APP_CMD 0x0040 ++ ++#define SD_CONFIG_CLKSTOP_ENABLE_ALL 0x1f ++ ++#define DONT_CARE_CARD_BITS ( \ ++ SD_CTRL_INTMASKCARD_SIGNAL_STATE_PRESENT_3 \ ++ | SD_CTRL_INTMASKCARD_WRITE_PROTECT \ ++ | SD_CTRL_INTMASKCARD_UNK6 \ ++ | SD_CTRL_INTMASKCARD_SIGNAL_STATE_PRESENT_0 \ ++ ) ++#define DONT_CARE_BUFFER_BITS ( SD_CTRL_INTMASKBUFFER_UNK7 | SD_CTRL_INTMASKBUFFER_CMD_BUSY ) ++ ++#endif // __ASIC3_MMC_H +Index: linux-2.6.24/drivers/input/keyboard/asic3_keys.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/drivers/input/keyboard/asic3_keys.c 2008-03-10 16:09:23.000000000 +0000 +@@ -0,0 +1,131 @@ ++/* ++ * Generic buttons driver for ASIC3 SoC. ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive for ++ * more details. ++ * ++ * Copyright (C) 2003 Joshua Wise ++ * Copyright (C) 2005 Pawel Kolodziejski ++ * Copyright (C) 2006 Paul Sokolovsky ++ * ++ */ ++ ++#include <linux/input.h> ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/interrupt.h> ++#include <linux/platform_device.h> ++#include <linux/irq.h> ++#include <linux/soc/asic3_base.h> ++#include <asm/mach/arch.h> ++#include <asm/mach/map.h> ++#include <asm/arch/irqs.h> ++#include <asm/hardware.h> ++#include <asm/hardware/ipaq-asic3.h> ++#include <asm/hardware/asic3_keys.h> ++ ++static irqreturn_t asic3_keys_asic_handle(int irq, void *data) ++{ ++ struct asic3_keys_platform_data *pdata = data; ++ int i, base_irq; ++ ++ base_irq = asic3_irq_base(pdata->asic3_dev); ++ for (i = 0; i < pdata->nbuttons; i++) { ++ struct asic3_keys_button *b = &pdata->buttons[i]; ++ if ((base_irq + b->gpio) == irq) { ++ int state = !!asic3_gpio_get_value(pdata->asic3_dev, b->gpio); ++ ++ if (pdata->buttons[i].type == EV_SW) ++ input_report_switch(pdata->input, pdata->buttons[i].keycode, state ^ b->active_low); ++ else ++ input_report_key(pdata->input, b->keycode, state ^ b->active_low); ++ input_sync(pdata->input); ++ } ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++static int __devinit asic3_keys_probe(struct platform_device *pdev) ++{ ++ struct asic3_keys_platform_data *pdata = pdev->dev.platform_data; ++ int i, base_irq; ++ int j, ret; ++ ++ pdata->input = input_allocate_device(); ++ ++ base_irq = asic3_irq_base(pdata->asic3_dev); ++ ++ for (i = 0; i < pdata->nbuttons; i++) { ++ struct asic3_keys_button *b = &pdata->buttons[i]; ++ set_bit(b->keycode, pdata->input->keybit); ++ ret=request_irq(base_irq + b->gpio, asic3_keys_asic_handle, SA_SAMPLE_RANDOM, b->desc, pdata); ++ if (ret) ++ { ++ printk(KERN_NOTICE "Failed to allocate asic3_keys irq=%d.\n",b->gpio); ++ ++ for(j=0; j<i ; j++) ++ free_irq(base_irq + pdata->buttons[i].gpio, NULL); ++ ++ input_unregister_device (pdata->input); ++ ++ return -ENODEV; ++ } ++ ++ set_irq_type(base_irq + b->gpio, IRQT_BOTHEDGE); ++ if (pdata->buttons[i].type == EV_SW) { ++ pdata->input->evbit[0] |= BIT(EV_SW); ++ set_bit(b->keycode, pdata->input->swbit); ++ } else { ++ pdata->input->evbit[0] |= BIT(EV_KEY); ++ set_bit(b->keycode, pdata->input->keybit); ++ } ++ } ++ ++ pdata->input->name = pdev->name; ++ input_register_device(pdata->input); ++ ++ return 0; ++} ++ ++static int __devexit asic3_keys_remove(struct platform_device *pdev) ++{ ++ struct asic3_keys_platform_data *pdata = pdev->dev.platform_data; ++ int i, base_irq; ++ ++ base_irq = asic3_irq_base(pdata->asic3_dev); ++ for (i = 0; i < pdata->nbuttons; i++) { ++ free_irq(base_irq + pdata->buttons[i].gpio, NULL); ++ } ++ ++ input_unregister_device(pdata->input); ++ ++ return 0; ++} ++ ++ ++static struct platform_driver asic3_keys_driver = { ++ .probe = asic3_keys_probe, ++ .remove = __devexit_p(asic3_keys_remove), ++ .driver = { ++ .name = "asic3-keys", ++ }, ++}; ++ ++static int __init asic3_keys_init(void) ++{ ++ return platform_driver_register(&asic3_keys_driver); ++} ++ ++static void __exit asic3_keys_exit(void) ++{ ++ platform_driver_unregister(&asic3_keys_driver); ++} ++ ++module_init(asic3_keys_init); ++module_exit(asic3_keys_exit); ++ ++MODULE_AUTHOR("Joshua Wise, Pawel Kolodziejski, Paul Sokolovsky"); ++MODULE_DESCRIPTION("Buttons driver for HTC ASIC3 SoC"); ++MODULE_LICENSE("GPL"); +Index: linux-2.6.24/include/asm-arm/arch-pxa/irqs.h +=================================================================== +--- linux-2.6.24.orig/include/asm-arm/arch-pxa/irqs.h 2008-01-24 22:58:37.000000000 +0000 ++++ linux-2.6.24/include/asm-arm/arch-pxa/irqs.h 2008-03-10 16:09:23.000000000 +0000 +@@ -182,6 +182,8 @@ + defined(CONFIG_MACH_LOGICPD_PXA270) || \ + defined(CONFIG_MACH_MAINSTONE) + #define NR_IRQS (IRQ_BOARD_END) ++#elif defined(CONFIG_MACH_HTCUNIVERSAL) ++#define NR_IRQS (IRQ_BOARD_START + 96) + #else + #define NR_IRQS (IRQ_BOARD_START) + #endif +Index: linux-2.6.24/include/asm-arm/arch-pxa/serial.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/include/asm-arm/arch-pxa/serial.h 2008-03-10 16:09:23.000000000 +0000 +@@ -0,0 +1,78 @@ ++/* ++ * linux/include/asm-arm/arch-pxa/serial.h ++ * ++ * Author: Nicolas Pitre ++ * Copyright: (C) 2001 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 <asm/arch/pxa-regs.h> ++ ++#define BAUD_BASE 921600 ++ ++/* Standard COM flags */ ++#define STD_COM_FLAGS (ASYNC_SKIP_TEST) ++ ++#define STD_SERIAL_PORT_DEFNS \ ++ { \ ++ type: PORT_PXA, \ ++ xmit_fifo_size: 64, \ ++ baud_base: BAUD_BASE, \ ++ iomem_base: &FFUART, \ ++ iomem_reg_shift: 2, \ ++ io_type: SERIAL_IO_MEM, \ ++ irq: IRQ_FFUART, \ ++ flags: STD_COM_FLAGS, \ ++ }, { \ ++ type: PORT_PXA, \ ++ xmit_fifo_size: 64, \ ++ baud_base: BAUD_BASE, \ ++ iomem_base: &STUART, \ ++ iomem_reg_shift: 2, \ ++ io_type: SERIAL_IO_MEM, \ ++ irq: IRQ_STUART, \ ++ flags: STD_COM_FLAGS, \ ++ }, { \ ++ type: PORT_PXA, \ ++ xmit_fifo_size: 64, \ ++ baud_base: BAUD_BASE, \ ++ iomem_base: &BTUART, \ ++ iomem_reg_shift: 2, \ ++ io_type: SERIAL_IO_MEM, \ ++ irq: IRQ_BTUART, \ ++ flags: STD_COM_FLAGS, \ ++ } ++ ++#define EXTRA_SERIAL_PORT_DEFNS ++ ++struct platform_pxa_serial_funcs { ++ ++ /* Initialize whatever is connected to this serial port. */ ++ void (*configure)(int state); ++#define PXA_UART_CFG_PRE_STARTUP 0 ++#define PXA_UART_CFG_POST_STARTUP 1 ++#define PXA_UART_CFG_PRE_SHUTDOWN 2 ++#define PXA_UART_CFG_POST_SHUTDOWN 3 ++ ++ /* Enable or disable the individual transmitter/receiver submodules. ++ * On transceivers without echo cancellation (e.g. SIR) ++ * transmitter always has priority; e.g. if both bits are set, ++ * only the transmitter is enabled. */ ++ void (*set_txrx)(int txrx); ++#define PXA_SERIAL_TX 1 ++#define PXA_SERIAL_RX 2 ++ ++ /* Get the current state of tx/rx. */ ++ int (*get_txrx)(void); ++ ++ int (*suspend)(struct platform_device *dev, pm_message_t state); ++ int (*resume)(struct platform_device *dev); ++}; ++ ++void pxa_set_ffuart_info(struct platform_pxa_serial_funcs *ffuart_funcs); ++void pxa_set_btuart_info(struct platform_pxa_serial_funcs *btuart_funcs); ++void pxa_set_stuart_info(struct platform_pxa_serial_funcs *stuart_funcs); ++void pxa_set_hwuart_info(struct platform_pxa_serial_funcs *hwuart_funcs); +Index: linux-2.6.24/drivers/serial/pxa.c +=================================================================== +--- linux-2.6.24.orig/drivers/serial/pxa.c 2008-01-24 22:58:37.000000000 +0000 ++++ linux-2.6.24/drivers/serial/pxa.c 2008-03-10 16:09:23.000000000 +0000 +@@ -47,6 +47,7 @@ + #include <asm/io.h> + #include <asm/hardware.h> + #include <asm/irq.h> ++#include <asm/arch/serial.h> + #include <asm/arch/pxa-regs.h> + + +@@ -60,6 +61,14 @@ + char *name; + }; + ++ ++#define IS_METHOD(dev, method) (dev && (dev)->platform_data && ((struct platform_pxa_serial_funcs *)(dev)->platform_data)->method) ++#define METHOD_CALL(dev, method) \ ++ ((struct platform_pxa_serial_funcs *)(dev)->platform_data)->method() ++#define SAFE_METHOD_CALL(dev, method, args...) \ ++ if (IS_METHOD(dev, method)) \ ++ ((struct platform_pxa_serial_funcs *)(dev)->platform_data)->method(args) ++ + static inline unsigned int serial_in(struct uart_pxa_port *up, int offset) + { + offset <<= 2; +@@ -347,6 +356,9 @@ + unsigned long flags; + int retval; + ++ /* Perform platform-specific port initialization, if needed. */ ++ SAFE_METHOD_CALL(port->dev, configure, PXA_UART_CFG_PRE_STARTUP); ++ + if (port->line == 3) /* HWUART */ + up->mcr |= UART_MCR_AFE; + else +@@ -404,6 +416,12 @@ + (void) serial_in(up, UART_IIR); + (void) serial_in(up, UART_MSR); + ++ /* ++ * Perform platform-specific port initialization if needed ++ */ ++ SAFE_METHOD_CALL(port->dev, configure, PXA_UART_CFG_POST_STARTUP); ++ SAFE_METHOD_CALL(port->dev, set_txrx, PXA_SERIAL_RX); ++ + return 0; + } + +@@ -412,6 +430,8 @@ + struct uart_pxa_port *up = (struct uart_pxa_port *)port; + unsigned long flags; + ++ SAFE_METHOD_CALL(port->dev, configure, PXA_UART_CFG_PRE_SHUTDOWN); ++ + free_irq(up->port.irq, up); + + /* +@@ -433,6 +453,8 @@ + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT); + serial_out(up, UART_FCR, 0); ++ ++ SAFE_METHOD_CALL(port->dev, configure, PXA_UART_CFG_POST_SHUTDOWN); + } + + static void +Index: linux-2.6.24/arch/arm/mach-pxa/generic.c +=================================================================== +--- linux-2.6.24.orig/arch/arm/mach-pxa/generic.c 2008-01-24 22:58:37.000000000 +0000 ++++ linux-2.6.24/arch/arm/mach-pxa/generic.c 2008-03-10 16:09:23.000000000 +0000 +@@ -38,6 +38,7 @@ + #include <asm/arch/mmc.h> + #include <asm/arch/irda.h> + #include <asm/arch/i2c.h> ++#include <asm/arch/serial.h> + + #include "devices.h" + #include "generic.h" +@@ -412,6 +413,18 @@ + .num_resources = ARRAY_SIZE(pxa_resource_hwuart), + }; + ++void __init pxa_set_ffuart_info(struct platform_pxa_serial_funcs *info) ++{ ++ pxa_device_ffuart.dev.platform_data = info; ++} ++EXPORT_SYMBOL(pxa_set_ffuart_info); ++ ++void __init pxa_set_btuart_info(struct platform_pxa_serial_funcs *info) ++{ ++ pxa_device_btuart.dev.platform_data = info; ++} ++EXPORT_SYMBOL(pxa_set_btuart_info); ++ + static struct resource pxai2c_resources[] = { + { + .start = 0x40301680, +Index: linux-2.6.24/drivers/leds/Makefile +=================================================================== +--- linux-2.6.24.orig/drivers/leds/Makefile 2008-01-24 22:58:37.000000000 +0000 ++++ linux-2.6.24/drivers/leds/Makefile 2008-03-10 16:09:23.000000000 +0000 +@@ -15,6 +15,7 @@ + obj-$(CONFIG_LEDS_NET48XX) += leds-net48xx.o + obj-$(CONFIG_LEDS_WRAP) += leds-wrap.o + obj-$(CONFIG_LEDS_H1940) += leds-h1940.o ++obj-$(CONFIG_LEDS_ASIC3) += leds-asic3.o + obj-$(CONFIG_LEDS_COBALT_QUBE) += leds-cobalt-qube.o + obj-$(CONFIG_LEDS_COBALT_RAQ) += leds-cobalt-raq.o + obj-$(CONFIG_LEDS_GPIO) += leds-gpio.o +Index: linux-2.6.24/drivers/input/keyboard/Kconfig +=================================================================== +--- linux-2.6.24.orig/drivers/input/keyboard/Kconfig 2008-01-24 22:58:37.000000000 +0000 ++++ linux-2.6.24/drivers/input/keyboard/Kconfig 2008-03-10 16:09:23.000000000 +0000 +@@ -293,4 +293,11 @@ + To compile this driver as a module, choose M here: the + module will be called bf54x-keys. + ++config KEYBOARD_ASIC3 ++ tristate "Buttons on ASIC3 SoC GPIOs (iPaqs, etc.)" ++ depends on HTC_ASIC3 ++ help ++ This enables support for the buttons attached to GPIOs of ++ HTC ASIC3 peripheral controller. ++ + endif +Index: linux-2.6.24/drivers/mmc/host/Kconfig +=================================================================== +--- linux-2.6.24.orig/drivers/mmc/host/Kconfig 2008-01-24 22:58:37.000000000 +0000 ++++ linux-2.6.24/drivers/mmc/host/Kconfig 2008-03-10 16:09:59.000000000 +0000 +@@ -24,6 +24,13 @@ + + If unsure, say N. + ++config MMC_ASIC3 ++ tristate "HTC ASIC3 SD/MMC support" ++ depends on MMC && HTC_ASIC3 ++ help ++ This provides support for the ASIC3 SD/MMC controller, used ++ in the iPAQ hx4700 and others. ++ + config MMC_SDHCI + tristate "Secure Digital Host Controller Interface support (EXPERIMENTAL)" + depends on PCI && EXPERIMENTAL +Index: linux-2.6.24/drivers/input/keyboard/Makefile +=================================================================== +--- linux-2.6.24.orig/drivers/input/keyboard/Makefile 2008-01-24 22:58:37.000000000 +0000 ++++ linux-2.6.24/drivers/input/keyboard/Makefile 2008-03-10 16:10:28.000000000 +0000 +@@ -6,6 +6,7 @@ + + obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o + obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o ++obj-$(CONFIG_KEYBOARD_ASIC3) += asic3_keys.o + obj-$(CONFIG_KEYBOARD_LKKBD) += lkkbd.o + obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o + obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/mtd-module.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/mtd-module.patch new file mode 100644 index 0000000000..4aa2f22aee --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/mtd-module.patch @@ -0,0 +1,13 @@ +Index: linux-2.6.23/drivers/mtd/maps/Kconfig +=================================================================== +--- linux-2.6.23/drivers/mtd/maps/Kconfig ++++ linux-2.6.23/drivers/mtd/maps/Kconfig +@@ -600,7 +600,7 @@ + default "4" + + config MTD_SHARP_SL +- bool "ROM mapped on Sharp SL Series" ++ tristate "ROM mapped on Sharp SL Series" + depends on ARCH_PXA + help + This enables access to the flash chip on the Sharp SL Series of PDAs. diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/poodle-bootparams.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/poodle-bootparams.patch new file mode 100644 index 0000000000..4c22fb7960 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/poodle-bootparams.patch @@ -0,0 +1,10 @@ +--- ../ref/linux-2.6.21.6/arch/arm/mach-pxa/poodle.c 2007-07-06 23:47:55.000000000 -0500 ++++ linux-2.6.21.6-armeb/arch/arm/mach-pxa/poodle.c 2007-08-06 23:02:50.000000000 -0500 +@@ -409,6 +409,7 @@ + + MACHINE_START(POODLE, "SHARP Poodle") + .phys_io = 0x40000000, ++ .boot_params = 0xa0000200, /* fake boot params loc for kexec */ + .io_pg_offst = (io_p2v(0x40000000) >> 18) & 0xfffc, + .fixup = fixup_poodle, + .map_io = pxa_map_io, diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/poodle/defconfig b/recipes/kexecboot/linux-kexecboot-2.6.24/poodle/defconfig new file mode 100644 index 0000000000..6ad99c785d --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/poodle/defconfig @@ -0,0 +1,903 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.24 +# Sun Feb 1 20:09:19 2009 +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +CONFIG_GENERIC_TIME=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_MMU=y +# CONFIG_NO_IOPORT is not set +CONFIG_GENERIC_HARDIRQS=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +# CONFIG_ARCH_HAS_ILOG2_U32 is not set +# CONFIG_ARCH_HAS_ILOG2_U64 is not set +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_ZONE_DMA=y +CONFIG_ARCH_MTD_XIP=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_LOCALVERSION="" +CONFIG_LOCALVERSION_AUTO=y +# CONFIG_SWAP is not set +# CONFIG_SYSVIPC is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_USER_NS is not set +# CONFIG_PID_NS is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_CGROUPS is not set +# CONFIG_FAIR_GROUP_SCHED is not set +CONFIG_SYSFS_DEPRECATED=y +# CONFIG_RELAY is not set +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="initramfs.cpio.gz" +CONFIG_INITRAMFS_ROOT_UID=0 +CONFIG_INITRAMFS_ROOT_GID=0 +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_EMBEDDED=y +# CONFIG_UID16 is not set +# CONFIG_SYSCTL_SYSCALL is not set +# CONFIG_KALLSYMS is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +# CONFIG_BUG is not set +# CONFIG_ELF_CORE is not set +CONFIG_BASE_FULL=y +# CONFIG_FUTEX is not set +CONFIG_ANON_INODES=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +# CONFIG_VM_EVENT_COUNTERS is not set +CONFIG_SLAB=y +# CONFIG_SLUB is not set +# CONFIG_SLOB is not set +CONFIG_SLABINFO=y +# CONFIG_TINY_SHMEM is not set +CONFIG_BASE_SMALL=0 +# CONFIG_MODULES is not set +CONFIG_BLOCK=y +# CONFIG_LBD is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_LSF is not set +# CONFIG_BLK_DEV_BSG is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_AS is not set +# CONFIG_IOSCHED_DEADLINE is not set +# CONFIG_IOSCHED_CFQ is not set +# CONFIG_DEFAULT_AS is not set +# CONFIG_DEFAULT_DEADLINE is not set +# CONFIG_DEFAULT_CFQ is not set +CONFIG_DEFAULT_NOOP=y +CONFIG_DEFAULT_IOSCHED="noop" + +# +# System Type +# +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_CLPS7500 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CO285 is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_IMX is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_PNX4008 is not set +CONFIG_ARCH_PXA=y +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP is not set + +# +# Intel PXA2xx/PXA3xx Implementations +# +# CONFIG_ARCH_LUBBOCK is not set +# CONFIG_MACH_LOGICPD_PXA270 is not set +# CONFIG_MACH_MAINSTONE is not set +# CONFIG_ARCH_PXA_IDP is not set +CONFIG_PXA_SHARPSL=y +# CONFIG_MACH_TRIZEPS4 is not set +# CONFIG_MACH_HX2750 is not set +# CONFIG_MACH_EM_X270 is not set +# CONFIG_MACH_ZYLONITE is not set +# CONFIG_MACH_ARMCORE is not set +CONFIG_PXA_SHARPSL_25x=y +# CONFIG_PXA_SHARPSL_27x is not set +# CONFIG_MACH_HTCUNIVERSAL is not set +CONFIG_MACH_POODLE=y +# CONFIG_MACH_CORGI is not set +# CONFIG_MACH_SHEPHERD is not set +# CONFIG_MACH_HUSKY is not set +# CONFIG_MACH_TOSA is not set +CONFIG_PXA25x=y +CONFIG_PXA_SSP=y +# CONFIG_PXA_KEYS is not set + +# +# Boot options +# + +# +# Power management +# + +# +# Processor Type +# +CONFIG_CPU_32=y +CONFIG_CPU_XSCALE=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5T=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_TLB_V4WBI=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_OUTER_CACHE is not set +# CONFIG_IWMMXT is not set +CONFIG_XSCALE_PMU=y +CONFIG_SHARP_LOCOMO=y +CONFIG_SHARP_PARAM=y +CONFIG_SHARPSL_PM=y +CONFIG_SHARP_SCOOP=y + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +CONFIG_PCCARD=y +# CONFIG_PCMCIA_DEBUG is not set +CONFIG_PCMCIA=y +# CONFIG_PCMCIA_LOAD_CIS is not set +# CONFIG_PCMCIA_IOCTL is not set + +# +# PC-card bridges +# +CONFIG_PCMCIA_PXA2XX=y + +# +# Kernel Features +# +# CONFIG_TICK_ONESHOT is not set +# CONFIG_NO_HZ is not set +# CONFIG_HIGH_RES_TIMERS is not set +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +# CONFIG_PREEMPT is not set +CONFIG_HZ=100 +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +# CONFIG_SPARSEMEM_STATIC is not set +# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set +CONFIG_SPLIT_PTLOCK_CPUS=4096 +# CONFIG_RESOURCES_64BIT is not set +CONFIG_ZONE_DMA_FLAG=1 +CONFIG_BOUNCE=y +CONFIG_VIRT_TO_BUS=y +CONFIG_ALIGNMENT_TRAP=y + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="console=ttyS0,115200n8 console=tty1 fbcon=rotate:1" +# CONFIG_XIP_KERNEL is not set +CONFIG_KEXEC=y +CONFIG_ATAGS_PROC=y + +# +# CPU Frequency scaling +# +# CONFIG_CPU_FREQ is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +CONFIG_PM=y +# CONFIG_PM_LEGACY is not set +# CONFIG_PM_DEBUG is not set +CONFIG_SUSPEND_UP_POSSIBLE=y +# CONFIG_SUSPEND is not set +CONFIG_APM_EMULATION=y + +# +# Networking +# +# CONFIG_NET is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +# CONFIG_SYS_HYPERVISOR is not set +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_CONCAT is not set +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS 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=y +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +CONFIG_MTD_COMPLEX_MAPPINGS=y +# CONFIG_MTD_PHYSMAP is not set +CONFIG_MTD_SHARP_SL=y +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +CONFIG_MTD_NAND=y +CONFIG_MTD_NAND_VERIFY_WRITE=y +# CONFIG_MTD_NAND_ECC_SMC is not set +# CONFIG_MTD_NAND_MUSEUM_IDS is not set +# CONFIG_MTD_NAND_H1900 is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND_DISKONCHIP is not set +CONFIG_MTD_NAND_SHARPSL=y +# CONFIG_MTD_NAND_NANDSIM is not set +# CONFIG_MTD_NAND_PLATFORM is not set +# CONFIG_MTD_ONENAND is not set + +# +# UBI - Unsorted block images +# +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +# CONFIG_BLK_DEV_LOOP is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_MISC_DEVICES is not set +CONFIG_IDE=y +CONFIG_IDE_MAX_HWIFS=4 +CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_IDE_SATA is not set +# CONFIG_BLK_DEV_IDEDISK is not set +# CONFIG_IDEDISK_MULTI_MODE is not set +CONFIG_BLK_DEV_IDECS=y +# CONFIG_BLK_DEV_IDECD is not set +# 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_PROC_FS is not set + +# +# IDE chipset support/bugfixes +# +# CONFIG_IDE_GENERIC is not set +# CONFIG_BLK_DEV_PLATFORM is not set +# CONFIG_IDE_ARM is not set +# CONFIG_BLK_DEV_IDEDMA is not set +CONFIG_IDE_ARCH_OBSOLETE_INIT=y +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI device support +# +# CONFIG_RAID_ATTRS is not set +CONFIG_SCSI=y +CONFIG_SCSI_DMA=y +# CONFIG_SCSI_TGT is not set +# CONFIG_SCSI_NETLINK is not set +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=y +CONFIG_CHR_DEV_ST=y +CONFIG_CHR_DEV_OSST=y +CONFIG_BLK_DEV_SR=y +# CONFIG_BLK_DEV_SR_VENDOR is not set +CONFIG_CHR_DEV_SG=y +# CONFIG_CHR_DEV_SCH is not set + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +CONFIG_SCSI_MULTI_LUN=y +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set +# CONFIG_SCSI_SCAN_ASYNC is not set + +# +# SCSI Transports +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SRP_ATTRS is not set +# CONFIG_SCSI_LOWLEVEL is not set +# CONFIG_SCSI_LOWLEVEL_PCMCIA is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=y +# CONFIG_INPUT_MOUSEDEV_PSAUX is not set +CONFIG_INPUT_MOUSEDEV_SCREEN_X=240 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=320 +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +CONFIG_INPUT_POWER=y + +# +# 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_LOCOMO=y +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_CORGI is not set +# CONFIG_KEYBOARD_SPITZ is not set +CONFIG_SHARPSL_RC=y +# CONFIG_KEYBOARD_GPIO is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_CORGI=y +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_UCB1400 is not set +CONFIG_INPUT_MISC=y +CONFIG_INPUT_UINPUT=y + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_VT_HW_CONSOLE_BINDING is not set +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=y +# CONFIG_SERIAL_8250_CONSOLE is not set +# CONFIG_SERIAL_8250_CS is not set +CONFIG_SERIAL_8250_NR_UARTS=4 +CONFIG_SERIAL_8250_RUNTIME_UARTS=4 +# CONFIG_SERIAL_8250_EXTENDED is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_PXA=y +CONFIG_SERIAL_PXA_CONSOLE=y +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_UNIX98_PTYS=y +# CONFIG_LEGACY_PTYS is not set +# CONFIG_IPMI_HANDLER is not set +CONFIG_HW_RANDOM=y +# CONFIG_NVRAM is not set +# CONFIG_R3964 is not set + +# +# PCMCIA character devices +# +# CONFIG_SYNCLINK_CS is not set +# CONFIG_CARDMAN_4000 is not set +# CONFIG_CARDMAN_4040 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +# CONFIG_I2C_CHARDEV is not set + +# +# I2C Algorithms +# +CONFIG_I2C_ALGOBIT=y +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set + +# +# I2C Hardware Bus support +# +# CONFIG_I2C_GPIO is not set +CONFIG_I2C_PXA=y +# CONFIG_I2C_PXA_SLAVE is not set +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_TAOS_EVM is not set + +# +# Miscellaneous I2C Chip support +# +# CONFIG_SENSORS_DS1337 is not set +# CONFIG_SENSORS_DS1374 is not set +# CONFIG_DS1682 is not set +# CONFIG_SENSORS_EEPROM is not set +# CONFIG_SENSORS_PCF8574 is not set +# CONFIG_SENSORS_PCA9539 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_MAX6875 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_DEBUG_CHIP is not set + +# +# SPI support +# +# CONFIG_SPI is not set +# CONFIG_SPI_MASTER is not set +# CONFIG_W1 is not set +# CONFIG_POWER_SUPPLY is not set +# CONFIG_HWMON is not set +# CONFIG_WATCHDOG is not set + +# +# Sonics Silicon Backplane +# +CONFIG_SSB_POSSIBLE=y +# CONFIG_SSB is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_SM501 is not set +# CONFIG_HTC_ASIC3 is not set +# CONFIG_HTC_ASIC3_DS1WM is not set + +# +# Multimedia devices +# +CONFIG_VIDEO_DEV=y +CONFIG_VIDEO_V4L1=y +CONFIG_VIDEO_V4L1_COMPAT=y +CONFIG_VIDEO_V4L2=y +CONFIG_VIDEO_CAPTURE_DRIVERS=y +# CONFIG_VIDEO_ADV_DEBUG is not set +CONFIG_VIDEO_HELPER_CHIPS_AUTO=y +# CONFIG_VIDEO_VIVI is not set +# CONFIG_VIDEO_CPIA is not set +# CONFIG_VIDEO_SAA5246A is not set +# CONFIG_VIDEO_SAA5249 is not set +# CONFIG_TUNER_3036 is not set +CONFIG_RADIO_ADAPTERS=y +CONFIG_DAB=y + +# +# Graphics support +# +# CONFIG_VGASTATE is not set +CONFIG_VIDEO_OUTPUT_CONTROL=y +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_DEFERRED_IO is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_S1D13XXX is not set +CONFIG_FB_PXA=y +CONFIG_FB_PXA_LCD_QVGA=y +# CONFIG_FB_PXA_LCD_VGA is not set +# CONFIG_FB_PXA_OVERLAY is not set +# CONFIG_FB_PXA_PARAMETERS is not set +# CONFIG_FB_MBX is not set +# CONFIG_FB_W100 is not set +# CONFIG_FB_VIRTUAL is not set +CONFIG_BACKLIGHT_LCD_SUPPORT=y +# CONFIG_LCD_CLASS_DEVICE is not set +CONFIG_BACKLIGHT_CLASS_DEVICE=y +# CONFIG_BACKLIGHT_CORGI is not set +CONFIG_BACKLIGHT_LOCOMO=y + +# +# Display device support +# +# CONFIG_DISPLAY_SUPPORT is not set + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set +CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y +CONFIG_FONTS=y +# CONFIG_FONT_8x8 is not set +# CONFIG_FONT_8x16 is not set +# CONFIG_FONT_6x11 is not set +# CONFIG_FONT_7x14 is not set +# CONFIG_FONT_PEARL_8x8 is not set +# CONFIG_FONT_ACORN_8x8 is not set +CONFIG_FONT_MINI_4x6=y +# CONFIG_FONT_SUN8x16 is not set +# CONFIG_FONT_SUN12x22 is not set +# CONFIG_FONT_10x18 is not set +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 is not set +# CONFIG_HID_SUPPORT is not set +# CONFIG_USB_SUPPORT is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +# CONFIG_MMC_UNSAFE_RESUME is not set + +# +# MMC/SD Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_BOUNCE=y +# CONFIG_SDIO_UART is not set + +# +# MMC/SD Host Controller Drivers +# +CONFIG_MMC_PXA=y +# CONFIG_NEW_LEDS is not set +CONFIG_RTC_LIB=y +# CONFIG_RTC_CLASS is not set + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XIP is not set +# CONFIG_EXT3_FS is not set +# CONFIG_EXT4DEV_FS is not set +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +CONFIG_INOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_QUOTA is not set +CONFIG_DNOTIFY=y +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +# CONFIG_MSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_WRITEBUFFER=y +# CONFIG_JFFS2_FS_WBUF_VERIFY is not set +CONFIG_JFFS2_SUMMARY=y +# CONFIG_JFFS2_FS_XATTR is not set +# CONFIG_JFFS2_SYSFS is not set +CONFIG_JFFS2_COMPRESSION_OPTIONS=y +CONFIG_JFFS2_ZLIB=y +CONFIG_JFFS2_LZO=y +CONFIG_JFFS2_RTIME=y +CONFIG_JFFS2_RUBIN=y +# CONFIG_JFFS2_CMODE_NONE is not set +CONFIG_JFFS2_CMODE_PRIORITY=y +# CONFIG_JFFS2_CMODE_SIZE is not set +# CONFIG_JFFS2_CMODE_FAVOURLZO is not set +CONFIG_CRAMFS=y +CONFIG_SQUASHFS=y +# CONFIG_SQUASHFS_EMBEDDED is not set +CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 +# 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 + +# +# 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_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_KARMA_PARTITION is not set +# CONFIG_EFI_PARTITION is not set +# CONFIG_SYSV68_PARTITION is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="cp437" +CONFIG_NLS_CODEPAGE_437=y +# CONFIG_NLS_CODEPAGE_737 is not set +# CONFIG_NLS_CODEPAGE_775 is not set +# CONFIG_NLS_CODEPAGE_850 is not set +# CONFIG_NLS_CODEPAGE_852 is not set +# CONFIG_NLS_CODEPAGE_855 is not set +# CONFIG_NLS_CODEPAGE_857 is not set +# CONFIG_NLS_CODEPAGE_860 is not set +# CONFIG_NLS_CODEPAGE_861 is not set +# CONFIG_NLS_CODEPAGE_862 is not set +# CONFIG_NLS_CODEPAGE_863 is not set +# CONFIG_NLS_CODEPAGE_864 is not set +# CONFIG_NLS_CODEPAGE_865 is not set +# CONFIG_NLS_CODEPAGE_866 is not set +# CONFIG_NLS_CODEPAGE_869 is not set +# CONFIG_NLS_CODEPAGE_936 is not set +# CONFIG_NLS_CODEPAGE_950 is not set +# CONFIG_NLS_CODEPAGE_932 is not set +# CONFIG_NLS_CODEPAGE_949 is not set +# CONFIG_NLS_CODEPAGE_874 is not set +# CONFIG_NLS_ISO8859_8 is not set +# CONFIG_NLS_CODEPAGE_1250 is not set +# CONFIG_NLS_CODEPAGE_1251 is not set +CONFIG_NLS_ASCII=y +# 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=y +# CONFIG_INSTRUMENTATION is not set + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_KERNEL is not set +CONFIG_FRAME_POINTER=y +# CONFIG_SAMPLES is not set +# CONFIG_DEBUG_USER is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITY_FILE_CAPABILITIES is not set +# CONFIG_CRYPTO is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_CRC_CCITT=y +# CONFIG_CRC16 is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +CONFIG_LIBCRC32C=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/poodle_buildfixes.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/poodle_buildfixes.patch new file mode 100644 index 0000000000..2447234a0a --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/poodle_buildfixes.patch @@ -0,0 +1,54 @@ +diff -up ./drivers/video/pxafb.c.helge ./drivers/video/pxafb.c +--- ./drivers/video/pxafb.c.helge 2008-01-27 13:26:44.000000000 +0100 ++++ ./drivers/video/pxafb.c 2008-01-27 13:27:39.000000000 +0100 +@@ -1194,7 +1194,7 @@ pxafb_freq_transition(struct notifier_bl + if ((clkinfo->old == 13000)) + break; + +- pcd = get_pcd(fbi->fb.var.pixclock); ++ pcd = get_pcd(fbi, fbi->fb.var.pixclock); + lccr3 = fbi->reg_lccr3; + set_hsync_time(fbi, pcd); + fbi->reg_lccr3 = (fbi->reg_lccr3 & ~0xff) | LCCR3_PixClkDiv(pcd); +diff -up ./drivers/video/backlight/locomolcd.c.helge ./drivers/video/backlight/locomolcd.c +--- ./drivers/video/backlight/locomolcd.c.helge 2008-01-27 15:40:33.000000000 +0100 ++++ ./drivers/video/backlight/locomolcd.c 2008-01-27 15:42:26.000000000 +0100 +@@ -80,7 +80,7 @@ static void locomolcd_off(int comadj) + locomo_gpio_write(locomolcd_dev->dev.parent, LOCOMO_GPIO_LCD_VSHD_ON, 0); + } + +-void locomolcd_power(int on) ++void locomolcd_power(int on, struct fb_var_screeninfo *var_unused) + { + int comadj = sharpsl_param.comadj; + unsigned long flags; +@@ -178,7 +178,7 @@ static int locomolcd_probe(struct locomo + * from fs_initcall, which is before locomo is activated. + * We need to recall poodle_lcd_power here*/ + if (machine_is_poodle()) +- locomolcd_power(1); ++ locomolcd_power(1, NULL); + + local_irq_restore(flags); + +diff -up ./arch/arm/mach-pxa/poodle.c.helge ./arch/arm/mach-pxa/poodle.c +--- ./arch/arm/mach-pxa/poodle.c.helge 2008-01-27 13:07:09.000000000 +0100 ++++ ./arch/arm/mach-pxa/poodle.c 2008-01-27 15:49:59.000000000 +0100 +@@ -48,7 +48,7 @@ + #include "devices.h" + #include "sharpsl.h" + +-extern void locomolcd_power(int on); ++extern void locomolcd_power(int on, struct fb_var_screeninfo *var_unused); + + static struct resource poodle_scoop_resources[] = { + [0] = { +@@ -176,7 +176,7 @@ static void poodle_null_hsync(void) + } + + static struct corgits_machinfo poodle_ts_machinfo = { +- .get_hsync_len = poodle_get_hsync_len, ++ .get_hsync_invperiod = poodle_get_hsync_len, + .put_hsync = poodle_null_hsync, + .wait_hsync = poodle_null_hsync, + }; diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/poodle_serial_vcc.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/poodle_serial_vcc.patch new file mode 100644 index 0000000000..80d1f95104 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/poodle_serial_vcc.patch @@ -0,0 +1,28 @@ +--- a/arch/arm/common/locomo.c 2007-05-06 23:42:34.000000000 +0200 ++++ a/arch/arm/common/locomo.c 2007-05-06 17:05:43.000000000 +0200 +@@ -677,7 +677,7 @@ + locomo_writel(0, lchip->base + LOCOMO_KEYBOARD + LOCOMO_KIC); + + /* GPIO */ +- locomo_writel(0, lchip->base + LOCOMO_GPO); ++ locomo_writel(POODLE_LOCOMO_GPIO_232VCC_ON, lchip->base + LOCOMO_GPO); + locomo_writel( (LOCOMO_GPIO(2) | LOCOMO_GPIO(3) | LOCOMO_GPIO(13) | LOCOMO_GPIO(14)) + , lchip->base + LOCOMO_GPE); + locomo_writel( (LOCOMO_GPIO(2) | LOCOMO_GPIO(3) | LOCOMO_GPIO(13) | LOCOMO_GPIO(14)) + + +--- a/drivers/serial/pxa.c 2006-06-18 03:49:35.000000000 +0200 ++++ b/drivers/serial/pxa.c 2007-05-06 17:04:48.000000000 +0200 +@@ -290,9 +290,9 @@ + unsigned char mcr = 0; + + if (mctrl & TIOCM_RTS) +- mcr |= UART_MCR_RTS; ++ mcr &= ~UART_MCR_RTS; + if (mctrl & TIOCM_DTR) +- mcr |= UART_MCR_DTR; ++ mcr &= ~UART_MCR_DTR; + if (mctrl & TIOCM_OUT1) + mcr |= UART_MCR_OUT1; + if (mctrl & TIOCM_OUT2) + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/poodle_ts.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/poodle_ts.patch new file mode 100644 index 0000000000..b10ee2eab8 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/poodle_ts.patch @@ -0,0 +1,22 @@ +Index: linux-2.6.23/arch/arm/mach-pxa/poodle.c +=================================================================== +--- linux-2.6.23.orig/arch/arm/mach-pxa/poodle.c 2008-01-25 12:10:10.000000000 -0800 ++++ linux-2.6.23/arch/arm/mach-pxa/poodle.c 2008-01-25 12:11:58.000000000 -0800 +@@ -166,7 +166,7 @@ + }, + }; + +-static unsigned long poodle_get_hsync_len(void) ++static unsigned long poodle_get_hsync_invperiod(void) + { + return 0; + } +@@ -176,7 +176,7 @@ + } + + static struct corgits_machinfo poodle_ts_machinfo = { +- .get_hsync_len = poodle_get_hsync_len, ++ .get_hsync_invperiod = poodle_get_hsync_invperiod, + .put_hsync = poodle_null_hsync, + .wait_hsync = poodle_null_hsync, + }; diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/pxa-resume.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/pxa-resume.patch new file mode 100644 index 0000000000..71d466e31c --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/pxa-resume.patch @@ -0,0 +1,58 @@ +diff --git a/arch/arm/mach-pxa/pm.c b/arch/arm/mach-pxa/pm.c + +index a941c71..039194c 100644 +--- a/arch/arm/mach-pxa/pm.c ++++ b/arch/arm/mach-pxa/pm.c +@@ -38,34 +38,37 @@ int pxa_pm_enter(suspend_state_t state) + iwmmxt_task_disable(NULL); + #endif + +- pxa_cpu_pm_fns->save(sleep_save); ++ /* skip registers saving for standby */ ++ if (state != PM_SUSPEND_STANDBY) { ++ pxa_cpu_pm_fns->save(sleep_save); ++ /* before sleeping, calculate and save a checksum */ ++ for (i = 0; i < pxa_cpu_pm_fns->save_size - 1; i++) ++ sleep_save_checksum += sleep_save[i]; ++ } + + /* Clear sleep reset status */ + RCSR = RCSR_SMR; + +- /* before sleeping, calculate and save a checksum */ +- for (i = 0; i < pxa_cpu_pm_fns->save_size - 1; i++) +- sleep_save_checksum += sleep_save[i]; +- + /* *** go zzz *** */ + pxa_cpu_pm_fns->enter(state); + cpu_init(); + +- /* after sleeping, validate the checksum */ +- for (i = 0; i < pxa_cpu_pm_fns->save_size - 1; i++) +- checksum += sleep_save[i]; ++ if (state != PM_SUSPEND_STANDBY) { ++ /* after sleeping, validate the checksum */ ++ for (i = 0; i < pxa_cpu_pm_fns->save_size - 1; i++) ++ checksum += sleep_save[i]; + +- /* if invalid, display message and wait for a hardware reset */ +- if (checksum != sleep_save_checksum) { ++ /* if invalid, display message and wait for a hardware reset */ ++ if (checksum != sleep_save_checksum) { + #ifdef CONFIG_ARCH_LUBBOCK +- LUB_HEXLED = 0xbadbadc5; ++ LUB_HEXLED = 0xbadbadc5; + #endif +- while (1) +- pxa_cpu_pm_fns->enter(state); ++ while (1) ++ pxa_cpu_pm_fns->enter(state); ++ } ++ pxa_cpu_pm_fns->restore(sleep_save); + } + +- pxa_cpu_pm_fns->restore(sleep_save); +- + pr_debug("*** made it back from resume\n"); + + return 0; diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/pxa-serial-hack.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/pxa-serial-hack.patch new file mode 100644 index 0000000000..bf20f46a05 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/pxa-serial-hack.patch @@ -0,0 +1,90 @@ +--- + drivers/serial/8250.c | 5 +++++ + drivers/serial/serial_core.c | 1 + + drivers/serial/serial_cs.c | 12 +++++++++--- + include/linux/serial_core.h | 1 + + 4 files changed, 16 insertions(+), 3 deletions(-) + +Index: linux-2.6.20/drivers/serial/8250.c +=================================================================== +--- linux-2.6.20.orig/drivers/serial/8250.c 2007-04-27 13:37:26.000000000 +0100 ++++ linux-2.6.20/drivers/serial/8250.c 2007-04-27 13:38:16.000000000 +0100 +@@ -2429,7 +2429,12 @@ + .driver_name = "serial", + .dev_name = "ttyS", + .major = TTY_MAJOR, ++#ifdef CONFIG_SERIAL_PXA ++ .minor = 64 + 4, ++ .name_base = 4, ++#else + .minor = 64, ++#endif + .nr = UART_NR, + .cons = SERIAL8250_CONSOLE, + }; +Index: linux-2.6.20/drivers/serial/serial_core.c +=================================================================== +--- linux-2.6.20.orig/drivers/serial/serial_core.c 2007-02-04 18:44:54.000000000 +0000 ++++ linux-2.6.20/drivers/serial/serial_core.c 2007-04-27 13:39:39.000000000 +0100 +@@ -2068,7 +2068,8 @@ + printk(KERN_INFO "%s%s%s%d at %s (irq = %d) is a %s\n", + port->dev ? port->dev->bus_id : "", + port->dev ? ": " : "", +- drv->dev_name, port->line, address, port->irq, uart_type(port)); ++ drv->dev_name, port->line + drv->name_base, address, port->irq, ++ uart_type(port)); + } + + static void +@@ -2183,6 +2184,7 @@ + normal->owner = drv->owner; + normal->driver_name = drv->driver_name; + normal->name = drv->dev_name; ++ normal->name_base = drv->name_base; + normal->major = drv->major; + normal->minor_start = drv->minor; + normal->type = TTY_DRIVER_TYPE_SERIAL; +Index: linux-2.6.20/include/linux/serial_core.h +=================================================================== +--- linux-2.6.20.orig/include/linux/serial_core.h 2007-02-04 18:44:54.000000000 +0000 ++++ linux-2.6.20/include/linux/serial_core.h 2007-04-27 13:37:27.000000000 +0100 +@@ -341,6 +341,7 @@ + struct module *owner; + const char *driver_name; + const char *dev_name; ++ int name_base; + int major; + int minor; + int nr; +Index: linux-2.6.20/drivers/serial/serial_cs.c +=================================================================== +--- linux-2.6.20.orig/drivers/serial/serial_cs.c 2007-02-04 18:44:54.000000000 +0000 ++++ linux-2.6.20/drivers/serial/serial_cs.c 2007-04-27 13:40:34.000000000 +0100 +@@ -390,7 +390,7 @@ + kio_addr_t iobase, int irq) + { + struct uart_port port; +- int line; ++ int line, linestart; + + memset(&port, 0, sizeof (struct uart_port)); + port.iobase = iobase; +@@ -411,10 +411,16 @@ + return -EINVAL; + } + ++#if CONFIG_SERIAL_PXA ++ linestart = 4; ++#else ++ linestart = 0; ++#endif ++ + info->line[info->ndev] = line; +- sprintf(info->node[info->ndev].dev_name, "ttyS%d", line); ++ sprintf(info->node[info->ndev].dev_name, "ttyS%d", line+linestart); + info->node[info->ndev].major = TTY_MAJOR; +- info->node[info->ndev].minor = 0x40 + line; ++ info->node[info->ndev].minor = 0x40 + line + linestart; + if (info->ndev > 0) + info->node[info->ndev - 1].next = &info->node[info->ndev]; + info->ndev++; diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/pxa27x-resume.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/pxa27x-resume.patch new file mode 100644 index 0000000000..6447a0b104 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/pxa27x-resume.patch @@ -0,0 +1,41 @@ +List: linux-arm-kernel +Subject: [PATCH] Fix PXA27x resume +From: Russell King - ARM Linux <linux@arm.linux.org.uk> +Date: 2008-01-21 13:53:31 +Message-ID: 20080121135331.GC30149@flint.arm.linux.org.uk +[Download message RAW] + +When PXA27x wakes up, tick_resume_oneshot() tries to set a timer +interrupt to occur immediately. Since PXA27x requires at least +MIN_OSCR_DELTA, this causes us to flag an error. + +tick_program_event() then increments the next event time by +min_delta_ns. However, by the time we get back to programming +the next event, the OSCR has incremented such that we fail again. +We repeatedly retry, but the OSCR is too fast for us - we never +catch up, so we never break out of the loop - resulting in us +never apparantly resuming. + +Fix this by doubling min_delta_ns. + +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> + +diff --git a/arch/arm/mach-pxa/time.c b/arch/arm/mach-pxa/time.c +index ac0bbad..7b7c017 100644 +--- a/arch/arm/mach-pxa/time.c ++++ b/arch/arm/mach-pxa/time.c +@@ -169,7 +169,7 @@ static void __init pxa_timer_init(void) + ckevt_pxa_osmr0.max_delta_ns = + clockevent_delta2ns(0x7fffffff, &ckevt_pxa_osmr0); + ckevt_pxa_osmr0.min_delta_ns = +- clockevent_delta2ns(MIN_OSCR_DELTA, &ckevt_pxa_osmr0) + 1; ++ clockevent_delta2ns(MIN_OSCR_DELTA * 2, &ckevt_pxa_osmr0) + 1; + + cksrc_pxa_oscr0.mult = + clocksource_hz2mult(clock_tick_rate, cksrc_pxa_oscr0.shift); + +------------------------------------------------------------------- +List admin: http://lists.arm.linux.org.uk/mailman/listinfo/linux-arm-kernel +FAQ: http://www.arm.linux.org.uk/mailinglists/faq.php +Etiquette: http://www.arm.linux.org.uk/mailinglists/etiquette.php + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/pxa2xx_udc-clock.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/pxa2xx_udc-clock.patch new file mode 100644 index 0000000000..14c496a31a --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/pxa2xx_udc-clock.patch @@ -0,0 +1,221 @@ +--- + drivers/usb/gadget/pxa2xx_udc.c | 88 ++++++++++++++++++++++------------------ + drivers/usb/gadget/pxa2xx_udc.h | 4 + + 2 files changed, 53 insertions(+), 39 deletions(-) + +--- g26.orig/drivers/usb/gadget/pxa2xx_udc.c 2008-02-19 12:47:06.000000000 -0800 ++++ g26/drivers/usb/gadget/pxa2xx_udc.c 2008-02-19 14:07:17.000000000 -0800 +@@ -103,6 +103,12 @@ static const char ep0name [] = "ep0"; + #error "Can't configure both IXP and PXA" + #endif + ++/* IXP doesn't yet support <linux/clk.h> */ ++#define clk_get(dev,name) NULL ++#define clk_enable(clk) do { } while (0) ++#define clk_disable(clk) do { } while (0) ++#define clk_put(clk) do { } while (0) ++ + #endif + + #include "pxa2xx_udc.h" +@@ -934,20 +940,31 @@ static void udc_disable(struct pxa2xx_ud + /* We disable the UDC -- and its 48 MHz clock -- whenever it's not + * in active use. + */ +-static int pullup(struct pxa2xx_udc *udc, int is_active) ++static int pullup(struct pxa2xx_udc *udc) + { +- is_active = is_active && udc->vbus && udc->pullup; ++ int is_active = udc->vbus && udc->pullup && !udc->suspended; + DMSG("%s\n", is_active ? "active" : "inactive"); +- if (is_active) +- udc_enable(udc); +- else { +- if (udc->gadget.speed != USB_SPEED_UNKNOWN) { +- DMSG("disconnect %s\n", udc->driver +- ? udc->driver->driver.name +- : "(no driver)"); +- stop_activity(udc, udc->driver); ++ if (is_active) { ++ if (!udc->active) { ++ udc->active = 1; ++ /* Enable clock for USB device */ ++ clk_enable(udc->clk); ++ udc_enable(udc); + } +- udc_disable(udc); ++ } else { ++ if (udc->active) { ++ if (udc->gadget.speed != USB_SPEED_UNKNOWN) { ++ DMSG("disconnect %s\n", udc->driver ++ ? udc->driver->driver.name ++ : "(no driver)"); ++ stop_activity(udc, udc->driver); ++ } ++ udc_disable(udc); ++ /* Disable clock for USB device */ ++ clk_disable(udc->clk); ++ udc->active = 0; ++ } ++ + } + return 0; + } +@@ -958,9 +975,9 @@ static int pxa2xx_udc_vbus_session(struc + struct pxa2xx_udc *udc; + + udc = container_of(_gadget, struct pxa2xx_udc, gadget); +- udc->vbus = is_active = (is_active != 0); ++ udc->vbus = (is_active != 0); + DMSG("vbus %s\n", is_active ? "supplied" : "inactive"); +- pullup(udc, is_active); ++ pullup(udc); + return 0; + } + +@@ -975,9 +992,8 @@ static int pxa2xx_udc_pullup(struct usb_ + if (!udc->mach->gpio_pullup && !udc->mach->udc_command) + return -EOPNOTSUPP; + +- is_active = (is_active != 0); +- udc->pullup = is_active; +- pullup(udc, is_active); ++ udc->pullup = (is_active != 0); ++ pullup(udc); + return 0; + } + +@@ -1146,11 +1162,6 @@ static void udc_disable(struct pxa2xx_ud + + udc_clear_mask_UDCCR(UDCCR_UDE); + +-#ifdef CONFIG_ARCH_PXA +- /* Disable clock for USB device */ +- clk_disable(dev->clk); +-#endif +- + ep0_idle (dev); + dev->gadget.speed = USB_SPEED_UNKNOWN; + } +@@ -1191,11 +1202,6 @@ static void udc_enable (struct pxa2xx_ud + { + udc_clear_mask_UDCCR(UDCCR_UDE); + +-#ifdef CONFIG_ARCH_PXA +- /* Enable clock for USB device */ +- clk_enable(dev->clk); +-#endif +- + /* try to clear these bits before we enable the udc */ + udc_ack_int_UDCCR(UDCCR_SUSIR|/*UDCCR_RSTIR|*/UDCCR_RESIR); + +@@ -1286,7 +1292,7 @@ fail: + * for set_configuration as well as eventual disconnect. + */ + DMSG("registered gadget driver '%s'\n", driver->driver.name); +- pullup(dev, 1); ++ pullup(dev); + dump_state(dev); + return 0; + } +@@ -1329,7 +1335,8 @@ int usb_gadget_unregister_driver(struct + return -EINVAL; + + local_irq_disable(); +- pullup(dev, 0); ++ dev->pullup = 0; ++ pullup(dev); + stop_activity(dev, driver); + local_irq_enable(); + +@@ -2131,13 +2138,11 @@ static int __init pxa2xx_udc_probe(struc + if (irq < 0) + return -ENODEV; + +-#ifdef CONFIG_ARCH_PXA + dev->clk = clk_get(&pdev->dev, "UDCCLK"); + if (IS_ERR(dev->clk)) { + retval = PTR_ERR(dev->clk); + goto err_clk; + } +-#endif + + pr_debug("%s: IRQ %d%s%s\n", driver_name, irq, + dev->has_cfr ? "" : " (!cfr)", +@@ -2250,10 +2255,8 @@ lubbock_fail0: + if (dev->mach->gpio_vbus) + gpio_free(dev->mach->gpio_vbus); + err_gpio_vbus: +-#ifdef CONFIG_ARCH_PXA + clk_put(dev->clk); + err_clk: +-#endif + return retval; + } + +@@ -2269,7 +2272,9 @@ static int __exit pxa2xx_udc_remove(stru + if (dev->driver) + return -EBUSY; + +- udc_disable(dev); ++ dev->pullup = 0; ++ pullup(dev); ++ + remove_proc_files(); + + if (dev->got_irq) { +@@ -2289,9 +2294,7 @@ static int __exit pxa2xx_udc_remove(stru + if (dev->mach->gpio_pullup) + gpio_free(dev->mach->gpio_pullup); + +-#ifdef CONFIG_ARCH_PXA + clk_put(dev->clk); +-#endif + + platform_set_drvdata(pdev, NULL); + the_controller = NULL; +@@ -2317,10 +2320,15 @@ static int __exit pxa2xx_udc_remove(stru + static int pxa2xx_udc_suspend(struct platform_device *dev, pm_message_t state) + { + struct pxa2xx_udc *udc = platform_get_drvdata(dev); ++ unsigned long flags; + + if (!udc->mach->gpio_pullup && !udc->mach->udc_command) + WARN("USB host won't detect disconnect!\n"); +- pullup(udc, 0); ++ udc->suspended = 1; ++ ++ local_irq_save(flags); ++ pullup(udc); ++ local_irq_restore(flags); + + return 0; + } +@@ -2328,8 +2336,12 @@ static int pxa2xx_udc_suspend(struct pla + static int pxa2xx_udc_resume(struct platform_device *dev) + { + struct pxa2xx_udc *udc = platform_get_drvdata(dev); ++ unsigned long flags; + +- pullup(udc, 1); ++ udc->suspended = 0; ++ local_irq_save(flags); ++ pullup(udc); ++ local_irq_restore(flags); + + return 0; + } +--- g26.orig/drivers/usb/gadget/pxa2xx_udc.h 2008-02-19 12:47:06.000000000 -0800 ++++ g26/drivers/usb/gadget/pxa2xx_udc.h 2008-02-19 12:57:42.000000000 -0800 +@@ -119,7 +119,9 @@ struct pxa2xx_udc { + has_cfr : 1, + req_pending : 1, + req_std : 1, +- req_config : 1; ++ req_config : 1, ++ suspended : 1, ++ active : 1; + + #define start_watchdog(dev) mod_timer(&dev->timer, jiffies + (HZ/200)) + struct timer_list timer; +- diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/pxa_fb_overlay.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/pxa_fb_overlay.patch new file mode 100644 index 0000000000..49c59b3275 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/pxa_fb_overlay.patch @@ -0,0 +1,26 @@ +--- + drivers/video/pxafb.h | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +Index: linux-2.6.22/drivers/video/pxafb.h +=================================================================== +--- linux-2.6.22.orig/drivers/video/pxafb.h 2007-09-25 15:44:42.000000000 +0200 ++++ linux-2.6.22/drivers/video/pxafb.h 2007-09-25 15:45:07.000000000 +0200 +@@ -36,7 +36,7 @@ + struct fb_bitfield transp; + }; + +-#ifdef CONFIG_PXA27x ++#if defined(CONFIG_PXA27x) || defined(CONFIG_PXA3xx) + /* PXA Overlay Framebuffer Support */ + struct overlayfb_info + { +@@ -142,7 +142,7 @@ + wait_queue_head_t ctrlr_wait; + struct work_struct task; + +-#ifdef CONFIG_PXA27x ++#if defined(CONFIG_PXA27x) || defined(CONFIG_PXA3xx) + /* PXA Overlay Framebuffer Support */ + struct overlayfb_info *overlay1fb; + struct overlayfb_info *overlay2fb; diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/pxafb.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/pxafb.patch new file mode 100644 index 0000000000..7fe693cd91 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/pxafb.patch @@ -0,0 +1,13 @@ +Index: linux-2.6.23/drivers/video/pxafb.c +=================================================================== +--- linux-2.6.23.orig/drivers/video/pxafb.c 2008-01-25 16:25:21.000000000 -0800 ++++ linux-2.6.23/drivers/video/pxafb.c 2008-01-25 16:32:14.000000000 -0800 +@@ -1194,7 +1194,7 @@ + if ((clkinfo->old == 13000)) + break; + +- pcd = get_pcd(fbi->fb.var.pixclock); ++ pcd = get_pcd(fbi,fbi->fb.var.pixclock); + lccr3 = fbi->reg_lccr3; + set_hsync_time(fbi, pcd); + fbi->reg_lccr3 = (fbi->reg_lccr3 & ~0xff) | LCCR3_PixClkDiv(pcd); diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/serial-add-support-for-non-standard-xtals-to-16c950-driver.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/serial-add-support-for-non-standard-xtals-to-16c950-driver.patch new file mode 100644 index 0000000000..b513ba1466 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/serial-add-support-for-non-standard-xtals-to-16c950-driver.patch @@ -0,0 +1,155 @@ + +From: Petr Vandrovec <vandrove@vc.cvut.cz> + +Patch below adds support for using different prescaler than 16 for 16c950 +chips. This is needed for using Fujitsu-Siemens Connect2Air compact-flash +card, which comes (apparently) with 806kHz clocks, and so you have to +program prescaler for division by 7, and DLAB to 1, to get 115200Bd. + +To get card properly running you also have to add lines below to +/etc/pcmcia/serial.opts so kernel knows that base speed is not 115200 but +50400 (50400 * 16 = 806400; 806400 / 7 = 115200). As I've found no code +specifying baud_rate in serial_cs, I assume that specifying it in +serial.opts is right way to do this type of things. + +Patch also fixes problem that for UPF_MAGIC_MULTIPLIER maximum possible +baud rate passed to uart code was uartclk / 16 while correct value for +these devices (and for 16c950) is uartclk / 4. + +Patch also fixes problem that for UPF_MAGIC_MULTIPLIER devices with +baud_rate 19200 or 9600 spd_cust did not work correctly. Not that such +devices exist, but we should not ignore spd_cust, user probably knows why +he asked for spd_cust. + +serial.opts: + +case "$MANFID-$FUNCID-$PRODID_1-$PRODID_2-$PRODID_3-$PRODID_4" in +'0279,950b-2-GPRS Modem---') + SERIAL_OPTS="baud_base 50400" + ;; +esac + +Cc: David Woodhouse <dwmw2@infradead.org> +Signed-off-by: Andrew Morton <akpm@osdl.org> +--- + + drivers/serial/8250.c | 82 +++++++++++++++++++++++++++++++++++++++----------- + 1 file changed, 64 insertions(+), 18 deletions(-) + +Index: linux-2.6.21/drivers/serial/8250.c +=================================================================== +--- linux-2.6.21.orig/drivers/serial/8250.c 2007-07-01 16:59:52.000000000 +0100 ++++ linux-2.6.21/drivers/serial/8250.c 2007-07-01 17:01:21.000000000 +0100 +@@ -1964,24 +1964,58 @@ static void serial8250_shutdown(struct u + serial_unlink_irq_chain(up); + } + +-static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int baud) ++static unsigned int serial8250_get_divisor(struct uart_port *port, unsigned int baud, ++ unsigned int *prescaler) + { +- unsigned int quot; +- +- /* +- * Handle magic divisors for baud rates above baud_base on +- * SMSC SuperIO chips. ++ /* ++ * Use special handling only if user did not supply its own divider. ++ * spd_cust is defined in terms of baud_base, so always use default ++ * prescaler when spd_cust is requested. + */ +- if ((port->flags & UPF_MAGIC_MULTIPLIER) && +- baud == (port->uartclk/4)) +- quot = 0x8001; +- else if ((port->flags & UPF_MAGIC_MULTIPLIER) && +- baud == (port->uartclk/8)) +- quot = 0x8002; +- else +- quot = uart_get_divisor(port, baud); + +- return quot; ++ *prescaler = 16; ++ if (baud != 38400 || (port->flags & UPF_SPD_MASK) != UPF_SPD_CUST) { ++ unsigned int quot = port->uartclk / baud; ++ ++ /* ++ * Handle magic divisors for baud rates above baud_base on ++ * SMSC SuperIO chips. ++ */ ++ if (port->flags & UPF_MAGIC_MULTIPLIER) { ++ if (quot == 4) { ++ return 0x8001; ++ } else if (quot == 8) { ++ return 0x8002; ++ } ++ } ++ if (port->type == PORT_16C950) { ++ /* ++ * This computes TCR value (4 to 16), not CPR value (which can ++ * be between 1.000 and 31.875) - chip I have uses XTAL of ++ * 806400Hz, and so a division by 7 is required to get 115200Bd. ++ * I'm leaving CPR disabled for now, until someone will ++ * hit even more exotic XTAL (it is needed to get 500kbps ++ * or 1000kbps from 18.432MHz XTAL, but I have no device ++ * which would benefit from doing that). ++ * ++ * If we can use divide by 16, use it. Otherwise look for ++ * better prescaler, from 15 to 4. If quotient cannot ++ * be divided by any integer value between 4 and 15, use 4. ++ */ ++ if (quot & 0x0F) { ++ unsigned int div; ++ ++ for (div = 15; div > 4; div--) { ++ if (quot % div == 0) { ++ break; ++ } ++ } ++ *prescaler = div; ++ return quot / div; ++ } ++ } ++ } ++ return uart_get_divisor(port, baud); + } + + static void +@@ -1991,7 +2025,7 @@ serial8250_set_termios(struct uart_port + struct uart_8250_port *up = (struct uart_8250_port *)port; + unsigned char cval, fcr = 0; + unsigned long flags; +- unsigned int baud, quot; ++ unsigned int baud, quot, prescaler; + + switch (termios->c_cflag & CSIZE) { + case CS5: +@@ -2023,8 +2057,13 @@ serial8250_set_termios(struct uart_port + /* + * Ask the core to calculate the divisor for us. + */ +- baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); +- quot = serial8250_get_divisor(port, baud); ++ if (port->type == PORT_16C950 || (port->flags & UPF_MAGIC_MULTIPLIER)) { ++ baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/4); ++ } else { ++ baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16); ++ } ++ quot = serial8250_get_divisor(port, baud, &prescaler); ++ + + /* + * Oxford Semi 952 rev B workaround +@@ -2139,6 +2178,13 @@ serial8250_set_termios(struct uart_port + serial_dl_write(up, quot); + + /* ++ * Program prescaler for 16C950 chips. ++ */ ++ if (up->port.type == PORT_16C950) { ++ serial_icr_write(up, UART_TCR, prescaler == 16 ? 0 : prescaler); ++ } ++ ++ /* + * LCR DLAB must be set to enable 64-byte FIFO mode. If the FCR + * is written without DLAB set, this mode will be disabled. + */ diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/sharpsl-rc-r1.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/sharpsl-rc-r1.patch new file mode 100644 index 0000000000..bed41c55d5 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/sharpsl-rc-r1.patch @@ -0,0 +1,527 @@ +This patch adds support for Sharp CE-RH2 on Spitz. + +It is not clean enough to be upstreamed: +- It is a bit syslog-noisy. +- Does not support other Zaurus models. +- Maybe split to more parts: + * MAX1111 driver + * linear input device + * virtual keyboard on top of linear input device + +Index: linux-2.6.24/arch/arm/mach-pxa/spitz.c +=================================================================== +--- linux-2.6.24.orig/arch/arm/mach-pxa/spitz.c 2008-03-10 17:05:37.000000000 +0000 ++++ linux-2.6.24/arch/arm/mach-pxa/spitz.c 2008-03-10 17:05:55.000000000 +0000 +@@ -259,6 +259,13 @@ + .id = -1, + }; + ++/* ++ * Spitz Remote Control Device ++ */ ++static struct platform_device sharpsl_rc_device = { ++ .name = "sharpsl-remote-control", ++ .id = -1, ++}; + + /* + * Spitz LEDs +@@ -548,6 +555,7 @@ + &spitzscoop_device, + &spitzssp_device, + &spitzkbd_device, ++ &sharpsl_rc_device, + &spitzts_device, + &spitzbl_device, + &spitzled_device, +Index: linux-2.6.24/drivers/input/keyboard/Kconfig +=================================================================== +--- linux-2.6.24.orig/drivers/input/keyboard/Kconfig 2008-03-10 17:05:40.000000000 +0000 ++++ linux-2.6.24/drivers/input/keyboard/Kconfig 2008-03-10 17:05:55.000000000 +0000 +@@ -154,6 +154,17 @@ + To compile this driver as a module, choose M here: the + module will be called spitzkbd. + ++config SHARPSL_RC ++ tristate "Sharp SL-Cxx00 Remote Control" ++ depends on PXA_SHARPSL ++ default y ++ help ++ Say Y here to enable the remote on the Sharp Zaurus SL-Cxx00, ++ SL-C1000, SL-C3000 and Sl-C3100 series of PDAs. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called sharpsl_rc. ++ + config KEYBOARD_AMIGA + tristate "Amiga keyboard" + depends on AMIGA +Index: linux-2.6.24/drivers/input/keyboard/Makefile +=================================================================== +--- linux-2.6.24.orig/drivers/input/keyboard/Makefile 2008-03-10 17:05:40.000000000 +0000 ++++ linux-2.6.24/drivers/input/keyboard/Makefile 2008-03-10 17:06:17.000000000 +0000 +@@ -26,3 +26,5 @@ + obj-$(CONFIG_KEYBOARD_HP7XX) += jornada720_kbd.o + obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o + obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o ++obj-$(CONFIG_SHARPSL_RC) += sharpsl_rc.o ++ +Index: linux-2.6.24/drivers/input/keyboard/sharpsl_rc.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.24/drivers/input/keyboard/sharpsl_rc.c 2008-03-10 17:05:55.000000000 +0000 +@@ -0,0 +1,291 @@ ++/* ++ * Keyboard driver for Sharp Clamshell Models (SL-Cxx00) ++ * ++ * Copyright (c) 2004-2005 Richard Purdie ++ * ++ * Based on corgikbd.c and Sharp's RC driver ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#define DEBUG 1 ++#include <linux/delay.h> ++#include <linux/platform_device.h> ++#include <linux/init.h> ++#include <linux/input.h> ++#include <linux/interrupt.h> ++#include <linux/jiffies.h> ++#include <linux/module.h> ++#include <linux/slab.h> ++ ++#ifdef CONFIG_MACH_SPITZ ++#include <asm/arch/spitz.h> ++#endif ++#ifdef CONFIG_MACH_CORGI ++#include <asm/arch/corgi.h> ++#endif ++ ++#include <asm/arch/hardware.h> ++#include <asm/arch/pxa-regs.h> ++#include <asm/hardware/scoop.h> ++#include <asm/arch/sharpsl.h> ++#include <asm/hardware/sharpsl_pm.h> ++ ++#define DPRINTK(fmt, args...) dev_dbg(data->dev, fmt "\n", ##args) ++ ++struct remote_control_key { ++ unsigned char min; ++ unsigned char max; ++ unsigned char key; ++}; ++ ++#ifdef CONFIG_MACH_SPITZ ++#define REMOTE_AKIN_PULLUP SPITZ_SCP2_AKIN_PULLUP ++#define REMOTE_SCOOP_DEVICE spitzscoop2_device ++#define REMOTE_GPIO_INT SPITZ_GPIO_AK_INT ++#define REMOTE_IRQ_INT SPITZ_IRQ_GPIO_AK_INT ++static struct remote_control_key remote_keys[] = { ++ { 25, 35, KEY_STOPCD}, ++ { 55, 65, KEY_PLAYPAUSE}, ++ { 85, 95, KEY_NEXTSONG}, ++ { 115, 125, KEY_VOLUMEUP}, ++ { 145, 155, KEY_PREVIOUSSONG}, ++ { 180, 190, KEY_MUTE}, ++ { 215, 225, KEY_VOLUMEDOWN}, ++}; ++#endif ++#ifdef CONFIG_MACH_CORGI ++#define REMOTE_AKIN_PULLUP CORGI_SCP_AKIN_PULLUP ++#define REMOTE_SCOOP_DEVICE corgiscoop_device ++#define REMOTE_GPIO_INT CORGI_GPIO_AK_INT ++#define REMOTE_IRQ_INT CORGI_IRQ_GPIO_AK_INT ++static struct remote_control_key remote_keys[] = { ++ //These need to be fixed for the CE-RH1's values ++ { 25, 35, KEY_STOPCD}, ++ { 55, 65, KEY_PLAYPAUSE}, ++ { 85, 95, KEY_NEXTSONG}, ++ { 115, 125, KEY_VOLUMEUP}, ++ { 145, 155, KEY_PREVIOUSSONG}, ++ { 180, 190, KEY_MUTE}, ++ { 215, 225, KEY_VOLUMEDOWN}, ++}; ++#endif ++ ++#define RELEASE_HI 230 ++#define MAX_EARPHONE 6 ++#define RC_POLL_MS 10 ++#define RC_FINISH_MS 500 ++#define WAIT_STATE 3 ++#define NOISE_THRESHOLD 100 ++ ++struct sharpsl_rc { ++ struct input_dev *input; ++ struct device *dev; ++ ++ spinlock_t lock; ++ struct timer_list rctimer; ++ struct timer_list rctimer_finish; ++ ++ unsigned int handling_press; ++ unsigned int noise; ++ unsigned int state; ++ unsigned int last_key; ++}; ++ ++static int get_remocon_raw(void) ++{ ++ int i, val; ++ ++ val = sharpsl_pm_pxa_read_max1111(MAX1111_REMCOM); ++ for (i = 0; i < ARRAY_SIZE(remote_keys); ++i) { ++ if (val >= remote_keys[i].min ++ && val <= remote_keys[i].max) { ++ printk("get_remocon_raw: VAL=%i, KEY=%i\n", val, remote_keys[i].key); ++ return remote_keys[i].key; ++ } ++ } ++ return 0; ++} ++ ++static irqreturn_t sharpsl_rc_interrupt(int irq, void *dev_id, struct pt_regs *regs) ++{ ++ struct sharpsl_rc *data = dev_id; ++ DPRINTK("sharpsl_rc_interrupt %d\n", irq); ++ if (!data->handling_press) { ++ DPRINTK("handling interrupt"); ++ data->handling_press = 1; ++ data->noise = 0; ++ data->state = 0; ++ data->last_key = 0; ++ ++ reset_scoop_gpio(&REMOTE_SCOOP_DEVICE.dev, REMOTE_AKIN_PULLUP); ++ ++ mod_timer(&data->rctimer, jiffies + msecs_to_jiffies(RC_POLL_MS)); ++ } ++ return IRQ_HANDLED; ++} ++ ++static void sharpsl_rc_timer_callback(unsigned long dataPtr) ++{ ++ struct sharpsl_rc *data = (struct sharpsl_rc *) dataPtr; ++ int timer = 1; ++ int key = get_remocon_raw(); ++ DPRINTK("timer callback, key: %d", key); ++ ++ //wait for value to stabilize ++ if (data->state < WAIT_STATE) { ++ if (data->last_key != key) { ++ ++data->noise; ++ if (data->noise > NOISE_THRESHOLD) { ++ DPRINTK("too much noise, bailing"); ++ timer = 0; ++ } ++ data->state = 0; ++ } else { ++ ++data->state; ++ } ++ data->last_key = key; ++ ++ //stable value, send event ++ } else if (data->state == WAIT_STATE) { ++ data->noise = 0; ++ //non-key returned, skip the rest of the states and bail now ++ if (data->last_key == 0) { ++ DPRINTK("non-key detected %d, noise: %d", data->last_key, data->noise); ++ timer = 0; ++ //send button press ++ } else { ++ DPRINTK("key press detected %d, noise %d", data->last_key, data->noise); ++ input_report_key(data->input, data->last_key, 1); ++ } ++ ++data->state; ++ ++ //wait until key is released ++ } else if (data->state < WAIT_STATE * 2) { ++ if (key == data->last_key ++ && data->noise < NOISE_THRESHOLD) { ++ data->state = WAIT_STATE + 1; ++ ++data->noise; ++ } else { ++ ++data->state; ++ } ++ //key is released, send event ++ } else { ++ //send button release ++ DPRINTK("release key %d", data->last_key); ++ input_report_key(data->input, data->last_key, 0); ++ timer = 0; ++ } ++ if (timer) { ++ mod_timer(&data->rctimer, jiffies + msecs_to_jiffies(RC_POLL_MS)); ++ } else { ++ set_scoop_gpio(&REMOTE_SCOOP_DEVICE.dev, REMOTE_AKIN_PULLUP); ++ data->handling_press = 0; ++ } ++} ++ ++static int __init sharpsl_rc_probe(struct platform_device *pdev) ++{ ++ struct sharpsl_rc *sharpsl_rc; ++ struct input_dev *input_dev; ++ int i, ret; ++ ++ dev_dbg(&pdev->dev, "sharpsl_rc_probe\n"); ++ ++ sharpsl_rc = kzalloc(sizeof(struct sharpsl_rc), GFP_KERNEL); ++ input_dev = input_allocate_device(); ++ if (!sharpsl_rc || !input_dev) { ++ kfree(sharpsl_rc); ++ input_free_device(input_dev); ++ return -ENOMEM; ++ } ++ ++ platform_set_drvdata(pdev, sharpsl_rc); ++ ++ sharpsl_rc->dev = &pdev->dev; ++ sharpsl_rc->input = input_dev; ++ spin_lock_init(&sharpsl_rc->lock); ++ ++ /* Init Remote Control Timer */ ++ init_timer(&sharpsl_rc->rctimer); ++ sharpsl_rc->rctimer.function = sharpsl_rc_timer_callback; ++ sharpsl_rc->rctimer.data = (unsigned long) sharpsl_rc; ++ ++ input_dev->name = "Sharp Remote Control CE-RHX"; ++ input_dev->phys = "sharpsl_rc/input0"; ++ input_dev->id.bustype = BUS_HOST; ++ input_dev->id.vendor = 0x0001; ++ input_dev->id.product = 0x0001; ++ input_dev->id.version = 0x0100; ++ input_dev->cdev.dev = &pdev->dev; ++ input_dev->private = sharpsl_rc; ++ ++ input_dev->evbit[0] = BIT(EV_KEY); ++ ++ for (i = 0; i <= ARRAY_SIZE(remote_keys); i++) ++ set_bit(remote_keys[i].key, input_dev->keybit); ++ ++ input_register_device(sharpsl_rc->input); ++ ++ pxa_gpio_mode(REMOTE_GPIO_INT | GPIO_IN); ++ ret = request_irq(REMOTE_IRQ_INT, ++ sharpsl_rc_interrupt, ++ IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED, ++ "sharpsl_rc", ++ sharpsl_rc); ++ if (ret < 0) { ++ dev_dbg(&pdev->dev, "Can't get IRQ: %d!\n", i); ++ kfree(sharpsl_rc); ++ input_free_device(input_dev); ++ return ret; ++ } ++ ++ return 0; ++} ++ ++static int sharpsl_rc_remove(struct platform_device *pdev) ++{ ++ struct sharpsl_rc *sharpsl_rc = platform_get_drvdata(pdev); ++ ++ dev_dbg(&pdev->dev, "sharpsl_rc_remove\n"); ++ ++ free_irq(REMOTE_IRQ_INT, sharpsl_rc); ++ del_timer_sync(&sharpsl_rc->rctimer); ++ input_unregister_device(sharpsl_rc->input); ++ kfree(sharpsl_rc); ++ ++ return 0; ++} ++ ++static struct platform_driver sharpsl_rc_driver = { ++ .probe = sharpsl_rc_probe, ++ .remove = sharpsl_rc_remove, ++ .suspend = NULL, ++ .resume = NULL, ++ .driver = { ++ .name = "sharpsl-remote-control", ++ }, ++}; ++ ++static int __devinit sharpsl_rc_init(void) ++{ ++ printk("sharpsl_rc_init\n"); ++ return platform_driver_register(&sharpsl_rc_driver); ++} ++ ++static void __exit sharpsl_rc_exit(void) ++{ ++ printk("sharpsl_rc_exit\n"); ++ platform_driver_unregister(&sharpsl_rc_driver); ++} ++ ++module_init(sharpsl_rc_init); ++module_exit(sharpsl_rc_exit); ++ ++MODULE_AUTHOR("Justin Patrin <papercrane@reversefold.com>"); ++MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>"); ++MODULE_DESCRIPTION("SharpSL Remote Control Driver"); ++MODULE_LICENSE("GPL"); +Index: linux-2.6.24/drivers/input/keyboard/spitzkbd.c +=================================================================== +--- linux-2.6.24.orig/drivers/input/keyboard/spitzkbd.c 2008-01-24 22:58:37.000000000 +0000 ++++ linux-2.6.24/drivers/input/keyboard/spitzkbd.c 2008-03-10 17:05:55.000000000 +0000 +@@ -19,6 +19,7 @@ + #include <linux/jiffies.h> + #include <linux/module.h> + #include <linux/slab.h> ++#include <linux/kmod.h> + + #include <asm/arch/spitz.h> + #include <asm/arch/hardware.h> +@@ -279,13 +280,21 @@ + static int sharpsl_hinge_state; + static int hinge_count; + ++void spitzkbd_handle_sharpsl_rc(void *arg) { ++ request_module("sharpsl_rc"); ++} ++ ++DECLARE_WORK(spitzkbd_work, spitzkbd_handle_sharpsl_rc); ++ + static void spitzkbd_hinge_timer(unsigned long data) + { + struct spitzkbd *spitzkbd_data = (struct spitzkbd *) data; + unsigned long state; + unsigned long flags; ++ unsigned int headphone, remote; + + state = GPLR(SPITZ_GPIO_SWA) & (GPIO_bit(SPITZ_GPIO_SWA)|GPIO_bit(SPITZ_GPIO_SWB)); ++ state |= (GPLR(SPITZ_GPIO_HP_IN) & GPIO_bit(SPITZ_GPIO_HP_IN)); + state |= (GPLR(SPITZ_GPIO_AK_INT) & GPIO_bit(SPITZ_GPIO_AK_INT)); + if (state != sharpsl_hinge_state) { + hinge_count = 0; +@@ -299,9 +308,18 @@ + + input_report_switch(spitzkbd_data->input, SW_LID, ((GPLR(SPITZ_GPIO_SWA) & GPIO_bit(SPITZ_GPIO_SWA)) != 0)); + input_report_switch(spitzkbd_data->input, SW_TABLET_MODE, ((GPLR(SPITZ_GPIO_SWB) & GPIO_bit(SPITZ_GPIO_SWB)) != 0)); +- input_report_switch(spitzkbd_data->input, SW_HEADPHONE_INSERT, ((GPLR(SPITZ_GPIO_AK_INT) & GPIO_bit(SPITZ_GPIO_AK_INT)) != 0)); ++ ++ headphone = ((GPLR(SPITZ_GPIO_HP_IN) & GPIO_bit(SPITZ_GPIO_HP_IN)) != 0); ++ input_report_switch(spitzkbd_data->input, SW_HEADPHONE_INSERT, headphone); ++ ++ remote = headphone && ((GPLR(SPITZ_GPIO_AK_INT) & GPIO_bit(SPITZ_GPIO_AK_INT)) == 0); ++ input_report_switch(spitzkbd_data->input, SW_REMOTE_INSERT, remote); + input_sync(spitzkbd_data->input); + ++ if (remote) { ++ schedule_work(&spitzkbd_work); ++ } ++ + spin_unlock_irqrestore(&spitzkbd_data->lock, flags); + } else { + mod_timer(&spitzkbd_data->htimer, jiffies + msecs_to_jiffies(HINGE_SCAN_INTERVAL)); +@@ -395,6 +413,7 @@ + set_bit(SW_LID, input_dev->swbit); + set_bit(SW_TABLET_MODE, input_dev->swbit); + set_bit(SW_HEADPHONE_INSERT, input_dev->swbit); ++ set_bit(SW_REMOTE_INSERT, input_dev->swbit); + + err = input_register_device(input_dev); + if (err) +@@ -432,9 +451,12 @@ + request_irq(SPITZ_IRQ_GPIO_SWB, spitzkbd_hinge_isr, + IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "Spitzkbd SWB", spitzkbd); +- request_irq(SPITZ_IRQ_GPIO_AK_INT, spitzkbd_hinge_isr, ++ request_irq(SPITZ_IRQ_GPIO_HP_IN, spitzkbd_hinge_isr, + IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "Spitzkbd HP", spitzkbd); ++ request_irq(SPITZ_IRQ_GPIO_AK_INT, spitzkbd_hinge_isr, ++ IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED, ++ "Spitzkbd HP Type", spitzkbd); + + return 0; + +@@ -455,6 +477,7 @@ + free_irq(SPITZ_IRQ_GPIO_ON_KEY, spitzkbd); + free_irq(SPITZ_IRQ_GPIO_SWA, spitzkbd); + free_irq(SPITZ_IRQ_GPIO_SWB, spitzkbd); ++ free_irq(SPITZ_IRQ_GPIO_HP_IN, spitzkbd); + free_irq(SPITZ_IRQ_GPIO_AK_INT, spitzkbd); + + del_timer_sync(&spitzkbd->htimer); +Index: linux-2.6.24/arch/arm/mach-pxa/sharpsl.h +=================================================================== +--- linux-2.6.24.orig/arch/arm/mach-pxa/sharpsl.h 2008-03-10 17:05:35.000000000 +0000 ++++ linux-2.6.24/arch/arm/mach-pxa/sharpsl.h 2008-03-10 17:05:55.000000000 +0000 +@@ -37,15 +37,10 @@ + */ + #define READ_GPIO_BIT(x) (GPLR(x) & GPIO_bit(x)) + +-/* MAX1111 Channel Definitions */ +-#define MAX1111_BATT_VOLT 4u +-#define MAX1111_BATT_TEMP 2u +-#define MAX1111_ACIN_VOLT 6u +- + extern struct battery_thresh spitz_battery_levels_acin[]; + extern struct battery_thresh spitz_battery_levels_noac[]; + void sharpsl_pm_pxa_init(void); + void sharpsl_pm_pxa_remove(void); +-int sharpsl_pm_pxa_read_max1111(int channel); ++ + + +Index: linux-2.6.24/arch/arm/mach-pxa/sharpsl_pm.c +=================================================================== +--- linux-2.6.24.orig/arch/arm/mach-pxa/sharpsl_pm.c 2008-01-24 22:58:37.000000000 +0000 ++++ linux-2.6.24/arch/arm/mach-pxa/sharpsl_pm.c 2008-03-10 17:05:55.000000000 +0000 +@@ -135,6 +135,8 @@ + | MAXCTRL_SGL | MAXCTRL_UNI | MAXCTRL_STR); + } + ++EXPORT_SYMBOL(sharpsl_pm_pxa_read_max1111); ++ + void sharpsl_pm_pxa_init(void) + { + pxa_gpio_mode(sharpsl_pm.machinfo->gpio_acin | GPIO_IN); +Index: linux-2.6.24/include/asm-arm/hardware/sharpsl_pm.h +=================================================================== +--- linux-2.6.24.orig/include/asm-arm/hardware/sharpsl_pm.h 2008-01-24 22:58:37.000000000 +0000 ++++ linux-2.6.24/include/asm-arm/hardware/sharpsl_pm.h 2008-03-10 17:05:55.000000000 +0000 +@@ -104,3 +104,10 @@ + irqreturn_t sharpsl_chrg_full_isr(int irq, void *dev_id); + irqreturn_t sharpsl_fatal_isr(int irq, void *dev_id); + ++/* MAX1111 Channel Definitions */ ++#define MAX1111_REMCOM 0u ++#define MAX1111_BATT_VOLT 4u ++#define MAX1111_BATT_TEMP 2u ++#define MAX1111_ACIN_VOLT 6u ++ ++int sharpsl_pm_pxa_read_max1111(int channel); +Index: linux-2.6.24/include/linux/input.h +=================================================================== +--- linux-2.6.24.orig/include/linux/input.h 2008-01-24 22:58:37.000000000 +0000 ++++ linux-2.6.24/include/linux/input.h 2008-03-10 17:05:55.000000000 +0000 +@@ -636,6 +636,7 @@ + #define SW_TABLET_MODE 0x01 /* set = tablet mode */ + #define SW_HEADPHONE_INSERT 0x02 /* set = inserted */ + #define SW_RADIO 0x03 /* set = radio enabled */ ++#define SW_REMOTE_INSERT 0x04 /* set = remote */ + #define SW_MAX 0x0f + #define SW_CNT (SW_MAX+1) + +Index: linux-2.6.24/arch/arm/mach-pxa/spitz_pm.c +=================================================================== +--- linux-2.6.24.orig/arch/arm/mach-pxa/spitz_pm.c 2008-03-10 17:05:40.000000000 +0000 ++++ linux-2.6.24/arch/arm/mach-pxa/spitz_pm.c 2008-03-10 17:05:55.000000000 +0000 +@@ -162,6 +162,13 @@ + if (resume_on_alarm && (PEDR & PWER_RTC)) + is_resume |= PWER_RTC; + ++ printk("wakeup: PEDR: %x, PKSR: %x, HP_IN: %x, AK_INT: %x\n", PEDR, PKSR, GPIO_bit(SPITZ_GPIO_HP_IN), GPIO_bit(SPITZ_GPIO_AK_INT)); ++ ++ //remote/headphone interrupt, wakeup ++ if (PEDR == 0 && (PKSR & 0xc0d01) != 0) { ++ is_resume |= PWER_RTC; ++ } ++ + dev_dbg(sharpsl_pm.dev, "is_resume: %x\n",is_resume); + return is_resume; + } diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/sharpsl-rc-r2.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/sharpsl-rc-r2.patch new file mode 100644 index 0000000000..6fb34ec179 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/sharpsl-rc-r2.patch @@ -0,0 +1,180 @@ +This patch adds support for Sharp CE-RH2 on Akita and CE-RH1 on C7x0. + +This patch is a bit ugly: +- Device specific functions should be moved to platform infrastructure. +- Maybe define generic *_scoopexp functions handling Akita x Spitz differences. + +Index: linux-2.6.24/drivers/input/keyboard/sharpsl_rc.c +=================================================================== +--- linux-2.6.24.orig/drivers/input/keyboard/sharpsl_rc.c 2008-03-08 19:29:02.000000000 +0000 ++++ linux-2.6.24/drivers/input/keyboard/sharpsl_rc.c 2008-03-08 22:23:35.000000000 +0000 +@@ -21,12 +21,10 @@ + #include <linux/module.h> + #include <linux/slab.h> + +-#ifdef CONFIG_MACH_SPITZ ++#include <asm/mach-types.h> + #include <asm/arch/spitz.h> +-#endif +-#ifdef CONFIG_MACH_CORGI ++#include <asm/arch/akita.h> + #include <asm/arch/corgi.h> +-#endif + + #include <asm/arch/hardware.h> + #include <asm/arch/pxa-regs.h> +@@ -42,12 +40,8 @@ + unsigned char key; + }; + +-#ifdef CONFIG_MACH_SPITZ +-#define REMOTE_AKIN_PULLUP SPITZ_SCP2_AKIN_PULLUP +-#define REMOTE_SCOOP_DEVICE spitzscoop2_device +-#define REMOTE_GPIO_INT SPITZ_GPIO_AK_INT +-#define REMOTE_IRQ_INT SPITZ_IRQ_GPIO_AK_INT +-static struct remote_control_key remote_keys[] = { ++static struct remote_control_key remote_keys_spitz[] = { ++ /* CE-RH2 values */ + { 25, 35, KEY_STOPCD}, + { 55, 65, KEY_PLAYPAUSE}, + { 85, 95, KEY_NEXTSONG}, +@@ -56,23 +50,15 @@ + { 180, 190, KEY_MUTE}, + { 215, 225, KEY_VOLUMEDOWN}, + }; +-#endif +-#ifdef CONFIG_MACH_CORGI +-#define REMOTE_AKIN_PULLUP CORGI_SCP_AKIN_PULLUP +-#define REMOTE_SCOOP_DEVICE corgiscoop_device +-#define REMOTE_GPIO_INT CORGI_GPIO_AK_INT +-#define REMOTE_IRQ_INT CORGI_IRQ_GPIO_AK_INT +-static struct remote_control_key remote_keys[] = { +- //These need to be fixed for the CE-RH1's values +- { 25, 35, KEY_STOPCD}, +- { 55, 65, KEY_PLAYPAUSE}, +- { 85, 95, KEY_NEXTSONG}, +- { 115, 125, KEY_VOLUMEUP}, +- { 145, 155, KEY_PREVIOUSSONG}, +- { 180, 190, KEY_MUTE}, +- { 215, 225, KEY_VOLUMEDOWN}, ++static struct remote_control_key remote_keys_corgi[] = { ++ /* CE-RH1 values */ ++ { 27, 35, KEY_STOPCD}, ++ { 7, 13, KEY_PLAYPAUSE}, ++ { 77, 93, KEY_NEXTSONG}, ++ { 115, 132, KEY_VOLUMEUP}, ++ { 46, 58, KEY_PREVIOUSSONG}, ++ { 170, 186, KEY_VOLUMEDOWN}, + }; +-#endif + + #define RELEASE_HI 230 + #define MAX_EARPHONE 6 +@@ -98,9 +84,17 @@ + static int get_remocon_raw(void) + { + int i, val; ++ struct remote_control_key *remote_keys; ++ ++ if (machine_is_borzoi() || machine_is_spitz() || machine_is_akita()) ++ remote_keys = remote_keys_spitz; ++ else ++ remote_keys = remote_keys_corgi; + + val = sharpsl_pm_pxa_read_max1111(MAX1111_REMCOM); +- for (i = 0; i < ARRAY_SIZE(remote_keys); ++i) { ++ for (i = 0; i < (machine_is_borzoi() || machine_is_spitz() || machine_is_akita() ? ++ ARRAY_SIZE(remote_keys_spitz) : ARRAY_SIZE(remote_keys_corgi)); ++ ++i) { + if (val >= remote_keys[i].min + && val <= remote_keys[i].max) { + printk("get_remocon_raw: VAL=%i, KEY=%i\n", val, remote_keys[i].key); +@@ -121,8 +115,12 @@ + data->state = 0; + data->last_key = 0; + +- reset_scoop_gpio(&REMOTE_SCOOP_DEVICE.dev, REMOTE_AKIN_PULLUP); +- ++ if (machine_is_borzoi() || machine_is_spitz()) ++ reset_scoop_gpio(platform_scoop_config->devs[1].dev, SPITZ_SCP2_AKIN_PULLUP); ++ else if (machine_is_akita()) ++ akita_reset_ioexp(&akitaioexp_device.dev, AKITA_IOEXP_AKIN_PULLUP); ++ else ++ reset_scoop_gpio(platform_scoop_config->devs[0].dev, CORGI_SCP_AKIN_PULLUP); + mod_timer(&data->rctimer, jiffies + msecs_to_jiffies(RC_POLL_MS)); + } + return IRQ_HANDLED; +@@ -182,7 +180,12 @@ + if (timer) { + mod_timer(&data->rctimer, jiffies + msecs_to_jiffies(RC_POLL_MS)); + } else { +- set_scoop_gpio(&REMOTE_SCOOP_DEVICE.dev, REMOTE_AKIN_PULLUP); ++ if (machine_is_borzoi() || machine_is_spitz()) ++ set_scoop_gpio(platform_scoop_config->devs[1].dev, SPITZ_SCP2_AKIN_PULLUP); ++ else if (machine_is_akita()) ++ akita_set_ioexp(&akitaioexp_device.dev, AKITA_IOEXP_AKIN_PULLUP); ++ else ++ set_scoop_gpio(platform_scoop_config->devs[0].dev, CORGI_SCP_AKIN_PULLUP); + data->handling_press = 0; + } + } +@@ -192,6 +195,7 @@ + struct sharpsl_rc *sharpsl_rc; + struct input_dev *input_dev; + int i, ret; ++ struct remote_control_key *remote_keys; + + dev_dbg(&pdev->dev, "sharpsl_rc_probe\n"); + +@@ -225,17 +229,32 @@ + + input_dev->evbit[0] = BIT(EV_KEY); + +- for (i = 0; i <= ARRAY_SIZE(remote_keys); i++) ++ if (machine_is_borzoi() || machine_is_spitz() || machine_is_akita()) ++ remote_keys = remote_keys_spitz; ++ else ++ remote_keys = remote_keys_corgi; ++ for (i = 0; i < (machine_is_borzoi() || machine_is_spitz() || machine_is_akita() ? ++ ARRAY_SIZE(remote_keys_spitz) : ARRAY_SIZE(remote_keys_corgi)); ++ ++i) + set_bit(remote_keys[i].key, input_dev->keybit); + + input_register_device(sharpsl_rc->input); + +- pxa_gpio_mode(REMOTE_GPIO_INT | GPIO_IN); +- ret = request_irq(REMOTE_IRQ_INT, +- sharpsl_rc_interrupt, +- IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED, +- "sharpsl_rc", +- sharpsl_rc); ++ if (machine_is_borzoi() || machine_is_spitz() || machine_is_akita()) { ++ pxa_gpio_mode(SPITZ_GPIO_AK_INT | GPIO_IN); ++ ret = request_irq(SPITZ_IRQ_GPIO_AK_INT, ++ sharpsl_rc_interrupt, ++ IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED, ++ "sharpsl_rc", ++ sharpsl_rc); ++ } else { ++ pxa_gpio_mode(CORGI_GPIO_AK_INT | GPIO_IN); ++ ret = request_irq(CORGI_IRQ_GPIO_AK_INT, ++ sharpsl_rc_interrupt, ++ IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_SHARED, ++ "sharpsl_rc", ++ sharpsl_rc); ++ } + if (ret < 0) { + dev_dbg(&pdev->dev, "Can't get IRQ: %d!\n", i); + kfree(sharpsl_rc); +@@ -252,7 +271,10 @@ + + dev_dbg(&pdev->dev, "sharpsl_rc_remove\n"); + +- free_irq(REMOTE_IRQ_INT, sharpsl_rc); ++ if (machine_is_borzoi() || machine_is_spitz() || machine_is_akita()) ++ free_irq(SPITZ_IRQ_GPIO_AK_INT, sharpsl_rc); ++ else ++ free_irq(CORGI_IRQ_GPIO_AK_INT, sharpsl_rc); + del_timer_sync(&sharpsl_rc->rctimer); + input_unregister_device(sharpsl_rc->input); + kfree(sharpsl_rc); diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/spitz/defconfig b/recipes/kexecboot/linux-kexecboot-2.6.24/spitz/defconfig new file mode 100644 index 0000000000..eab0e478d8 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/spitz/defconfig @@ -0,0 +1,1239 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.24 +# Sun Feb 1 20:48:10 2009 +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +CONFIG_GENERIC_TIME=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_MMU=y +# CONFIG_NO_IOPORT is not set +CONFIG_GENERIC_HARDIRQS=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +# CONFIG_ARCH_HAS_ILOG2_U32 is not set +# CONFIG_ARCH_HAS_ILOG2_U64 is not set +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_ZONE_DMA=y +CONFIG_ARCH_MTD_XIP=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_LOCK_KERNEL=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_LOCALVERSION="" +# CONFIG_LOCALVERSION_AUTO is not set +CONFIG_SWAP=y +CONFIG_SYSVIPC=y +CONFIG_SYSVIPC_SYSCTL=y +CONFIG_BSD_PROCESS_ACCT=y +CONFIG_BSD_PROCESS_ACCT_V3=y +# CONFIG_USER_NS is not set +# CONFIG_PID_NS is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_CGROUPS is not set +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_FAIR_USER_SCHED=y +# CONFIG_FAIR_CGROUP_SCHED is not set +CONFIG_SYSFS_DEPRECATED=y +# CONFIG_RELAY is not set +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="initramfs.cpio.gz" +CONFIG_INITRAMFS_ROOT_UID=0 +CONFIG_INITRAMFS_ROOT_GID=0 +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_EMBEDDED=y +CONFIG_UID16=y +CONFIG_SYSCTL_SYSCALL=y +CONFIG_KALLSYMS=y +# CONFIG_KALLSYMS_EXTRA_PASS is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +CONFIG_ELF_CORE=y +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_ANON_INODES=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +CONFIG_VM_EVENT_COUNTERS=y +CONFIG_SLAB=y +# CONFIG_SLUB is not set +# CONFIG_SLOB is not set +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +# CONFIG_TINY_SHMEM is not set +CONFIG_BASE_SMALL=0 +CONFIG_MODULES=y +CONFIG_MODULE_UNLOAD=y +CONFIG_MODULE_FORCE_UNLOAD=y +# CONFIG_MODVERSIONS is not set +# CONFIG_MODULE_SRCVERSION_ALL is not set +CONFIG_KMOD=y +CONFIG_BLOCK=y +# CONFIG_LBD is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_LSF is not set +# CONFIG_BLK_DEV_BSG is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +CONFIG_IOSCHED_AS=y +CONFIG_IOSCHED_DEADLINE=m +CONFIG_IOSCHED_CFQ=m +CONFIG_DEFAULT_AS=y +# CONFIG_DEFAULT_DEADLINE is not set +# CONFIG_DEFAULT_CFQ is not set +# CONFIG_DEFAULT_NOOP is not set +CONFIG_DEFAULT_IOSCHED="anticipatory" + +# +# System Type +# +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_CLPS7500 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CO285 is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_IMX is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_PNX4008 is not set +CONFIG_ARCH_PXA=y +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP is not set + +# +# Intel PXA2xx/PXA3xx Implementations +# +# CONFIG_ARCH_LUBBOCK is not set +# CONFIG_MACH_LOGICPD_PXA270 is not set +# CONFIG_MACH_MAINSTONE is not set +# CONFIG_ARCH_PXA_IDP is not set +CONFIG_PXA_SHARPSL=y +# CONFIG_MACH_TRIZEPS4 is not set +# CONFIG_MACH_HX2750 is not set +# CONFIG_MACH_EM_X270 is not set +# CONFIG_MACH_ZYLONITE is not set +# CONFIG_MACH_ARMCORE is not set +# CONFIG_PXA_SHARPSL_25x is not set +CONFIG_PXA_SHARPSL_27x=y +# CONFIG_MACH_HTCUNIVERSAL is not set +CONFIG_MACH_AKITA=y +CONFIG_MACH_SPITZ=y +CONFIG_MACH_BORZOI=y +CONFIG_PXA27x=y +CONFIG_PXA_SHARP_Cxx00=y +CONFIG_PXA_SSP=y +# CONFIG_PXA_KEYS is not set + +# +# Boot options +# + +# +# Power management +# + +# +# Processor Type +# +CONFIG_CPU_32=y +CONFIG_CPU_XSCALE=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5T=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_TLB_V4WBI=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_OUTER_CACHE is not set +CONFIG_IWMMXT=y +CONFIG_XSCALE_PMU=y +CONFIG_SHARP_PARAM=y +CONFIG_SHARPSL_PM=y +CONFIG_SHARP_SCOOP=y + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +CONFIG_PCCARD=y +# CONFIG_PCMCIA_DEBUG is not set +CONFIG_PCMCIA=y +CONFIG_PCMCIA_LOAD_CIS=y +CONFIG_PCMCIA_IOCTL=y + +# +# PC-card bridges +# +CONFIG_PCMCIA_PXA2XX=y + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +CONFIG_NO_HZ=y +CONFIG_HIGH_RES_TIMERS=y +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_PREEMPT=y +CONFIG_HZ=100 +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +# CONFIG_SPARSEMEM_STATIC is not set +# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set +CONFIG_SPLIT_PTLOCK_CPUS=4096 +# CONFIG_RESOURCES_64BIT is not set +CONFIG_ZONE_DMA_FLAG=1 +CONFIG_BOUNCE=y +CONFIG_VIRT_TO_BUS=y +CONFIG_ALIGNMENT_TRAP=y + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="console=ttyS0,115200n8 console=tty1 fbcon=rotate:1" +# CONFIG_XIP_KERNEL is not set +CONFIG_KEXEC=y +CONFIG_ATAGS_PROC=y + +# +# CPU Frequency scaling +# +# CONFIG_CPU_FREQ is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +CONFIG_BINFMT_AOUT=m +CONFIG_BINFMT_MISC=m + +# +# Power management options +# +CONFIG_PM=y +# CONFIG_PM_LEGACY is not set +# CONFIG_PM_DEBUG is not set +CONFIG_PM_SLEEP=y +CONFIG_SUSPEND_UP_POSSIBLE=y +CONFIG_SUSPEND=y +CONFIG_APM_EMULATION=y + +# +# Networking +# +# CONFIG_NET is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +# CONFIG_SYS_HYPERVISOR is not set +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_CONCAT is not set +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +CONFIG_MTD_CMDLINE_PARTS=y +# CONFIG_MTD_AFS_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS 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=y +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +CONFIG_MTD_COMPLEX_MAPPINGS=y +# CONFIG_MTD_PHYSMAP is not set +CONFIG_MTD_SHARP_SL=y +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +CONFIG_MTD_NAND=y +CONFIG_MTD_NAND_VERIFY_WRITE=y +# CONFIG_MTD_NAND_ECC_SMC is not set +# CONFIG_MTD_NAND_MUSEUM_IDS is not set +# CONFIG_MTD_NAND_H1900 is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND_DISKONCHIP is not set +CONFIG_MTD_NAND_SHARPSL=y +# CONFIG_MTD_NAND_NANDSIM is not set +# CONFIG_MTD_NAND_PLATFORM is not set +# CONFIG_MTD_ALAUDA is not set +# CONFIG_MTD_ONENAND is not set + +# +# UBI - Unsorted block images +# +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +# CONFIG_BLK_DEV_UB is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_CDROM_PKTCDVD is not set +CONFIG_MISC_DEVICES=y +# CONFIG_EEPROM_93CX6 is not set +CONFIG_IDE=y +CONFIG_IDE_MAX_HWIFS=4 +CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_IDE_SATA is not set +CONFIG_BLK_DEV_IDEDISK=y +# CONFIG_IDEDISK_MULTI_MODE is not set +CONFIG_BLK_DEV_IDECS=y +# CONFIG_BLK_DEV_IDECD is not set +# 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_PROC_FS=y + +# +# IDE chipset support/bugfixes +# +CONFIG_IDE_GENERIC=y +# CONFIG_BLK_DEV_PLATFORM is not set +# CONFIG_IDE_ARM is not set +# CONFIG_BLK_DEV_IDEDMA is not set +CONFIG_IDE_ARCH_OBSOLETE_INIT=y +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI device support +# +# CONFIG_RAID_ATTRS is not set +CONFIG_SCSI=m +CONFIG_SCSI_DMA=y +# CONFIG_SCSI_TGT is not set +# CONFIG_SCSI_NETLINK is not set +CONFIG_SCSI_PROC_FS=y + +# +# SCSI support type (disk, tape, CD-ROM) +# +CONFIG_BLK_DEV_SD=m +CONFIG_CHR_DEV_ST=m +CONFIG_CHR_DEV_OSST=m +CONFIG_BLK_DEV_SR=m +# CONFIG_BLK_DEV_SR_VENDOR is not set +CONFIG_CHR_DEV_SG=m +# CONFIG_CHR_DEV_SCH is not set + +# +# Some SCSI devices (e.g. CD jukebox) support multiple LUNs +# +CONFIG_SCSI_MULTI_LUN=y +# CONFIG_SCSI_CONSTANTS is not set +# CONFIG_SCSI_LOGGING is not set +# CONFIG_SCSI_SCAN_ASYNC is not set +CONFIG_SCSI_WAIT_SCAN=m + +# +# SCSI Transports +# +# CONFIG_SCSI_SPI_ATTRS is not set +# CONFIG_SCSI_FC_ATTRS is not set +# CONFIG_SCSI_SAS_LIBSAS is not set +# CONFIG_SCSI_SRP_ATTRS is not set +CONFIG_SCSI_LOWLEVEL=y +# CONFIG_SCSI_DEBUG is not set +# CONFIG_SCSI_LOWLEVEL_PCMCIA is not set +# CONFIG_ATA is not set +CONFIG_MD=y +# CONFIG_BLK_DEV_MD is not set +CONFIG_BLK_DEV_DM=m +# CONFIG_DM_DEBUG is not set +CONFIG_DM_CRYPT=m +CONFIG_DM_SNAPSHOT=m +CONFIG_DM_MIRROR=m +CONFIG_DM_ZERO=m +CONFIG_DM_MULTIPATH=m +CONFIG_DM_MULTIPATH_EMC=m +# CONFIG_DM_MULTIPATH_RDAC is not set +# CONFIG_DM_MULTIPATH_HP is not set +# CONFIG_DM_DELAY is not set +# CONFIG_DM_UEVENT is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set + +# +# Userland interfaces +# +CONFIG_INPUT_MOUSEDEV=m +# CONFIG_INPUT_MOUSEDEV_PSAUX is not set +CONFIG_INPUT_MOUSEDEV_SCREEN_X=640 +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=480 +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +CONFIG_INPUT_POWER=y + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_CORGI is not set +CONFIG_KEYBOARD_SPITZ=y +CONFIG_SHARPSL_RC=y +# CONFIG_KEYBOARD_PXA27x is not set +# CONFIG_KEYBOARD_GPIO is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +CONFIG_INPUT_TOUCHSCREEN=y +CONFIG_TOUCHSCREEN_CORGI=y +# CONFIG_TOUCHSCREEN_FUJITSU is not set +# CONFIG_TOUCHSCREEN_GUNZE is not set +# CONFIG_TOUCHSCREEN_ELO is not set +# CONFIG_TOUCHSCREEN_MTOUCH is not set +# CONFIG_TOUCHSCREEN_MK712 is not set +# CONFIG_TOUCHSCREEN_PENMOUNT is not set +# CONFIG_TOUCHSCREEN_TOUCHRIGHT is not set +# CONFIG_TOUCHSCREEN_TOUCHWIN is not set +# CONFIG_TOUCHSCREEN_UCB1400 is not set +# CONFIG_TOUCHSCREEN_USB_COMPOSITE is not set +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_ATI_REMOTE is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_YEALINK is not set +CONFIG_INPUT_UINPUT=m + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +# CONFIG_VT_HW_CONSOLE_BINDING is not set +# CONFIG_SERIAL_NONSTANDARD is not set + +# +# Serial drivers +# +CONFIG_SERIAL_8250=m +CONFIG_SERIAL_8250_CS=m +CONFIG_SERIAL_8250_NR_UARTS=4 +CONFIG_SERIAL_8250_RUNTIME_UARTS=4 +# CONFIG_SERIAL_8250_EXTENDED is not set + +# +# Non-8250 serial port support +# +CONFIG_SERIAL_PXA=y +CONFIG_SERIAL_PXA_CONSOLE=y +CONFIG_SERIAL_CORE=y +CONFIG_SERIAL_CORE_CONSOLE=y +CONFIG_UNIX98_PTYS=y +# CONFIG_LEGACY_PTYS is not set +# CONFIG_IPMI_HANDLER is not set +CONFIG_HW_RANDOM=m +# CONFIG_NVRAM is not set +# CONFIG_R3964 is not set + +# +# PCMCIA character devices +# +# CONFIG_SYNCLINK_CS is not set +# CONFIG_CARDMAN_4000 is not set +# CONFIG_CARDMAN_4040 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +# CONFIG_I2C_CHARDEV is not set + +# +# I2C Algorithms +# +CONFIG_I2C_ALGOBIT=y +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set + +# +# I2C Hardware Bus support +# +# CONFIG_I2C_GPIO is not set +CONFIG_I2C_PXA=y +# CONFIG_I2C_PXA_SLAVE is not set +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_TAOS_EVM is not set +# CONFIG_I2C_STUB is not set +# CONFIG_I2C_TINY_USB is not set + +# +# Miscellaneous I2C Chip support +# +# CONFIG_SENSORS_DS1337 is not set +# CONFIG_SENSORS_DS1374 is not set +# CONFIG_DS1682 is not set +# CONFIG_SENSORS_EEPROM is not set +# CONFIG_SENSORS_PCF8574 is not set +# CONFIG_SENSORS_PCA9539 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_MAX6875 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_DEBUG_CHIP is not set + +# +# SPI support +# +# CONFIG_SPI is not set +# CONFIG_SPI_MASTER is not set +# CONFIG_W1 is not set +# CONFIG_POWER_SUPPLY is not set +# CONFIG_HWMON is not set +# CONFIG_WATCHDOG is not set + +# +# Sonics Silicon Backplane +# +CONFIG_SSB_POSSIBLE=y +# CONFIG_SSB is not set + +# +# Multifunction device drivers +# +# CONFIG_MFD_SM501 is not set +# CONFIG_HTC_ASIC3 is not set +# CONFIG_HTC_ASIC3_DS1WM is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set +# CONFIG_DAB is not set + +# +# Graphics support +# +# CONFIG_VGASTATE is not set +CONFIG_VIDEO_OUTPUT_CONTROL=m +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_DEFERRED_IO is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_S1D13XXX is not set +CONFIG_FB_PXA=y +CONFIG_FB_PXA_LCD_QVGA=y +# CONFIG_FB_PXA_LCD_VGA is not set +CONFIG_FB_PXA_OVERLAY=y +# CONFIG_FB_PXA_PARAMETERS is not set +# CONFIG_FB_MBX is not set +# CONFIG_FB_W100 is not set +# CONFIG_FB_VIRTUAL is not set +CONFIG_BACKLIGHT_LCD_SUPPORT=y +# CONFIG_LCD_CLASS_DEVICE is not set +CONFIG_BACKLIGHT_CLASS_DEVICE=y +CONFIG_BACKLIGHT_CORGI=y + +# +# Display device support +# +# CONFIG_DISPLAY_SUPPORT is not set + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set +CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y +CONFIG_FONTS=y +# CONFIG_FONT_8x8 is not set +CONFIG_FONT_8x16=y +# CONFIG_FONT_6x11 is not set +# CONFIG_FONT_7x14 is not set +# CONFIG_FONT_PEARL_8x8 is not set +# CONFIG_FONT_ACORN_8x8 is not set +# CONFIG_FONT_MINI_4x6 is not set +# CONFIG_FONT_SUN8x16 is not set +# CONFIG_FONT_SUN12x22 is not set +# CONFIG_FONT_10x18 is not set +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 is not set +CONFIG_HID_SUPPORT=y +CONFIG_HID=m +# CONFIG_HID_DEBUG is not set +# CONFIG_HIDRAW is not set + +# +# USB Input Devices +# +CONFIG_USB_HID=m +# CONFIG_USB_HIDINPUT_POWERBOOK is not set +# CONFIG_HID_FF is not set +# CONFIG_USB_HIDDEV is not set + +# +# USB HID Boot Protocol drivers +# +CONFIG_USB_KBD=m +CONFIG_USB_MOUSE=m +CONFIG_USB_SUPPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARCH_HAS_OHCI=y +# CONFIG_USB_ARCH_HAS_EHCI is not set +CONFIG_USB=m +# CONFIG_USB_DEBUG is not set + +# +# Miscellaneous USB options +# +CONFIG_USB_DEVICEFS=y +CONFIG_USB_DEVICE_CLASS=y +# CONFIG_USB_DYNAMIC_MINORS is not set +# CONFIG_USB_SUSPEND is not set +# CONFIG_USB_PERSIST is not set +# CONFIG_USB_OTG is not set + +# +# USB Host Controller Drivers +# +# CONFIG_USB_ISP116X_HCD is not set +CONFIG_USB_OHCI_HCD=m +# CONFIG_USB_OHCI_BIG_ENDIAN_DESC is not set +# CONFIG_USB_OHCI_BIG_ENDIAN_MMIO is not set +CONFIG_USB_OHCI_LITTLE_ENDIAN=y +CONFIG_USB_SL811_HCD=m +CONFIG_USB_SL811_CS=m +# CONFIG_USB_R8A66597_HCD is not set + +# +# USB Device Class drivers +# +CONFIG_USB_ACM=m +CONFIG_USB_PRINTER=m + +# +# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' +# + +# +# may also be needed; see USB_STORAGE Help for more information +# +CONFIG_USB_STORAGE=m +# CONFIG_USB_STORAGE_DEBUG is not set +# CONFIG_USB_STORAGE_DATAFAB is not set +# CONFIG_USB_STORAGE_FREECOM is not set +# CONFIG_USB_STORAGE_ISD200 is not set +# CONFIG_USB_STORAGE_DPCM is not set +# CONFIG_USB_STORAGE_USBAT is not set +# CONFIG_USB_STORAGE_SDDR09 is not set +# CONFIG_USB_STORAGE_SDDR55 is not set +# CONFIG_USB_STORAGE_JUMPSHOT is not set +# CONFIG_USB_STORAGE_ALAUDA is not set +# CONFIG_USB_STORAGE_KARMA is not set +# CONFIG_USB_LIBUSUAL is not set + +# +# USB Imaging devices +# +CONFIG_USB_MDC800=m +CONFIG_USB_MICROTEK=m +CONFIG_USB_MON=y + +# +# USB port drivers +# + +# +# USB Serial Converter support +# +CONFIG_USB_SERIAL=m +CONFIG_USB_SERIAL_GENERIC=y +# CONFIG_USB_SERIAL_AIRCABLE is not set +# CONFIG_USB_SERIAL_AIRPRIME is not set +# CONFIG_USB_SERIAL_ARK3116 is not set +CONFIG_USB_SERIAL_BELKIN=m +# CONFIG_USB_SERIAL_CH341 is not set +# CONFIG_USB_SERIAL_WHITEHEAT is not set +CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m +CONFIG_USB_SERIAL_CP2101=m +CONFIG_USB_SERIAL_CYPRESS_M8=m +CONFIG_USB_SERIAL_EMPEG=m +CONFIG_USB_SERIAL_FTDI_SIO=m +# CONFIG_USB_SERIAL_FUNSOFT is not set +CONFIG_USB_SERIAL_VISOR=m +CONFIG_USB_SERIAL_IPAQ=m +CONFIG_USB_SERIAL_IR=m +CONFIG_USB_SERIAL_EDGEPORT=m +CONFIG_USB_SERIAL_EDGEPORT_TI=m +CONFIG_USB_SERIAL_GARMIN=m +CONFIG_USB_SERIAL_IPW=m +CONFIG_USB_SERIAL_KEYSPAN_PDA=m +CONFIG_USB_SERIAL_KEYSPAN=m +# CONFIG_USB_SERIAL_KEYSPAN_MPR is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XA is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA28XB is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19 is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA18X is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19QW is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA19QI is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49W is not set +# CONFIG_USB_SERIAL_KEYSPAN_USA49WLC is not set +CONFIG_USB_SERIAL_KLSI=m +CONFIG_USB_SERIAL_KOBIL_SCT=m +CONFIG_USB_SERIAL_MCT_U232=m +# CONFIG_USB_SERIAL_MOS7720 is not set +# CONFIG_USB_SERIAL_MOS7840 is not set +# CONFIG_USB_SERIAL_NAVMAN is not set +CONFIG_USB_SERIAL_PL2303=m +# CONFIG_USB_SERIAL_OTI6858 is not set +# CONFIG_USB_SERIAL_HP4X is not set +CONFIG_USB_SERIAL_SAFE=m +# CONFIG_USB_SERIAL_SAFE_PADDED is not set +# CONFIG_USB_SERIAL_SIERRAWIRELESS is not set +CONFIG_USB_SERIAL_TI=m +CONFIG_USB_SERIAL_CYBERJACK=m +CONFIG_USB_SERIAL_XIRCOM=m +# CONFIG_USB_SERIAL_OPTION is not set +CONFIG_USB_SERIAL_OMNINET=m +# CONFIG_USB_SERIAL_DEBUG is not set +CONFIG_USB_EZUSB=y + +# +# USB Miscellaneous drivers +# +CONFIG_USB_EMI62=m +CONFIG_USB_EMI26=m +# CONFIG_USB_ADUTUX is not set +CONFIG_USB_AUERSWALD=m +CONFIG_USB_RIO500=m +CONFIG_USB_LEGOTOWER=m +CONFIG_USB_LCD=m +# CONFIG_USB_BERRY_CHARGE is not set +CONFIG_USB_LED=m +# CONFIG_USB_CYPRESS_CY7C63 is not set +CONFIG_USB_CYTHERM=m +# CONFIG_USB_PHIDGET is not set +CONFIG_USB_IDMOUSE=m +# CONFIG_USB_FTDI_ELAN is not set +# CONFIG_USB_APPLEDISPLAY is not set +# CONFIG_USB_LD is not set +# CONFIG_USB_TRANCEVIBRATOR is not set +# CONFIG_USB_IOWARRIOR is not set +# CONFIG_USB_TEST is not set + +# +# USB DSL modem support +# + +# +# USB Gadget Support +# +CONFIG_USB_GADGET=m +# CONFIG_USB_GADGET_DEBUG_FILES is not set +CONFIG_USB_GADGET_SELECTED=y +# CONFIG_USB_GADGET_AMD5536UDC is not set +# CONFIG_USB_GADGET_ATMEL_USBA is not set +# CONFIG_USB_GADGET_FSL_USB2 is not set +# CONFIG_USB_GADGET_NET2280 is not set +# CONFIG_USB_GADGET_PXA2XX is not set +# CONFIG_USB_GADGET_M66592 is not set +CONFIG_USB_GADGET_PXA27X=y +CONFIG_USB_PXA27X=m +# CONFIG_USB_GADGET_GOKU is not set +# CONFIG_USB_GADGET_LH7A40X is not set +# CONFIG_USB_GADGET_OMAP is not set +# CONFIG_USB_GADGET_S3C2410 is not set +# CONFIG_USB_GADGET_AT91 is not set +# CONFIG_USB_GADGET_DUMMY_HCD is not set +# CONFIG_USB_GADGET_DUALSPEED is not set +CONFIG_USB_ZERO=m +# CONFIG_USB_ETH is not set +CONFIG_USB_GADGETFS=m +CONFIG_USB_FILE_STORAGE=m +# CONFIG_USB_FILE_STORAGE_TEST is not set +CONFIG_USB_G_SERIAL=m +# CONFIG_USB_MIDI_GADGET is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_UNSAFE_RESUME=y + +# +# MMC/SD Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_BOUNCE=y +# CONFIG_SDIO_UART is not set + +# +# MMC/SD Host Controller Drivers +# +CONFIG_MMC_PXA=y +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +CONFIG_LEDS_SPITZ=y +# CONFIG_LEDS_TOSA is not set +# CONFIG_LEDS_GPIO is not set + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGERS=y +CONFIG_LEDS_TRIGGER_TIMER=y +CONFIG_LEDS_TRIGGER_IDE_DISK=y +# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set + +# +# SPI RTC drivers +# + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_SA1100=y + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XIP is not set +CONFIG_EXT3_FS=y +# CONFIG_EXT3_FS_XATTR is not set +# CONFIG_EXT4DEV_FS is not set +CONFIG_JBD=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +CONFIG_INOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_QUOTA is not set +CONFIG_DNOTIFY=y +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +CONFIG_FAT_FS=y +# CONFIG_MSDOS_FS is not set +CONFIG_VFAT_FS=y +CONFIG_FAT_DEFAULT_CODEPAGE=437 +CONFIG_FAT_DEFAULT_IOCHARSET="iso8859-1" +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_WRITEBUFFER=y +# CONFIG_JFFS2_FS_WBUF_VERIFY is not set +CONFIG_JFFS2_SUMMARY=y +# CONFIG_JFFS2_FS_XATTR is not set +# CONFIG_JFFS2_SYSFS is not set +CONFIG_JFFS2_COMPRESSION_OPTIONS=y +CONFIG_JFFS2_ZLIB=y +CONFIG_JFFS2_LZO=y +CONFIG_JFFS2_RTIME=y +CONFIG_JFFS2_RUBIN=y +# CONFIG_JFFS2_CMODE_NONE is not set +CONFIG_JFFS2_CMODE_PRIORITY=y +# CONFIG_JFFS2_CMODE_SIZE is not set +# CONFIG_JFFS2_CMODE_FAVOURLZO is not set +CONFIG_CRAMFS=m +CONFIG_SQUASHFS=m +# CONFIG_SQUASHFS_EMBEDDED is not set +CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE=3 +# 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 + +# +# 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_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_KARMA_PARTITION is not set +# CONFIG_EFI_PARTITION is not set +# CONFIG_SYSV68_PARTITION is not set +CONFIG_NLS=y +CONFIG_NLS_DEFAULT="cp437" +CONFIG_NLS_CODEPAGE_437=y +CONFIG_NLS_CODEPAGE_737=m +CONFIG_NLS_CODEPAGE_775=m +CONFIG_NLS_CODEPAGE_850=m +CONFIG_NLS_CODEPAGE_852=m +CONFIG_NLS_CODEPAGE_855=m +CONFIG_NLS_CODEPAGE_857=m +CONFIG_NLS_CODEPAGE_860=m +CONFIG_NLS_CODEPAGE_861=m +CONFIG_NLS_CODEPAGE_862=m +CONFIG_NLS_CODEPAGE_863=m +CONFIG_NLS_CODEPAGE_864=m +CONFIG_NLS_CODEPAGE_865=m +CONFIG_NLS_CODEPAGE_866=m +CONFIG_NLS_CODEPAGE_869=m +CONFIG_NLS_CODEPAGE_936=m +CONFIG_NLS_CODEPAGE_950=m +CONFIG_NLS_CODEPAGE_932=m +CONFIG_NLS_CODEPAGE_949=m +CONFIG_NLS_CODEPAGE_874=m +CONFIG_NLS_ISO8859_8=m +CONFIG_NLS_CODEPAGE_1250=m +CONFIG_NLS_CODEPAGE_1251=m +CONFIG_NLS_ASCII=m +CONFIG_NLS_ISO8859_1=y +CONFIG_NLS_ISO8859_2=m +CONFIG_NLS_ISO8859_3=m +CONFIG_NLS_ISO8859_4=m +CONFIG_NLS_ISO8859_5=m +CONFIG_NLS_ISO8859_6=m +CONFIG_NLS_ISO8859_7=m +CONFIG_NLS_ISO8859_9=m +CONFIG_NLS_ISO8859_13=m +CONFIG_NLS_ISO8859_14=m +CONFIG_NLS_ISO8859_15=m +CONFIG_NLS_KOI8_R=m +CONFIG_NLS_KOI8_U=m +CONFIG_NLS_UTF8=y +# CONFIG_INSTRUMENTATION is not set + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_KERNEL is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +CONFIG_FRAME_POINTER=y +# CONFIG_SAMPLES is not set +# CONFIG_DEBUG_USER is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITY_FILE_CAPABILITIES is not set +CONFIG_CRYPTO=y +CONFIG_CRYPTO_ALGAPI=m +CONFIG_CRYPTO_BLKCIPHER=m +CONFIG_CRYPTO_HASH=m +CONFIG_CRYPTO_MANAGER=m +CONFIG_CRYPTO_HMAC=m +# CONFIG_CRYPTO_XCBC is not set +CONFIG_CRYPTO_NULL=m +CONFIG_CRYPTO_MD4=m +CONFIG_CRYPTO_MD5=m +CONFIG_CRYPTO_SHA1=m +CONFIG_CRYPTO_SHA256=m +CONFIG_CRYPTO_SHA512=m +CONFIG_CRYPTO_WP512=m +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_GF128MUL is not set +CONFIG_CRYPTO_ECB=m +CONFIG_CRYPTO_CBC=m +CONFIG_CRYPTO_PCBC=m +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_XTS is not set +# CONFIG_CRYPTO_CRYPTD is not set +CONFIG_CRYPTO_DES=m +# CONFIG_CRYPTO_FCRYPT is not set +CONFIG_CRYPTO_BLOWFISH=m +CONFIG_CRYPTO_TWOFISH=m +CONFIG_CRYPTO_TWOFISH_COMMON=m +CONFIG_CRYPTO_SERPENT=m +CONFIG_CRYPTO_AES=m +CONFIG_CRYPTO_CAST5=m +CONFIG_CRYPTO_CAST6=m +CONFIG_CRYPTO_TEA=m +CONFIG_CRYPTO_ARC4=m +CONFIG_CRYPTO_KHAZAD=m +CONFIG_CRYPTO_ANUBIS=m +# CONFIG_CRYPTO_SEED is not set +CONFIG_CRYPTO_DEFLATE=m +CONFIG_CRYPTO_LZO=m +CONFIG_CRYPTO_MICHAEL_MIC=m +CONFIG_CRYPTO_CRC32C=m +CONFIG_CRYPTO_CAMELLIA=m +CONFIG_CRYPTO_TEST=m +# CONFIG_CRYPTO_AUTHENC is not set +# CONFIG_CRYPTO_HW is not set + +# +# Library routines +# +CONFIG_BITREVERSE=y +CONFIG_CRC_CCITT=y +# CONFIG_CRC16 is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +CONFIG_LIBCRC32C=m +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_PLIST=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/spitz_h_rewrite.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/spitz_h_rewrite.patch new file mode 100644 index 0000000000..df6d5f66c2 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/spitz_h_rewrite.patch @@ -0,0 +1,497 @@ +http://www.uwsg.indiana.edu/hypermail/linux/kernel/0802.1/3541.html + +Here is a rewrite of spitz.h, which includes comments documenting +function of particular GPIO pins. + +spitz_h_rewrite.patch provides: +- no changes in compiled code +- partial spitz.h rewrite: + * organized by function + * describes complete GPIO pinout + * comments added + * removed defines cloning pxa-regs.h +- prefer generic pxa-regs.h GPIO if available +- use GPIO names instead of numbers + +Thanks to Trisoft for providing needed information. + +Index: linux-2.6.24/arch/arm/mach-pxa/spitz_pm.c +=================================================================== +--- linux-2.6.24.orig/arch/arm/mach-pxa/spitz_pm.c 2008-01-24 22:58:37.000000000 +0000 ++++ linux-2.6.24/arch/arm/mach-pxa/spitz_pm.c 2008-02-13 13:49:22.000000000 +0000 +@@ -110,9 +110,9 @@ + pxa_gpio_mode(GPIO18_RDY|GPIO_OUT | GPIO_DFLT_HIGH); + + PRER = GPIO_bit(SPITZ_GPIO_KEY_INT); +- PFER = GPIO_bit(SPITZ_GPIO_KEY_INT) | GPIO_bit(SPITZ_GPIO_RESET); +- PWER = GPIO_bit(SPITZ_GPIO_KEY_INT) | GPIO_bit(SPITZ_GPIO_RESET) | PWER_RTC; +- PKWR = GPIO_bit(SPITZ_GPIO_SYNC) | GPIO_bit(SPITZ_GPIO_KEY_INT) | GPIO_bit(SPITZ_GPIO_RESET); ++ PFER = GPIO_bit(SPITZ_GPIO_KEY_INT) | GPIO_bit(GPIO1_RST); ++ PWER = GPIO_bit(SPITZ_GPIO_KEY_INT) | GPIO_bit(GPIO1_RST) | PWER_RTC; ++ PKWR = GPIO_bit(SPITZ_GPIO_SYNC) | GPIO_bit(SPITZ_GPIO_KEY_INT) | GPIO_bit(GPIO1_RST); + PKSR = 0xffffffff; // clear + + /* nRESET_OUT Disable */ +@@ -128,7 +128,7 @@ + static void spitz_postsuspend(void) + { + pxa_gpio_mode(GPIO18_RDY_MD); +- pxa_gpio_mode(10 | GPIO_IN); ++ pxa_gpio_mode(SPITZ_GPIO_NC_10 | GPIO_IN); + } + + static int spitz_should_wakeup(unsigned int resume_on_alarm) +Index: linux-2.6.24/drivers/video/pxafb.c +=================================================================== +--- linux-2.6.24.orig/drivers/video/pxafb.c 2008-02-13 13:49:04.000000000 +0000 ++++ linux-2.6.24/drivers/video/pxafb.c 2008-02-13 13:49:57.000000000 +0000 +@@ -920,7 +920,7 @@ + return; + } + +- for (gpio = 58; ldd_bits > 0; gpio++, ldd_bits--) { ++ for (gpio = GPIO58_LDD_0; ldd_bits > 0; gpio++, ldd_bits--) { + pxa_gpio_mode(gpio | GPIO_ALT_FN_2_OUT); + } + pxa_gpio_mode(GPIO74_LCD_FCLK_MD); +Index: linux-2.6.24/include/asm-arm/arch-pxa/akita.h +=================================================================== +--- linux-2.6.24.orig/include/asm-arm/arch-pxa/akita.h 2008-01-24 22:58:37.000000000 +0000 ++++ linux-2.6.24/include/asm-arm/arch-pxa/akita.h 2008-02-13 13:49:22.000000000 +0000 +@@ -12,11 +12,11 @@ + /* Akita IO Expander GPIOs */ + + #define AKITA_IOEXP_RESERVED_7 (1 << 7) +-#define AKITA_IOEXP_IR_ON (1 << 6) +-#define AKITA_IOEXP_AKIN_PULLUP (1 << 5) +-#define AKITA_IOEXP_BACKLIGHT_CONT (1 << 4) +-#define AKITA_IOEXP_BACKLIGHT_ON (1 << 3) +-#define AKITA_IOEXP_MIC_BIAS (1 << 2) ++#define AKITA_IOEXP_IR_ON (1 << 6) /* IrDA On */ ++#define AKITA_IOEXP_AKIN_PULLUP (1 << 5) /* Pull-Up for Remote */ ++#define AKITA_IOEXP_BACKLIGHT_CONT (1 << 4) /* Backlight Control */ ++#define AKITA_IOEXP_BACKLIGHT_ON (1 << 3) /* Backlight On */ ++#define AKITA_IOEXP_MIC_BIAS (1 << 2) /* Mic Bias On */ + #define AKITA_IOEXP_RESERVED_1 (1 << 1) + #define AKITA_IOEXP_RESERVED_0 (1 << 0) + +Index: linux-2.6.24/include/asm-arm/arch-pxa/pxa-regs.h +=================================================================== +--- linux-2.6.24.orig/include/asm-arm/arch-pxa/pxa-regs.h 2008-02-13 13:49:07.000000000 +0000 ++++ linux-2.6.24/include/asm-arm/arch-pxa/pxa-regs.h 2008-02-13 13:49:22.000000000 +0000 +@@ -1334,6 +1334,7 @@ + #define GPIO85_nPCE_1 85 /* Card Enable for Card Space (PXA27x) */ + #define GPIO92_MMCDAT0 92 /* MMC DAT0 (PXA27x) */ + #define GPIO102_nPCE_1 102 /* PCMCIA (PXA27x) */ ++#define GPIO104_pSKTSEL 104 /* PCMCIA Socket Select (PXA27x) */ + #define GPIO109_MMCDAT1 109 /* MMC DAT1 (PXA27x) */ + #define GPIO110_MMCDAT2 110 /* MMC DAT2 (PXA27x) */ + #define GPIO110_MMCCS0 110 /* MMC Chip Select 0 (PXA27x) */ +Index: linux-2.6.24/include/asm-arm/arch-pxa/spitz.h +=================================================================== +--- linux-2.6.24.orig/include/asm-arm/arch-pxa/spitz.h 2008-02-13 13:49:01.000000000 +0000 ++++ linux-2.6.24/include/asm-arm/arch-pxa/spitz.h 2008-02-13 13:49:22.000000000 +0000 +@@ -1,8 +1,9 @@ + /* +- * Hardware specific definitions for SL-Cx000 series of PDAs ++ * Hardware specific definitions for SL-Cxx00 series of PDAs + * + * Copyright (c) 2005 Alexander Wykes + * Copyright (c) 2005 Richard Purdie ++ * Copyright (c) 2008 Stanislav Brabec + * + * Based on Sharp's 2.4 kernel patches + * +@@ -13,140 +14,257 @@ + */ + #ifndef __ASM_ARCH_SPITZ_H + #define __ASM_ARCH_SPITZ_H 1 +-#endif + +-#include <linux/fb.h> ++#include <asm-arm/arch-pxa/irqs.h> ++#include <linux/platform_device.h> + + /* Spitz/Akita GPIOs */ + +-#define SPITZ_GPIO_KEY_INT (0) /* Key Interrupt */ +-#define SPITZ_GPIO_RESET (1) +-#define SPITZ_GPIO_nSD_DETECT (9) +-#define SPITZ_GPIO_TP_INT (11) /* Touch Panel interrupt */ +-#define SPITZ_GPIO_AK_INT (13) /* Remote Control */ +-#define SPITZ_GPIO_ADS7846_CS (14) +-#define SPITZ_GPIO_SYNC (16) +-#define SPITZ_GPIO_MAX1111_CS (20) +-#define SPITZ_GPIO_FATAL_BAT (21) +-#define SPITZ_GPIO_HSYNC (22) +-#define SPITZ_GPIO_nSD_CLK (32) +-#define SPITZ_GPIO_USB_DEVICE (35) +-#define SPITZ_GPIO_USB_HOST (37) +-#define SPITZ_GPIO_USB_CONNECT (41) +-#define SPITZ_GPIO_LCDCON_CS (53) +-#define SPITZ_GPIO_nPCE (54) +-#define SPITZ_GPIO_nSD_WP (81) +-#define SPITZ_GPIO_ON_RESET (89) +-#define SPITZ_GPIO_BAT_COVER (90) +-#define SPITZ_GPIO_CF_CD (94) +-#define SPITZ_GPIO_ON_KEY (95) +-#define SPITZ_GPIO_SWA (97) +-#define SPITZ_GPIO_SWB (96) +-#define SPITZ_GPIO_CHRG_FULL (101) +-#define SPITZ_GPIO_CO (101) +-#define SPITZ_GPIO_CF_IRQ (105) +-#define SPITZ_GPIO_AC_IN (115) +-#define SPITZ_GPIO_HP_IN (116) ++/* This list refers to all GPIO pins either in defines or in comments. ++ * ++ * GPIO pins not listed: ++ * GPIO2 SYS_EN: System Power Enable ++ * GPIO5-GPIO8 PWR_CAP0-PWR_CAP3: sleep DC-DC converter power capacitors ++ * GPIO40 not connected ++ */ + +-/* Spitz Only GPIOs */ + +-#define SPITZ_GPIO_CF2_IRQ (106) /* CF slot1 Ready */ +-#define SPITZ_GPIO_CF2_CD (93) ++/* Spitz/Akita System GPIO */ ++ ++#define SPITZ_GPIO_KEY_INT (0) /* Key Interrupt */ ++#define SPITZ_GPIO_SYNC (16) /* IOPORT Wake Up (input) */ ++#define SPITZ_GPIO_NAND_CS (79) /* NAND Flash Chip Select */ ++#define SPITZ_GPIO_NC_10 (10) /* Not Connected (but used in kernel) */ ++/* This GPIO pin is connected: ++ * GPIO1_RST ++ */ + + ++/* Compact Flash Interface */ ++ ++/* Spitz/Akita Compact Flash Interface */ ++#define SPITZ_GPIO_CF_CD (94) /* CF IRQ */ ++#define SPITZ_GPIO_CF_IRQ (105) /* CF Ready */ ++/* These GPIO pins are connected: ++ * GPIO48_nPOE ++ * GPIO49_nPWE ++ * GPIO50_nPIOR ++ * GPIO51_nPIOW ++ * GPIO54_nPCE_2 ++ * GPIO55_nPREG ++ * GPIO56_nPWAIT ++ * GPIO57_nIOIS16 ++ * GPIO80_nCS_4 ++ * GPIO85_nPCE_1 ++ * GPIO104_pSKTSEL ++ */ ++ ++/* Spitz only Compact Flash Interface */ ++#define SPITZ_GPIO_CF2_CD (93) /* CF slot1 IRQ */ ++#define SPITZ_GPIO_CF2_IRQ (106) /* CF slot1 Ready */ ++/* This GPIO pin is connected: ++ * GPIO78_nCS_2 ++ */ ++ ++ ++/* Spitz/Akita Battery, Power and Service Connector */ ++ ++#define SPITZ_GPIO_FATAL_BAT (21) /* Fatal Battery */ ++#define SPITZ_GPIO_BAT_COVER (90) /* Battery Cover switch */ ++#define SPITZ_GPIO_BAT_COVER2 (15) /* Battery Cover switch, parallel pin */ ++#define SPITZ_GPIO_CHRG_FULL (101) /* Battery Full */ ++#define SPITZ_GPIO_AC_IN (115) /* External Power Supply is active */ ++#define SPITZ_GPIO_ON_RESET (89) /* Software Reset */ ++#define SPITZ_GPIO_SERVICE0 (83) /* Service Connector */ ++#define SPITZ_GPIO_SERVICE1 (84) /* Service Connector */ ++/* This GPIO pin is connected: ++ * GPIO18_RDY ++ */ ++ ++ ++/* Spitz/Akita Display Controller */ ++ ++#define SPITZ_GPIO_HSYNC (22) /* Line Sync Feedback */ ++/* These GPIO pins are connected: ++ * GPIO58_LDD_0-GPIO58_LDD_15 ++ * GPIO74_LCD_FCLK ++ * GPIO75_LCD_LCLK ++ * GPIO76_LCD_PCLK ++ * GPIO77_LCD_ACBIAS ++ */ ++ ++ ++/* Spitz/Akita SSP/SPI Bus and Devices */ ++ ++#define SPITZ_GPIO_SSP_CLK (19) /* SSP bus Clock */ ++#define SPITZ_GPIO_SSP_RXD (86) /* SSP bus RxD */ ++#define SPITZ_GPIO_SSP_TXD (87) /* SSP bus TxD */ ++#define SPITZ_GPIO_TP_INT (11) /* Touch Panel IRQ */ ++#define SPITZ_GPIO_ADS7846_CS (14) /* Touch Panel Controller Chip Select */ ++#define SPITZ_GPIO_MAX1111_CS (20) /* Multi Channel ADC Chip Select */ ++#define SPITZ_GPIO_LCDCON_CS (53) /* LCD Controller Chip Select */ ++ ++ ++/* Spitz/Akita Supplementary USB OTG Pins */ ++ ++#define SPITZ_GPIO_USB_DEVICE (35) /* USB Client power is present */ ++#define SPITZ_GPIO_USB_HOST (37) /* USB OTG 5V Host power supply control */ ++#define SPITZ_GPIO_USB_CONNECT (41) /* USB Host Cable is connected */ ++ ++ ++/* Spitz/Akita Audio */ ++ ++#define SPITZ_GPIO_HP_IN (116) /* CPU Headphone detect */ ++#define SPITZ_GPIO_AK_INT (13) /* Remote Control detect */ ++/* These GPIO AC97 pins are connected: ++ * GPIO28_BITCLK ++ * GPIO29_SDATA_IN ++ * GPIO30_SDATA_OUT ++ * GPIO31_SYNC ++ * GPIO113_AC97_RESET_N ++ */ ++ ++ ++/* Spitz/Akita SD Slot */ ++ ++#define SPITZ_GPIO_nSD_DETECT (9) /* SD Card Presence */ ++#define SPITZ_GPIO_nSD_WP (81) /* SD Write Protection */ ++/* These GPIO pins are connected: ++ * GPIO32_MMCCLK ++ * GPIO92_MMCDAT0 ++ * GPIO109_MMCDAT1 ++ * GPIO110_MMCDAT2 ++ * GPIO111_MMCDAT3 ++ * GPIO112_MMCCMD ++ */ ++ ++/* Spitz/Akita I2C bus */ ++#define SPITZ_GPIO_SCL (117) /* I2C SCL */ ++#define SPITZ_GPIO_SDA (118) /* I2C SDA */ ++#define SPITZ_GPIO_PWR_SCL (3) /* I2C SCL power */ ++#define SPITZ_GPIO_PWR_SDA (4) /* I2C SDA power */ ++ ++/* audio codec pins */ ++ ++ ++/* Spitz/Akita UART ports */ ++ ++/* Fully Featured UART - connected to IOPORT connector */ ++#define SPITZ_GPIO_FFRXD (102) /* IOPORT has nRXD inverted levels */ ++#define SPITZ_GPIO_FFTXD (99) /* IOPORT has nTXD inverted levels */ ++#define SPITZ_GPIO_FFRTS (98) ++#define SPITZ_GPIO_FFCTS (100) ++#define SPITZ_GPIO_FFDTR (82) ++#define SPITZ_GPIO_FFDSR (33) ++ ++/* These UART GPIO pins are connected to Bluetooth ++ * (only on Akita version with Bluetooth) ++ * GPIO42_BTRXD ++ * GPIO43_BTTXD ++ * GPIO44_BTCTS ++ * GPIO45_BTRTS ++ */ ++ ++/* These UART GPIO pins are connected to IrDA: ++ * GPIO46_STRXD ++ * GPIO47_STTXD ++ */ ++ + /* Spitz/Akita Keyboard Definitions */ + +-#define SPITZ_KEY_STROBE_NUM (11) +-#define SPITZ_KEY_SENSE_NUM (7) +-#define SPITZ_GPIO_G0_STROBE_BIT 0x0f800000 +-#define SPITZ_GPIO_G1_STROBE_BIT 0x00100000 +-#define SPITZ_GPIO_G2_STROBE_BIT 0x01000000 +-#define SPITZ_GPIO_G3_STROBE_BIT 0x00041880 +-#define SPITZ_GPIO_G0_SENSE_BIT 0x00021000 +-#define SPITZ_GPIO_G1_SENSE_BIT 0x000000d4 +-#define SPITZ_GPIO_G2_SENSE_BIT 0x08000000 +-#define SPITZ_GPIO_G3_SENSE_BIT 0x00000000 +- +-#define SPITZ_GPIO_KEY_STROBE0 88 +-#define SPITZ_GPIO_KEY_STROBE1 23 +-#define SPITZ_GPIO_KEY_STROBE2 24 +-#define SPITZ_GPIO_KEY_STROBE3 25 +-#define SPITZ_GPIO_KEY_STROBE4 26 +-#define SPITZ_GPIO_KEY_STROBE5 27 +-#define SPITZ_GPIO_KEY_STROBE6 52 +-#define SPITZ_GPIO_KEY_STROBE7 103 +-#define SPITZ_GPIO_KEY_STROBE8 107 +-#define SPITZ_GPIO_KEY_STROBE9 108 +-#define SPITZ_GPIO_KEY_STROBE10 114 +- +-#define SPITZ_GPIO_KEY_SENSE0 12 +-#define SPITZ_GPIO_KEY_SENSE1 17 +-#define SPITZ_GPIO_KEY_SENSE2 91 +-#define SPITZ_GPIO_KEY_SENSE3 34 +-#define SPITZ_GPIO_KEY_SENSE4 36 +-#define SPITZ_GPIO_KEY_SENSE5 38 +-#define SPITZ_GPIO_KEY_SENSE6 39 ++#define SPITZ_KEY_STROBE_NUM (11) ++#define SPITZ_KEY_SENSE_NUM (7) ++#define SPITZ_GPIO_G0_STROBE_BIT 0x0f800000 ++#define SPITZ_GPIO_G1_STROBE_BIT 0x00100000 ++#define SPITZ_GPIO_G2_STROBE_BIT 0x01000000 ++#define SPITZ_GPIO_G3_STROBE_BIT 0x00041880 ++#define SPITZ_GPIO_G0_SENSE_BIT 0x00021000 ++#define SPITZ_GPIO_G1_SENSE_BIT 0x000000d4 ++#define SPITZ_GPIO_G2_SENSE_BIT 0x08000000 ++#define SPITZ_GPIO_G3_SENSE_BIT 0x00000000 ++#define SPITZ_GPIO_KEY_STROBE0 (88) ++#define SPITZ_GPIO_KEY_STROBE1 (23) ++#define SPITZ_GPIO_KEY_STROBE2 (24) ++#define SPITZ_GPIO_KEY_STROBE3 (25) ++#define SPITZ_GPIO_KEY_STROBE4 (26) ++#define SPITZ_GPIO_KEY_STROBE5 (27) ++#define SPITZ_GPIO_KEY_STROBE6 (52) ++#define SPITZ_GPIO_KEY_STROBE7 (103) ++#define SPITZ_GPIO_KEY_STROBE8 (107) ++#define SPITZ_GPIO_KEY_STROBE9 (108) ++#define SPITZ_GPIO_KEY_STROBE10 (114) ++#define SPITZ_GPIO_KEY_SENSE0 (12) ++#define SPITZ_GPIO_KEY_SENSE1 (17) ++#define SPITZ_GPIO_KEY_SENSE2 (91) ++#define SPITZ_GPIO_KEY_SENSE3 (34) ++#define SPITZ_GPIO_KEY_SENSE4 (36) ++#define SPITZ_GPIO_KEY_SENSE5 (38) ++#define SPITZ_GPIO_KEY_SENSE6 (39) ++ ++#define SPITZ_GPIO_SWA (97) /* Keyboard Interrupt A */ ++#define SPITZ_GPIO_SWB (96) /* Keyboard Interrupt B */ ++#define SPITZ_GPIO_ON_KEY (95) /* Power On Key */ + + +-/* Spitz Scoop Device (No. 1) GPIOs */ ++/* Spitz/Akita Scoop Device (No. 1) GPIOs */ + /* Suspend States in comments */ +-#define SPITZ_SCP_LED_GREEN SCOOP_GPCR_PA11 /* Keep */ +-#define SPITZ_SCP_JK_B SCOOP_GPCR_PA12 /* Keep */ +-#define SPITZ_SCP_CHRG_ON SCOOP_GPCR_PA13 /* Keep */ +-#define SPITZ_SCP_MUTE_L SCOOP_GPCR_PA14 /* Low */ +-#define SPITZ_SCP_MUTE_R SCOOP_GPCR_PA15 /* Low */ +-#define SPITZ_SCP_CF_POWER SCOOP_GPCR_PA16 /* Keep */ +-#define SPITZ_SCP_LED_ORANGE SCOOP_GPCR_PA17 /* Keep */ +-#define SPITZ_SCP_JK_A SCOOP_GPCR_PA18 /* Low */ +-#define SPITZ_SCP_ADC_TEMP_ON SCOOP_GPCR_PA19 /* Low */ ++#define SPITZ_SCP_LED_GREEN SCOOP_GPCR_PA11 /* Green LED, Keep */ ++#define SPITZ_SCP_JK_B SCOOP_GPCR_PA12 /* Fast Charge On, Keep */ ++#define SPITZ_SCP_CHRG_ON SCOOP_GPCR_PA13 /* Charge On, Keep */ ++#define SPITZ_SCP_MUTE_L SCOOP_GPCR_PA14 /* Extra Mute Left, Low */ ++#define SPITZ_SCP_MUTE_R SCOOP_GPCR_PA15 /* Extra Mute Right, Low */ ++#define SPITZ_SCP_CF_POWER SCOOP_GPCR_PA16 /* CF+SD Power Circuit, Keep */ ++#define SPITZ_SCP_LED_ORANGE SCOOP_GPCR_PA17 /* Orange LED, Keep */ ++#define SPITZ_SCP_JK_A SCOOP_GPCR_PA18 /* Dummy Load, Low */ ++#define SPITZ_SCP_ADC_TEMP_ON SCOOP_GPCR_PA19 /* Battery Sensor On, Low */ + + #define SPITZ_SCP_IO_DIR (SPITZ_SCP_LED_GREEN | SPITZ_SCP_JK_B | SPITZ_SCP_CHRG_ON | \ +- SPITZ_SCP_MUTE_L | SPITZ_SCP_MUTE_R | SPITZ_SCP_LED_ORANGE | \ +- SPITZ_SCP_CF_POWER | SPITZ_SCP_JK_A | SPITZ_SCP_ADC_TEMP_ON) ++ SPITZ_SCP_MUTE_L | SPITZ_SCP_MUTE_R | SPITZ_SCP_LED_ORANGE | \ ++ SPITZ_SCP_CF_POWER | SPITZ_SCP_JK_A | SPITZ_SCP_ADC_TEMP_ON) + #define SPITZ_SCP_IO_OUT (SPITZ_SCP_CHRG_ON | SPITZ_SCP_MUTE_L | SPITZ_SCP_MUTE_R) + #define SPITZ_SCP_SUS_CLR (SPITZ_SCP_MUTE_L | SPITZ_SCP_MUTE_R | SPITZ_SCP_JK_A | SPITZ_SCP_ADC_TEMP_ON) + #define SPITZ_SCP_SUS_SET 0 + + /* Spitz Scoop Device (No. 2) GPIOs */ +-/* Suspend States in comments */ +-#define SPITZ_SCP2_IR_ON SCOOP_GPCR_PA11 /* High */ +-#define SPITZ_SCP2_AKIN_PULLUP SCOOP_GPCR_PA12 /* Keep */ +-#define SPITZ_SCP2_RESERVED_1 SCOOP_GPCR_PA13 /* High */ +-#define SPITZ_SCP2_RESERVED_2 SCOOP_GPCR_PA14 /* Low */ +-#define SPITZ_SCP2_RESERVED_3 SCOOP_GPCR_PA15 /* Low */ +-#define SPITZ_SCP2_RESERVED_4 SCOOP_GPCR_PA16 /* Low */ +-#define SPITZ_SCP2_BACKLIGHT_CONT SCOOP_GPCR_PA17 /* Low */ +-#define SPITZ_SCP2_BACKLIGHT_ON SCOOP_GPCR_PA18 /* Low */ +-#define SPITZ_SCP2_MIC_BIAS SCOOP_GPCR_PA19 /* Low */ ++/* Suspend States in comments ++ * Spitz only, Akita uses corresponding AKITA_IOEXP_ */ ++#define SPITZ_SCP2_IR_ON SCOOP_GPCR_PA11 /* IrDA On, High */ ++#define SPITZ_SCP2_AKIN_PULLUP SCOOP_GPCR_PA12 /* Pull-Up for Remote, Keep */ ++#define SPITZ_SCP2_RESERVED_1 SCOOP_GPCR_PA13 /* High */ ++#define SPITZ_SCP2_RESERVED_2 SCOOP_GPCR_PA14 /* Low */ ++#define SPITZ_SCP2_RESERVED_3 SCOOP_GPCR_PA15 /* Low */ ++#define SPITZ_SCP2_RESERVED_4 SCOOP_GPCR_PA16 /* Low */ ++#define SPITZ_SCP2_BACKLIGHT_CONT SCOOP_GPCR_PA17 /* Backlight Control, Low */ ++#define SPITZ_SCP2_BACKLIGHT_ON SCOOP_GPCR_PA18 /* Backlight On, Low */ ++#define SPITZ_SCP2_MIC_BIAS SCOOP_GPCR_PA19 /* Mic Bias On, Low */ + + #define SPITZ_SCP2_IO_DIR (SPITZ_SCP2_IR_ON | SPITZ_SCP2_AKIN_PULLUP | SPITZ_SCP2_RESERVED_1 | \ +- SPITZ_SCP2_RESERVED_2 | SPITZ_SCP2_RESERVED_3 | SPITZ_SCP2_RESERVED_4 | \ +- SPITZ_SCP2_BACKLIGHT_CONT | SPITZ_SCP2_BACKLIGHT_ON | SPITZ_SCP2_MIC_BIAS) ++ SPITZ_SCP2_RESERVED_2 | SPITZ_SCP2_RESERVED_3 | SPITZ_SCP2_RESERVED_4 | \ ++ SPITZ_SCP2_BACKLIGHT_CONT | SPITZ_SCP2_BACKLIGHT_ON | SPITZ_SCP2_MIC_BIAS) + + #define SPITZ_SCP2_IO_OUT (SPITZ_SCP2_IR_ON | SPITZ_SCP2_AKIN_PULLUP | SPITZ_SCP2_RESERVED_1) + #define SPITZ_SCP2_SUS_CLR (SPITZ_SCP2_RESERVED_2 | SPITZ_SCP2_RESERVED_3 | SPITZ_SCP2_RESERVED_4 | \ +- SPITZ_SCP2_BACKLIGHT_CONT | SPITZ_SCP2_BACKLIGHT_ON | SPITZ_SCP2_MIC_BIAS) ++ SPITZ_SCP2_BACKLIGHT_CONT | SPITZ_SCP2_BACKLIGHT_ON | SPITZ_SCP2_MIC_BIAS) + #define SPITZ_SCP2_SUS_SET (SPITZ_SCP2_IR_ON | SPITZ_SCP2_RESERVED_1) + + +-/* Spitz IRQ Definitions */ ++/* Spitz/Akita IRQ Definitions */ + +-#define SPITZ_IRQ_GPIO_KEY_INT IRQ_GPIO(SPITZ_GPIO_KEY_INT) +-#define SPITZ_IRQ_GPIO_AC_IN IRQ_GPIO(SPITZ_GPIO_AC_IN) +-#define SPITZ_IRQ_GPIO_AK_INT IRQ_GPIO(SPITZ_GPIO_AK_INT) +-#define SPITZ_IRQ_GPIO_HP_IN IRQ_GPIO(SPITZ_GPIO_HP_IN) +-#define SPITZ_IRQ_GPIO_TP_INT IRQ_GPIO(SPITZ_GPIO_TP_INT) +-#define SPITZ_IRQ_GPIO_SYNC IRQ_GPIO(SPITZ_GPIO_SYNC) +-#define SPITZ_IRQ_GPIO_ON_KEY IRQ_GPIO(SPITZ_GPIO_ON_KEY) +-#define SPITZ_IRQ_GPIO_SWA IRQ_GPIO(SPITZ_GPIO_SWA) +-#define SPITZ_IRQ_GPIO_SWB IRQ_GPIO(SPITZ_GPIO_SWB) ++#define SPITZ_IRQ_GPIO_KEY_INT IRQ_GPIO(SPITZ_GPIO_KEY_INT) ++#define SPITZ_IRQ_GPIO_AC_IN IRQ_GPIO(SPITZ_GPIO_AC_IN) ++#define SPITZ_IRQ_GPIO_AK_INT IRQ_GPIO(SPITZ_GPIO_AK_INT) ++#define SPITZ_IRQ_GPIO_HP_IN IRQ_GPIO(SPITZ_GPIO_HP_IN) ++#define SPITZ_IRQ_GPIO_TP_INT IRQ_GPIO(SPITZ_GPIO_TP_INT) ++#define SPITZ_IRQ_GPIO_SYNC IRQ_GPIO(SPITZ_GPIO_SYNC) ++#define SPITZ_IRQ_GPIO_ON_KEY IRQ_GPIO(SPITZ_GPIO_ON_KEY) ++#define SPITZ_IRQ_GPIO_SWA IRQ_GPIO(SPITZ_GPIO_SWA) ++#define SPITZ_IRQ_GPIO_SWB IRQ_GPIO(SPITZ_GPIO_SWB) + #define SPITZ_IRQ_GPIO_BAT_COVER IRQ_GPIO(SPITZ_GPIO_BAT_COVER) + #define SPITZ_IRQ_GPIO_FATAL_BAT IRQ_GPIO(SPITZ_GPIO_FATAL_BAT) +-#define SPITZ_IRQ_GPIO_CO IRQ_GPIO(SPITZ_GPIO_CO) +-#define SPITZ_IRQ_GPIO_CF_IRQ IRQ_GPIO(SPITZ_GPIO_CF_IRQ) +-#define SPITZ_IRQ_GPIO_CF_CD IRQ_GPIO(SPITZ_GPIO_CF_CD) +-#define SPITZ_IRQ_GPIO_CF2_IRQ IRQ_GPIO(SPITZ_GPIO_CF2_IRQ) +-#define SPITZ_IRQ_GPIO_nSD_INT IRQ_GPIO(SPITZ_GPIO_nSD_INT) ++#define SPITZ_IRQ_GPIO_CF_IRQ IRQ_GPIO(SPITZ_GPIO_CF_IRQ) ++#define SPITZ_IRQ_GPIO_CF_CD IRQ_GPIO(SPITZ_GPIO_CF_CD) ++#define SPITZ_IRQ_GPIO_CF2_IRQ IRQ_GPIO(SPITZ_GPIO_CF2_IRQ) ++#define SPITZ_IRQ_GPIO_nSD_INT IRQ_GPIO(SPITZ_GPIO_nSD_INT) + #define SPITZ_IRQ_GPIO_nSD_DETECT IRQ_GPIO(SPITZ_GPIO_nSD_DETECT) + + /* +@@ -156,3 +274,5 @@ + extern struct platform_device spitzscoop2_device; + extern struct platform_device spitzssp_device; + extern struct sharpsl_charger_machinfo spitz_pm_machinfo; ++ ++#endif +Index: linux-2.6.24/sound/arm/pxa2xx-ac97.c +=================================================================== +--- linux-2.6.24.orig/sound/arm/pxa2xx-ac97.c 2008-01-24 22:58:37.000000000 +0000 ++++ linux-2.6.24/sound/arm/pxa2xx-ac97.c 2008-02-13 13:49:22.000000000 +0000 +@@ -133,10 +133,10 @@ + #ifdef CONFIG_PXA27x + /* warm reset broken on Bulverde, + so manually keep AC97 reset high */ +- pxa_gpio_mode(113 | GPIO_OUT | GPIO_DFLT_HIGH); ++ pxa_gpio_mode(GPIO113_AC97_RESET_N | GPIO_OUT | GPIO_DFLT_HIGH); + udelay(10); + GCR |= GCR_WARM_RST; +- pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT); ++ pxa_gpio_mode(GPIO113_AC97_RESET_N_MD); + udelay(500); + #else + GCR |= GCR_WARM_RST|GCR_PRIRDY_IEN|GCR_SECRDY_IEN; +@@ -335,7 +335,7 @@ + pxa_gpio_mode(GPIO29_SDATA_IN_AC97_MD); + #ifdef CONFIG_PXA27x + /* Use GPIO 113 as AC97 Reset on Bulverde */ +- pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT); ++ pxa_gpio_mode(GPIO113_AC97_RESET_N_MD); + #endif + pxa_set_cken(CKEN_AC97, 1); + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/squashfs3.3.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/squashfs3.3.patch new file mode 100644 index 0000000000..cb9a5c49e4 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/squashfs3.3.patch @@ -0,0 +1,4234 @@ +diff -x .gitignore -Nurp linux-2.6.24/fs/Kconfig linux-2.6.24-squashfs3.3/fs/Kconfig +--- linux-2.6.24/fs/Kconfig 2007-10-25 17:41:45.000000000 +0100 ++++ linux-2.6.24-squashfs3.3/fs/Kconfig 2007-11-01 05:06:25.000000000 +0000 +@@ -1396,6 +1396,56 @@ config CRAMFS + + If unsure, say N. + ++config SQUASHFS ++ tristate "SquashFS 3.3 - Squashed file system support" ++ select ZLIB_INFLATE ++ help ++ Saying Y here includes support for SquashFS 3.3 (a Compressed ++ Read-Only File System). Squashfs is a highly compressed read-only ++ filesystem for Linux. It uses zlib compression to compress both ++ files, inodes and directories. Inodes in the system are very small ++ and all blocks are packed to minimise data overhead. Block sizes ++ greater than 4K are supported up to a maximum of 1 Mbytes (default ++ block size 128K). SquashFS 3.3 supports 64 bit filesystems and files ++ (larger than 4GB), full uid/gid information, hard links and timestamps. ++ ++ Squashfs is intended for general read-only filesystem use, for ++ archival use (i.e. in cases where a .tar.gz file may be used), and in ++ embedded systems where low overhead is needed. Further information ++ and filesystem tools are available from http://squashfs.sourceforge.net. ++ ++ If you want to compile this as a module ( = code which can be ++ inserted in and removed from the running kernel whenever you want), ++ say M here and read <file:Documentation/modules.txt>. The module ++ will be called squashfs. Note that the root file system (the one ++ containing the directory /) cannot be compiled as a module. ++ ++ If unsure, say N. ++ ++config SQUASHFS_EMBEDDED ++ ++ bool "Additional option for memory-constrained systems" ++ depends on SQUASHFS ++ default n ++ help ++ Saying Y here allows you to specify cache size. ++ ++ If unsure, say N. ++ ++config SQUASHFS_FRAGMENT_CACHE_SIZE ++ int "Number of fragments cached" if SQUASHFS_EMBEDDED ++ depends on SQUASHFS ++ default "3" ++ help ++ By default SquashFS caches the last 3 fragments read from ++ the filesystem. Increasing this amount may mean SquashFS ++ has to re-read fragments less often from disk, at the expense ++ of extra system memory. Decreasing this amount will mean ++ SquashFS uses less memory at the expense of extra reads from disk. ++ ++ Note there must be at least one cached fragment. Anything ++ much more than three will probably not make much difference. ++ + config VXFS_FS + tristate "FreeVxFS file system support (VERITAS VxFS(TM) compatible)" + depends on BLOCK +diff -x .gitignore -Nurp linux-2.6.24/fs/Makefile linux-2.6.24-squashfs3.3/fs/Makefile +--- linux-2.6.24/fs/Makefile 2007-10-25 17:41:45.000000000 +0100 ++++ linux-2.6.24-squashfs3.3/fs/Makefile 2007-11-01 05:08:09.000000000 +0000 +@@ -72,6 +72,7 @@ obj-$(CONFIG_JBD) += jbd/ + obj-$(CONFIG_JBD2) += jbd2/ + obj-$(CONFIG_EXT2_FS) += ext2/ + obj-$(CONFIG_CRAMFS) += cramfs/ ++obj-$(CONFIG_SQUASHFS) += squashfs/ + obj-y += ramfs/ + obj-$(CONFIG_HUGETLBFS) += hugetlbfs/ + obj-$(CONFIG_CODA_FS) += coda/ +diff -x .gitignore -Nurp linux-2.6.24/fs/squashfs/inode.c linux-2.6.24-squashfs3.3/fs/squashfs/inode.c +--- linux-2.6.24/fs/squashfs/inode.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24-squashfs3.3/fs/squashfs/inode.c 2007-11-01 05:05:00.000000000 +0000 +@@ -0,0 +1,2192 @@ ++/* ++ * Squashfs - a compressed read only filesystem for Linux ++ * ++ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007 ++ * Phillip Lougher <phillip@lougher.demon.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2, ++ * 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * inode.c ++ */ ++ ++#include <linux/squashfs_fs.h> ++#include <linux/module.h> ++#include <linux/zlib.h> ++#include <linux/fs.h> ++#include <linux/squashfs_fs_sb.h> ++#include <linux/squashfs_fs_i.h> ++#include <linux/buffer_head.h> ++#include <linux/vfs.h> ++#include <linux/vmalloc.h> ++#include <linux/smp_lock.h> ++#include <linux/exportfs.h> ++ ++#include "squashfs.h" ++ ++int squashfs_cached_blks; ++ ++static void vfs_read_inode(struct inode *i); ++static struct dentry *squashfs_get_parent(struct dentry *child); ++static int squashfs_read_inode(struct inode *i, squashfs_inode_t inode); ++static int squashfs_statfs(struct dentry *, struct kstatfs *); ++static int squashfs_symlink_readpage(struct file *file, struct page *page); ++static long long read_blocklist(struct inode *inode, int index, ++ int readahead_blks, char *block_list, ++ unsigned short **block_p, unsigned int *bsize); ++static int squashfs_readpage(struct file *file, struct page *page); ++static int squashfs_readdir(struct file *, void *, filldir_t); ++static struct dentry *squashfs_lookup(struct inode *, struct dentry *, ++ struct nameidata *); ++static int squashfs_remount(struct super_block *s, int *flags, char *data); ++static void squashfs_put_super(struct super_block *); ++static int squashfs_get_sb(struct file_system_type *,int, const char *, void *, ++ struct vfsmount *); ++static struct inode *squashfs_alloc_inode(struct super_block *sb); ++static void squashfs_destroy_inode(struct inode *inode); ++static int init_inodecache(void); ++static void destroy_inodecache(void); ++ ++static struct file_system_type squashfs_fs_type = { ++ .owner = THIS_MODULE, ++ .name = "squashfs", ++ .get_sb = squashfs_get_sb, ++ .kill_sb = kill_block_super, ++ .fs_flags = FS_REQUIRES_DEV ++}; ++ ++static const unsigned char squashfs_filetype_table[] = { ++ DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK ++}; ++ ++static struct super_operations squashfs_super_ops = { ++ .alloc_inode = squashfs_alloc_inode, ++ .destroy_inode = squashfs_destroy_inode, ++ .statfs = squashfs_statfs, ++ .put_super = squashfs_put_super, ++ .remount_fs = squashfs_remount ++}; ++ ++static struct super_operations squashfs_export_super_ops = { ++ .alloc_inode = squashfs_alloc_inode, ++ .destroy_inode = squashfs_destroy_inode, ++ .statfs = squashfs_statfs, ++ .put_super = squashfs_put_super, ++ .read_inode = vfs_read_inode ++}; ++ ++static struct export_operations squashfs_export_ops = { ++ .get_parent = squashfs_get_parent ++}; ++ ++SQSH_EXTERN const struct address_space_operations squashfs_symlink_aops = { ++ .readpage = squashfs_symlink_readpage ++}; ++ ++SQSH_EXTERN const struct address_space_operations squashfs_aops = { ++ .readpage = squashfs_readpage ++}; ++ ++static const struct file_operations squashfs_dir_ops = { ++ .read = generic_read_dir, ++ .readdir = squashfs_readdir ++}; ++ ++SQSH_EXTERN struct inode_operations squashfs_dir_inode_ops = { ++ .lookup = squashfs_lookup ++}; ++ ++ ++static struct buffer_head *get_block_length(struct super_block *s, ++ int *cur_index, int *offset, int *c_byte) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ unsigned short temp; ++ struct buffer_head *bh; ++ ++ if (!(bh = sb_bread(s, *cur_index))) ++ goto out; ++ ++ if (msblk->devblksize - *offset == 1) { ++ if (msblk->swap) ++ ((unsigned char *) &temp)[1] = *((unsigned char *) ++ (bh->b_data + *offset)); ++ else ++ ((unsigned char *) &temp)[0] = *((unsigned char *) ++ (bh->b_data + *offset)); ++ brelse(bh); ++ if (!(bh = sb_bread(s, ++(*cur_index)))) ++ goto out; ++ if (msblk->swap) ++ ((unsigned char *) &temp)[0] = *((unsigned char *) ++ bh->b_data); ++ else ++ ((unsigned char *) &temp)[1] = *((unsigned char *) ++ bh->b_data); ++ *c_byte = temp; ++ *offset = 1; ++ } else { ++ if (msblk->swap) { ++ ((unsigned char *) &temp)[1] = *((unsigned char *) ++ (bh->b_data + *offset)); ++ ((unsigned char *) &temp)[0] = *((unsigned char *) ++ (bh->b_data + *offset + 1)); ++ } else { ++ ((unsigned char *) &temp)[0] = *((unsigned char *) ++ (bh->b_data + *offset)); ++ ((unsigned char *) &temp)[1] = *((unsigned char *) ++ (bh->b_data + *offset + 1)); ++ } ++ *c_byte = temp; ++ *offset += 2; ++ } ++ ++ if (SQUASHFS_CHECK_DATA(msblk->sblk.flags)) { ++ if (*offset == msblk->devblksize) { ++ brelse(bh); ++ if (!(bh = sb_bread(s, ++(*cur_index)))) ++ goto out; ++ *offset = 0; ++ } ++ if (*((unsigned char *) (bh->b_data + *offset)) != ++ SQUASHFS_MARKER_BYTE) { ++ ERROR("Metadata block marker corrupt @ %x\n", ++ *cur_index); ++ brelse(bh); ++ goto out; ++ } ++ (*offset)++; ++ } ++ return bh; ++ ++out: ++ return NULL; ++} ++ ++ ++SQSH_EXTERN unsigned int squashfs_read_data(struct super_block *s, char *buffer, ++ long long index, unsigned int length, ++ long long *next_index, int srclength) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ struct buffer_head **bh; ++ unsigned int offset = index & ((1 << msblk->devblksize_log2) - 1); ++ unsigned int cur_index = index >> msblk->devblksize_log2; ++ int bytes, avail_bytes, b = 0, k = 0; ++ unsigned int compressed; ++ unsigned int c_byte = length; ++ ++ bh = kmalloc(((sblk->block_size >> msblk->devblksize_log2) + 1) * ++ sizeof(struct buffer_head *), GFP_KERNEL); ++ if (bh == NULL) ++ goto read_failure; ++ ++ if (c_byte) { ++ bytes = msblk->devblksize - offset; ++ compressed = SQUASHFS_COMPRESSED_BLOCK(c_byte); ++ c_byte = SQUASHFS_COMPRESSED_SIZE_BLOCK(c_byte); ++ ++ TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n", index, ++ compressed ? "" : "un", (unsigned int) c_byte, srclength); ++ ++ if (c_byte > srclength || index < 0 || (index + c_byte) > sblk->bytes_used) ++ goto read_failure; ++ ++ bh[0] = sb_getblk(s, cur_index); ++ if (bh[0] == NULL) ++ goto block_release; ++ ++ for (b = 1; bytes < c_byte; b++) { ++ bh[b] = sb_getblk(s, ++cur_index); ++ if (bh[b] == NULL) ++ goto block_release; ++ bytes += msblk->devblksize; ++ } ++ ll_rw_block(READ, b, bh); ++ } else { ++ if (index < 0 || (index + 2) > sblk->bytes_used) ++ goto read_failure; ++ ++ bh[0] = get_block_length(s, &cur_index, &offset, &c_byte); ++ if (bh[0] == NULL) ++ goto read_failure; ++ ++ bytes = msblk->devblksize - offset; ++ compressed = SQUASHFS_COMPRESSED(c_byte); ++ c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte); ++ ++ TRACE("Block @ 0x%llx, %scompressed size %d\n", index, compressed ++ ? "" : "un", (unsigned int) c_byte); ++ ++ if (c_byte > srclength || (index + c_byte) > sblk->bytes_used) ++ goto read_failure; ++ ++ for (b = 1; bytes < c_byte; b++) { ++ bh[b] = sb_getblk(s, ++cur_index); ++ if (bh[b] == NULL) ++ goto block_release; ++ bytes += msblk->devblksize; ++ } ++ ll_rw_block(READ, b - 1, bh + 1); ++ } ++ ++ if (compressed) { ++ int zlib_err = 0; ++ ++ /* ++ * uncompress block ++ */ ++ ++ mutex_lock(&msblk->read_data_mutex); ++ ++ msblk->stream.next_out = buffer; ++ msblk->stream.avail_out = srclength; ++ ++ for (bytes = 0; k < b; k++) { ++ avail_bytes = min(c_byte - bytes, msblk->devblksize - offset); ++ ++ wait_on_buffer(bh[k]); ++ if (!buffer_uptodate(bh[k])) ++ goto release_mutex; ++ ++ msblk->stream.next_in = bh[k]->b_data + offset; ++ msblk->stream.avail_in = avail_bytes; ++ ++ if (k == 0) { ++ zlib_err = zlib_inflateInit(&msblk->stream); ++ if (zlib_err != Z_OK) { ++ ERROR("zlib_inflateInit returned unexpected result 0x%x," ++ " srclength %d\n", zlib_err, srclength); ++ goto release_mutex; ++ } ++ ++ if (avail_bytes == 0) { ++ offset = 0; ++ brelse(bh[k]); ++ continue; ++ } ++ } ++ ++ zlib_err = zlib_inflate(&msblk->stream, Z_NO_FLUSH); ++ if (zlib_err != Z_OK && zlib_err != Z_STREAM_END) { ++ ERROR("zlib_inflate returned unexpected result 0x%x," ++ " srclength %d, avail_in %d, avail_out %d\n", zlib_err, ++ srclength, msblk->stream.avail_in, msblk->stream.avail_out); ++ goto release_mutex; ++ } ++ ++ bytes += avail_bytes; ++ offset = 0; ++ brelse(bh[k]); ++ } ++ ++ if (zlib_err != Z_STREAM_END) ++ goto release_mutex; ++ ++ zlib_err = zlib_inflateEnd(&msblk->stream); ++ if (zlib_err != Z_OK) { ++ ERROR("zlib_inflateEnd returned unexpected result 0x%x," ++ " srclength %d\n", zlib_err, srclength); ++ goto release_mutex; ++ } ++ bytes = msblk->stream.total_out; ++ mutex_unlock(&msblk->read_data_mutex); ++ } else { ++ int i; ++ ++ for(i = 0; i < b; i++) { ++ wait_on_buffer(bh[i]); ++ if (!buffer_uptodate(bh[i])) ++ goto block_release; ++ } ++ ++ for (bytes = 0; k < b; k++) { ++ avail_bytes = min(c_byte - bytes, msblk->devblksize - offset); ++ ++ memcpy(buffer + bytes, bh[k]->b_data + offset, avail_bytes); ++ bytes += avail_bytes; ++ offset = 0; ++ brelse(bh[k]); ++ } ++ } ++ ++ if (next_index) ++ *next_index = index + c_byte + (length ? 0 : ++ (SQUASHFS_CHECK_DATA(msblk->sblk.flags) ? 3 : 2)); ++ ++ kfree(bh); ++ return bytes; ++ ++release_mutex: ++ mutex_unlock(&msblk->read_data_mutex); ++ ++block_release: ++ for (; k < b; k++) ++ brelse(bh[k]); ++ ++read_failure: ++ ERROR("sb_bread failed reading block 0x%x\n", cur_index); ++ kfree(bh); ++ return 0; ++} ++ ++ ++SQSH_EXTERN int squashfs_get_cached_block(struct super_block *s, void *buffer, ++ long long block, unsigned int offset, ++ int length, long long *next_block, ++ unsigned int *next_offset) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ int n, i, bytes, return_length = length; ++ long long next_index; ++ ++ TRACE("Entered squashfs_get_cached_block [%llx:%x]\n", block, offset); ++ ++ while (1) { ++ for (i = 0; i < squashfs_cached_blks; i++) ++ if (msblk->block_cache[i].block == block) ++ break; ++ ++ mutex_lock(&msblk->block_cache_mutex); ++ ++ if (i == squashfs_cached_blks) { ++ /* read inode header block */ ++ if (msblk->unused_cache_blks == 0) { ++ mutex_unlock(&msblk->block_cache_mutex); ++ wait_event(msblk->waitq, msblk->unused_cache_blks); ++ continue; ++ } ++ ++ i = msblk->next_cache; ++ for (n = 0; n < squashfs_cached_blks; n++) { ++ if (msblk->block_cache[i].block != SQUASHFS_USED_BLK) ++ break; ++ i = (i + 1) % squashfs_cached_blks; ++ } ++ ++ msblk->next_cache = (i + 1) % squashfs_cached_blks; ++ ++ if (msblk->block_cache[i].block == SQUASHFS_INVALID_BLK) { ++ msblk->block_cache[i].data = vmalloc(SQUASHFS_METADATA_SIZE); ++ if (msblk->block_cache[i].data == NULL) { ++ ERROR("Failed to allocate cache block\n"); ++ mutex_unlock(&msblk->block_cache_mutex); ++ goto out; ++ } ++ } ++ ++ msblk->block_cache[i].block = SQUASHFS_USED_BLK; ++ msblk->unused_cache_blks --; ++ mutex_unlock(&msblk->block_cache_mutex); ++ ++ msblk->block_cache[i].length = squashfs_read_data(s, ++ msblk->block_cache[i].data, block, 0, &next_index, ++ SQUASHFS_METADATA_SIZE); ++ ++ if (msblk->block_cache[i].length == 0) { ++ ERROR("Unable to read cache block [%llx:%x]\n", block, offset); ++ mutex_lock(&msblk->block_cache_mutex); ++ msblk->block_cache[i].block = SQUASHFS_INVALID_BLK; ++ msblk->unused_cache_blks ++; ++ smp_mb(); ++ vfree(msblk->block_cache[i].data); ++ wake_up(&msblk->waitq); ++ mutex_unlock(&msblk->block_cache_mutex); ++ goto out; ++ } ++ ++ mutex_lock(&msblk->block_cache_mutex); ++ msblk->block_cache[i].block = block; ++ msblk->block_cache[i].next_index = next_index; ++ msblk->unused_cache_blks ++; ++ smp_mb(); ++ wake_up(&msblk->waitq); ++ TRACE("Read cache block [%llx:%x]\n", block, offset); ++ } ++ ++ if (msblk->block_cache[i].block != block) { ++ mutex_unlock(&msblk->block_cache_mutex); ++ continue; ++ } ++ ++ bytes = msblk->block_cache[i].length - offset; ++ ++ if (bytes < 1) { ++ mutex_unlock(&msblk->block_cache_mutex); ++ goto out; ++ } else if (bytes >= length) { ++ if (buffer) ++ memcpy(buffer, msblk->block_cache[i].data + offset, length); ++ if (msblk->block_cache[i].length - offset == length) { ++ *next_block = msblk->block_cache[i].next_index; ++ *next_offset = 0; ++ } else { ++ *next_block = block; ++ *next_offset = offset + length; ++ } ++ mutex_unlock(&msblk->block_cache_mutex); ++ goto finish; ++ } else { ++ if (buffer) { ++ memcpy(buffer, msblk->block_cache[i].data + offset, bytes); ++ buffer = (char *) buffer + bytes; ++ } ++ block = msblk->block_cache[i].next_index; ++ mutex_unlock(&msblk->block_cache_mutex); ++ length -= bytes; ++ offset = 0; ++ } ++ } ++ ++finish: ++ return return_length; ++out: ++ return 0; ++} ++ ++ ++static int get_fragment_location(struct super_block *s, unsigned int fragment, ++ long long *fragment_start_block, ++ unsigned int *fragment_size) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ long long start_block = ++ msblk->fragment_index[SQUASHFS_FRAGMENT_INDEX(fragment)]; ++ int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET(fragment); ++ struct squashfs_fragment_entry fragment_entry; ++ ++ if (msblk->swap) { ++ struct squashfs_fragment_entry sfragment_entry; ++ ++ if (!squashfs_get_cached_block(s, &sfragment_entry, start_block, offset, ++ sizeof(sfragment_entry), &start_block, &offset)) ++ goto out; ++ SQUASHFS_SWAP_FRAGMENT_ENTRY(&fragment_entry, &sfragment_entry); ++ } else ++ if (!squashfs_get_cached_block(s, &fragment_entry, start_block, offset, ++ sizeof(fragment_entry), &start_block, &offset)) ++ goto out; ++ ++ *fragment_start_block = fragment_entry.start_block; ++ *fragment_size = fragment_entry.size; ++ ++ return 1; ++ ++out: ++ return 0; ++} ++ ++ ++SQSH_EXTERN void release_cached_fragment(struct squashfs_sb_info *msblk, ++ struct squashfs_fragment_cache *fragment) ++{ ++ mutex_lock(&msblk->fragment_mutex); ++ fragment->locked --; ++ if (fragment->locked == 0) { ++ msblk->unused_frag_blks ++; ++ smp_mb(); ++ wake_up(&msblk->fragment_wait_queue); ++ } ++ mutex_unlock(&msblk->fragment_mutex); ++} ++ ++ ++SQSH_EXTERN ++struct squashfs_fragment_cache *get_cached_fragment(struct super_block *s, ++ long long start_block, int length) ++{ ++ int i, n; ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ ++ while (1) { ++ mutex_lock(&msblk->fragment_mutex); ++ ++ for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS && ++ msblk->fragment[i].block != start_block; i++); ++ ++ if (i == SQUASHFS_CACHED_FRAGMENTS) { ++ if (msblk->unused_frag_blks == 0) { ++ mutex_unlock(&msblk->fragment_mutex); ++ wait_event(msblk->fragment_wait_queue, msblk->unused_frag_blks); ++ continue; ++ } ++ ++ i = msblk->next_fragment; ++ for (n = 0; n < SQUASHFS_CACHED_FRAGMENTS; n++) { ++ if (msblk->fragment[i].locked == 0) ++ break; ++ i = (i + 1) % SQUASHFS_CACHED_FRAGMENTS; ++ } ++ ++ msblk->next_fragment = (msblk->next_fragment + 1) % ++ SQUASHFS_CACHED_FRAGMENTS; ++ ++ if (msblk->fragment[i].data == NULL) { ++ msblk->fragment[i].data = vmalloc(sblk->block_size); ++ if (msblk->fragment[i].data == NULL) { ++ ERROR("Failed to allocate fragment cache block\n"); ++ mutex_unlock(&msblk->fragment_mutex); ++ goto out; ++ } ++ } ++ ++ msblk->unused_frag_blks --; ++ msblk->fragment[i].block = SQUASHFS_INVALID_BLK; ++ msblk->fragment[i].locked = 1; ++ mutex_unlock(&msblk->fragment_mutex); ++ ++ msblk->fragment[i].length = squashfs_read_data(s, ++ msblk->fragment[i].data, start_block, length, NULL, ++ sblk->block_size); ++ ++ if (msblk->fragment[i].length == 0) { ++ ERROR("Unable to read fragment cache block [%llx]\n", start_block); ++ msblk->fragment[i].locked = 0; ++ msblk->unused_frag_blks ++; ++ smp_mb(); ++ wake_up(&msblk->fragment_wait_queue); ++ goto out; ++ } ++ ++ mutex_lock(&msblk->fragment_mutex); ++ msblk->fragment[i].block = start_block; ++ TRACE("New fragment %d, start block %lld, locked %d\n", ++ i, msblk->fragment[i].block, msblk->fragment[i].locked); ++ mutex_unlock(&msblk->fragment_mutex); ++ break; ++ } ++ ++ if (msblk->fragment[i].locked == 0) ++ msblk->unused_frag_blks --; ++ msblk->fragment[i].locked++; ++ mutex_unlock(&msblk->fragment_mutex); ++ TRACE("Got fragment %d, start block %lld, locked %d\n", i, ++ msblk->fragment[i].block, msblk->fragment[i].locked); ++ break; ++ } ++ ++ return &msblk->fragment[i]; ++ ++out: ++ return NULL; ++} ++ ++ ++static void squashfs_new_inode(struct squashfs_sb_info *msblk, struct inode *i, ++ struct squashfs_base_inode_header *inodeb) ++{ ++ i->i_ino = inodeb->inode_number; ++ i->i_mtime.tv_sec = inodeb->mtime; ++ i->i_atime.tv_sec = inodeb->mtime; ++ i->i_ctime.tv_sec = inodeb->mtime; ++ i->i_uid = msblk->uid[inodeb->uid]; ++ i->i_mode = inodeb->mode; ++ i->i_size = 0; ++ ++ if (inodeb->guid == SQUASHFS_GUIDS) ++ i->i_gid = i->i_uid; ++ else ++ i->i_gid = msblk->guid[inodeb->guid]; ++} ++ ++ ++static squashfs_inode_t squashfs_inode_lookup(struct super_block *s, int ino) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ long long start = msblk->inode_lookup_table[SQUASHFS_LOOKUP_BLOCK(ino - 1)]; ++ int offset = SQUASHFS_LOOKUP_BLOCK_OFFSET(ino - 1); ++ squashfs_inode_t inode; ++ ++ TRACE("Entered squashfs_inode_lookup, inode_number = %d\n", ino); ++ ++ if (msblk->swap) { ++ squashfs_inode_t sinode; ++ ++ if (!squashfs_get_cached_block(s, &sinode, start, offset, ++ sizeof(sinode), &start, &offset)) ++ goto out; ++ SQUASHFS_SWAP_INODE_T((&inode), &sinode); ++ } else if (!squashfs_get_cached_block(s, &inode, start, offset, ++ sizeof(inode), &start, &offset)) ++ goto out; ++ ++ TRACE("squashfs_inode_lookup, inode = 0x%llx\n", inode); ++ ++ return inode; ++ ++out: ++ return SQUASHFS_INVALID_BLK; ++} ++ ++ ++static void vfs_read_inode(struct inode *i) ++{ ++ struct squashfs_sb_info *msblk = i->i_sb->s_fs_info; ++ squashfs_inode_t inode = squashfs_inode_lookup(i->i_sb, i->i_ino); ++ ++ TRACE("Entered vfs_read_inode\n"); ++ ++ if(inode != SQUASHFS_INVALID_BLK) ++ (msblk->read_inode)(i, inode); ++} ++ ++ ++static struct dentry *squashfs_get_parent(struct dentry *child) ++{ ++ struct inode *i = child->d_inode; ++ struct inode *parent = iget(i->i_sb, SQUASHFS_I(i)->u.s2.parent_inode); ++ struct dentry *rv; ++ ++ TRACE("Entered squashfs_get_parent\n"); ++ ++ if(parent == NULL) { ++ rv = ERR_PTR(-EACCES); ++ goto out; ++ } ++ ++ rv = d_alloc_anon(parent); ++ if(rv == NULL) ++ rv = ERR_PTR(-ENOMEM); ++ ++out: ++ return rv; ++} ++ ++ ++SQSH_EXTERN struct inode *squashfs_iget(struct super_block *s, ++ squashfs_inode_t inode, unsigned int inode_number) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct inode *i = iget_locked(s, inode_number); ++ ++ TRACE("Entered squashfs_iget\n"); ++ ++ if(i && (i->i_state & I_NEW)) { ++ (msblk->read_inode)(i, inode); ++ unlock_new_inode(i); ++ } ++ ++ return i; ++} ++ ++ ++static int squashfs_read_inode(struct inode *i, squashfs_inode_t inode) ++{ ++ struct super_block *s = i->i_sb; ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ long long block = SQUASHFS_INODE_BLK(inode) + sblk->inode_table_start; ++ unsigned int offset = SQUASHFS_INODE_OFFSET(inode); ++ long long next_block; ++ unsigned int next_offset; ++ union squashfs_inode_header id, sid; ++ struct squashfs_base_inode_header *inodeb = &id.base, *sinodeb = &sid.base; ++ ++ TRACE("Entered squashfs_read_inode\n"); ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, sinodeb, block, offset, ++ sizeof(*sinodeb), &next_block, &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_BASE_INODE_HEADER(inodeb, sinodeb, sizeof(*sinodeb)); ++ } else ++ if (!squashfs_get_cached_block(s, inodeb, block, offset, ++ sizeof(*inodeb), &next_block, &next_offset)) ++ goto failed_read; ++ ++ squashfs_new_inode(msblk, i, inodeb); ++ ++ switch(inodeb->inode_type) { ++ case SQUASHFS_FILE_TYPE: { ++ unsigned int frag_size; ++ long long frag_blk; ++ struct squashfs_reg_inode_header *inodep = &id.reg; ++ struct squashfs_reg_inode_header *sinodep = &sid.reg; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, sinodep, block, offset, ++ sizeof(*sinodep), &next_block, &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_REG_INODE_HEADER(inodep, sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, inodep, block, offset, ++ sizeof(*inodep), &next_block, &next_offset)) ++ goto failed_read; ++ ++ frag_blk = SQUASHFS_INVALID_BLK; ++ ++ if (inodep->fragment != SQUASHFS_INVALID_FRAG) ++ if(!get_fragment_location(s, inodep->fragment, &frag_blk, ++ &frag_size)) ++ goto failed_read; ++ ++ i->i_nlink = 1; ++ i->i_size = inodep->file_size; ++ i->i_fop = &generic_ro_fops; ++ i->i_mode |= S_IFREG; ++ i->i_blocks = ((i->i_size - 1) >> 9) + 1; ++ SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk; ++ SQUASHFS_I(i)->u.s1.fragment_size = frag_size; ++ SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset; ++ SQUASHFS_I(i)->start_block = inodep->start_block; ++ SQUASHFS_I(i)->u.s1.block_list_start = next_block; ++ SQUASHFS_I(i)->offset = next_offset; ++ i->i_data.a_ops = &squashfs_aops; ++ ++ TRACE("File inode %x:%x, start_block %llx, " ++ "block_list_start %llx, offset %x\n", ++ SQUASHFS_INODE_BLK(inode), offset, ++ inodep->start_block, next_block, ++ next_offset); ++ break; ++ } ++ case SQUASHFS_LREG_TYPE: { ++ unsigned int frag_size; ++ long long frag_blk; ++ struct squashfs_lreg_inode_header *inodep = &id.lreg; ++ struct squashfs_lreg_inode_header *sinodep = &sid.lreg; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, sinodep, block, offset, ++ sizeof(*sinodep), &next_block, &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_LREG_INODE_HEADER(inodep, sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, inodep, block, offset, ++ sizeof(*inodep), &next_block, &next_offset)) ++ goto failed_read; ++ ++ frag_blk = SQUASHFS_INVALID_BLK; ++ ++ if (inodep->fragment != SQUASHFS_INVALID_FRAG) ++ if (!get_fragment_location(s, inodep->fragment, &frag_blk, ++ &frag_size)) ++ goto failed_read; ++ ++ i->i_nlink = inodep->nlink; ++ i->i_size = inodep->file_size; ++ i->i_fop = &generic_ro_fops; ++ i->i_mode |= S_IFREG; ++ i->i_blocks = ((i->i_size - 1) >> 9) + 1; ++ SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk; ++ SQUASHFS_I(i)->u.s1.fragment_size = frag_size; ++ SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset; ++ SQUASHFS_I(i)->start_block = inodep->start_block; ++ SQUASHFS_I(i)->u.s1.block_list_start = next_block; ++ SQUASHFS_I(i)->offset = next_offset; ++ i->i_data.a_ops = &squashfs_aops; ++ ++ TRACE("File inode %x:%x, start_block %llx, " ++ "block_list_start %llx, offset %x\n", ++ SQUASHFS_INODE_BLK(inode), offset, ++ inodep->start_block, next_block, ++ next_offset); ++ break; ++ } ++ case SQUASHFS_DIR_TYPE: { ++ struct squashfs_dir_inode_header *inodep = &id.dir; ++ struct squashfs_dir_inode_header *sinodep = &sid.dir; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, sinodep, block, offset, ++ sizeof(*sinodep), &next_block, &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_DIR_INODE_HEADER(inodep, sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, inodep, block, offset, ++ sizeof(*inodep), &next_block, &next_offset)) ++ goto failed_read; ++ ++ i->i_nlink = inodep->nlink; ++ i->i_size = inodep->file_size; ++ i->i_op = &squashfs_dir_inode_ops; ++ i->i_fop = &squashfs_dir_ops; ++ i->i_mode |= S_IFDIR; ++ SQUASHFS_I(i)->start_block = inodep->start_block; ++ SQUASHFS_I(i)->offset = inodep->offset; ++ SQUASHFS_I(i)->u.s2.directory_index_count = 0; ++ SQUASHFS_I(i)->u.s2.parent_inode = inodep->parent_inode; ++ ++ TRACE("Directory inode %x:%x, start_block %x, offset " ++ "%x\n", SQUASHFS_INODE_BLK(inode), ++ offset, inodep->start_block, ++ inodep->offset); ++ break; ++ } ++ case SQUASHFS_LDIR_TYPE: { ++ struct squashfs_ldir_inode_header *inodep = &id.ldir; ++ struct squashfs_ldir_inode_header *sinodep = &sid.ldir; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, sinodep, block, offset, ++ sizeof(*sinodep), &next_block, &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_LDIR_INODE_HEADER(inodep, sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, inodep, block, offset, ++ sizeof(*inodep), &next_block, &next_offset)) ++ goto failed_read; ++ ++ i->i_nlink = inodep->nlink; ++ i->i_size = inodep->file_size; ++ i->i_op = &squashfs_dir_inode_ops; ++ i->i_fop = &squashfs_dir_ops; ++ i->i_mode |= S_IFDIR; ++ SQUASHFS_I(i)->start_block = inodep->start_block; ++ SQUASHFS_I(i)->offset = inodep->offset; ++ SQUASHFS_I(i)->u.s2.directory_index_start = next_block; ++ SQUASHFS_I(i)->u.s2.directory_index_offset = next_offset; ++ SQUASHFS_I(i)->u.s2.directory_index_count = inodep->i_count; ++ SQUASHFS_I(i)->u.s2.parent_inode = inodep->parent_inode; ++ ++ TRACE("Long directory inode %x:%x, start_block %x, offset %x\n", ++ SQUASHFS_INODE_BLK(inode), offset, ++ inodep->start_block, inodep->offset); ++ break; ++ } ++ case SQUASHFS_SYMLINK_TYPE: { ++ struct squashfs_symlink_inode_header *inodep = &id.symlink; ++ struct squashfs_symlink_inode_header *sinodep = &sid.symlink; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, sinodep, block, offset, ++ sizeof(*sinodep), &next_block, &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_SYMLINK_INODE_HEADER(inodep, sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, inodep, block, offset, ++ sizeof(*inodep), &next_block, &next_offset)) ++ goto failed_read; ++ ++ i->i_nlink = inodep->nlink; ++ i->i_size = inodep->symlink_size; ++ i->i_op = &page_symlink_inode_operations; ++ i->i_data.a_ops = &squashfs_symlink_aops; ++ i->i_mode |= S_IFLNK; ++ SQUASHFS_I(i)->start_block = next_block; ++ SQUASHFS_I(i)->offset = next_offset; ++ ++ TRACE("Symbolic link inode %x:%x, start_block %llx, offset %x\n", ++ SQUASHFS_INODE_BLK(inode), offset, ++ next_block, next_offset); ++ break; ++ } ++ case SQUASHFS_BLKDEV_TYPE: ++ case SQUASHFS_CHRDEV_TYPE: { ++ struct squashfs_dev_inode_header *inodep = &id.dev; ++ struct squashfs_dev_inode_header *sinodep = &sid.dev; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, sinodep, block, offset, ++ sizeof(*sinodep), &next_block, &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_DEV_INODE_HEADER(inodep, sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, inodep, block, offset, ++ sizeof(*inodep), &next_block, &next_offset)) ++ goto failed_read; ++ ++ i->i_nlink = inodep->nlink; ++ i->i_mode |= (inodeb->inode_type == SQUASHFS_CHRDEV_TYPE) ? ++ S_IFCHR : S_IFBLK; ++ init_special_inode(i, i->i_mode, old_decode_dev(inodep->rdev)); ++ ++ TRACE("Device inode %x:%x, rdev %x\n", ++ SQUASHFS_INODE_BLK(inode), offset, inodep->rdev); ++ break; ++ } ++ case SQUASHFS_FIFO_TYPE: ++ case SQUASHFS_SOCKET_TYPE: { ++ struct squashfs_ipc_inode_header *inodep = &id.ipc; ++ struct squashfs_ipc_inode_header *sinodep = &sid.ipc; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, sinodep, block, offset, ++ sizeof(*sinodep), &next_block, &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_IPC_INODE_HEADER(inodep, sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, inodep, block, offset, ++ sizeof(*inodep), &next_block, &next_offset)) ++ goto failed_read; ++ ++ i->i_nlink = inodep->nlink; ++ i->i_mode |= (inodeb->inode_type == SQUASHFS_FIFO_TYPE) ++ ? S_IFIFO : S_IFSOCK; ++ init_special_inode(i, i->i_mode, 0); ++ break; ++ } ++ default: ++ ERROR("Unknown inode type %d in squashfs_iget!\n", ++ inodeb->inode_type); ++ goto failed_read1; ++ } ++ ++ return 1; ++ ++failed_read: ++ ERROR("Unable to read inode [%llx:%x]\n", block, offset); ++ ++failed_read1: ++ make_bad_inode(i); ++ return 0; ++} ++ ++ ++static int read_inode_lookup_table(struct super_block *s) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ unsigned int length = SQUASHFS_LOOKUP_BLOCK_BYTES(sblk->inodes); ++ ++ TRACE("In read_inode_lookup_table, length %d\n", length); ++ ++ /* Allocate inode lookup table */ ++ msblk->inode_lookup_table = kmalloc(length, GFP_KERNEL); ++ if (msblk->inode_lookup_table == NULL) { ++ ERROR("Failed to allocate inode lookup table\n"); ++ return 0; ++ } ++ ++ if (!squashfs_read_data(s, (char *) msblk->inode_lookup_table, ++ sblk->lookup_table_start, length | ++ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, length)) { ++ ERROR("unable to read inode lookup table\n"); ++ return 0; ++ } ++ ++ if (msblk->swap) { ++ int i; ++ long long block; ++ ++ for (i = 0; i < SQUASHFS_LOOKUP_BLOCKS(sblk->inodes); i++) { ++ /* XXX */ ++ SQUASHFS_SWAP_LOOKUP_BLOCKS((&block), ++ &msblk->inode_lookup_table[i], 1); ++ msblk->inode_lookup_table[i] = block; ++ } ++ } ++ ++ return 1; ++} ++ ++ ++static int read_fragment_index_table(struct super_block *s) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ unsigned int length = SQUASHFS_FRAGMENT_INDEX_BYTES(sblk->fragments); ++ ++ if(length == 0) ++ return 1; ++ ++ /* Allocate fragment index table */ ++ msblk->fragment_index = kmalloc(length, GFP_KERNEL); ++ if (msblk->fragment_index == NULL) { ++ ERROR("Failed to allocate fragment index table\n"); ++ return 0; ++ } ++ ++ if (!squashfs_read_data(s, (char *) msblk->fragment_index, ++ sblk->fragment_table_start, length | ++ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, length)) { ++ ERROR("unable to read fragment index table\n"); ++ return 0; ++ } ++ ++ if (msblk->swap) { ++ int i; ++ long long fragment; ++ ++ for (i = 0; i < SQUASHFS_FRAGMENT_INDEXES(sblk->fragments); i++) { ++ /* XXX */ ++ SQUASHFS_SWAP_FRAGMENT_INDEXES((&fragment), ++ &msblk->fragment_index[i], 1); ++ msblk->fragment_index[i] = fragment; ++ } ++ } ++ ++ return 1; ++} ++ ++ ++static int readahead_metadata(struct super_block *s) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ int i; ++ ++ squashfs_cached_blks = SQUASHFS_CACHED_BLKS; ++ ++ /* Init inode_table block pointer array */ ++ msblk->block_cache = kmalloc(sizeof(struct squashfs_cache) * ++ squashfs_cached_blks, GFP_KERNEL); ++ if (msblk->block_cache == NULL) { ++ ERROR("Failed to allocate block cache\n"); ++ goto failed; ++ } ++ ++ for (i = 0; i < squashfs_cached_blks; i++) ++ msblk->block_cache[i].block = SQUASHFS_INVALID_BLK; ++ ++ msblk->next_cache = 0; ++ msblk->unused_cache_blks = squashfs_cached_blks; ++ ++ return 1; ++ ++failed: ++ return 0; ++} ++ ++ ++static int supported_squashfs_filesystem(struct squashfs_sb_info *msblk, int silent) ++{ ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ ++ msblk->read_inode = squashfs_read_inode; ++ msblk->read_blocklist = read_blocklist; ++ msblk->read_fragment_index_table = read_fragment_index_table; ++ ++ if (sblk->s_major == 1) { ++ if (!squashfs_1_0_supported(msblk)) { ++ SERROR("Major/Minor mismatch, Squashfs 1.0 filesystems " ++ "are unsupported\n"); ++ SERROR("Please recompile with Squashfs 1.0 support enabled\n"); ++ return 0; ++ } ++ } else if (sblk->s_major == 2) { ++ if (!squashfs_2_0_supported(msblk)) { ++ SERROR("Major/Minor mismatch, Squashfs 2.0 filesystems " ++ "are unsupported\n"); ++ SERROR("Please recompile with Squashfs 2.0 support enabled\n"); ++ return 0; ++ } ++ } else if(sblk->s_major != SQUASHFS_MAJOR || sblk->s_minor > ++ SQUASHFS_MINOR) { ++ SERROR("Major/Minor mismatch, trying to mount newer %d.%d " ++ "filesystem\n", sblk->s_major, sblk->s_minor); ++ SERROR("Please update your kernel\n"); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++ ++static int squashfs_fill_super(struct super_block *s, void *data, int silent) ++{ ++ struct squashfs_sb_info *msblk; ++ struct squashfs_super_block *sblk; ++ int i; ++ char b[BDEVNAME_SIZE]; ++ struct inode *root; ++ ++ TRACE("Entered squashfs_fill_superblock\n"); ++ ++ s->s_fs_info = kzalloc(sizeof(struct squashfs_sb_info), GFP_KERNEL); ++ if (s->s_fs_info == NULL) { ++ ERROR("Failed to allocate superblock\n"); ++ goto failure; ++ } ++ msblk = s->s_fs_info; ++ ++ msblk->stream.workspace = vmalloc(zlib_inflate_workspacesize()); ++ if (msblk->stream.workspace == NULL) { ++ ERROR("Failed to allocate zlib workspace\n"); ++ goto failure; ++ } ++ sblk = &msblk->sblk; ++ ++ msblk->devblksize = sb_min_blocksize(s, BLOCK_SIZE); ++ msblk->devblksize_log2 = ffz(~msblk->devblksize); ++ ++ mutex_init(&msblk->read_data_mutex); ++ mutex_init(&msblk->read_page_mutex); ++ mutex_init(&msblk->block_cache_mutex); ++ mutex_init(&msblk->fragment_mutex); ++ mutex_init(&msblk->meta_index_mutex); ++ ++ init_waitqueue_head(&msblk->waitq); ++ init_waitqueue_head(&msblk->fragment_wait_queue); ++ ++ /* sblk->bytes_used is checked in squashfs_read_data to ensure reads are not ++ * beyond filesystem end. As we're using squashfs_read_data to read sblk here, ++ * first set sblk->bytes_used to a useful value */ ++ sblk->bytes_used = sizeof(struct squashfs_super_block); ++ if (!squashfs_read_data(s, (char *) sblk, SQUASHFS_START, ++ sizeof(struct squashfs_super_block) | ++ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, sizeof(struct squashfs_super_block))) { ++ SERROR("unable to read superblock\n"); ++ goto failed_mount; ++ } ++ ++ /* Check it is a SQUASHFS superblock */ ++ if ((s->s_magic = sblk->s_magic) != SQUASHFS_MAGIC) { ++ if (sblk->s_magic == SQUASHFS_MAGIC_SWAP) { ++ struct squashfs_super_block ssblk; ++ ++ WARNING("Mounting a different endian SQUASHFS filesystem on %s\n", ++ bdevname(s->s_bdev, b)); ++ ++ SQUASHFS_SWAP_SUPER_BLOCK(&ssblk, sblk); ++ memcpy(sblk, &ssblk, sizeof(struct squashfs_super_block)); ++ msblk->swap = 1; ++ } else { ++ SERROR("Can't find a SQUASHFS superblock on %s\n", ++ bdevname(s->s_bdev, b)); ++ goto failed_mount; ++ } ++ } ++ ++ /* Check the MAJOR & MINOR versions */ ++ if(!supported_squashfs_filesystem(msblk, silent)) ++ goto failed_mount; ++ ++ /* Check the filesystem does not extend beyond the end of the ++ block device */ ++ if(sblk->bytes_used < 0 || sblk->bytes_used > i_size_read(s->s_bdev->bd_inode)) ++ goto failed_mount; ++ ++ /* Check the root inode for sanity */ ++ if (SQUASHFS_INODE_OFFSET(sblk->root_inode) > SQUASHFS_METADATA_SIZE) ++ goto failed_mount; ++ ++ TRACE("Found valid superblock on %s\n", bdevname(s->s_bdev, b)); ++ TRACE("Inodes are %scompressed\n", SQUASHFS_UNCOMPRESSED_INODES(sblk->flags) ++ ? "un" : ""); ++ TRACE("Data is %scompressed\n", SQUASHFS_UNCOMPRESSED_DATA(sblk->flags) ++ ? "un" : ""); ++ TRACE("Check data is %spresent in the filesystem\n", ++ SQUASHFS_CHECK_DATA(sblk->flags) ? "" : "not "); ++ TRACE("Filesystem size %lld bytes\n", sblk->bytes_used); ++ TRACE("Block size %d\n", sblk->block_size); ++ TRACE("Number of inodes %d\n", sblk->inodes); ++ if (sblk->s_major > 1) ++ TRACE("Number of fragments %d\n", sblk->fragments); ++ TRACE("Number of uids %d\n", sblk->no_uids); ++ TRACE("Number of gids %d\n", sblk->no_guids); ++ TRACE("sblk->inode_table_start %llx\n", sblk->inode_table_start); ++ TRACE("sblk->directory_table_start %llx\n", sblk->directory_table_start); ++ if (sblk->s_major > 1) ++ TRACE("sblk->fragment_table_start %llx\n", sblk->fragment_table_start); ++ TRACE("sblk->uid_start %llx\n", sblk->uid_start); ++ ++ s->s_maxbytes = MAX_LFS_FILESIZE; ++ s->s_flags |= MS_RDONLY; ++ s->s_op = &squashfs_super_ops; ++ ++ if (readahead_metadata(s) == 0) ++ goto failed_mount; ++ ++ /* Allocate read_page block */ ++ msblk->read_page = vmalloc(sblk->block_size); ++ if (msblk->read_page == NULL) { ++ ERROR("Failed to allocate read_page block\n"); ++ goto failed_mount; ++ } ++ ++ /* Allocate uid and gid tables */ ++ msblk->uid = kmalloc((sblk->no_uids + sblk->no_guids) * ++ sizeof(unsigned int), GFP_KERNEL); ++ if (msblk->uid == NULL) { ++ ERROR("Failed to allocate uid/gid table\n"); ++ goto failed_mount; ++ } ++ msblk->guid = msblk->uid + sblk->no_uids; ++ ++ if (msblk->swap) { ++ unsigned int suid[sblk->no_uids + sblk->no_guids]; ++ ++ if (!squashfs_read_data(s, (char *) &suid, sblk->uid_start, ++ ((sblk->no_uids + sblk->no_guids) * ++ sizeof(unsigned int)) | ++ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, (sblk->no_uids + sblk->no_guids) * sizeof(unsigned int))) { ++ ERROR("unable to read uid/gid table\n"); ++ goto failed_mount; ++ } ++ ++ SQUASHFS_SWAP_DATA(msblk->uid, suid, (sblk->no_uids + ++ sblk->no_guids), (sizeof(unsigned int) * 8)); ++ } else ++ if (!squashfs_read_data(s, (char *) msblk->uid, sblk->uid_start, ++ ((sblk->no_uids + sblk->no_guids) * ++ sizeof(unsigned int)) | ++ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, (sblk->no_uids + sblk->no_guids) * sizeof(unsigned int))) { ++ ERROR("unable to read uid/gid table\n"); ++ goto failed_mount; ++ } ++ ++ ++ if (sblk->s_major == 1 && squashfs_1_0_supported(msblk)) ++ goto allocate_root; ++ ++ msblk->fragment = kzalloc(sizeof(struct squashfs_fragment_cache) * ++ SQUASHFS_CACHED_FRAGMENTS, GFP_KERNEL); ++ if (msblk->fragment == NULL) { ++ ERROR("Failed to allocate fragment block cache\n"); ++ goto failed_mount; ++ } ++ ++ for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS; i++) { ++ msblk->fragment[i].block = SQUASHFS_INVALID_BLK; ++ } ++ ++ msblk->next_fragment = 0; ++ msblk->unused_frag_blks = SQUASHFS_CACHED_FRAGMENTS; ++ ++ /* Allocate and read fragment index table */ ++ if (msblk->read_fragment_index_table(s) == 0) ++ goto failed_mount; ++ ++ if(sblk->s_major < 3 || sblk->lookup_table_start == SQUASHFS_INVALID_BLK) ++ goto allocate_root; ++ ++ /* Allocate and read inode lookup table */ ++ if (read_inode_lookup_table(s) == 0) ++ goto failed_mount; ++ ++ s->s_op = &squashfs_export_super_ops; ++ s->s_export_op = &squashfs_export_ops; ++ ++allocate_root: ++ root = new_inode(s); ++ if ((msblk->read_inode)(root, sblk->root_inode) == 0) ++ goto failed_mount; ++ insert_inode_hash(root); ++ ++ s->s_root = d_alloc_root(root); ++ if (s->s_root == NULL) { ++ ERROR("Root inode create failed\n"); ++ iput(root); ++ goto failed_mount; ++ } ++ ++ TRACE("Leaving squashfs_fill_super\n"); ++ return 0; ++ ++failed_mount: ++ kfree(msblk->inode_lookup_table); ++ kfree(msblk->fragment_index); ++ kfree(msblk->fragment); ++ kfree(msblk->uid); ++ vfree(msblk->read_page); ++ kfree(msblk->block_cache); ++ kfree(msblk->fragment_index_2); ++ vfree(msblk->stream.workspace); ++ kfree(s->s_fs_info); ++ s->s_fs_info = NULL; ++ return -EINVAL; ++ ++failure: ++ return -ENOMEM; ++} ++ ++ ++static int squashfs_statfs(struct dentry *dentry, struct kstatfs *buf) ++{ ++ struct squashfs_sb_info *msblk = dentry->d_sb->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ ++ TRACE("Entered squashfs_statfs\n"); ++ ++ buf->f_type = SQUASHFS_MAGIC; ++ buf->f_bsize = sblk->block_size; ++ buf->f_blocks = ((sblk->bytes_used - 1) >> sblk->block_log) + 1; ++ buf->f_bfree = buf->f_bavail = 0; ++ buf->f_files = sblk->inodes; ++ buf->f_ffree = 0; ++ buf->f_namelen = SQUASHFS_NAME_LEN; ++ ++ return 0; ++} ++ ++ ++static int squashfs_symlink_readpage(struct file *file, struct page *page) ++{ ++ struct inode *inode = page->mapping->host; ++ int index = page->index << PAGE_CACHE_SHIFT, length, bytes, avail_bytes; ++ long long block = SQUASHFS_I(inode)->start_block; ++ int offset = SQUASHFS_I(inode)->offset; ++ void *pageaddr = kmap(page); ++ ++ TRACE("Entered squashfs_symlink_readpage, page index %ld, start block " ++ "%llx, offset %x\n", page->index, ++ SQUASHFS_I(inode)->start_block, ++ SQUASHFS_I(inode)->offset); ++ ++ for (length = 0; length < index; length += bytes) { ++ bytes = squashfs_get_cached_block(inode->i_sb, NULL, block, ++ offset, PAGE_CACHE_SIZE, &block, &offset); ++ if (bytes == 0) { ++ ERROR("Unable to read symbolic link [%llx:%x]\n", block, offset); ++ goto skip_read; ++ } ++ } ++ ++ if (length != index) { ++ ERROR("(squashfs_symlink_readpage) length != index\n"); ++ bytes = 0; ++ goto skip_read; ++ } ++ ++ avail_bytes = min_t(int, i_size_read(inode) - length, PAGE_CACHE_SIZE); ++ ++ bytes = squashfs_get_cached_block(inode->i_sb, pageaddr, block, offset, ++ avail_bytes, &block, &offset); ++ if (bytes == 0) ++ ERROR("Unable to read symbolic link [%llx:%x]\n", block, offset); ++ ++skip_read: ++ memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes); ++ kunmap(page); ++ flush_dcache_page(page); ++ SetPageUptodate(page); ++ unlock_page(page); ++ ++ return 0; ++} ++ ++ ++struct meta_index *locate_meta_index(struct inode *inode, int index, int offset) ++{ ++ struct meta_index *meta = NULL; ++ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; ++ int i; ++ ++ mutex_lock(&msblk->meta_index_mutex); ++ ++ TRACE("locate_meta_index: index %d, offset %d\n", index, offset); ++ ++ if (msblk->meta_index == NULL) ++ goto not_allocated; ++ ++ for (i = 0; i < SQUASHFS_META_NUMBER; i ++) { ++ if (msblk->meta_index[i].inode_number == inode->i_ino && ++ msblk->meta_index[i].offset >= offset && ++ msblk->meta_index[i].offset <= index && ++ msblk->meta_index[i].locked == 0) { ++ TRACE("locate_meta_index: entry %d, offset %d\n", i, ++ msblk->meta_index[i].offset); ++ meta = &msblk->meta_index[i]; ++ offset = meta->offset; ++ } ++ } ++ ++ if (meta) ++ meta->locked = 1; ++ ++not_allocated: ++ mutex_unlock(&msblk->meta_index_mutex); ++ ++ return meta; ++} ++ ++ ++struct meta_index *empty_meta_index(struct inode *inode, int offset, int skip) ++{ ++ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; ++ struct meta_index *meta = NULL; ++ int i; ++ ++ mutex_lock(&msblk->meta_index_mutex); ++ ++ TRACE("empty_meta_index: offset %d, skip %d\n", offset, skip); ++ ++ if (msblk->meta_index == NULL) { ++ msblk->meta_index = kmalloc(sizeof(struct meta_index) * ++ SQUASHFS_META_NUMBER, GFP_KERNEL); ++ if (msblk->meta_index == NULL) { ++ ERROR("Failed to allocate meta_index\n"); ++ goto failed; ++ } ++ for (i = 0; i < SQUASHFS_META_NUMBER; i++) { ++ msblk->meta_index[i].inode_number = 0; ++ msblk->meta_index[i].locked = 0; ++ } ++ msblk->next_meta_index = 0; ++ } ++ ++ for (i = SQUASHFS_META_NUMBER; i && ++ msblk->meta_index[msblk->next_meta_index].locked; i --) ++ msblk->next_meta_index = (msblk->next_meta_index + 1) % ++ SQUASHFS_META_NUMBER; ++ ++ if (i == 0) { ++ TRACE("empty_meta_index: failed!\n"); ++ goto failed; ++ } ++ ++ TRACE("empty_meta_index: returned meta entry %d, %p\n", ++ msblk->next_meta_index, ++ &msblk->meta_index[msblk->next_meta_index]); ++ ++ meta = &msblk->meta_index[msblk->next_meta_index]; ++ msblk->next_meta_index = (msblk->next_meta_index + 1) % ++ SQUASHFS_META_NUMBER; ++ ++ meta->inode_number = inode->i_ino; ++ meta->offset = offset; ++ meta->skip = skip; ++ meta->entries = 0; ++ meta->locked = 1; ++ ++failed: ++ mutex_unlock(&msblk->meta_index_mutex); ++ return meta; ++} ++ ++ ++void release_meta_index(struct inode *inode, struct meta_index *meta) ++{ ++ meta->locked = 0; ++ smp_mb(); ++} ++ ++ ++static int read_block_index(struct super_block *s, int blocks, char *block_list, ++ long long *start_block, int *offset) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ unsigned int *block_listp; ++ int block = 0; ++ ++ if (msblk->swap) { ++ char sblock_list[blocks << 2]; ++ ++ if (!squashfs_get_cached_block(s, sblock_list, *start_block, ++ *offset, blocks << 2, start_block, offset)) { ++ ERROR("Fail reading block list [%llx:%x]\n", *start_block, *offset); ++ goto failure; ++ } ++ SQUASHFS_SWAP_INTS(((unsigned int *)block_list), ++ ((unsigned int *)sblock_list), blocks); ++ } else { ++ if (!squashfs_get_cached_block(s, block_list, *start_block, ++ *offset, blocks << 2, start_block, offset)) { ++ ERROR("Fail reading block list [%llx:%x]\n", *start_block, *offset); ++ goto failure; ++ } ++ } ++ ++ for (block_listp = (unsigned int *) block_list; blocks; ++ block_listp++, blocks --) ++ block += SQUASHFS_COMPRESSED_SIZE_BLOCK(*block_listp); ++ ++ return block; ++ ++failure: ++ return -1; ++} ++ ++ ++#define SIZE 256 ++ ++static inline int calculate_skip(int blocks) { ++ int skip = (blocks - 1) / ((SQUASHFS_SLOTS * SQUASHFS_META_ENTRIES + 1) * SQUASHFS_META_INDEXES); ++ return skip >= 7 ? 7 : skip + 1; ++} ++ ++ ++static int get_meta_index(struct inode *inode, int index, ++ long long *index_block, int *index_offset, ++ long long *data_block, char *block_list) ++{ ++ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ int skip = calculate_skip(i_size_read(inode) >> sblk->block_log); ++ int offset = 0; ++ struct meta_index *meta; ++ struct meta_entry *meta_entry; ++ long long cur_index_block = SQUASHFS_I(inode)->u.s1.block_list_start; ++ int cur_offset = SQUASHFS_I(inode)->offset; ++ long long cur_data_block = SQUASHFS_I(inode)->start_block; ++ int i; ++ ++ index /= SQUASHFS_META_INDEXES * skip; ++ ++ while (offset < index) { ++ meta = locate_meta_index(inode, index, offset + 1); ++ ++ if (meta == NULL) { ++ meta = empty_meta_index(inode, offset + 1, skip); ++ if (meta == NULL) ++ goto all_done; ++ } else { ++ if(meta->entries == 0) ++ goto failed; ++ /* XXX */ ++ offset = index < meta->offset + meta->entries ? index : ++ meta->offset + meta->entries - 1; ++ /* XXX */ ++ meta_entry = &meta->meta_entry[offset - meta->offset]; ++ cur_index_block = meta_entry->index_block + sblk->inode_table_start; ++ cur_offset = meta_entry->offset; ++ cur_data_block = meta_entry->data_block; ++ TRACE("get_meta_index: offset %d, meta->offset %d, " ++ "meta->entries %d\n", offset, meta->offset, meta->entries); ++ TRACE("get_meta_index: index_block 0x%llx, offset 0x%x" ++ " data_block 0x%llx\n", cur_index_block, ++ cur_offset, cur_data_block); ++ } ++ ++ for (i = meta->offset + meta->entries; i <= index && ++ i < meta->offset + SQUASHFS_META_ENTRIES; i++) { ++ int blocks = skip * SQUASHFS_META_INDEXES; ++ ++ while (blocks) { ++ int block = blocks > (SIZE >> 2) ? (SIZE >> 2) : blocks; ++ int res = read_block_index(inode->i_sb, block, block_list, ++ &cur_index_block, &cur_offset); ++ ++ if (res == -1) ++ goto failed; ++ ++ cur_data_block += res; ++ blocks -= block; ++ } ++ ++ meta_entry = &meta->meta_entry[i - meta->offset]; ++ meta_entry->index_block = cur_index_block - sblk->inode_table_start; ++ meta_entry->offset = cur_offset; ++ meta_entry->data_block = cur_data_block; ++ meta->entries ++; ++ offset ++; ++ } ++ ++ TRACE("get_meta_index: meta->offset %d, meta->entries %d\n", ++ meta->offset, meta->entries); ++ ++ release_meta_index(inode, meta); ++ } ++ ++all_done: ++ *index_block = cur_index_block; ++ *index_offset = cur_offset; ++ *data_block = cur_data_block; ++ ++ return offset * SQUASHFS_META_INDEXES * skip; ++ ++failed: ++ release_meta_index(inode, meta); ++ return -1; ++} ++ ++ ++static long long read_blocklist(struct inode *inode, int index, ++ int readahead_blks, char *block_list, ++ unsigned short **block_p, unsigned int *bsize) ++{ ++ long long block_ptr; ++ int offset; ++ long long block; ++ int res = get_meta_index(inode, index, &block_ptr, &offset, &block, ++ block_list); ++ ++ TRACE("read_blocklist: res %d, index %d, block_ptr 0x%llx, offset" ++ " 0x%x, block 0x%llx\n", res, index, block_ptr, offset, block); ++ ++ if(res == -1) ++ goto failure; ++ ++ index -= res; ++ ++ while (index) { ++ int blocks = index > (SIZE >> 2) ? (SIZE >> 2) : index; ++ int res = read_block_index(inode->i_sb, blocks, block_list, ++ &block_ptr, &offset); ++ if (res == -1) ++ goto failure; ++ block += res; ++ index -= blocks; ++ } ++ ++ if (read_block_index(inode->i_sb, 1, block_list, &block_ptr, &offset) == -1) ++ goto failure; ++ *bsize = *((unsigned int *) block_list); ++ ++ return block; ++ ++failure: ++ return 0; ++} ++ ++ ++static int squashfs_readpage(struct file *file, struct page *page) ++{ ++ struct inode *inode = page->mapping->host; ++ struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ unsigned char *block_list = NULL; ++ long long block; ++ unsigned int bsize, i; ++ int bytes; ++ int index = page->index >> (sblk->block_log - PAGE_CACHE_SHIFT); ++ void *pageaddr; ++ struct squashfs_fragment_cache *fragment = NULL; ++ char *data_ptr = msblk->read_page; ++ ++ int mask = (1 << (sblk->block_log - PAGE_CACHE_SHIFT)) - 1; ++ int start_index = page->index & ~mask; ++ int end_index = start_index | mask; ++ int file_end = i_size_read(inode) >> sblk->block_log; ++ int sparse = 0; ++ ++ TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n", ++ page->index, SQUASHFS_I(inode)->start_block); ++ ++ if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >> ++ PAGE_CACHE_SHIFT)) ++ goto out; ++ ++ if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK ++ || index < file_end) { ++ block_list = kmalloc(SIZE, GFP_KERNEL); ++ if (block_list == NULL) { ++ ERROR("Failed to allocate block_list\n"); ++ goto error_out; ++ } ++ ++ block = (msblk->read_blocklist)(inode, index, 1, block_list, NULL, &bsize); ++ if (block == 0) ++ goto error_out; ++ ++ if (bsize == 0) { /* hole */ ++ bytes = index == file_end ? ++ (i_size_read(inode) & (sblk->block_size - 1)) : sblk->block_size; ++ sparse = 1; ++ } else { ++ mutex_lock(&msblk->read_page_mutex); ++ ++ bytes = squashfs_read_data(inode->i_sb, msblk->read_page, block, ++ bsize, NULL, sblk->block_size); ++ ++ if (bytes == 0) { ++ ERROR("Unable to read page, block %llx, size %x\n", block, bsize); ++ mutex_unlock(&msblk->read_page_mutex); ++ goto error_out; ++ } ++ } ++ } else { ++ fragment = get_cached_fragment(inode->i_sb, ++ SQUASHFS_I(inode)-> u.s1.fragment_start_block, ++ SQUASHFS_I(inode)->u.s1.fragment_size); ++ ++ if (fragment == NULL) { ++ ERROR("Unable to read page, block %llx, size %x\n", ++ SQUASHFS_I(inode)->u.s1.fragment_start_block, ++ (int) SQUASHFS_I(inode)->u.s1.fragment_size); ++ goto error_out; ++ } ++ bytes = i_size_read(inode) & (sblk->block_size - 1); ++ data_ptr = fragment->data + SQUASHFS_I(inode)->u.s1.fragment_offset; ++ } ++ ++ for (i = start_index; i <= end_index && bytes > 0; i++, ++ bytes -= PAGE_CACHE_SIZE, data_ptr += PAGE_CACHE_SIZE) { ++ struct page *push_page; ++ int avail = sparse ? 0 : min_t(unsigned int, bytes, PAGE_CACHE_SIZE); ++ ++ TRACE("bytes %d, i %d, available_bytes %d\n", bytes, i, avail); ++ ++ push_page = (i == page->index) ? page : ++ grab_cache_page_nowait(page->mapping, i); ++ ++ if (!push_page) ++ continue; ++ ++ if (PageUptodate(push_page)) ++ goto skip_page; ++ ++ pageaddr = kmap_atomic(push_page, KM_USER0); ++ memcpy(pageaddr, data_ptr, avail); ++ memset(pageaddr + avail, 0, PAGE_CACHE_SIZE - avail); ++ kunmap_atomic(pageaddr, KM_USER0); ++ flush_dcache_page(push_page); ++ SetPageUptodate(push_page); ++skip_page: ++ unlock_page(push_page); ++ if(i != page->index) ++ page_cache_release(push_page); ++ } ++ ++ if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK ++ || index < file_end) { ++ if (!sparse) ++ mutex_unlock(&msblk->read_page_mutex); ++ kfree(block_list); ++ } else ++ release_cached_fragment(msblk, fragment); ++ ++ return 0; ++ ++error_out: ++ SetPageError(page); ++out: ++ pageaddr = kmap_atomic(page, KM_USER0); ++ memset(pageaddr, 0, PAGE_CACHE_SIZE); ++ kunmap_atomic(pageaddr, KM_USER0); ++ flush_dcache_page(page); ++ if (!PageError(page)) ++ SetPageUptodate(page); ++ unlock_page(page); ++ ++ kfree(block_list); ++ return 0; ++} ++ ++ ++static int get_dir_index_using_offset(struct super_block *s, ++ long long *next_block, unsigned int *next_offset, ++ long long index_start, unsigned int index_offset, int i_count, ++ long long f_pos) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ int i, length = 0; ++ struct squashfs_dir_index index; ++ ++ TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %d\n", ++ i_count, (unsigned int) f_pos); ++ ++ f_pos =- 3; ++ if (f_pos == 0) ++ goto finish; ++ ++ for (i = 0; i < i_count; i++) { ++ if (msblk->swap) { ++ struct squashfs_dir_index sindex; ++ squashfs_get_cached_block(s, &sindex, index_start, index_offset, ++ sizeof(sindex), &index_start, &index_offset); ++ SQUASHFS_SWAP_DIR_INDEX(&index, &sindex); ++ } else ++ squashfs_get_cached_block(s, &index, index_start, index_offset, ++ sizeof(index), &index_start, &index_offset); ++ ++ if (index.index > f_pos) ++ break; ++ ++ squashfs_get_cached_block(s, NULL, index_start, index_offset, ++ index.size + 1, &index_start, &index_offset); ++ ++ length = index.index; ++ *next_block = index.start_block + sblk->directory_table_start; ++ } ++ ++ *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE; ++ ++finish: ++ return length + 3; ++} ++ ++ ++static int get_dir_index_using_name(struct super_block *s, ++ long long *next_block, unsigned int *next_offset, ++ long long index_start, unsigned int index_offset, int i_count, ++ const char *name, int size) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ int i, length = 0; ++ struct squashfs_dir_index *index; ++ char *str; ++ ++ TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count); ++ ++ str = kmalloc(sizeof(struct squashfs_dir_index) + ++ (SQUASHFS_NAME_LEN + 1) * 2, GFP_KERNEL); ++ if (str == NULL) { ++ ERROR("Failed to allocate squashfs_dir_index\n"); ++ goto failure; ++ } ++ ++ index = (struct squashfs_dir_index *) (str + SQUASHFS_NAME_LEN + 1); ++ strncpy(str, name, size); ++ str[size] = '\0'; ++ ++ for (i = 0; i < i_count; i++) { ++ if (msblk->swap) { ++ struct squashfs_dir_index sindex; ++ squashfs_get_cached_block(s, &sindex, index_start, index_offset, ++ sizeof(sindex), &index_start, &index_offset); ++ SQUASHFS_SWAP_DIR_INDEX(index, &sindex); ++ } else ++ squashfs_get_cached_block(s, index, index_start, index_offset, ++ sizeof(struct squashfs_dir_index), &index_start, &index_offset); ++ ++ squashfs_get_cached_block(s, index->name, index_start, index_offset, ++ index->size + 1, &index_start, &index_offset); ++ ++ index->name[index->size + 1] = '\0'; ++ ++ if (strcmp(index->name, str) > 0) ++ break; ++ ++ length = index->index; ++ *next_block = index->start_block + sblk->directory_table_start; ++ } ++ ++ *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE; ++ kfree(str); ++ ++failure: ++ return length + 3; ++} ++ ++ ++static int squashfs_readdir(struct file *file, void *dirent, filldir_t filldir) ++{ ++ struct inode *i = file->f_dentry->d_inode; ++ struct squashfs_sb_info *msblk = i->i_sb->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ long long next_block = SQUASHFS_I(i)->start_block + ++ sblk->directory_table_start; ++ int next_offset = SQUASHFS_I(i)->offset, length = 0, dir_count; ++ struct squashfs_dir_header dirh; ++ struct squashfs_dir_entry *dire; ++ ++ TRACE("Entered squashfs_readdir [%llx:%x]\n", next_block, next_offset); ++ ++ dire = kmalloc(sizeof(struct squashfs_dir_entry) + ++ SQUASHFS_NAME_LEN + 1, GFP_KERNEL); ++ if (dire == NULL) { ++ ERROR("Failed to allocate squashfs_dir_entry\n"); ++ goto finish; ++ } ++ ++ while(file->f_pos < 3) { ++ char *name; ++ int size, i_ino; ++ ++ if(file->f_pos == 0) { ++ name = "."; ++ size = 1; ++ i_ino = i->i_ino; ++ } else { ++ name = ".."; ++ size = 2; ++ i_ino = SQUASHFS_I(i)->u.s2.parent_inode; ++ } ++ TRACE("Calling filldir(%x, %s, %d, %d, %d, %d)\n", ++ (unsigned int) dirent, name, size, (int) ++ file->f_pos, i_ino, squashfs_filetype_table[1]); ++ ++ if (filldir(dirent, name, size, file->f_pos, i_ino, ++ squashfs_filetype_table[1]) < 0) { ++ TRACE("Filldir returned less than 0\n"); ++ goto finish; ++ } ++ file->f_pos += size; ++ } ++ ++ length = get_dir_index_using_offset(i->i_sb, &next_block, &next_offset, ++ SQUASHFS_I(i)->u.s2.directory_index_start, ++ SQUASHFS_I(i)->u.s2.directory_index_offset, ++ SQUASHFS_I(i)->u.s2.directory_index_count, file->f_pos); ++ ++ while (length < i_size_read(i)) { ++ /* read directory header */ ++ if (msblk->swap) { ++ struct squashfs_dir_header sdirh; ++ ++ if (!squashfs_get_cached_block(i->i_sb, &sdirh, next_block, ++ next_offset, sizeof(sdirh), &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(sdirh); ++ SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh); ++ } else { ++ if (!squashfs_get_cached_block(i->i_sb, &dirh, next_block, ++ next_offset, sizeof(dirh), &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(dirh); ++ } ++ ++ dir_count = dirh.count + 1; ++ while (dir_count--) { ++ if (msblk->swap) { ++ struct squashfs_dir_entry sdire; ++ if (!squashfs_get_cached_block(i->i_sb, &sdire, next_block, ++ next_offset, sizeof(sdire), &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(sdire); ++ SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire); ++ } else { ++ if (!squashfs_get_cached_block(i->i_sb, dire, next_block, ++ next_offset, sizeof(*dire), &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(*dire); ++ } ++ ++ if (!squashfs_get_cached_block(i->i_sb, dire->name, next_block, ++ next_offset, dire->size + 1, &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += dire->size + 1; ++ ++ if (file->f_pos >= length) ++ continue; ++ ++ dire->name[dire->size + 1] = '\0'; ++ ++ TRACE("Calling filldir(%x, %s, %d, %d, %x:%x, %d, %d)\n", ++ (unsigned int) dirent, dire->name, dire->size + 1, ++ (int) file->f_pos, dirh.start_block, dire->offset, ++ dirh.inode_number + dire->inode_number, ++ squashfs_filetype_table[dire->type]); ++ ++ if (filldir(dirent, dire->name, dire->size + 1, file->f_pos, ++ dirh.inode_number + dire->inode_number, ++ squashfs_filetype_table[dire->type]) < 0) { ++ TRACE("Filldir returned less than 0\n"); ++ goto finish; ++ } ++ file->f_pos = length; ++ } ++ } ++ ++finish: ++ kfree(dire); ++ return 0; ++ ++failed_read: ++ ERROR("Unable to read directory block [%llx:%x]\n", next_block, ++ next_offset); ++ kfree(dire); ++ return 0; ++} ++ ++ ++static struct dentry *squashfs_lookup(struct inode *i, struct dentry *dentry, ++ struct nameidata *nd) ++{ ++ const unsigned char *name = dentry->d_name.name; ++ int len = dentry->d_name.len; ++ struct inode *inode = NULL; ++ struct squashfs_sb_info *msblk = i->i_sb->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ long long next_block = SQUASHFS_I(i)->start_block + ++ sblk->directory_table_start; ++ int next_offset = SQUASHFS_I(i)->offset, length = 0, dir_count; ++ struct squashfs_dir_header dirh; ++ struct squashfs_dir_entry *dire; ++ ++ TRACE("Entered squashfs_lookup [%llx:%x]\n", next_block, next_offset); ++ ++ dire = kmalloc(sizeof(struct squashfs_dir_entry) + ++ SQUASHFS_NAME_LEN + 1, GFP_KERNEL); ++ if (dire == NULL) { ++ ERROR("Failed to allocate squashfs_dir_entry\n"); ++ goto exit_lookup; ++ } ++ ++ if (len > SQUASHFS_NAME_LEN) ++ goto exit_lookup; ++ ++ length = get_dir_index_using_name(i->i_sb, &next_block, &next_offset, ++ SQUASHFS_I(i)->u.s2.directory_index_start, ++ SQUASHFS_I(i)->u.s2.directory_index_offset, ++ SQUASHFS_I(i)->u.s2.directory_index_count, name, len); ++ ++ while (length < i_size_read(i)) { ++ /* read directory header */ ++ if (msblk->swap) { ++ struct squashfs_dir_header sdirh; ++ if (!squashfs_get_cached_block(i->i_sb, &sdirh, next_block, ++ next_offset, sizeof(sdirh), &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(sdirh); ++ SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh); ++ } else { ++ if (!squashfs_get_cached_block(i->i_sb, &dirh, next_block, ++ next_offset, sizeof(dirh), &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(dirh); ++ } ++ ++ dir_count = dirh.count + 1; ++ while (dir_count--) { ++ if (msblk->swap) { ++ struct squashfs_dir_entry sdire; ++ if (!squashfs_get_cached_block(i->i_sb, &sdire, next_block, ++ next_offset, sizeof(sdire), &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(sdire); ++ SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire); ++ } else { ++ if (!squashfs_get_cached_block(i->i_sb, dire, next_block, ++ next_offset, sizeof(*dire), &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(*dire); ++ } ++ ++ if (!squashfs_get_cached_block(i->i_sb, dire->name, next_block, ++ next_offset, dire->size + 1, &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += dire->size + 1; ++ ++ if (name[0] < dire->name[0]) ++ goto exit_lookup; ++ ++ if ((len == dire->size + 1) && !strncmp(name, dire->name, len)) { ++ squashfs_inode_t ino = SQUASHFS_MKINODE(dirh.start_block, ++ dire->offset); ++ ++ TRACE("calling squashfs_iget for directory entry %s, inode" ++ " %x:%x, %d\n", name, dirh.start_block, dire->offset, ++ dirh.inode_number + dire->inode_number); ++ ++ inode = squashfs_iget(i->i_sb, ino, dirh.inode_number + dire->inode_number); ++ ++ goto exit_lookup; ++ } ++ } ++ } ++ ++exit_lookup: ++ kfree(dire); ++ if (inode) ++ return d_splice_alias(inode, dentry); ++ d_add(dentry, inode); ++ return ERR_PTR(0); ++ ++failed_read: ++ ERROR("Unable to read directory block [%llx:%x]\n", next_block, ++ next_offset); ++ goto exit_lookup; ++} ++ ++ ++static int squashfs_remount(struct super_block *s, int *flags, char *data) ++{ ++ *flags |= MS_RDONLY; ++ return 0; ++} ++ ++ ++static void squashfs_put_super(struct super_block *s) ++{ ++ int i; ++ ++ if (s->s_fs_info) { ++ struct squashfs_sb_info *sbi = s->s_fs_info; ++ if (sbi->block_cache) ++ for (i = 0; i < squashfs_cached_blks; i++) ++ if (sbi->block_cache[i].block != SQUASHFS_INVALID_BLK) ++ vfree(sbi->block_cache[i].data); ++ if (sbi->fragment) ++ for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS; i++) ++ vfree(sbi->fragment[i].data); ++ kfree(sbi->fragment); ++ kfree(sbi->block_cache); ++ vfree(sbi->read_page); ++ kfree(sbi->uid); ++ kfree(sbi->fragment_index); ++ kfree(sbi->fragment_index_2); ++ kfree(sbi->meta_index); ++ vfree(sbi->stream.workspace); ++ kfree(s->s_fs_info); ++ s->s_fs_info = NULL; ++ } ++} ++ ++ ++static int squashfs_get_sb(struct file_system_type *fs_type, int flags, ++ const char *dev_name, void *data, struct vfsmount *mnt) ++{ ++ return get_sb_bdev(fs_type, flags, dev_name, data, squashfs_fill_super, ++ mnt); ++} ++ ++ ++static int __init init_squashfs_fs(void) ++{ ++ int err = init_inodecache(); ++ if (err) ++ goto out; ++ ++ printk(KERN_INFO "squashfs: version 3.3 (2007/10/31) " ++ "Phillip Lougher\n"); ++ ++ err = register_filesystem(&squashfs_fs_type); ++ if (err) ++ destroy_inodecache(); ++ ++out: ++ return err; ++} ++ ++ ++static void __exit exit_squashfs_fs(void) ++{ ++ unregister_filesystem(&squashfs_fs_type); ++ destroy_inodecache(); ++} ++ ++ ++static struct kmem_cache * squashfs_inode_cachep; ++ ++ ++static struct inode *squashfs_alloc_inode(struct super_block *sb) ++{ ++ struct squashfs_inode_info *ei; ++ ei = kmem_cache_alloc(squashfs_inode_cachep, GFP_KERNEL); ++ return ei ? &ei->vfs_inode : NULL; ++} ++ ++ ++static void squashfs_destroy_inode(struct inode *inode) ++{ ++ kmem_cache_free(squashfs_inode_cachep, SQUASHFS_I(inode)); ++} ++ ++ ++static void init_once(struct kmem_cache *cachep, void *foo) ++{ ++ struct squashfs_inode_info *ei = foo; ++ ++ inode_init_once(&ei->vfs_inode); ++} ++ ++ ++static int __init init_inodecache(void) ++{ ++ squashfs_inode_cachep = kmem_cache_create("squashfs_inode_cache", ++ sizeof(struct squashfs_inode_info), 0, ++ SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, init_once); ++ if (squashfs_inode_cachep == NULL) ++ return -ENOMEM; ++ return 0; ++} ++ ++ ++static void destroy_inodecache(void) ++{ ++ kmem_cache_destroy(squashfs_inode_cachep); ++} ++ ++ ++module_init(init_squashfs_fs); ++module_exit(exit_squashfs_fs); ++MODULE_DESCRIPTION("squashfs 3.2-r2-CVS, a compressed read-only filesystem"); ++MODULE_AUTHOR("Phillip Lougher <phillip@lougher.demon.co.uk>"); ++MODULE_LICENSE("GPL"); +diff -x .gitignore -Nurp linux-2.6.24/fs/squashfs/Makefile linux-2.6.24-squashfs3.3/fs/squashfs/Makefile +--- linux-2.6.24/fs/squashfs/Makefile 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24-squashfs3.3/fs/squashfs/Makefile 2005-11-20 14:31:00.000000000 +0000 +@@ -0,0 +1,7 @@ ++# ++# Makefile for the linux squashfs routines. ++# ++ ++obj-$(CONFIG_SQUASHFS) += squashfs.o ++squashfs-y += inode.o ++squashfs-y += squashfs2_0.o +diff -x .gitignore -Nurp linux-2.6.24/fs/squashfs/squashfs2_0.c linux-2.6.24-squashfs3.3/fs/squashfs/squashfs2_0.c +--- linux-2.6.24/fs/squashfs/squashfs2_0.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24-squashfs3.3/fs/squashfs/squashfs2_0.c 2007-10-25 00:43:59.000000000 +0100 +@@ -0,0 +1,740 @@ ++/* ++ * Squashfs - a compressed read only filesystem for Linux ++ * ++ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007 ++ * Phillip Lougher <phillip@lougher.demon.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2, ++ * 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * squashfs2_0.c ++ */ ++ ++#include <linux/squashfs_fs.h> ++#include <linux/module.h> ++#include <linux/zlib.h> ++#include <linux/fs.h> ++#include <linux/squashfs_fs_sb.h> ++#include <linux/squashfs_fs_i.h> ++ ++#include "squashfs.h" ++static int squashfs_readdir_2(struct file *file, void *dirent, filldir_t filldir); ++static struct dentry *squashfs_lookup_2(struct inode *, struct dentry *, ++ struct nameidata *); ++ ++static struct file_operations squashfs_dir_ops_2 = { ++ .read = generic_read_dir, ++ .readdir = squashfs_readdir_2 ++}; ++ ++static struct inode_operations squashfs_dir_inode_ops_2 = { ++ .lookup = squashfs_lookup_2 ++}; ++ ++static unsigned char squashfs_filetype_table[] = { ++ DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK ++}; ++ ++static int read_fragment_index_table_2(struct super_block *s) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ ++ if (!(msblk->fragment_index_2 = kmalloc(SQUASHFS_FRAGMENT_INDEX_BYTES_2 ++ (sblk->fragments), GFP_KERNEL))) { ++ ERROR("Failed to allocate uid/gid table\n"); ++ return 0; ++ } ++ ++ if (SQUASHFS_FRAGMENT_INDEX_BYTES_2(sblk->fragments) && ++ !squashfs_read_data(s, (char *) ++ msblk->fragment_index_2, ++ sblk->fragment_table_start, ++ SQUASHFS_FRAGMENT_INDEX_BYTES_2 ++ (sblk->fragments) | ++ SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, SQUASHFS_FRAGMENT_INDEX_BYTES_2(sblk->fragments))) { ++ ERROR("unable to read fragment index table\n"); ++ return 0; ++ } ++ ++ if (msblk->swap) { ++ int i; ++ unsigned int fragment; ++ ++ for (i = 0; i < SQUASHFS_FRAGMENT_INDEXES_2(sblk->fragments); ++ i++) { ++ SQUASHFS_SWAP_FRAGMENT_INDEXES_2((&fragment), ++ &msblk->fragment_index_2[i], 1); ++ msblk->fragment_index_2[i] = fragment; ++ } ++ } ++ ++ return 1; ++} ++ ++ ++static int get_fragment_location_2(struct super_block *s, unsigned int fragment, ++ long long *fragment_start_block, ++ unsigned int *fragment_size) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ long long start_block = ++ msblk->fragment_index_2[SQUASHFS_FRAGMENT_INDEX_2(fragment)]; ++ int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET_2(fragment); ++ struct squashfs_fragment_entry_2 fragment_entry; ++ ++ if (msblk->swap) { ++ struct squashfs_fragment_entry_2 sfragment_entry; ++ ++ if (!squashfs_get_cached_block(s, (char *) &sfragment_entry, ++ start_block, offset, ++ sizeof(sfragment_entry), &start_block, ++ &offset)) ++ goto out; ++ SQUASHFS_SWAP_FRAGMENT_ENTRY_2(&fragment_entry, &sfragment_entry); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) &fragment_entry, ++ start_block, offset, ++ sizeof(fragment_entry), &start_block, ++ &offset)) ++ goto out; ++ ++ *fragment_start_block = fragment_entry.start_block; ++ *fragment_size = fragment_entry.size; ++ ++ return 1; ++ ++out: ++ return 0; ++} ++ ++ ++static void squashfs_new_inode(struct squashfs_sb_info *msblk, struct inode *i, ++ struct squashfs_base_inode_header_2 *inodeb, unsigned int ino) ++{ ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ ++ i->i_ino = ino; ++ i->i_mtime.tv_sec = sblk->mkfs_time; ++ i->i_atime.tv_sec = sblk->mkfs_time; ++ i->i_ctime.tv_sec = sblk->mkfs_time; ++ i->i_uid = msblk->uid[inodeb->uid]; ++ i->i_mode = inodeb->mode; ++ i->i_nlink = 1; ++ i->i_size = 0; ++ if (inodeb->guid == SQUASHFS_GUIDS) ++ i->i_gid = i->i_uid; ++ else ++ i->i_gid = msblk->guid[inodeb->guid]; ++} ++ ++ ++static int squashfs_read_inode_2(struct inode *i, squashfs_inode_t inode) ++{ ++ struct super_block *s = i->i_sb; ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ unsigned int block = SQUASHFS_INODE_BLK(inode) + ++ sblk->inode_table_start; ++ unsigned int offset = SQUASHFS_INODE_OFFSET(inode); ++ unsigned int ino = SQUASHFS_MK_VFS_INODE(block - ++ sblk->inode_table_start, offset); ++ long long next_block; ++ unsigned int next_offset; ++ union squashfs_inode_header_2 id, sid; ++ struct squashfs_base_inode_header_2 *inodeb = &id.base, ++ *sinodeb = &sid.base; ++ ++ TRACE("Entered squashfs_read_inode_2\n"); ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) sinodeb, block, ++ offset, sizeof(*sinodeb), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_BASE_INODE_HEADER_2(inodeb, sinodeb, ++ sizeof(*sinodeb)); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) inodeb, block, ++ offset, sizeof(*inodeb), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ squashfs_new_inode(msblk, i, inodeb, ino); ++ ++ switch(inodeb->inode_type) { ++ case SQUASHFS_FILE_TYPE: { ++ struct squashfs_reg_inode_header_2 *inodep = &id.reg; ++ struct squashfs_reg_inode_header_2 *sinodep = &sid.reg; ++ long long frag_blk; ++ unsigned int frag_size = 0; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) ++ sinodep, block, offset, ++ sizeof(*sinodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_REG_INODE_HEADER_2(inodep, sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) ++ inodep, block, offset, ++ sizeof(*inodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ frag_blk = SQUASHFS_INVALID_BLK; ++ if (inodep->fragment != SQUASHFS_INVALID_FRAG && ++ !get_fragment_location_2(s, ++ inodep->fragment, &frag_blk, &frag_size)) ++ goto failed_read; ++ ++ i->i_size = inodep->file_size; ++ i->i_fop = &generic_ro_fops; ++ i->i_mode |= S_IFREG; ++ i->i_mtime.tv_sec = inodep->mtime; ++ i->i_atime.tv_sec = inodep->mtime; ++ i->i_ctime.tv_sec = inodep->mtime; ++ i->i_blocks = ((i->i_size - 1) >> 9) + 1; ++ SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk; ++ SQUASHFS_I(i)->u.s1.fragment_size = frag_size; ++ SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset; ++ SQUASHFS_I(i)->start_block = inodep->start_block; ++ SQUASHFS_I(i)->u.s1.block_list_start = next_block; ++ SQUASHFS_I(i)->offset = next_offset; ++ i->i_data.a_ops = &squashfs_aops; ++ ++ TRACE("File inode %x:%x, start_block %x, " ++ "block_list_start %llx, offset %x\n", ++ SQUASHFS_INODE_BLK(inode), offset, ++ inodep->start_block, next_block, ++ next_offset); ++ break; ++ } ++ case SQUASHFS_DIR_TYPE: { ++ struct squashfs_dir_inode_header_2 *inodep = &id.dir; ++ struct squashfs_dir_inode_header_2 *sinodep = &sid.dir; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) ++ sinodep, block, offset, ++ sizeof(*sinodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_DIR_INODE_HEADER_2(inodep, sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) ++ inodep, block, offset, ++ sizeof(*inodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ i->i_size = inodep->file_size; ++ i->i_op = &squashfs_dir_inode_ops_2; ++ i->i_fop = &squashfs_dir_ops_2; ++ i->i_mode |= S_IFDIR; ++ i->i_mtime.tv_sec = inodep->mtime; ++ i->i_atime.tv_sec = inodep->mtime; ++ i->i_ctime.tv_sec = inodep->mtime; ++ SQUASHFS_I(i)->start_block = inodep->start_block; ++ SQUASHFS_I(i)->offset = inodep->offset; ++ SQUASHFS_I(i)->u.s2.directory_index_count = 0; ++ SQUASHFS_I(i)->u.s2.parent_inode = 0; ++ ++ TRACE("Directory inode %x:%x, start_block %x, offset " ++ "%x\n", SQUASHFS_INODE_BLK(inode), ++ offset, inodep->start_block, ++ inodep->offset); ++ break; ++ } ++ case SQUASHFS_LDIR_TYPE: { ++ struct squashfs_ldir_inode_header_2 *inodep = &id.ldir; ++ struct squashfs_ldir_inode_header_2 *sinodep = &sid.ldir; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) ++ sinodep, block, offset, ++ sizeof(*sinodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_LDIR_INODE_HEADER_2(inodep, ++ sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) ++ inodep, block, offset, ++ sizeof(*inodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ i->i_size = inodep->file_size; ++ i->i_op = &squashfs_dir_inode_ops_2; ++ i->i_fop = &squashfs_dir_ops_2; ++ i->i_mode |= S_IFDIR; ++ i->i_mtime.tv_sec = inodep->mtime; ++ i->i_atime.tv_sec = inodep->mtime; ++ i->i_ctime.tv_sec = inodep->mtime; ++ SQUASHFS_I(i)->start_block = inodep->start_block; ++ SQUASHFS_I(i)->offset = inodep->offset; ++ SQUASHFS_I(i)->u.s2.directory_index_start = next_block; ++ SQUASHFS_I(i)->u.s2.directory_index_offset = ++ next_offset; ++ SQUASHFS_I(i)->u.s2.directory_index_count = ++ inodep->i_count; ++ SQUASHFS_I(i)->u.s2.parent_inode = 0; ++ ++ TRACE("Long directory inode %x:%x, start_block %x, " ++ "offset %x\n", ++ SQUASHFS_INODE_BLK(inode), offset, ++ inodep->start_block, inodep->offset); ++ break; ++ } ++ case SQUASHFS_SYMLINK_TYPE: { ++ struct squashfs_symlink_inode_header_2 *inodep = ++ &id.symlink; ++ struct squashfs_symlink_inode_header_2 *sinodep = ++ &sid.symlink; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) ++ sinodep, block, offset, ++ sizeof(*sinodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(inodep, ++ sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) ++ inodep, block, offset, ++ sizeof(*inodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ i->i_size = inodep->symlink_size; ++ i->i_op = &page_symlink_inode_operations; ++ i->i_data.a_ops = &squashfs_symlink_aops; ++ i->i_mode |= S_IFLNK; ++ SQUASHFS_I(i)->start_block = next_block; ++ SQUASHFS_I(i)->offset = next_offset; ++ ++ TRACE("Symbolic link inode %x:%x, start_block %llx, " ++ "offset %x\n", ++ SQUASHFS_INODE_BLK(inode), offset, ++ next_block, next_offset); ++ break; ++ } ++ case SQUASHFS_BLKDEV_TYPE: ++ case SQUASHFS_CHRDEV_TYPE: { ++ struct squashfs_dev_inode_header_2 *inodep = &id.dev; ++ struct squashfs_dev_inode_header_2 *sinodep = &sid.dev; ++ ++ if (msblk->swap) { ++ if (!squashfs_get_cached_block(s, (char *) ++ sinodep, block, offset, ++ sizeof(*sinodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ SQUASHFS_SWAP_DEV_INODE_HEADER_2(inodep, sinodep); ++ } else ++ if (!squashfs_get_cached_block(s, (char *) ++ inodep, block, offset, ++ sizeof(*inodep), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ i->i_mode |= (inodeb->inode_type == ++ SQUASHFS_CHRDEV_TYPE) ? S_IFCHR : ++ S_IFBLK; ++ init_special_inode(i, i->i_mode, ++ old_decode_dev(inodep->rdev)); ++ ++ TRACE("Device inode %x:%x, rdev %x\n", ++ SQUASHFS_INODE_BLK(inode), offset, ++ inodep->rdev); ++ break; ++ } ++ case SQUASHFS_FIFO_TYPE: ++ case SQUASHFS_SOCKET_TYPE: { ++ ++ i->i_mode |= (inodeb->inode_type == SQUASHFS_FIFO_TYPE) ++ ? S_IFIFO : S_IFSOCK; ++ init_special_inode(i, i->i_mode, 0); ++ break; ++ } ++ default: ++ ERROR("Unknown inode type %d in squashfs_iget!\n", ++ inodeb->inode_type); ++ goto failed_read1; ++ } ++ ++ return 1; ++ ++failed_read: ++ ERROR("Unable to read inode [%x:%x]\n", block, offset); ++ ++failed_read1: ++ return 0; ++} ++ ++ ++static int get_dir_index_using_offset(struct super_block *s, long long ++ *next_block, unsigned int *next_offset, ++ long long index_start, ++ unsigned int index_offset, int i_count, ++ long long f_pos) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ int i, length = 0; ++ struct squashfs_dir_index_2 index; ++ ++ TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %d\n", ++ i_count, (unsigned int) f_pos); ++ ++ if (f_pos == 0) ++ goto finish; ++ ++ for (i = 0; i < i_count; i++) { ++ if (msblk->swap) { ++ struct squashfs_dir_index_2 sindex; ++ squashfs_get_cached_block(s, (char *) &sindex, ++ index_start, index_offset, ++ sizeof(sindex), &index_start, ++ &index_offset); ++ SQUASHFS_SWAP_DIR_INDEX_2(&index, &sindex); ++ } else ++ squashfs_get_cached_block(s, (char *) &index, ++ index_start, index_offset, ++ sizeof(index), &index_start, ++ &index_offset); ++ ++ if (index.index > f_pos) ++ break; ++ ++ squashfs_get_cached_block(s, NULL, index_start, index_offset, ++ index.size + 1, &index_start, ++ &index_offset); ++ ++ length = index.index; ++ *next_block = index.start_block + sblk->directory_table_start; ++ } ++ ++ *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE; ++ ++finish: ++ return length; ++} ++ ++ ++static int get_dir_index_using_name(struct super_block *s, long long ++ *next_block, unsigned int *next_offset, ++ long long index_start, ++ unsigned int index_offset, int i_count, ++ const char *name, int size) ++{ ++ struct squashfs_sb_info *msblk = s->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ int i, length = 0; ++ struct squashfs_dir_index_2 *index; ++ char *str; ++ ++ TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count); ++ ++ if (!(str = kmalloc(sizeof(struct squashfs_dir_index) + ++ (SQUASHFS_NAME_LEN + 1) * 2, GFP_KERNEL))) { ++ ERROR("Failed to allocate squashfs_dir_index\n"); ++ goto failure; ++ } ++ ++ index = (struct squashfs_dir_index_2 *) (str + SQUASHFS_NAME_LEN + 1); ++ strncpy(str, name, size); ++ str[size] = '\0'; ++ ++ for (i = 0; i < i_count; i++) { ++ if (msblk->swap) { ++ struct squashfs_dir_index_2 sindex; ++ squashfs_get_cached_block(s, (char *) &sindex, ++ index_start, index_offset, ++ sizeof(sindex), &index_start, ++ &index_offset); ++ SQUASHFS_SWAP_DIR_INDEX_2(index, &sindex); ++ } else ++ squashfs_get_cached_block(s, (char *) index, ++ index_start, index_offset, ++ sizeof(struct squashfs_dir_index_2), ++ &index_start, &index_offset); ++ ++ squashfs_get_cached_block(s, index->name, index_start, ++ index_offset, index->size + 1, ++ &index_start, &index_offset); ++ ++ index->name[index->size + 1] = '\0'; ++ ++ if (strcmp(index->name, str) > 0) ++ break; ++ ++ length = index->index; ++ *next_block = index->start_block + sblk->directory_table_start; ++ } ++ ++ *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE; ++ kfree(str); ++failure: ++ return length; ++} ++ ++ ++static int squashfs_readdir_2(struct file *file, void *dirent, filldir_t filldir) ++{ ++ struct inode *i = file->f_dentry->d_inode; ++ struct squashfs_sb_info *msblk = i->i_sb->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ long long next_block = SQUASHFS_I(i)->start_block + ++ sblk->directory_table_start; ++ int next_offset = SQUASHFS_I(i)->offset, length = 0, ++ dir_count; ++ struct squashfs_dir_header_2 dirh; ++ struct squashfs_dir_entry_2 *dire; ++ ++ TRACE("Entered squashfs_readdir_2 [%llx:%x]\n", next_block, next_offset); ++ ++ if (!(dire = kmalloc(sizeof(struct squashfs_dir_entry) + ++ SQUASHFS_NAME_LEN + 1, GFP_KERNEL))) { ++ ERROR("Failed to allocate squashfs_dir_entry\n"); ++ goto finish; ++ } ++ ++ length = get_dir_index_using_offset(i->i_sb, &next_block, &next_offset, ++ SQUASHFS_I(i)->u.s2.directory_index_start, ++ SQUASHFS_I(i)->u.s2.directory_index_offset, ++ SQUASHFS_I(i)->u.s2.directory_index_count, ++ file->f_pos); ++ ++ while (length < i_size_read(i)) { ++ /* read directory header */ ++ if (msblk->swap) { ++ struct squashfs_dir_header_2 sdirh; ++ ++ if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh, ++ next_block, next_offset, sizeof(sdirh), ++ &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(sdirh); ++ SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh); ++ } else { ++ if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh, ++ next_block, next_offset, sizeof(dirh), ++ &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(dirh); ++ } ++ ++ dir_count = dirh.count + 1; ++ while (dir_count--) { ++ if (msblk->swap) { ++ struct squashfs_dir_entry_2 sdire; ++ if (!squashfs_get_cached_block(i->i_sb, (char *) ++ &sdire, next_block, next_offset, ++ sizeof(sdire), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(sdire); ++ SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire); ++ } else { ++ if (!squashfs_get_cached_block(i->i_sb, (char *) ++ dire, next_block, next_offset, ++ sizeof(*dire), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(*dire); ++ } ++ ++ if (!squashfs_get_cached_block(i->i_sb, dire->name, ++ next_block, next_offset, ++ dire->size + 1, &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ length += dire->size + 1; ++ ++ if (file->f_pos >= length) ++ continue; ++ ++ dire->name[dire->size + 1] = '\0'; ++ ++ TRACE("Calling filldir(%x, %s, %d, %d, %x:%x, %d)\n", ++ (unsigned int) dirent, dire->name, ++ dire->size + 1, (int) file->f_pos, ++ dirh.start_block, dire->offset, ++ squashfs_filetype_table[dire->type]); ++ ++ if (filldir(dirent, dire->name, dire->size + 1, ++ file->f_pos, SQUASHFS_MK_VFS_INODE( ++ dirh.start_block, dire->offset), ++ squashfs_filetype_table[dire->type]) ++ < 0) { ++ TRACE("Filldir returned less than 0\n"); ++ goto finish; ++ } ++ file->f_pos = length; ++ } ++ } ++ ++finish: ++ kfree(dire); ++ return 0; ++ ++failed_read: ++ ERROR("Unable to read directory block [%llx:%x]\n", next_block, ++ next_offset); ++ kfree(dire); ++ return 0; ++} ++ ++ ++static struct dentry *squashfs_lookup_2(struct inode *i, struct dentry *dentry, ++ struct nameidata *nd) ++{ ++ const unsigned char *name = dentry->d_name.name; ++ int len = dentry->d_name.len; ++ struct inode *inode = NULL; ++ struct squashfs_sb_info *msblk = i->i_sb->s_fs_info; ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ long long next_block = SQUASHFS_I(i)->start_block + ++ sblk->directory_table_start; ++ int next_offset = SQUASHFS_I(i)->offset, length = 0, ++ dir_count; ++ struct squashfs_dir_header_2 dirh; ++ struct squashfs_dir_entry_2 *dire; ++ int sorted = sblk->s_major == 2 && sblk->s_minor >= 1; ++ ++ TRACE("Entered squashfs_lookup_2 [%llx:%x]\n", next_block, next_offset); ++ ++ if (!(dire = kmalloc(sizeof(struct squashfs_dir_entry) + ++ SQUASHFS_NAME_LEN + 1, GFP_KERNEL))) { ++ ERROR("Failed to allocate squashfs_dir_entry\n"); ++ goto exit_loop; ++ } ++ ++ if (len > SQUASHFS_NAME_LEN) ++ goto exit_loop; ++ ++ length = get_dir_index_using_name(i->i_sb, &next_block, &next_offset, ++ SQUASHFS_I(i)->u.s2.directory_index_start, ++ SQUASHFS_I(i)->u.s2.directory_index_offset, ++ SQUASHFS_I(i)->u.s2.directory_index_count, name, ++ len); ++ ++ while (length < i_size_read(i)) { ++ /* read directory header */ ++ if (msblk->swap) { ++ struct squashfs_dir_header_2 sdirh; ++ if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh, ++ next_block, next_offset, sizeof(sdirh), ++ &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(sdirh); ++ SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh); ++ } else { ++ if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh, ++ next_block, next_offset, sizeof(dirh), ++ &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(dirh); ++ } ++ ++ dir_count = dirh.count + 1; ++ while (dir_count--) { ++ if (msblk->swap) { ++ struct squashfs_dir_entry_2 sdire; ++ if (!squashfs_get_cached_block(i->i_sb, (char *) ++ &sdire, next_block,next_offset, ++ sizeof(sdire), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(sdire); ++ SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire); ++ } else { ++ if (!squashfs_get_cached_block(i->i_sb, (char *) ++ dire, next_block,next_offset, ++ sizeof(*dire), &next_block, ++ &next_offset)) ++ goto failed_read; ++ ++ length += sizeof(*dire); ++ } ++ ++ if (!squashfs_get_cached_block(i->i_sb, dire->name, ++ next_block, next_offset, dire->size + 1, ++ &next_block, &next_offset)) ++ goto failed_read; ++ ++ length += dire->size + 1; ++ ++ if (sorted && name[0] < dire->name[0]) ++ goto exit_loop; ++ ++ if ((len == dire->size + 1) && !strncmp(name, ++ dire->name, len)) { ++ squashfs_inode_t ino = ++ SQUASHFS_MKINODE(dirh.start_block, ++ dire->offset); ++ unsigned int inode_number = SQUASHFS_MK_VFS_INODE(dirh.start_block, ++ dire->offset); ++ ++ TRACE("calling squashfs_iget for directory " ++ "entry %s, inode %x:%x, %lld\n", name, ++ dirh.start_block, dire->offset, ino); ++ ++ inode = squashfs_iget(i->i_sb, ino, inode_number); ++ ++ goto exit_loop; ++ } ++ } ++ } ++ ++exit_loop: ++ kfree(dire); ++ d_add(dentry, inode); ++ return ERR_PTR(0); ++ ++failed_read: ++ ERROR("Unable to read directory block [%llx:%x]\n", next_block, ++ next_offset); ++ goto exit_loop; ++} ++ ++ ++int squashfs_2_0_supported(struct squashfs_sb_info *msblk) ++{ ++ struct squashfs_super_block *sblk = &msblk->sblk; ++ ++ msblk->read_inode = squashfs_read_inode_2; ++ msblk->read_fragment_index_table = read_fragment_index_table_2; ++ ++ sblk->bytes_used = sblk->bytes_used_2; ++ sblk->uid_start = sblk->uid_start_2; ++ sblk->guid_start = sblk->guid_start_2; ++ sblk->inode_table_start = sblk->inode_table_start_2; ++ sblk->directory_table_start = sblk->directory_table_start_2; ++ sblk->fragment_table_start = sblk->fragment_table_start_2; ++ ++ return 1; ++} +diff -x .gitignore -Nurp linux-2.6.24/fs/squashfs/squashfs.h linux-2.6.24-squashfs3.3/fs/squashfs/squashfs.h +--- linux-2.6.24/fs/squashfs/squashfs.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24-squashfs3.3/fs/squashfs/squashfs.h 2007-08-19 04:23:16.000000000 +0100 +@@ -0,0 +1,86 @@ ++/* ++ * Squashfs - a compressed read only filesystem for Linux ++ * ++ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007 ++ * Phillip Lougher <phillip@lougher.demon.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2, ++ * 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * squashfs.h ++ */ ++ ++#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY ++#undef CONFIG_SQUASHFS_1_0_COMPATIBILITY ++#endif ++ ++#ifdef SQUASHFS_TRACE ++#define TRACE(s, args...) printk(KERN_NOTICE "SQUASHFS: "s, ## args) ++#else ++#define TRACE(s, args...) {} ++#endif ++ ++#define ERROR(s, args...) printk(KERN_ERR "SQUASHFS error: "s, ## args) ++ ++#define SERROR(s, args...) do { \ ++ if (!silent) \ ++ printk(KERN_ERR "SQUASHFS error: "s, ## args);\ ++ } while(0) ++ ++#define WARNING(s, args...) printk(KERN_WARNING "SQUASHFS: "s, ## args) ++ ++static inline struct squashfs_inode_info *SQUASHFS_I(struct inode *inode) ++{ ++ return list_entry(inode, struct squashfs_inode_info, vfs_inode); ++} ++ ++#if defined(CONFIG_SQUASHFS_1_0_COMPATIBILITY ) || defined(CONFIG_SQUASHFS_2_0_COMPATIBILITY) ++#define SQSH_EXTERN ++extern unsigned int squashfs_read_data(struct super_block *s, char *buffer, ++ long long index, unsigned int length, ++ long long *next_index, int srclength); ++extern int squashfs_get_cached_block(struct super_block *s, void *buffer, ++ long long block, unsigned int offset, ++ int length, long long *next_block, ++ unsigned int *next_offset); ++extern void release_cached_fragment(struct squashfs_sb_info *msblk, struct ++ squashfs_fragment_cache *fragment); ++extern struct squashfs_fragment_cache *get_cached_fragment(struct super_block ++ *s, long long start_block, ++ int length); ++extern struct inode *squashfs_iget(struct super_block *s, squashfs_inode_t inode, unsigned int inode_number); ++extern const struct address_space_operations squashfs_symlink_aops; ++extern const struct address_space_operations squashfs_aops; ++extern struct inode_operations squashfs_dir_inode_ops; ++#else ++#define SQSH_EXTERN static ++#endif ++ ++#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY ++extern int squashfs_1_0_supported(struct squashfs_sb_info *msblk); ++#else ++static inline int squashfs_1_0_supported(struct squashfs_sb_info *msblk) ++{ ++ return 0; ++} ++#endif ++ ++#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY ++extern int squashfs_2_0_supported(struct squashfs_sb_info *msblk); ++#else ++static inline int squashfs_2_0_supported(struct squashfs_sb_info *msblk) ++{ ++ return 0; ++} ++#endif +diff -x .gitignore -Nurp linux-2.6.24/include/linux/squashfs_fs.h linux-2.6.24-squashfs3.3/include/linux/squashfs_fs.h +--- linux-2.6.24/include/linux/squashfs_fs.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24-squashfs3.3/include/linux/squashfs_fs.h 2007-11-01 03:50:57.000000000 +0000 +@@ -0,0 +1,935 @@ ++#ifndef SQUASHFS_FS ++#define SQUASHFS_FS ++ ++/* ++ * Squashfs ++ * ++ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007 ++ * Phillip Lougher <phillip@lougher.demon.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2, ++ * 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * squashfs_fs.h ++ */ ++ ++#ifndef CONFIG_SQUASHFS_2_0_COMPATIBILITY ++#define CONFIG_SQUASHFS_2_0_COMPATIBILITY ++#endif ++ ++#define SQUASHFS_CACHED_FRAGMENTS CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE ++#define SQUASHFS_MAJOR 3 ++#define SQUASHFS_MINOR 1 ++#define SQUASHFS_MAGIC 0x73717368 ++#define SQUASHFS_MAGIC_SWAP 0x68737173 ++#define SQUASHFS_START 0 ++ ++/* size of metadata (inode and directory) blocks */ ++#define SQUASHFS_METADATA_SIZE 8192 ++#define SQUASHFS_METADATA_LOG 13 ++ ++/* default size of data blocks */ ++#define SQUASHFS_FILE_SIZE 131072 ++#define SQUASHFS_FILE_LOG 17 ++ ++#define SQUASHFS_FILE_MAX_SIZE 1048576 ++ ++/* Max number of uids and gids */ ++#define SQUASHFS_UIDS 256 ++#define SQUASHFS_GUIDS 255 ++ ++/* Max length of filename (not 255) */ ++#define SQUASHFS_NAME_LEN 256 ++ ++#define SQUASHFS_INVALID ((long long) 0xffffffffffff) ++#define SQUASHFS_INVALID_FRAG ((unsigned int) 0xffffffff) ++#define SQUASHFS_INVALID_BLK ((long long) -1) ++#define SQUASHFS_USED_BLK ((long long) -2) ++ ++/* Filesystem flags */ ++#define SQUASHFS_NOI 0 ++#define SQUASHFS_NOD 1 ++#define SQUASHFS_CHECK 2 ++#define SQUASHFS_NOF 3 ++#define SQUASHFS_NO_FRAG 4 ++#define SQUASHFS_ALWAYS_FRAG 5 ++#define SQUASHFS_DUPLICATE 6 ++#define SQUASHFS_EXPORT 7 ++ ++#define SQUASHFS_BIT(flag, bit) ((flag >> bit) & 1) ++ ++#define SQUASHFS_UNCOMPRESSED_INODES(flags) SQUASHFS_BIT(flags, \ ++ SQUASHFS_NOI) ++ ++#define SQUASHFS_UNCOMPRESSED_DATA(flags) SQUASHFS_BIT(flags, \ ++ SQUASHFS_NOD) ++ ++#define SQUASHFS_UNCOMPRESSED_FRAGMENTS(flags) SQUASHFS_BIT(flags, \ ++ SQUASHFS_NOF) ++ ++#define SQUASHFS_NO_FRAGMENTS(flags) SQUASHFS_BIT(flags, \ ++ SQUASHFS_NO_FRAG) ++ ++#define SQUASHFS_ALWAYS_FRAGMENTS(flags) SQUASHFS_BIT(flags, \ ++ SQUASHFS_ALWAYS_FRAG) ++ ++#define SQUASHFS_DUPLICATES(flags) SQUASHFS_BIT(flags, \ ++ SQUASHFS_DUPLICATE) ++ ++#define SQUASHFS_EXPORTABLE(flags) SQUASHFS_BIT(flags, \ ++ SQUASHFS_EXPORT) ++ ++#define SQUASHFS_CHECK_DATA(flags) SQUASHFS_BIT(flags, \ ++ SQUASHFS_CHECK) ++ ++#define SQUASHFS_MKFLAGS(noi, nod, check_data, nof, no_frag, always_frag, \ ++ duplicate_checking, exortable) (noi | (nod << 1) | (check_data << 2) \ ++ | (nof << 3) | (no_frag << 4) | (always_frag << 5) | \ ++ (duplicate_checking << 6) | (exportable << 7)) ++ ++/* Max number of types and file types */ ++#define SQUASHFS_DIR_TYPE 1 ++#define SQUASHFS_FILE_TYPE 2 ++#define SQUASHFS_SYMLINK_TYPE 3 ++#define SQUASHFS_BLKDEV_TYPE 4 ++#define SQUASHFS_CHRDEV_TYPE 5 ++#define SQUASHFS_FIFO_TYPE 6 ++#define SQUASHFS_SOCKET_TYPE 7 ++#define SQUASHFS_LDIR_TYPE 8 ++#define SQUASHFS_LREG_TYPE 9 ++ ++/* 1.0 filesystem type definitions */ ++#define SQUASHFS_TYPES 5 ++#define SQUASHFS_IPC_TYPE 0 ++ ++/* Flag whether block is compressed or uncompressed, bit is set if block is ++ * uncompressed */ ++#define SQUASHFS_COMPRESSED_BIT (1 << 15) ++ ++#define SQUASHFS_COMPRESSED_SIZE(B) (((B) & ~SQUASHFS_COMPRESSED_BIT) ? \ ++ (B) & ~SQUASHFS_COMPRESSED_BIT : SQUASHFS_COMPRESSED_BIT) ++ ++#define SQUASHFS_COMPRESSED(B) (!((B) & SQUASHFS_COMPRESSED_BIT)) ++ ++#define SQUASHFS_COMPRESSED_BIT_BLOCK (1 << 24) ++ ++#define SQUASHFS_COMPRESSED_SIZE_BLOCK(B) ((B) & \ ++ ~SQUASHFS_COMPRESSED_BIT_BLOCK) ++ ++#define SQUASHFS_COMPRESSED_BLOCK(B) (!((B) & SQUASHFS_COMPRESSED_BIT_BLOCK)) ++ ++/* ++ * Inode number ops. Inodes consist of a compressed block number, and an ++ * uncompressed offset within that block ++ */ ++#define SQUASHFS_INODE_BLK(a) ((unsigned int) ((a) >> 16)) ++ ++#define SQUASHFS_INODE_OFFSET(a) ((unsigned int) ((a) & 0xffff)) ++ ++#define SQUASHFS_MKINODE(A, B) ((squashfs_inode_t)(((squashfs_inode_t) (A)\ ++ << 16) + (B))) ++ ++/* Compute 32 bit VFS inode number from squashfs inode number */ ++#define SQUASHFS_MK_VFS_INODE(a, b) ((unsigned int) (((a) << 8) + \ ++ ((b) >> 2) + 1)) ++/* XXX */ ++ ++/* Translate between VFS mode and squashfs mode */ ++#define SQUASHFS_MODE(a) ((a) & 0xfff) ++ ++/* fragment and fragment table defines */ ++#define SQUASHFS_FRAGMENT_BYTES(A) ((A) * sizeof(struct squashfs_fragment_entry)) ++ ++#define SQUASHFS_FRAGMENT_INDEX(A) (SQUASHFS_FRAGMENT_BYTES(A) / \ ++ SQUASHFS_METADATA_SIZE) ++ ++#define SQUASHFS_FRAGMENT_INDEX_OFFSET(A) (SQUASHFS_FRAGMENT_BYTES(A) % \ ++ SQUASHFS_METADATA_SIZE) ++ ++#define SQUASHFS_FRAGMENT_INDEXES(A) ((SQUASHFS_FRAGMENT_BYTES(A) + \ ++ SQUASHFS_METADATA_SIZE - 1) / \ ++ SQUASHFS_METADATA_SIZE) ++ ++#define SQUASHFS_FRAGMENT_INDEX_BYTES(A) (SQUASHFS_FRAGMENT_INDEXES(A) *\ ++ sizeof(long long)) ++ ++/* inode lookup table defines */ ++#define SQUASHFS_LOOKUP_BYTES(A) ((A) * sizeof(squashfs_inode_t)) ++ ++#define SQUASHFS_LOOKUP_BLOCK(A) (SQUASHFS_LOOKUP_BYTES(A) / \ ++ SQUASHFS_METADATA_SIZE) ++ ++#define SQUASHFS_LOOKUP_BLOCK_OFFSET(A) (SQUASHFS_LOOKUP_BYTES(A) % \ ++ SQUASHFS_METADATA_SIZE) ++ ++#define SQUASHFS_LOOKUP_BLOCKS(A) ((SQUASHFS_LOOKUP_BYTES(A) + \ ++ SQUASHFS_METADATA_SIZE - 1) / \ ++ SQUASHFS_METADATA_SIZE) ++ ++#define SQUASHFS_LOOKUP_BLOCK_BYTES(A) (SQUASHFS_LOOKUP_BLOCKS(A) *\ ++ sizeof(long long)) ++ ++/* cached data constants for filesystem */ ++#define SQUASHFS_CACHED_BLKS 8 ++ ++#define SQUASHFS_MAX_FILE_SIZE_LOG 64 ++ ++#define SQUASHFS_MAX_FILE_SIZE ((long long) 1 << \ ++ (SQUASHFS_MAX_FILE_SIZE_LOG - 2)) ++ ++#define SQUASHFS_MARKER_BYTE 0xff ++ ++/* meta index cache */ ++#define SQUASHFS_META_INDEXES (SQUASHFS_METADATA_SIZE / sizeof(unsigned int)) ++#define SQUASHFS_META_ENTRIES 31 ++#define SQUASHFS_META_NUMBER 8 ++#define SQUASHFS_SLOTS 4 ++ ++struct meta_entry { ++ long long data_block; ++ unsigned int index_block; ++ unsigned short offset; ++ unsigned short pad; ++}; ++ ++struct meta_index { ++ unsigned int inode_number; ++ unsigned int offset; ++ unsigned short entries; ++ unsigned short skip; ++ unsigned short locked; ++ unsigned short pad; ++ struct meta_entry meta_entry[SQUASHFS_META_ENTRIES]; ++}; ++ ++ ++/* ++ * definitions for structures on disk ++ */ ++ ++typedef long long squashfs_block_t; ++typedef long long squashfs_inode_t; ++ ++struct squashfs_super_block { ++ unsigned int s_magic; ++ unsigned int inodes; ++ unsigned int bytes_used_2; ++ unsigned int uid_start_2; ++ unsigned int guid_start_2; ++ unsigned int inode_table_start_2; ++ unsigned int directory_table_start_2; ++ unsigned int s_major:16; ++ unsigned int s_minor:16; ++ unsigned int block_size_1:16; ++ unsigned int block_log:16; ++ unsigned int flags:8; ++ unsigned int no_uids:8; ++ unsigned int no_guids:8; ++ unsigned int mkfs_time /* time of filesystem creation */; ++ squashfs_inode_t root_inode; ++ unsigned int block_size; ++ unsigned int fragments; ++ unsigned int fragment_table_start_2; ++ long long bytes_used; ++ long long uid_start; ++ long long guid_start; ++ long long inode_table_start; ++ long long directory_table_start; ++ long long fragment_table_start; ++ long long lookup_table_start; ++} __attribute__ ((packed)); ++ ++struct squashfs_dir_index { ++ unsigned int index; ++ unsigned int start_block; ++ unsigned char size; ++ unsigned char name[0]; ++} __attribute__ ((packed)); ++ ++#define SQUASHFS_BASE_INODE_HEADER \ ++ unsigned int inode_type:4; \ ++ unsigned int mode:12; \ ++ unsigned int uid:8; \ ++ unsigned int guid:8; \ ++ unsigned int mtime; \ ++ unsigned int inode_number; ++ ++struct squashfs_base_inode_header { ++ SQUASHFS_BASE_INODE_HEADER; ++} __attribute__ ((packed)); ++ ++struct squashfs_ipc_inode_header { ++ SQUASHFS_BASE_INODE_HEADER; ++ unsigned int nlink; ++} __attribute__ ((packed)); ++ ++struct squashfs_dev_inode_header { ++ SQUASHFS_BASE_INODE_HEADER; ++ unsigned int nlink; ++ unsigned short rdev; ++} __attribute__ ((packed)); ++ ++struct squashfs_symlink_inode_header { ++ SQUASHFS_BASE_INODE_HEADER; ++ unsigned int nlink; ++ unsigned short symlink_size; ++ char symlink[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_reg_inode_header { ++ SQUASHFS_BASE_INODE_HEADER; ++ squashfs_block_t start_block; ++ unsigned int fragment; ++ unsigned int offset; ++ unsigned int file_size; ++ unsigned short block_list[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_lreg_inode_header { ++ SQUASHFS_BASE_INODE_HEADER; ++ unsigned int nlink; ++ squashfs_block_t start_block; ++ unsigned int fragment; ++ unsigned int offset; ++ long long file_size; ++ unsigned short block_list[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_dir_inode_header { ++ SQUASHFS_BASE_INODE_HEADER; ++ unsigned int nlink; ++ unsigned int file_size:19; ++ unsigned int offset:13; ++ unsigned int start_block; ++ unsigned int parent_inode; ++} __attribute__ ((packed)); ++ ++struct squashfs_ldir_inode_header { ++ SQUASHFS_BASE_INODE_HEADER; ++ unsigned int nlink; ++ unsigned int file_size:27; ++ unsigned int offset:13; ++ unsigned int start_block; ++ unsigned int i_count:16; ++ unsigned int parent_inode; ++ struct squashfs_dir_index index[0]; ++} __attribute__ ((packed)); ++ ++union squashfs_inode_header { ++ struct squashfs_base_inode_header base; ++ struct squashfs_dev_inode_header dev; ++ struct squashfs_symlink_inode_header symlink; ++ struct squashfs_reg_inode_header reg; ++ struct squashfs_lreg_inode_header lreg; ++ struct squashfs_dir_inode_header dir; ++ struct squashfs_ldir_inode_header ldir; ++ struct squashfs_ipc_inode_header ipc; ++}; ++ ++struct squashfs_dir_entry { ++ unsigned int offset:13; ++ unsigned int type:3; ++ unsigned int size:8; ++ int inode_number:16; ++ char name[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_dir_header { ++ unsigned int count:8; ++ unsigned int start_block; ++ unsigned int inode_number; ++} __attribute__ ((packed)); ++ ++struct squashfs_fragment_entry { ++ long long start_block; ++ unsigned int size; ++ unsigned int pending; ++} __attribute__ ((packed)); ++ ++extern int squashfs_uncompress_block(void *d, int dstlen, void *s, int srclen); ++extern int squashfs_uncompress_init(void); ++extern int squashfs_uncompress_exit(void); ++ ++/* ++ * macros to convert each packed bitfield structure from little endian to big ++ * endian and vice versa. These are needed when creating or using a filesystem ++ * on a machine with different byte ordering to the target architecture. ++ * ++ */ ++ ++#define SQUASHFS_SWAP_START \ ++ int bits;\ ++ int b_pos;\ ++ unsigned long long val;\ ++ unsigned char *s;\ ++ unsigned char *d; ++ ++#define SQUASHFS_SWAP_SUPER_BLOCK(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_super_block));\ ++ SQUASHFS_SWAP((s)->s_magic, d, 0, 32);\ ++ SQUASHFS_SWAP((s)->inodes, d, 32, 32);\ ++ SQUASHFS_SWAP((s)->bytes_used_2, d, 64, 32);\ ++ SQUASHFS_SWAP((s)->uid_start_2, d, 96, 32);\ ++ SQUASHFS_SWAP((s)->guid_start_2, d, 128, 32);\ ++ SQUASHFS_SWAP((s)->inode_table_start_2, d, 160, 32);\ ++ SQUASHFS_SWAP((s)->directory_table_start_2, d, 192, 32);\ ++ SQUASHFS_SWAP((s)->s_major, d, 224, 16);\ ++ SQUASHFS_SWAP((s)->s_minor, d, 240, 16);\ ++ SQUASHFS_SWAP((s)->block_size_1, d, 256, 16);\ ++ SQUASHFS_SWAP((s)->block_log, d, 272, 16);\ ++ SQUASHFS_SWAP((s)->flags, d, 288, 8);\ ++ SQUASHFS_SWAP((s)->no_uids, d, 296, 8);\ ++ SQUASHFS_SWAP((s)->no_guids, d, 304, 8);\ ++ SQUASHFS_SWAP((s)->mkfs_time, d, 312, 32);\ ++ SQUASHFS_SWAP((s)->root_inode, d, 344, 64);\ ++ SQUASHFS_SWAP((s)->block_size, d, 408, 32);\ ++ SQUASHFS_SWAP((s)->fragments, d, 440, 32);\ ++ SQUASHFS_SWAP((s)->fragment_table_start_2, d, 472, 32);\ ++ SQUASHFS_SWAP((s)->bytes_used, d, 504, 64);\ ++ SQUASHFS_SWAP((s)->uid_start, d, 568, 64);\ ++ SQUASHFS_SWAP((s)->guid_start, d, 632, 64);\ ++ SQUASHFS_SWAP((s)->inode_table_start, d, 696, 64);\ ++ SQUASHFS_SWAP((s)->directory_table_start, d, 760, 64);\ ++ SQUASHFS_SWAP((s)->fragment_table_start, d, 824, 64);\ ++ SQUASHFS_SWAP((s)->lookup_table_start, d, 888, 64);\ ++} ++ ++#define SQUASHFS_SWAP_BASE_INODE_CORE(s, d, n)\ ++ SQUASHFS_MEMSET(s, d, n);\ ++ SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\ ++ SQUASHFS_SWAP((s)->mode, d, 4, 12);\ ++ SQUASHFS_SWAP((s)->uid, d, 16, 8);\ ++ SQUASHFS_SWAP((s)->guid, d, 24, 8);\ ++ SQUASHFS_SWAP((s)->mtime, d, 32, 32);\ ++ SQUASHFS_SWAP((s)->inode_number, d, 64, 32); ++ ++#define SQUASHFS_SWAP_BASE_INODE_HEADER(s, d, n) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, n)\ ++} ++ ++#define SQUASHFS_SWAP_IPC_INODE_HEADER(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ ++ sizeof(struct squashfs_ipc_inode_header))\ ++ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ ++} ++ ++#define SQUASHFS_SWAP_DEV_INODE_HEADER(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ ++ sizeof(struct squashfs_dev_inode_header)); \ ++ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ ++ SQUASHFS_SWAP((s)->rdev, d, 128, 16);\ ++} ++ ++#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ ++ sizeof(struct squashfs_symlink_inode_header));\ ++ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ ++ SQUASHFS_SWAP((s)->symlink_size, d, 128, 16);\ ++} ++ ++#define SQUASHFS_SWAP_REG_INODE_HEADER(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ ++ sizeof(struct squashfs_reg_inode_header));\ ++ SQUASHFS_SWAP((s)->start_block, d, 96, 64);\ ++ SQUASHFS_SWAP((s)->fragment, d, 160, 32);\ ++ SQUASHFS_SWAP((s)->offset, d, 192, 32);\ ++ SQUASHFS_SWAP((s)->file_size, d, 224, 32);\ ++} ++ ++#define SQUASHFS_SWAP_LREG_INODE_HEADER(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ ++ sizeof(struct squashfs_lreg_inode_header));\ ++ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ ++ SQUASHFS_SWAP((s)->start_block, d, 128, 64);\ ++ SQUASHFS_SWAP((s)->fragment, d, 192, 32);\ ++ SQUASHFS_SWAP((s)->offset, d, 224, 32);\ ++ SQUASHFS_SWAP((s)->file_size, d, 256, 64);\ ++} ++ ++#define SQUASHFS_SWAP_DIR_INODE_HEADER(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ ++ sizeof(struct squashfs_dir_inode_header));\ ++ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ ++ SQUASHFS_SWAP((s)->file_size, d, 128, 19);\ ++ SQUASHFS_SWAP((s)->offset, d, 147, 13);\ ++ SQUASHFS_SWAP((s)->start_block, d, 160, 32);\ ++ SQUASHFS_SWAP((s)->parent_inode, d, 192, 32);\ ++} ++ ++#define SQUASHFS_SWAP_LDIR_INODE_HEADER(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \ ++ sizeof(struct squashfs_ldir_inode_header));\ ++ SQUASHFS_SWAP((s)->nlink, d, 96, 32);\ ++ SQUASHFS_SWAP((s)->file_size, d, 128, 27);\ ++ SQUASHFS_SWAP((s)->offset, d, 155, 13);\ ++ SQUASHFS_SWAP((s)->start_block, d, 168, 32);\ ++ SQUASHFS_SWAP((s)->i_count, d, 200, 16);\ ++ SQUASHFS_SWAP((s)->parent_inode, d, 216, 32);\ ++} ++ ++#define SQUASHFS_SWAP_DIR_INDEX(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index));\ ++ SQUASHFS_SWAP((s)->index, d, 0, 32);\ ++ SQUASHFS_SWAP((s)->start_block, d, 32, 32);\ ++ SQUASHFS_SWAP((s)->size, d, 64, 8);\ ++} ++ ++#define SQUASHFS_SWAP_DIR_HEADER(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header));\ ++ SQUASHFS_SWAP((s)->count, d, 0, 8);\ ++ SQUASHFS_SWAP((s)->start_block, d, 8, 32);\ ++ SQUASHFS_SWAP((s)->inode_number, d, 40, 32);\ ++} ++ ++#define SQUASHFS_SWAP_DIR_ENTRY(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry));\ ++ SQUASHFS_SWAP((s)->offset, d, 0, 13);\ ++ SQUASHFS_SWAP((s)->type, d, 13, 3);\ ++ SQUASHFS_SWAP((s)->size, d, 16, 8);\ ++ SQUASHFS_SWAP((s)->inode_number, d, 24, 16);\ ++} ++ ++#define SQUASHFS_SWAP_FRAGMENT_ENTRY(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry));\ ++ SQUASHFS_SWAP((s)->start_block, d, 0, 64);\ ++ SQUASHFS_SWAP((s)->size, d, 64, 32);\ ++} ++ ++#define SQUASHFS_SWAP_INODE_T(s, d) SQUASHFS_SWAP_LONG_LONGS(s, d, 1) ++ ++#define SQUASHFS_SWAP_SHORTS(s, d, n) {\ ++ int entry;\ ++ int bit_position;\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, n * 2);\ ++ for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \ ++ 16)\ ++ SQUASHFS_SWAP(s[entry], d, bit_position, 16);\ ++} ++ ++#define SQUASHFS_SWAP_INTS(s, d, n) {\ ++ int entry;\ ++ int bit_position;\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, n * 4);\ ++ for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \ ++ 32)\ ++ SQUASHFS_SWAP(s[entry], d, bit_position, 32);\ ++} ++ ++#define SQUASHFS_SWAP_LONG_LONGS(s, d, n) {\ ++ int entry;\ ++ int bit_position;\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, n * 8);\ ++ for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \ ++ 64)\ ++ SQUASHFS_SWAP(s[entry], d, bit_position, 64);\ ++} ++ ++#define SQUASHFS_SWAP_DATA(s, d, n, bits) {\ ++ int entry;\ ++ int bit_position;\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, n * bits / 8);\ ++ for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \ ++ bits)\ ++ SQUASHFS_SWAP(s[entry], d, bit_position, bits);\ ++} ++ ++#define SQUASHFS_SWAP_FRAGMENT_INDEXES(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n) ++#define SQUASHFS_SWAP_LOOKUP_BLOCKS(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n) ++ ++#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY ++ ++struct squashfs_base_inode_header_1 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:4; /* index into uid table */ ++ unsigned int guid:4; /* index into guid table */ ++} __attribute__ ((packed)); ++ ++struct squashfs_ipc_inode_header_1 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:4; /* index into uid table */ ++ unsigned int guid:4; /* index into guid table */ ++ unsigned int type:4; ++ unsigned int offset:4; ++} __attribute__ ((packed)); ++ ++struct squashfs_dev_inode_header_1 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:4; /* index into uid table */ ++ unsigned int guid:4; /* index into guid table */ ++ unsigned short rdev; ++} __attribute__ ((packed)); ++ ++struct squashfs_symlink_inode_header_1 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:4; /* index into uid table */ ++ unsigned int guid:4; /* index into guid table */ ++ unsigned short symlink_size; ++ char symlink[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_reg_inode_header_1 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:4; /* index into uid table */ ++ unsigned int guid:4; /* index into guid table */ ++ unsigned int mtime; ++ unsigned int start_block; ++ unsigned int file_size:32; ++ unsigned short block_list[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_dir_inode_header_1 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:4; /* index into uid table */ ++ unsigned int guid:4; /* index into guid table */ ++ unsigned int file_size:19; ++ unsigned int offset:13; ++ unsigned int mtime; ++ unsigned int start_block:24; ++} __attribute__ ((packed)); ++ ++union squashfs_inode_header_1 { ++ struct squashfs_base_inode_header_1 base; ++ struct squashfs_dev_inode_header_1 dev; ++ struct squashfs_symlink_inode_header_1 symlink; ++ struct squashfs_reg_inode_header_1 reg; ++ struct squashfs_dir_inode_header_1 dir; ++ struct squashfs_ipc_inode_header_1 ipc; ++}; ++ ++#define SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n) \ ++ SQUASHFS_MEMSET(s, d, n);\ ++ SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\ ++ SQUASHFS_SWAP((s)->mode, d, 4, 12);\ ++ SQUASHFS_SWAP((s)->uid, d, 16, 4);\ ++ SQUASHFS_SWAP((s)->guid, d, 20, 4); ++ ++#define SQUASHFS_SWAP_BASE_INODE_HEADER_1(s, d, n) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n)\ ++} ++ ++#define SQUASHFS_SWAP_IPC_INODE_HEADER_1(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ ++ sizeof(struct squashfs_ipc_inode_header_1));\ ++ SQUASHFS_SWAP((s)->type, d, 24, 4);\ ++ SQUASHFS_SWAP((s)->offset, d, 28, 4);\ ++} ++ ++#define SQUASHFS_SWAP_DEV_INODE_HEADER_1(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ ++ sizeof(struct squashfs_dev_inode_header_1));\ ++ SQUASHFS_SWAP((s)->rdev, d, 24, 16);\ ++} ++ ++#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_1(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ ++ sizeof(struct squashfs_symlink_inode_header_1));\ ++ SQUASHFS_SWAP((s)->symlink_size, d, 24, 16);\ ++} ++ ++#define SQUASHFS_SWAP_REG_INODE_HEADER_1(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ ++ sizeof(struct squashfs_reg_inode_header_1));\ ++ SQUASHFS_SWAP((s)->mtime, d, 24, 32);\ ++ SQUASHFS_SWAP((s)->start_block, d, 56, 32);\ ++ SQUASHFS_SWAP((s)->file_size, d, 88, 32);\ ++} ++ ++#define SQUASHFS_SWAP_DIR_INODE_HEADER_1(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \ ++ sizeof(struct squashfs_dir_inode_header_1));\ ++ SQUASHFS_SWAP((s)->file_size, d, 24, 19);\ ++ SQUASHFS_SWAP((s)->offset, d, 43, 13);\ ++ SQUASHFS_SWAP((s)->mtime, d, 56, 32);\ ++ SQUASHFS_SWAP((s)->start_block, d, 88, 24);\ ++} ++ ++#endif ++ ++#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY ++ ++struct squashfs_dir_index_2 { ++ unsigned int index:27; ++ unsigned int start_block:29; ++ unsigned char size; ++ unsigned char name[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_base_inode_header_2 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:8; /* index into uid table */ ++ unsigned int guid:8; /* index into guid table */ ++} __attribute__ ((packed)); ++ ++struct squashfs_ipc_inode_header_2 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:8; /* index into uid table */ ++ unsigned int guid:8; /* index into guid table */ ++} __attribute__ ((packed)); ++ ++struct squashfs_dev_inode_header_2 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:8; /* index into uid table */ ++ unsigned int guid:8; /* index into guid table */ ++ unsigned short rdev; ++} __attribute__ ((packed)); ++ ++struct squashfs_symlink_inode_header_2 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:8; /* index into uid table */ ++ unsigned int guid:8; /* index into guid table */ ++ unsigned short symlink_size; ++ char symlink[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_reg_inode_header_2 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:8; /* index into uid table */ ++ unsigned int guid:8; /* index into guid table */ ++ unsigned int mtime; ++ unsigned int start_block; ++ unsigned int fragment; ++ unsigned int offset; ++ unsigned int file_size:32; ++ unsigned short block_list[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_dir_inode_header_2 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:8; /* index into uid table */ ++ unsigned int guid:8; /* index into guid table */ ++ unsigned int file_size:19; ++ unsigned int offset:13; ++ unsigned int mtime; ++ unsigned int start_block:24; ++} __attribute__ ((packed)); ++ ++struct squashfs_ldir_inode_header_2 { ++ unsigned int inode_type:4; ++ unsigned int mode:12; /* protection */ ++ unsigned int uid:8; /* index into uid table */ ++ unsigned int guid:8; /* index into guid table */ ++ unsigned int file_size:27; ++ unsigned int offset:13; ++ unsigned int mtime; ++ unsigned int start_block:24; ++ unsigned int i_count:16; ++ struct squashfs_dir_index_2 index[0]; ++} __attribute__ ((packed)); ++ ++union squashfs_inode_header_2 { ++ struct squashfs_base_inode_header_2 base; ++ struct squashfs_dev_inode_header_2 dev; ++ struct squashfs_symlink_inode_header_2 symlink; ++ struct squashfs_reg_inode_header_2 reg; ++ struct squashfs_dir_inode_header_2 dir; ++ struct squashfs_ldir_inode_header_2 ldir; ++ struct squashfs_ipc_inode_header_2 ipc; ++}; ++ ++struct squashfs_dir_header_2 { ++ unsigned int count:8; ++ unsigned int start_block:24; ++} __attribute__ ((packed)); ++ ++struct squashfs_dir_entry_2 { ++ unsigned int offset:13; ++ unsigned int type:3; ++ unsigned int size:8; ++ char name[0]; ++} __attribute__ ((packed)); ++ ++struct squashfs_fragment_entry_2 { ++ unsigned int start_block; ++ unsigned int size; ++} __attribute__ ((packed)); ++ ++#define SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\ ++ SQUASHFS_MEMSET(s, d, n);\ ++ SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\ ++ SQUASHFS_SWAP((s)->mode, d, 4, 12);\ ++ SQUASHFS_SWAP((s)->uid, d, 16, 8);\ ++ SQUASHFS_SWAP((s)->guid, d, 24, 8);\ ++ ++#define SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, n) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\ ++} ++ ++#define SQUASHFS_SWAP_IPC_INODE_HEADER_2(s, d) \ ++ SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, sizeof(struct squashfs_ipc_inode_header_2)) ++ ++#define SQUASHFS_SWAP_DEV_INODE_HEADER_2(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ ++ sizeof(struct squashfs_dev_inode_header_2)); \ ++ SQUASHFS_SWAP((s)->rdev, d, 32, 16);\ ++} ++ ++#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ ++ sizeof(struct squashfs_symlink_inode_header_2));\ ++ SQUASHFS_SWAP((s)->symlink_size, d, 32, 16);\ ++} ++ ++#define SQUASHFS_SWAP_REG_INODE_HEADER_2(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ ++ sizeof(struct squashfs_reg_inode_header_2));\ ++ SQUASHFS_SWAP((s)->mtime, d, 32, 32);\ ++ SQUASHFS_SWAP((s)->start_block, d, 64, 32);\ ++ SQUASHFS_SWAP((s)->fragment, d, 96, 32);\ ++ SQUASHFS_SWAP((s)->offset, d, 128, 32);\ ++ SQUASHFS_SWAP((s)->file_size, d, 160, 32);\ ++} ++ ++#define SQUASHFS_SWAP_DIR_INODE_HEADER_2(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ ++ sizeof(struct squashfs_dir_inode_header_2));\ ++ SQUASHFS_SWAP((s)->file_size, d, 32, 19);\ ++ SQUASHFS_SWAP((s)->offset, d, 51, 13);\ ++ SQUASHFS_SWAP((s)->mtime, d, 64, 32);\ ++ SQUASHFS_SWAP((s)->start_block, d, 96, 24);\ ++} ++ ++#define SQUASHFS_SWAP_LDIR_INODE_HEADER_2(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \ ++ sizeof(struct squashfs_ldir_inode_header_2));\ ++ SQUASHFS_SWAP((s)->file_size, d, 32, 27);\ ++ SQUASHFS_SWAP((s)->offset, d, 59, 13);\ ++ SQUASHFS_SWAP((s)->mtime, d, 72, 32);\ ++ SQUASHFS_SWAP((s)->start_block, d, 104, 24);\ ++ SQUASHFS_SWAP((s)->i_count, d, 128, 16);\ ++} ++ ++#define SQUASHFS_SWAP_DIR_INDEX_2(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index_2));\ ++ SQUASHFS_SWAP((s)->index, d, 0, 27);\ ++ SQUASHFS_SWAP((s)->start_block, d, 27, 29);\ ++ SQUASHFS_SWAP((s)->size, d, 56, 8);\ ++} ++#define SQUASHFS_SWAP_DIR_HEADER_2(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header_2));\ ++ SQUASHFS_SWAP((s)->count, d, 0, 8);\ ++ SQUASHFS_SWAP((s)->start_block, d, 8, 24);\ ++} ++ ++#define SQUASHFS_SWAP_DIR_ENTRY_2(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry_2));\ ++ SQUASHFS_SWAP((s)->offset, d, 0, 13);\ ++ SQUASHFS_SWAP((s)->type, d, 13, 3);\ ++ SQUASHFS_SWAP((s)->size, d, 16, 8);\ ++} ++ ++#define SQUASHFS_SWAP_FRAGMENT_ENTRY_2(s, d) {\ ++ SQUASHFS_SWAP_START\ ++ SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry_2));\ ++ SQUASHFS_SWAP((s)->start_block, d, 0, 32);\ ++ SQUASHFS_SWAP((s)->size, d, 32, 32);\ ++} ++ ++#define SQUASHFS_SWAP_FRAGMENT_INDEXES_2(s, d, n) SQUASHFS_SWAP_INTS(s, d, n) ++ ++/* fragment and fragment table defines */ ++#define SQUASHFS_FRAGMENT_BYTES_2(A) (A * sizeof(struct squashfs_fragment_entry_2)) ++ ++#define SQUASHFS_FRAGMENT_INDEX_2(A) (SQUASHFS_FRAGMENT_BYTES_2(A) / \ ++ SQUASHFS_METADATA_SIZE) ++ ++#define SQUASHFS_FRAGMENT_INDEX_OFFSET_2(A) (SQUASHFS_FRAGMENT_BYTES_2(A) % \ ++ SQUASHFS_METADATA_SIZE) ++ ++#define SQUASHFS_FRAGMENT_INDEXES_2(A) ((SQUASHFS_FRAGMENT_BYTES_2(A) + \ ++ SQUASHFS_METADATA_SIZE - 1) / \ ++ SQUASHFS_METADATA_SIZE) ++ ++#define SQUASHFS_FRAGMENT_INDEX_BYTES_2(A) (SQUASHFS_FRAGMENT_INDEXES_2(A) *\ ++ sizeof(int)) ++ ++#endif ++ ++#ifdef __KERNEL__ ++ ++/* ++ * macros used to swap each structure entry, taking into account ++ * bitfields and different bitfield placing conventions on differing ++ * architectures ++ */ ++ ++#include <asm/byteorder.h> ++ ++#ifdef __BIG_ENDIAN ++ /* convert from little endian to big endian */ ++#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, \ ++ tbits, b_pos) ++#else ++ /* convert from big endian to little endian */ ++#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, \ ++ tbits, 64 - tbits - b_pos) ++#endif ++ ++#define _SQUASHFS_SWAP(value, p, pos, tbits, SHIFT) {\ ++ b_pos = pos % 8;\ ++ val = 0;\ ++ s = (unsigned char *)p + (pos / 8);\ ++ d = ((unsigned char *) &val) + 7;\ ++ for(bits = 0; bits < (tbits + b_pos); bits += 8) \ ++ *d-- = *s++;\ ++ value = (val >> (SHIFT))/* & ((1 << tbits) - 1)*/;\ ++} ++ ++#define SQUASHFS_MEMSET(s, d, n) memset(s, 0, n); ++ ++#endif ++#endif +diff -x .gitignore -Nurp linux-2.6.24/include/linux/squashfs_fs_i.h linux-2.6.24-squashfs3.3/include/linux/squashfs_fs_i.h +--- linux-2.6.24/include/linux/squashfs_fs_i.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24-squashfs3.3/include/linux/squashfs_fs_i.h 2007-08-19 04:24:08.000000000 +0100 +@@ -0,0 +1,45 @@ ++#ifndef SQUASHFS_FS_I ++#define SQUASHFS_FS_I ++/* ++ * Squashfs ++ * ++ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007 ++ * Phillip Lougher <phillip@lougher.demon.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2, ++ * 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * squashfs_fs_i.h ++ */ ++ ++struct squashfs_inode_info { ++ long long start_block; ++ unsigned int offset; ++ union { ++ struct { ++ long long fragment_start_block; ++ unsigned int fragment_size; ++ unsigned int fragment_offset; ++ long long block_list_start; ++ } s1; ++ struct { ++ long long directory_index_start; ++ unsigned int directory_index_offset; ++ unsigned int directory_index_count; ++ unsigned int parent_inode; ++ } s2; ++ } u; ++ struct inode vfs_inode; ++}; ++#endif +diff -x .gitignore -Nurp linux-2.6.24/include/linux/squashfs_fs_sb.h linux-2.6.24-squashfs3.3/include/linux/squashfs_fs_sb.h +--- linux-2.6.24/include/linux/squashfs_fs_sb.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux-2.6.24-squashfs3.3/include/linux/squashfs_fs_sb.h 2007-08-19 04:24:26.000000000 +0100 +@@ -0,0 +1,76 @@ ++#ifndef SQUASHFS_FS_SB ++#define SQUASHFS_FS_SB ++/* ++ * Squashfs ++ * ++ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007 ++ * Phillip Lougher <phillip@lougher.demon.co.uk> ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2, ++ * 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, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. ++ * ++ * squashfs_fs_sb.h ++ */ ++ ++#include <linux/squashfs_fs.h> ++ ++struct squashfs_cache { ++ long long block; ++ int length; ++ long long next_index; ++ char *data; ++}; ++ ++struct squashfs_fragment_cache { ++ long long block; ++ int length; ++ unsigned int locked; ++ char *data; ++}; ++ ++struct squashfs_sb_info { ++ struct squashfs_super_block sblk; ++ int devblksize; ++ int devblksize_log2; ++ int swap; ++ struct squashfs_cache *block_cache; ++ struct squashfs_fragment_cache *fragment; ++ int next_cache; ++ int next_fragment; ++ int next_meta_index; ++ unsigned int *uid; ++ unsigned int *guid; ++ long long *fragment_index; ++ unsigned int *fragment_index_2; ++ char *read_page; ++ struct mutex read_data_mutex; ++ struct mutex read_page_mutex; ++ struct mutex block_cache_mutex; ++ struct mutex fragment_mutex; ++ struct mutex meta_index_mutex; ++ wait_queue_head_t waitq; ++ wait_queue_head_t fragment_wait_queue; ++ struct meta_index *meta_index; ++ z_stream stream; ++ long long *inode_lookup_table; ++ int unused_cache_blks; ++ int unused_frag_blks; ++ int (*read_inode)(struct inode *i, squashfs_inode_t \ ++ inode); ++ long long (*read_blocklist)(struct inode *inode, int \ ++ index, int readahead_blks, char *block_list, \ ++ unsigned short **block_p, unsigned int *bsize); ++ int (*read_fragment_index_table)(struct super_block *s); ++}; ++#endif +diff -x .gitignore -Nurp linux-2.6.24/init/do_mounts_rd.c linux-2.6.24-squashfs3.3/init/do_mounts_rd.c +--- linux-2.6.24/init/do_mounts_rd.c 2007-10-25 17:41:49.000000000 +0100 ++++ linux-2.6.24-squashfs3.3/init/do_mounts_rd.c 2007-11-01 05:06:25.000000000 +0000 +@@ -5,6 +5,7 @@ + #include <linux/ext2_fs.h> + #include <linux/romfs_fs.h> + #include <linux/cramfs_fs.h> ++#include <linux/squashfs_fs.h> + #include <linux/initrd.h> + #include <linux/string.h> + +@@ -39,6 +40,7 @@ static int __init crd_load(int in_fd, in + * numbers could not be found. + * + * We currently check for the following magic numbers: ++ * squashfs + * minix + * ext2 + * romfs +@@ -53,6 +55,7 @@ identify_ramdisk_image(int fd, int start + struct ext2_super_block *ext2sb; + struct romfs_super_block *romfsb; + struct cramfs_super *cramfsb; ++ struct squashfs_super_block *squashfsb; + int nblocks = -1; + unsigned char *buf; + +@@ -64,6 +67,7 @@ identify_ramdisk_image(int fd, int start + ext2sb = (struct ext2_super_block *) buf; + romfsb = (struct romfs_super_block *) buf; + cramfsb = (struct cramfs_super *) buf; ++ squashfsb = (struct squashfs_super_block *) buf; + memset(buf, 0xe5, size); + + /* +@@ -101,6 +105,18 @@ identify_ramdisk_image(int fd, int start + goto done; + } + ++ /* squashfs is at block zero too */ ++ if (squashfsb->s_magic == SQUASHFS_MAGIC) { ++ printk(KERN_NOTICE ++ "RAMDISK: squashfs filesystem found at block %d\n", ++ start_block); ++ if (squashfsb->s_major < 3) ++ nblocks = (squashfsb->bytes_used_2+BLOCK_SIZE-1)>>BLOCK_SIZE_BITS; ++ else ++ nblocks = (squashfsb->bytes_used+BLOCK_SIZE-1)>>BLOCK_SIZE_BITS; ++ goto done; ++ } ++ + /* + * Read block 1 to test for minix and ext2 superblock + */ diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0001-Allow-runtime-registration-of-regions-of-memory-that.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0001-Allow-runtime-registration-of-regions-of-memory-that.patch new file mode 100644 index 0000000000..ba79b4a470 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0001-Allow-runtime-registration-of-regions-of-memory-that.patch @@ -0,0 +1,201 @@ +From d48a09b301d9a460d5ce027433e8cb8872e7b5c3 Mon Sep 17 00:00:00 2001 +From: Ian Molton <spyro@f2s.com> +Date: Fri, 4 Jan 2008 18:26:38 +0000 +Subject: [PATCH 01/64] Allow runtime registration of regions of memory that require dma bouncing. + +--- + arch/arm/common/Kconfig | 4 ++ + arch/arm/common/dmabounce.c | 82 ++++++++++++++++++++++++++++++++++++- + arch/arm/common/sa1111.c | 2 +- + arch/arm/mach-ixp4xx/Kconfig | 1 + + arch/arm/mach-ixp4xx/common-pci.c | 2 +- + 5 files changed, 87 insertions(+), 4 deletions(-) + +diff --git a/arch/arm/common/Kconfig b/arch/arm/common/Kconfig +index 3e07346..5f357fb 100644 +--- a/arch/arm/common/Kconfig ++++ b/arch/arm/common/Kconfig +@@ -13,10 +13,14 @@ config ICST307 + config SA1111 + bool + select DMABOUNCE ++ select PLATFORM_DMABOUNCE + + config DMABOUNCE + bool + ++config PLATFORM_DMABOUNCE ++ bool ++ + config TIMER_ACORN + bool + +diff --git a/arch/arm/common/dmabounce.c b/arch/arm/common/dmabounce.c +index 52fc6a8..ed80abe 100644 +--- a/arch/arm/common/dmabounce.c ++++ b/arch/arm/common/dmabounce.c +@@ -16,6 +16,7 @@ + * + * Copyright (C) 2002 Hewlett Packard Company. + * Copyright (C) 2004 MontaVista Software, Inc. ++ * Copyright (C) 2007 Dmitry Baryshkov <dbaryshkov@gmail.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License +@@ -24,6 +25,7 @@ + + #include <linux/module.h> + #include <linux/init.h> ++#include <linux/rwsem.h> + #include <linux/slab.h> + #include <linux/device.h> + #include <linux/dma-mapping.h> +@@ -80,6 +82,80 @@ struct dmabounce_device_info { + rwlock_t lock; + }; + ++struct dmabounce_check_entry { ++ struct list_head list; ++ dmabounce_check checker; ++ void *data; ++}; ++ ++static struct list_head checkers = LIST_HEAD_INIT(checkers); ++static rwlock_t checkers_lock = RW_LOCK_UNLOCKED; ++ ++int ++dmabounce_register_checker(dmabounce_check function, void *data) ++{ ++ unsigned long flags; ++ struct dmabounce_check_entry *entry = ++ kzalloc(sizeof(struct dmabounce_check_entry), GFP_ATOMIC); ++ ++ if (!entry) ++ return ENOMEM; ++ ++ INIT_LIST_HEAD(&entry->list); ++ entry->checker = function; ++ entry->data = data; ++ ++ write_lock_irqsave(&checkers_lock, flags); ++ list_add(&entry->list, &checkers); ++ write_unlock_irqrestore(&checkers_lock, flags); ++ ++ return 0; ++} ++ ++void ++dmabounce_remove_checker(dmabounce_check function, void *data) ++{ ++ unsigned long flags; ++ struct list_head *pos; ++ ++ write_lock_irqsave(&checkers_lock, flags); ++ __list_for_each(pos, &checkers) { ++ struct dmabounce_check_entry *entry = container_of(pos, ++ struct dmabounce_check_entry, list); ++ if (entry->checker == function && entry->data == data) { ++ list_del(pos); ++ write_unlock_irqrestore(&checkers_lock, flags); ++ kfree(entry); ++ return; ++ } ++ } ++ ++ write_unlock_irqrestore(&checkers_lock, flags); ++ printk(KERN_WARNING "dmabounce checker not found: %p\n", function); ++} ++ ++static int dma_needs_bounce(struct device *dev, dma_addr_t dma, size_t size) ++{ ++ unsigned long flags; ++ struct list_head *pos; ++ ++ read_lock_irqsave(&checkers_lock, flags); ++ __list_for_each(pos, &checkers) { ++ struct dmabounce_check_entry *entry = container_of(pos, ++ struct dmabounce_check_entry, list); ++ if (entry->checker(dev, dma, size, entry->data)) { ++ read_unlock_irqrestore(&checkers_lock, flags); ++ return 1; ++ } ++ } ++ ++ read_unlock_irqrestore(&checkers_lock, flags); ++#ifdef CONFIG_PLATFORM_DMABOUNCE ++ return platform_dma_needs_bounce(dev, dma, size); ++#else ++ return 0; ++#endif ++} + #ifdef STATS + static ssize_t dmabounce_show(struct device *dev, struct device_attribute *attr, + char *buf) +@@ -239,7 +315,7 @@ map_single(struct device *dev, void *ptr, size_t size, + struct safe_buffer *buf; + + buf = alloc_safe_buffer(device_info, ptr, size, dir); +- if (buf == 0) { ++ if (buf == NULL) { + dev_err(dev, "%s: unable to map unsafe buffer %p!\n", + __func__, ptr); + return 0; +@@ -643,7 +719,6 @@ dmabounce_unregister_dev(struct device *dev) + dev->bus_id, dev->bus->name); + } + +- + EXPORT_SYMBOL(dma_map_single); + EXPORT_SYMBOL(dma_unmap_single); + EXPORT_SYMBOL(dma_map_sg); +@@ -653,6 +728,9 @@ EXPORT_SYMBOL(dma_sync_single_for_device); + EXPORT_SYMBOL(dma_sync_sg); + EXPORT_SYMBOL(dmabounce_register_dev); + EXPORT_SYMBOL(dmabounce_unregister_dev); ++EXPORT_SYMBOL(dmabounce_register_checker); ++EXPORT_SYMBOL(dmabounce_remove_checker); ++ + + MODULE_AUTHOR("Christopher Hoover <ch@hpl.hp.com>, Deepak Saxena <dsaxena@plexity.net>"); + MODULE_DESCRIPTION("Special dma_{map/unmap/dma_sync}_* routines for systems with limited DMA windows"); +diff --git a/arch/arm/common/sa1111.c b/arch/arm/common/sa1111.c +index eb06d0b..3b8fbdd 100644 +--- a/arch/arm/common/sa1111.c ++++ b/arch/arm/common/sa1111.c +@@ -778,7 +778,7 @@ static void __sa1111_remove(struct sa1111 *sachip) + * This should only get called for sa1111_device types due to the + * way we configure our device dma_masks. + */ +-int dma_needs_bounce(struct device *dev, dma_addr_t addr, size_t size) ++int platform_dma_needs_bounce(struct device *dev, dma_addr_t addr, size_t size) + { + /* + * Section 4.6 of the "Intel StrongARM SA-1111 Development Module +diff --git a/arch/arm/mach-ixp4xx/Kconfig b/arch/arm/mach-ixp4xx/Kconfig +index 61b2dfc..5870371 100644 +--- a/arch/arm/mach-ixp4xx/Kconfig ++++ b/arch/arm/mach-ixp4xx/Kconfig +@@ -161,6 +161,7 @@ comment "IXP4xx Options" + config DMABOUNCE + bool + default y ++ select PLATFORM_DMABOUNCE + depends on PCI + + config IXP4XX_INDIRECT_PCI +diff --git a/arch/arm/mach-ixp4xx/common-pci.c b/arch/arm/mach-ixp4xx/common-pci.c +index bf04121..ac46492 100644 +--- a/arch/arm/mach-ixp4xx/common-pci.c ++++ b/arch/arm/mach-ixp4xx/common-pci.c +@@ -336,7 +336,7 @@ static int ixp4xx_pci_platform_notify_remove(struct device *dev) + return 0; + } + +-int dma_needs_bounce(struct device *dev, dma_addr_t dma_addr, size_t size) ++int platform_dma_needs_bounce(struct device *dev, dma_addr_t dma_addr, size_t size) + { + return (dev->bus == &pci_bus_type ) && ((dma_addr + size) >= SZ_64M); + } +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0001-pxa2xx-ac97-switch-AC-unit-to-correct-state-before.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0001-pxa2xx-ac97-switch-AC-unit-to-correct-state-before.patch new file mode 100644 index 0000000000..09f0cb946c --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0001-pxa2xx-ac97-switch-AC-unit-to-correct-state-before.patch @@ -0,0 +1,56 @@ +From 688df15bb534519e0698cc8e4a4d9234afd32105 Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Fri, 7 Nov 2008 15:50:39 +0300 +Subject: [PATCH] pxa2xx-ac97: switch AC unit to correct state before probing + +If AC97 unit is in partially enabled state, early request_irq can trigger +IRQ storm or even full hang up. Workaround this by forcibly switching ACLINK off +at the start of the probe. + +Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> +--- + sound/soc/pxa/pxa2xx-ac97.c | 30 +++++++++++++++++------------- + 1 files changed, 17 insertions(+), 13 deletions(-) + +Index: linux-2.6.24/sound/soc/pxa/pxa2xx-ac97.c +=================================================================== +--- linux-2.6.24.orig/sound/soc/pxa/pxa2xx-ac97.c 2008-01-25 01:58:37.000000000 +0300 ++++ linux-2.6.24/sound/soc/pxa/pxa2xx-ac97.c 2008-11-15 20:02:45.396976363 +0300 +@@ -284,10 +284,6 @@ static int pxa2xx_ac97_probe(struct plat + { + int ret; + +- ret = request_irq(IRQ_AC97, pxa2xx_ac97_irq, IRQF_DISABLED, "AC97", NULL); +- if (ret < 0) +- goto err; +- + pxa_gpio_mode(GPIO31_SYNC_AC97_MD); + pxa_gpio_mode(GPIO30_SDATA_OUT_AC97_MD); + pxa_gpio_mode(GPIO28_BITCLK_AC97_MD); +@@ -296,15 +292,23 @@ static int pxa2xx_ac97_probe(struct plat + /* Use GPIO 113 as AC97 Reset on Bulverde */ + pxa_gpio_mode(113 | GPIO_ALT_FN_2_OUT); + #endif ++ GCR = GCR_ACLINK_OFF; ++ + pxa_set_cken(CKEN_AC97, 1); ++ ++ ret = request_irq(IRQ_AC97, pxa2xx_ac97_irq, IRQF_DISABLED, "AC97", NULL); ++ if (ret < 0) ++ goto err; ++ ++ + return 0; + +- err: +- if (CKEN & (1 << CKEN_AC97)) { ++err: ++/* if (CKEN & (1 << CKEN_AC97)) {*/ + GCR |= GCR_ACLINK_OFF; + free_irq(IRQ_AC97, NULL); + pxa_set_cken(CKEN_AC97, 0); +- } ++/* }*/ + return ret; + } + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0002-Modify-dma_alloc_coherent-on-ARM-so-that-it-supports.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0002-Modify-dma_alloc_coherent-on-ARM-so-that-it-supports.patch new file mode 100644 index 0000000000..a562ef921b --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0002-Modify-dma_alloc_coherent-on-ARM-so-that-it-supports.patch @@ -0,0 +1,260 @@ +From 8e95f90487d2fb46fd862744ddb34f47c30b0c5a Mon Sep 17 00:00:00 2001 +From: Ian Molton <spyro@f2s.com> +Date: Fri, 4 Jan 2008 18:27:50 +0000 +Subject: [PATCH 02/64] Modify dma_alloc_coherent on ARM so that it supports device local DMA. + +--- + arch/arm/mm/consistent.c | 125 +++++++++++++++++++++++++++++++++++++++++ + include/asm-arm/dma-mapping.h | 37 +++++++------ + 2 files changed, 145 insertions(+), 17 deletions(-) + +diff --git a/arch/arm/mm/consistent.c b/arch/arm/mm/consistent.c +index 333a82a..3da0f94 100644 +--- a/arch/arm/mm/consistent.c ++++ b/arch/arm/mm/consistent.c +@@ -3,6 +3,8 @@ + * + * Copyright (C) 2000-2004 Russell King + * ++ * Device local coherent memory support added by Ian Molton (spyro@f2s.com) ++ * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. +@@ -20,6 +22,7 @@ + + #include <asm/memory.h> + #include <asm/cacheflush.h> ++#include <asm/io.h> + #include <asm/tlbflush.h> + #include <asm/sizes.h> + +@@ -35,6 +38,13 @@ + #define CONSISTENT_PTE_INDEX(x) (((unsigned long)(x) - CONSISTENT_BASE) >> PGDIR_SHIFT) + #define NUM_CONSISTENT_PTES (CONSISTENT_DMA_SIZE >> PGDIR_SHIFT) + ++struct dma_coherent_mem { ++ void *virt_base; ++ u32 device_base; ++ int size; ++ int flags; ++ unsigned long *bitmap; ++}; + + /* + * These are the page tables (2MB each) covering uncached, DMA consistent allocations +@@ -153,6 +163,13 @@ __dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp, + unsigned long order; + u64 mask = ISA_DMA_THRESHOLD, limit; + ++ /* Following is a work-around (a.k.a. hack) to prevent pages ++ * with __GFP_COMP being passed to split_page() which cannot ++ * handle them. The real problem is that this flag probably ++ * should be 0 on ARM as it is not supported on this ++ * platform--see CONFIG_HUGETLB_PAGE. */ ++ gfp &= ~(__GFP_COMP); ++ + if (!consistent_pte[0]) { + printk(KERN_ERR "%s: not initialised\n", __func__); + dump_stack(); +@@ -160,6 +177,26 @@ __dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp, + } + + if (dev) { ++ ++ if (dev->dma_mem) { ++ unsigned long flags; ++ int pgnum; ++ void *ret; ++ ++ spin_lock_irqsave(&consistent_lock, flags); ++ pgnum = bitmap_find_free_region(dev->dma_mem->bitmap, ++ dev->dma_mem->size, ++ get_order(size)); ++ spin_unlock_irqrestore(&consistent_lock, flags); ++ ++ if (pgnum >= 0) { ++ *handle = dev->dma_mem->device_base + (pgnum << PAGE_SHIFT); ++ ret = dev->dma_mem->virt_base + (pgnum << PAGE_SHIFT); ++ memset(ret, 0, size); ++ return ret; ++ } ++ } ++ + mask = dev->coherent_dma_mask; + + /* +@@ -177,6 +214,9 @@ __dma_alloc(struct device *dev, size_t size, dma_addr_t *handle, gfp_t gfp, + mask, (unsigned long long)ISA_DMA_THRESHOLD); + goto no_page; + } ++ ++ if (dev->dma_mem && dev->dma_mem->flags & DMA_MEMORY_EXCLUSIVE) ++ return NULL; + } + + /* +@@ -359,6 +399,8 @@ void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr + pte_t *ptep; + int idx; + u32 off; ++ struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL; ++ unsigned long order; + + WARN_ON(irqs_disabled()); + +@@ -368,6 +410,15 @@ void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr + } + + size = PAGE_ALIGN(size); ++ order = get_order(size); ++ ++ /* What if mem is valid and the range is not? */ ++ if (mem && cpu_addr >= mem->virt_base && cpu_addr < (mem->virt_base + (mem->size << PAGE_SHIFT))) { ++ int page = (cpu_addr - mem->virt_base) >> PAGE_SHIFT; ++ ++ bitmap_release_region(mem->bitmap, page, order); ++ return; ++ } + + spin_lock_irqsave(&consistent_lock, flags); + c = vm_region_find(&consistent_head, (unsigned long)cpu_addr); +@@ -437,6 +488,80 @@ void dma_free_coherent(struct device *dev, size_t size, void *cpu_addr, dma_addr + } + EXPORT_SYMBOL(dma_free_coherent); + ++int dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr, ++ dma_addr_t device_addr, size_t size, int flags) ++{ ++ void __iomem *mem_base; ++ int pages = size >> PAGE_SHIFT; ++ int bitmap_size = (pages + 31)/32; ++ ++ if ((flags & (DMA_MEMORY_MAP | DMA_MEMORY_IO)) == 0) ++ goto out; ++ if (!size) ++ goto out; ++ if (dev->dma_mem) ++ goto out; ++ ++ /* FIXME: this routine just ignores DMA_MEMORY_INCLUDES_CHILDREN */ ++ mem_base = ioremap_nocache(bus_addr, size); ++ if (!mem_base) ++ goto out; ++ ++ dev->dma_mem = kzalloc(sizeof(struct dma_coherent_mem), GFP_KERNEL); ++ if (!dev->dma_mem) ++ goto out; ++ memset(dev->dma_mem, 0, sizeof(struct dma_coherent_mem)); ++ dev->dma_mem->bitmap = kzalloc(bitmap_size, GFP_KERNEL); ++ if (!dev->dma_mem->bitmap) ++ goto free1_out; ++ ++ dev->dma_mem->virt_base = mem_base; ++ dev->dma_mem->device_base = device_addr; ++ dev->dma_mem->size = pages; ++ dev->dma_mem->flags = flags; ++ ++ if (flags & DMA_MEMORY_MAP) ++ return DMA_MEMORY_MAP; ++ ++ return DMA_MEMORY_IO; ++ ++ free1_out: ++ kfree(dev->dma_mem->bitmap); ++ out: ++ return 0; ++} ++EXPORT_SYMBOL(dma_declare_coherent_memory); ++ ++void dma_release_declared_memory(struct device *dev) ++{ ++ struct dma_coherent_mem *mem = dev->dma_mem; ++ ++ if (!mem) ++ return; ++ dev->dma_mem = NULL; ++ kfree(mem->bitmap); ++ kfree(mem); ++} ++EXPORT_SYMBOL(dma_release_declared_memory); ++ ++void *dma_mark_declared_memory_occupied(struct device *dev, ++ dma_addr_t device_addr, size_t size) ++{ ++ struct dma_coherent_mem *mem = dev->dma_mem; ++ int pages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; ++ int pos, err; ++ ++ if (!mem) ++ return ERR_PTR(-EINVAL); ++ ++ pos = (device_addr - mem->device_base) >> PAGE_SHIFT; ++ err = bitmap_allocate_region(mem->bitmap, pos, get_order(pages)); ++ if (err != 0) ++ return ERR_PTR(err); ++ return mem->virt_base + (pos << PAGE_SHIFT); ++} ++EXPORT_SYMBOL(dma_mark_declared_memory_occupied); ++ + /* + * Initialise the consistent memory allocation. + */ +diff --git a/include/asm-arm/dma-mapping.h b/include/asm-arm/dma-mapping.h +index e99406a..f18ba05 100644 +--- a/include/asm-arm/dma-mapping.h ++++ b/include/asm-arm/dma-mapping.h +@@ -7,6 +7,19 @@ + + #include <linux/scatterlist.h> + ++#define ARCH_HAS_DMA_DECLARE_COHERENT_MEMORY ++extern int ++dma_declare_coherent_memory(struct device *dev, dma_addr_t bus_addr, ++ dma_addr_t device_addr, size_t size, int flags); ++ ++extern void ++dma_release_declared_memory(struct device *dev); ++ ++extern void * ++dma_mark_declared_memory_occupied(struct device *dev, ++ dma_addr_t device_addr, size_t size); ++ ++ + /* + * DMA-consistent mapping functions. These allocate/free a region of + * uncached, unwrite-buffered mapped memory space for use with DMA +@@ -433,23 +446,13 @@ extern int dmabounce_register_dev(struct device *, unsigned long, unsigned long) + */ + extern void dmabounce_unregister_dev(struct device *); + +-/** +- * dma_needs_bounce +- * +- * @dev: valid struct device pointer +- * @dma_handle: dma_handle of unbounced buffer +- * @size: size of region being mapped +- * +- * Platforms that utilize the dmabounce mechanism must implement +- * this function. +- * +- * The dmabounce routines call this function whenever a dma-mapping +- * is requested to determine whether a given buffer needs to be bounced +- * or not. The function must return 0 if the buffer is OK for +- * DMA access and 1 if the buffer needs to be bounced. +- * +- */ +-extern int dma_needs_bounce(struct device*, dma_addr_t, size_t); ++typedef int (*dmabounce_check)(struct device *dev, dma_addr_t dma, size_t size, void *data); ++extern int dmabounce_register_checker(dmabounce_check, void *data); ++extern void dmabounce_remove_checker(dmabounce_check, void *data); ++#ifdef CONFIG_PLATFORM_DMABOUNCE ++extern int platform_dma_needs_bounce(struct device *dev, dma_addr_t dma, size_t size, void *data); ++#endif ++ + #endif /* CONFIG_DMABOUNCE */ + + #endif /* __KERNEL__ */ +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0003-Core-MFD-support.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0003-Core-MFD-support.patch new file mode 100644 index 0000000000..d84a4f7835 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0003-Core-MFD-support.patch @@ -0,0 +1,243 @@ +From a07910753f9965842b6647f0561db125b538f5ed Mon Sep 17 00:00:00 2001 +From: Ian Molton <spyro@f2s.com> +Date: Fri, 4 Jan 2008 18:32:44 +0000 +Subject: [PATCH 03/64] Core MFD support + +This patch provides a common subdevice registration system for MFD type +chips, using platfrom device. + +It also provides a new resource type for IRQs such that a subdevices IRQ may +be computed based on the MFD cores IRQ handler, since many MFDs provide an IRQ +multiplex. +--- + drivers/mfd/Kconfig | 4 ++ + drivers/mfd/Makefile | 2 + + drivers/mfd/mfd-core.c | 116 ++++++++++++++++++++++++++++++++++++++++++++++ + include/linux/ioport.h | 1 + + include/linux/mfd-core.h | 51 ++++++++++++++++++++ + 5 files changed, 174 insertions(+), 0 deletions(-) + create mode 100644 drivers/mfd/mfd-core.c + create mode 100644 include/linux/mfd-core.h + +diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig +index 2571619..1205c89 100644 +--- a/drivers/mfd/Kconfig ++++ b/drivers/mfd/Kconfig +@@ -5,6 +5,10 @@ + menu "Multifunction device drivers" + depends on HAS_IOMEM + ++config MFD_CORE ++ tristate ++ default n ++ + config MFD_SM501 + tristate "Support for Silicon Motion SM501" + ---help--- +diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile +index 5143209..6c20064 100644 +--- a/drivers/mfd/Makefile ++++ b/drivers/mfd/Makefile +@@ -4,6 +4,8 @@ + + obj-$(CONFIG_MFD_SM501) += sm501.o + ++obj-$(CONFIG_MFD_CORE) += mfd-core.o ++ + obj-$(CONFIG_MCP) += mcp-core.o + obj-$(CONFIG_MCP_SA11X0) += mcp-sa11x0.o + obj-$(CONFIG_MCP_UCB1200) += ucb1x00-core.o +diff --git a/drivers/mfd/mfd-core.c b/drivers/mfd/mfd-core.c +new file mode 100644 +index 0000000..88874e1 +--- /dev/null ++++ b/drivers/mfd/mfd-core.c +@@ -0,0 +1,116 @@ ++/* ++ * drivers/mfd/mfd-core.c ++ * ++ * core MFD support ++ * Copyright (c) 2006 Ian Molton ++ * Copyright (c) 2007 Dmitry Baryshkov ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/platform_device.h> ++#include <linux/mfd-core.h> ++ ++#define SIGNED_SHIFT(val, shift) ((shift) >= 0 ? \ ++ ((val) << (shift)) : \ ++ ((val) >> -(shift))) ++ ++int mfd_add_devices( ++ struct platform_device *parent, ++ const struct mfd_cell *cells, int n_devs, ++ struct resource *mem, ++ int relative_addr_shift, ++ int irq_base) ++{ ++ int i; ++ ++ for (i = 0; i < n_devs; i++) { ++ struct resource *res = NULL; ++ const struct mfd_cell *cell = cells + i; ++ struct platform_device *pdev; ++ int ret = -ENOMEM; ++ int r; ++ ++ pdev = platform_device_alloc(cell->name, -1); ++ if (!pdev) ++ goto fail_alloc; ++ ++ pdev->dev.uevent_suppress = 0; ++ pdev->dev.parent = &parent->dev; ++ ++ ret = platform_device_add_data(pdev, &cell, sizeof(struct mfd_cell *)); ++ if (ret) ++ goto fail_device; ++ ++ res = kzalloc(cell->num_resources * sizeof(struct resource), ++ GFP_KERNEL); ++ if (!res) ++ goto fail_device; ++ ++ for (r = 0; r < cell->num_resources; r++) { ++ res[r].name = cell->resources[r].name; ++ ++ /* Find out base to use */ ++ if (cell->resources[r].flags & IORESOURCE_MEM) { ++ res[r].parent = mem; ++ res[r].start = mem->start + ++ SIGNED_SHIFT(cell->resources[r].start, ++ relative_addr_shift); ++ res[r].end = mem->start + ++ SIGNED_SHIFT(cell->resources[r].end, ++ relative_addr_shift); ++ } else if ((cell->resources[r].flags & IORESOURCE_IRQ) && ++ (cell->resources[r].flags & IORESOURCE_IRQ_MFD_SUBDEVICE)) { ++ res[r].start = irq_base + ++ cell->resources[r].start; ++ res[r].end = irq_base + ++ cell->resources[r].end; ++ } else { ++ res[r].start = cell->resources[r].start; ++ res[r].end = cell->resources[r].end; ++ } ++ ++ res[r].flags = cell->resources[r].flags; ++ } ++ ++ ret = platform_device_add_resources(pdev, ++ res, ++ cell->num_resources); ++ kfree(res); ++ ++ if (ret) ++ goto fail_device; ++ ++ ret = platform_device_add(pdev); ++ ++ if (ret) { ++ platform_device_del(pdev); ++fail_device: ++ platform_device_put(pdev); ++fail_alloc: ++ mfd_remove_devices(parent); ++ return ret; ++ } ++ } ++ return 0; ++} ++EXPORT_SYMBOL(mfd_add_devices); ++ ++static int mfd_remove_devices_fn(struct device *dev, void *unused) ++{ ++ platform_device_unregister(container_of(dev, struct platform_device, dev)); ++ return 0; ++} ++ ++void mfd_remove_devices(struct platform_device *parent) ++{ ++ device_for_each_child(&parent->dev, NULL, mfd_remove_devices_fn); ++} ++EXPORT_SYMBOL(mfd_remove_devices); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Ian Molton, Dmitry Baryshkov"); +diff --git a/include/linux/ioport.h b/include/linux/ioport.h +index 6187a85..0348c71 100644 +--- a/include/linux/ioport.h ++++ b/include/linux/ioport.h +@@ -56,6 +56,7 @@ struct resource_list { + #define IORESOURCE_IRQ_HIGHLEVEL (1<<2) + #define IORESOURCE_IRQ_LOWLEVEL (1<<3) + #define IORESOURCE_IRQ_SHAREABLE (1<<4) ++#define IORESOURCE_IRQ_MFD_SUBDEVICE (1<<5) + + /* ISA PnP DMA specific bits (IORESOURCE_BITS) */ + #define IORESOURCE_DMA_TYPE_MASK (3<<0) +diff --git a/include/linux/mfd-core.h b/include/linux/mfd-core.h +new file mode 100644 +index 0000000..0e9de78 +--- /dev/null ++++ b/include/linux/mfd-core.h +@@ -0,0 +1,51 @@ ++#ifndef MFD_CORE_H ++#define MFD_CORE_H ++/* ++ * drivers/mfd/mfd-core.h ++ * ++ * core MFD support ++ * Copyright (c) 2006 Ian Molton ++ * Copyright (c) 2007 Dmitry Baryshkov ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include <linux/platform_device.h> ++ ++struct mfd_cell { ++ const char *name; ++ ++ int (*enable)(struct platform_device *dev); ++ int (*disable)(struct platform_device *dev); ++ int (*suspend)(struct platform_device *dev); ++ int (*resume)(struct platform_device *dev); ++ ++ void *driver_data; /* data passed to drivers */ ++ ++ /* ++ * This resources can be specified relatievly to the parent device. ++ * For accessing device you should use resources from device ++ */ ++ int num_resources; ++ const struct resource *resources; ++}; ++ ++static inline __maybe_unused struct mfd_cell * ++mfd_get_cell(struct platform_device *pdev) ++{ ++ return *((struct mfd_cell **)(pdev->dev.platform_data)); ++} ++ ++extern int mfd_add_devices( ++ struct platform_device *parent, ++ const struct mfd_cell *cells, int n_devs, ++ struct resource *mem, ++ int relative_addr_shift, ++ int irq_base); ++ ++extern void mfd_remove_devices(struct platform_device *parent); ++ ++#endif +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0004-Add-support-for-tc6393xb-MFD-core.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0004-Add-support-for-tc6393xb-MFD-core.patch new file mode 100644 index 0000000000..a78c0f37f3 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0004-Add-support-for-tc6393xb-MFD-core.patch @@ -0,0 +1,907 @@ +From 3f56cac281fb407b7d8e574d18ee7d72aa7e7c28 Mon Sep 17 00:00:00 2001 +From: Ian Molton <spyro@f2s.com> +Date: Sat, 29 Dec 2007 15:02:30 +0000 +Subject: [PATCH 04/64] Add support for tc6393xb MFD core + +--- + drivers/mfd/Kconfig | 6 + + drivers/mfd/Makefile | 2 + + drivers/mfd/tc6393xb.c | 740 ++++++++++++++++++++++++++++++++++++++++++ + include/linux/mfd/tc6393xb.h | 108 ++++++ + 4 files changed, 856 insertions(+), 0 deletions(-) + create mode 100644 drivers/mfd/tc6393xb.c + create mode 100644 include/linux/mfd/tc6393xb.h + +diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig +index 1205c89..9903d0a 100644 +--- a/drivers/mfd/Kconfig ++++ b/drivers/mfd/Kconfig +@@ -9,6 +9,12 @@ config MFD_CORE + tristate + default n + ++config MFD_TC6393XB ++ bool "Support Toshiba TC6393XB" ++ select MFD_CORE ++ help ++ Support for Toshiba Mobile IO Controller TC6393XB ++ + config MFD_SM501 + tristate "Support for Silicon Motion SM501" + ---help--- +diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile +index 6c20064..ffd342e 100644 +--- a/drivers/mfd/Makefile ++++ b/drivers/mfd/Makefile +@@ -6,6 +6,8 @@ obj-$(CONFIG_MFD_SM501) += sm501.o + + obj-$(CONFIG_MFD_CORE) += mfd-core.o + ++obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o ++ + obj-$(CONFIG_MCP) += mcp-core.o + obj-$(CONFIG_MCP_SA11X0) += mcp-sa11x0.o + obj-$(CONFIG_MCP_UCB1200) += ucb1x00-core.o +diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c +new file mode 100644 +index 0000000..9439f39 +--- /dev/null ++++ b/drivers/mfd/tc6393xb.c +@@ -0,0 +1,740 @@ ++/* ++ * Toshiba TC6393XB SoC support ++ * ++ * Copyright(c) 2005-2006 Chris Humbert ++ * Copyright(c) 2005 Dirk Opfer ++ * Copyright(c) 2005 Ian Molton <spyro@f2s.com> ++ * Copyright(c) 2007 Dmitry Baryshkov ++ * ++ * Based on code written by Sharp/Lineo for 2.4 kernels ++ * Based on locomo.c ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/io.h> ++#include <linux/irq.h> ++#include <linux/platform_device.h> ++#include <linux/fb.h> ++#include <linux/mfd-core.h> ++#include <linux/mfd/tmio.h> ++#include <linux/mfd/tc6393xb.h> ++ ++struct tc6393xb_scr { ++ u8 x00[8]; ++ u8 revid; /* 0x08 Revision ID */ ++ u8 x01[0x47]; ++ u8 isr; /* 0x50 Interrupt Status */ ++ u8 x02; ++ u8 imr; /* 0x52 Interrupt Mask */ ++ u8 x03; ++ u8 irr; /* 0x54 Interrupt Routing */ ++ u8 x04[0x0b]; ++ u16 gper; /* 0x60 GP Enable */ ++ u8 x05[2]; ++ u16 gpi_sr[2]; /* 0x64 GPI Status */ ++ u16 gpi_imr[2]; /* 0x68 GPI INT Mask */ ++ u16 gpi_eder[2]; /* 0x6c GPI Edge Detect Enable */ ++ u16 gpi_lir[4]; /* 0x70 GPI Level Invert */ ++ u16 gpo_dsr[2]; /* 0x78 GPO Data Set */ ++ u16 gpo_doecr[2]; /* 0x7c GPO Data OE Control */ ++ u16 gp_iarcr[2]; /* 0x80 GP Internal Active Reg Control */ ++ u16 gp_iarlcr[2]; /* 0x84 GP Internal Active Reg Level Con*/ ++ u8 gpi_bcr[4]; /* 0x88 GPI Buffer Control */ ++ u16 gpa_iarcr; /* 0x8c GPa Internal Active Reg Control */ ++ u8 x06[2]; ++ u16 gpa_iarlcr; /* 0x90 GPa Internal Active Reg Level Co*/ ++ u8 x07[2]; ++ u16 gpa_bcr; /* 0x94 GPa Buffer Control */ ++ u8 x08[2]; ++ u16 ccr; /* 0x98 Clock Control */ ++ u16 pll2cr; /* 0x9a PLL2 Control */ ++ u16 pll1cr[2]; /* 0x9c PLL1 Control */ ++ u8 diarcr; /* 0xa0 Device Internal Active Reg Contr*/ ++ u8 dbocr; /* 0xa1 Device Buffer Off Control */ ++ u8 x09[0x3e]; ++ u8 fer; /* 0xe0 Function Enable */ ++ u8 x10[3]; ++ u16 mcr; /* 0xe4 Mode Control */ ++ u8 x11[0x14]; ++ u8 config; /* 0xfc Configuration Control */ ++ u8 x12[2]; ++ u8 debug; /* 0xff Debug */ ++} __attribute__ ((packed)); ++ ++/*--------------------------------------------------------------------------*/ ++ ++struct tc6393xb { ++ struct tc6393xb_scr __iomem *scr; ++ ++ spinlock_t lock; /* protects RMW cycles */ ++ ++ struct { ++ union tc6393xb_scr_fer fer; ++ union tc6393xb_scr_ccr ccr; ++ u8 gpi_bcr[4]; ++ } suspend_state; ++ ++ struct resource rscr; ++ struct resource *iomem; ++ int irq; ++}; ++ ++/*--------------------------------------------------------------------------*/ ++ ++static int tc6393xb_mmc_enable(struct platform_device *mmc) { ++ struct platform_device *dev = to_platform_device(mmc->dev.parent); ++ struct tc6393xb *tc6393xb = platform_get_drvdata(dev); ++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr; ++ union tc6393xb_scr_ccr ccr; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&tc6393xb->lock, flags); ++ ccr.raw = ioread16(&scr->ccr); ++ ccr.bits.ck32ken = 1; ++ iowrite16(ccr.raw, &scr->ccr); ++ spin_unlock_irqrestore(&tc6393xb->lock, flags); ++ ++ return 0; ++} ++ ++static int tc6393xb_mmc_disable(struct platform_device *mmc) { ++ struct platform_device *dev = to_platform_device(mmc->dev.parent); ++ struct tc6393xb *tc6393xb = platform_get_drvdata(dev); ++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr; ++ union tc6393xb_scr_ccr ccr; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&tc6393xb->lock, flags); ++ ccr.raw = ioread16(&scr->ccr); ++ ccr.bits.ck32ken = 0; ++ iowrite16(ccr.raw, &scr->ccr); ++ spin_unlock_irqrestore(&tc6393xb->lock, flags); ++ ++ return 0; ++} ++ ++/*--------------------------------------------------------------------------*/ ++ ++static int tc6393xb_nand_disable(struct platform_device *nand) ++{ ++ return 0; ++} ++ ++static int tc6393xb_nand_enable(struct platform_device *nand) ++{ ++ struct platform_device *dev = to_platform_device(nand->dev.parent); ++ struct tc6393xb *tc6393xb = platform_get_drvdata(dev); ++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&tc6393xb->lock, flags); ++ ++ /* SMD buffer on */ ++ dev_dbg(&dev->dev, "SMD buffer on\n"); ++ iowrite8(0xff, scr->gpi_bcr + 1); ++ ++ spin_unlock_irqrestore(&tc6393xb->lock, flags); ++ ++ return 0; ++} ++ ++int tc6393xb_lcd_set_power(struct platform_device *fb, bool on) ++{ ++ struct platform_device *dev = to_platform_device(fb->dev.parent); ++ struct tc6393xb *tc6393xb = platform_get_drvdata(dev); ++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr; ++ union tc6393xb_scr_fer fer; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&tc6393xb->lock, flags); ++ ++ fer.raw = ioread8(&scr->fer); ++ fer.bits.slcden = on ? 1 : 0; ++ iowrite8(fer.raw, &scr->fer); ++ ++ spin_unlock_irqrestore(&tc6393xb->lock, flags); ++ ++ return 0; ++} ++EXPORT_SYMBOL(tc6393xb_lcd_set_power); ++ ++int tc6393xb_lcd_mode(struct platform_device *fb_dev, ++ struct fb_videomode *mode) { ++ struct tc6393xb *tc6393xb = ++ platform_get_drvdata(to_platform_device(fb_dev->dev.parent)); ++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr; ++ ++ iowrite16(mode->pixclock, scr->pll1cr + 0); ++ iowrite16(mode->pixclock >> 16, scr->pll1cr + 1); ++ ++ return 0; ++} ++EXPORT_SYMBOL(tc6393xb_lcd_mode); ++ ++static int tc6393xb_ohci_disable(struct platform_device *ohci) ++{ ++ struct platform_device *dev = to_platform_device(ohci->dev.parent); ++ struct tc6393xb *tc6393xb = platform_get_drvdata(dev); ++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr; ++ union tc6393xb_scr_ccr ccr; ++ union tc6393xb_scr_fer fer; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&tc6393xb->lock, flags); ++ ++ fer.raw = ioread8(&scr->fer); ++ fer.bits.usben = 0; ++ iowrite8(fer.raw, &scr->fer); ++ ++ ccr.raw = ioread16(&scr->ccr); ++ ccr.bits.usbcken = 0; ++ iowrite16(ccr.raw, &scr->ccr); ++ ++ spin_unlock_irqrestore(&tc6393xb->lock, flags); ++ ++ return 0; ++} ++ ++static int tc6393xb_ohci_enable(struct platform_device *ohci) ++{ ++ struct platform_device *dev = to_platform_device(ohci->dev.parent); ++ struct tc6393xb *tc6393xb = platform_get_drvdata(dev); ++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr; ++ union tc6393xb_scr_ccr ccr; ++ union tc6393xb_scr_fer fer; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&tc6393xb->lock, flags); ++ ++ ccr.raw = ioread16(&scr->ccr); ++ ccr.bits.usbcken = 1; ++ iowrite16(ccr.raw, &scr->ccr); ++ ++ fer.raw = ioread8(&scr->fer); ++ fer.bits.usben = 1; ++ iowrite8(fer.raw, &scr->fer); ++ ++ spin_unlock_irqrestore(&tc6393xb->lock, flags); ++ ++ return 0; ++} ++ ++static int tc6393xb_fb_disable(struct platform_device *fb) ++{ ++ struct platform_device *dev = to_platform_device(fb->dev.parent); ++ struct tc6393xb *tc6393xb = platform_get_drvdata(dev); ++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr; ++ union tc6393xb_scr_ccr ccr; ++ union tc6393xb_scr_fer fer; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&tc6393xb->lock, flags); ++ ++ /* ++ * FIXME: is this correct or it should be moved to other _disable? ++ */ ++ fer.raw = ioread8(&scr->fer); ++ fer.bits.slcden = 0; ++/* fer.bits.lcdcven = 0; */ ++ iowrite8(fer.raw, &scr->fer); ++ ++ ccr.raw = ioread16(&scr->ccr); ++ ccr.bits.mclksel = disable; ++ iowrite16(ccr.raw, &scr->ccr); ++ ++ spin_unlock_irqrestore(&tc6393xb->lock, flags); ++ ++ return 0; ++} ++ ++static int tc6393xb_fb_enable(struct platform_device *fb) ++{ ++ struct platform_device *dev = to_platform_device(fb->dev.parent); ++ struct tc6393xb *tc6393xb = platform_get_drvdata(dev); ++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr; ++ union tc6393xb_scr_ccr ccr; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&tc6393xb->lock, flags); ++ ++ ccr.raw = ioread16(&scr->ccr); ++ ccr.bits.mclksel = m48MHz; ++ iowrite16(ccr.raw, &scr->ccr); ++ ++ spin_unlock_irqrestore(&tc6393xb->lock, flags); ++ ++ return 0; ++} ++ ++static int tc6393xb_fb_suspend(struct platform_device *fb) ++{ ++ struct platform_device *dev = to_platform_device(fb->dev.parent); ++ struct tc6393xb *tc6393xb = platform_get_drvdata(dev); ++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr; ++ union tc6393xb_scr_ccr ccr; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&tc6393xb->lock, flags); ++ ++ ccr.raw = ioread16(&scr->ccr); ++ ccr.bits.mclksel = disable; ++ iowrite16(ccr.raw, &scr->ccr); ++ ++ spin_unlock_irqrestore(&tc6393xb->lock, flags); ++ ++ return 0; ++} ++ ++static int tc6393xb_fb_resume(struct platform_device *fb) ++{ ++ struct platform_device *dev = to_platform_device(fb->dev.parent); ++ struct tc6393xb *tc6393xb = platform_get_drvdata(dev); ++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr; ++ union tc6393xb_scr_ccr ccr; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&tc6393xb->lock, flags); ++ ++ ccr.raw = ioread16(&scr->ccr); ++ ccr.bits.mclksel = m48MHz; ++ iowrite16(ccr.raw, &scr->ccr); ++ ++ spin_unlock_irqrestore(&tc6393xb->lock, flags); ++ ++ return 0; ++} ++ ++static struct resource tc6393xb_mmc_resources[] = { ++ { ++ .name = TMIO_MMC_CONTROL, ++ .start = 0x800, ++ .end = 0x9ff, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .name = TMIO_MMC_CONFIG, ++ .start = 0x200, ++ .end = 0x2ff, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .name = TMIO_MMC_IRQ, ++ .start = IRQ_TC6393_MMC, ++ .end = IRQ_TC6393_MMC, ++ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_MFD_SUBDEVICE, ++ }, ++}; ++ ++const static struct resource tc6393xb_nand_resources[] = { ++ { ++ .name = TMIO_NAND_CONFIG, ++ .start = 0x0100, ++ .end = 0x01ff, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .name = TMIO_NAND_CONTROL, ++ .start = 0x1000, ++ .end = 0x1007, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .name = TMIO_NAND_IRQ, ++ .start = IRQ_TC6393_NAND, ++ .end = IRQ_TC6393_NAND, ++ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_MFD_SUBDEVICE, ++ }, ++}; ++ ++const static struct resource tc6393xb_ohci_resources[] = { ++ { ++ .name = TMIO_OHCI_CONFIG, ++ .start = 0x0300, ++ .end = 0x03ff, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .name = TMIO_OHCI_CONTROL, ++ .start = 0x3000, ++ .end = 0x31ff, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .name = TMIO_OHCI_SRAM, ++ .start = 0x010000, ++ .end = 0x017fff, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .name = TMIO_OHCI_SRAM_ALIAS, ++ .start = 0x018000, ++ .end = 0x01ffff, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .name = TMIO_OHCI_IRQ, ++ .start = IRQ_TC6393_OHCI, ++ .end = IRQ_TC6393_OHCI, ++ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_MFD_SUBDEVICE, ++ }, ++}; ++ ++const static struct resource tc6393xb_fb_resources[] = { ++ { ++ .name = TMIO_FB_CONFIG, ++ .start = 0x0500, ++ .end = 0x05ff, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .name = TMIO_FB_CONTROL, ++ .start = 0x5000, ++ .end = 0x51ff, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .name = TMIO_FB_VRAM, ++ .start = 0x100000, ++ .end = 0x1fffff, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .name = TMIO_FB_IRQ, ++ .start = IRQ_TC6393_FB, ++ .end = IRQ_TC6393_FB, ++ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_MFD_SUBDEVICE, ++ }, ++}; ++ ++static struct mfd_cell tc6393xb_cells[] = { ++ { ++ .name = "tmio-nand", ++ .enable = tc6393xb_nand_enable, ++ .disable = tc6393xb_nand_disable, ++ .num_resources = ARRAY_SIZE(tc6393xb_nand_resources), ++ .resources = tc6393xb_nand_resources, ++ }, ++ { ++ .name = "tmio-ohci", ++ .enable = tc6393xb_ohci_enable, ++ .disable = tc6393xb_ohci_disable, ++ .num_resources = ARRAY_SIZE(tc6393xb_ohci_resources), ++ .resources = tc6393xb_ohci_resources, ++ }, ++ { ++ .name = "tmio-fb", ++ .enable = tc6393xb_fb_enable, ++ .disable = tc6393xb_fb_disable, ++ .suspend = tc6393xb_fb_suspend, ++ .resume = tc6393xb_fb_resume, ++ .num_resources = ARRAY_SIZE(tc6393xb_fb_resources), ++ .resources = tc6393xb_fb_resources, ++ }, ++ { ++ .name = "tmio-mmc", ++ .enable = tc6393xb_mmc_enable, ++ .disable = tc6393xb_mmc_disable, ++ .num_resources = ARRAY_SIZE(tc6393xb_mmc_resources), ++ .resources = tc6393xb_mmc_resources, ++ }, ++}; ++ ++/*--------------------------------------------------------------------------*/ ++ ++static void ++tc6393xb_irq(unsigned int irq, struct irq_desc *desc) ++{ ++ struct platform_device *dev = get_irq_chip_data(irq); ++ struct tc6393xb_platform_data *tcpd = dev->dev.platform_data; ++ struct tc6393xb *tc6393xb = platform_get_drvdata(dev); ++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr; ++ unsigned int isr; ++ unsigned int i; ++ ++ desc->chip->ack(irq); ++ ++ while ((isr = ioread8(&scr->isr) & ~ioread8(&scr->imr))) ++ for (i = 0; i < TC6393XB_NR_IRQS; i++) { ++ if (isr & (1 << i)) ++ desc_handle_irq(tcpd->irq_base + i, ++ irq_desc + tcpd->irq_base + i); ++ } ++} ++ ++static void tc6393xb_irq_ack(unsigned int irq) ++{ ++} ++ ++static void tc6393xb_irq_mask(unsigned int irq) ++{ ++ struct platform_device *dev = get_irq_chip_data(irq); ++ struct tc6393xb_platform_data *tcpd = dev->dev.platform_data; ++ struct tc6393xb *tc6393xb = platform_get_drvdata(dev); ++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&tc6393xb->lock, flags); ++ iowrite8(ioread8(&scr->imr) | (1 << (irq - tcpd->irq_base)), ++ &scr->imr); ++ spin_unlock_irqrestore(&tc6393xb->lock, flags); ++} ++ ++static void tc6393xb_irq_unmask(unsigned int irq) ++{ ++ struct platform_device *dev = get_irq_chip_data(irq); ++ struct tc6393xb_platform_data *tcpd = dev->dev.platform_data; ++ struct tc6393xb *tc6393xb = platform_get_drvdata(dev); ++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&tc6393xb->lock, flags); ++ iowrite8(ioread8(&scr->imr) & ~(1 << (irq - tcpd->irq_base)), ++ &scr->imr); ++ spin_unlock_irqrestore(&tc6393xb->lock, flags); ++} ++ ++static struct irq_chip tc6393xb_chip = { ++ .name = "tc6393xb", ++ .ack = tc6393xb_irq_ack, ++ .mask = tc6393xb_irq_mask, ++ .unmask = tc6393xb_irq_unmask, ++}; ++ ++static void tc6393xb_attach_irq(struct platform_device *dev) ++{ ++ struct tc6393xb_platform_data *tcpd = dev->dev.platform_data; ++ struct tc6393xb *tc6393xb = platform_get_drvdata(dev); ++ unsigned int irq; ++ ++ for ( ++ irq = tcpd->irq_base; ++ irq <= tcpd->irq_base + TC6393XB_NR_IRQS; ++ irq++) { ++ set_irq_chip(irq, &tc6393xb_chip); ++ set_irq_chip_data(irq, dev); ++ set_irq_handler(irq, handle_edge_irq); ++ set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); ++ } ++ ++ set_irq_type(tc6393xb->irq, IRQT_FALLING); ++ set_irq_chip_data(tc6393xb->irq, dev); ++ set_irq_chained_handler(tc6393xb->irq, tc6393xb_irq); ++} ++ ++static void tc6393xb_detach_irq(struct platform_device *dev) ++{ ++ struct tc6393xb_platform_data *tcpd = dev->dev.platform_data; ++ struct tc6393xb *tc6393xb = platform_get_drvdata(dev); ++ unsigned int irq; ++ ++ set_irq_chained_handler(tc6393xb->irq, NULL); ++ set_irq_chip_data(tc6393xb->irq, NULL); ++ ++ for ( ++ irq = tcpd->irq_base; ++ irq <= tcpd->irq_base + TC6393XB_NR_IRQS; ++ irq++) { ++ set_irq_flags(irq, 0); ++ set_irq_chip(irq, NULL); ++ set_irq_chip_data(irq, NULL); ++ } ++} ++ ++static int tc6393xb_hw_init(struct platform_device *dev, int resume) ++{ ++ struct tc6393xb_platform_data *tcpd = dev->dev.platform_data; ++ struct tc6393xb *tc6393xb = platform_get_drvdata(dev); ++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr; ++ int ret; ++ int i; ++ ++ if (resume) ++ ret = tcpd->resume(dev); ++ else ++ ret = tcpd->enable(dev); ++ if (ret) ++ return ret; ++ ++ iowrite8(resume ? ++ tc6393xb->suspend_state.fer.raw : ++ 0, &scr->fer); ++ iowrite16(tcpd->scr_pll2cr, &scr->pll2cr); ++ iowrite16(resume? ++ tc6393xb->suspend_state.ccr.raw : ++ tcpd->scr_ccr.raw, &scr->ccr); ++ iowrite16(tcpd->scr_mcr.raw, &scr->mcr); ++ iowrite16(tcpd->scr_gper, &scr->gper); ++ iowrite8(0, &scr->irr); ++ iowrite8(0xbf, &scr->imr); ++ iowrite16(tcpd->scr_gpo_dsr, scr->gpo_dsr + 0); ++ iowrite16(tcpd->scr_gpo_dsr >> 16, scr->gpo_dsr + 1); ++ iowrite16(tcpd->scr_gpo_doecr, scr->gpo_doecr + 0); ++ iowrite16(tcpd->scr_gpo_doecr >> 16, scr->gpo_doecr + 1); ++ ++ if (resume) ++ for (i = 0; i < 4; i++) ++ iowrite8(tc6393xb->suspend_state.gpi_bcr[i], ++ scr->gpi_bcr + i); ++ ++ return 0; ++} ++ ++static int __devinit tc6393xb_probe(struct platform_device *dev) ++{ ++ struct tc6393xb_platform_data *tcpd = dev->dev.platform_data; ++ struct tc6393xb *tc6393xb; ++ struct resource *iomem; ++ struct resource *rscr; ++ int retval; ++ ++ iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); ++ if (!iomem) ++ return -EINVAL; ++ ++ tc6393xb = kzalloc(sizeof *tc6393xb, GFP_KERNEL); ++ if (!tc6393xb) { ++ retval = -ENOMEM; ++ goto err_kzalloc; ++ } ++ ++ spin_lock_init(&tc6393xb->lock); ++ ++ platform_set_drvdata(dev, tc6393xb); ++ tc6393xb->iomem = iomem; ++ tc6393xb->irq = platform_get_irq(dev, 0); ++ ++ rscr = &tc6393xb->rscr; ++ rscr->name = "tc6393xb-core"; ++ rscr->start = iomem->start; ++ rscr->end = iomem->start + 0xff; ++ rscr->flags = IORESOURCE_MEM; ++ ++ retval = request_resource(iomem, rscr); ++ if (retval) ++ goto err_request_scr; ++ ++ tc6393xb->scr = ioremap(rscr->start, rscr->end - rscr->start + 1); ++ if (!tc6393xb->scr) { ++ retval = -ENOMEM; ++ goto err_ioremap; ++ } ++ ++ retval = tc6393xb_hw_init(dev, 0); ++ if (retval) ++ goto err_hw_init; ++ ++ printk(KERN_INFO "Toshiba tc6393xb revision %d at 0x%08lx, irq %d\n", ++ ioread8(&tc6393xb->scr->revid), ++ (unsigned long) iomem->start, tc6393xb->irq); ++ ++ if (tc6393xb->irq) ++ tc6393xb_attach_irq(dev); ++ ++ tc6393xb_cells[0].driver_data = tcpd->nand_data; ++ tc6393xb_cells[1].driver_data = NULL; /* tcpd->ohci_data; */ ++ tc6393xb_cells[2].driver_data = tcpd->fb_data; ++ ++ retval = mfd_add_devices(dev, ++ tc6393xb_cells, ARRAY_SIZE(tc6393xb_cells), ++ iomem, 0, tcpd->irq_base); ++ ++ if (retval == 0) ++ return 0; ++ ++ if (tc6393xb->irq) ++ tc6393xb_detach_irq(dev); ++ ++err_hw_init: ++ iounmap(tc6393xb->scr); ++err_ioremap: ++ release_resource(rscr); ++err_request_scr: ++ kfree(tc6393xb); ++err_kzalloc: ++ release_resource(iomem); ++ return retval; ++} ++ ++static int __devexit tc6393xb_remove(struct platform_device *dev) { ++ struct tc6393xb_platform_data *tcpd = dev->dev.platform_data; ++ struct tc6393xb *tc6393xb = platform_get_drvdata(dev); ++ int ret; ++ ++ if (tc6393xb->irq) ++ tc6393xb_detach_irq(dev); ++ ++ ret = tcpd->disable(dev); ++ ++ iounmap(tc6393xb->scr); ++ release_resource(&tc6393xb->rscr); ++ release_resource(tc6393xb->iomem); ++ ++ mfd_remove_devices(dev); ++ ++ platform_set_drvdata(dev, NULL); ++ ++ kfree(tc6393xb); ++ ++ return ret; ++} ++ ++#ifdef CONFIG_PM ++static int tc6393xb_suspend(struct platform_device *dev, pm_message_t state) ++{ ++ struct tc6393xb_platform_data *tcpd = dev->dev.platform_data; ++ struct tc6393xb *tc6393xb = platform_get_drvdata(dev); ++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr; ++ int i; ++ ++ ++ tc6393xb->suspend_state.ccr.raw = ioread16(&scr->ccr); ++ tc6393xb->suspend_state.fer.raw = ioread8(&scr->fer); ++ for (i = 0; i < 4; i++) ++ tc6393xb->suspend_state.gpi_bcr[i] = ++ ioread8(scr->gpi_bcr + i); ++ ++ return tcpd->suspend(dev); ++} ++ ++static int tc6393xb_resume(struct platform_device *dev) ++{ ++ return tc6393xb_hw_init(dev, 1); ++} ++#else ++#define tc6393xb_suspend NULL ++#define tc6393xb_resume NULL ++#endif ++ ++static struct platform_driver tc6393xb_driver = { ++ .probe = tc6393xb_probe, ++ .remove = __devexit_p(tc6393xb_remove), ++ .suspend = tc6393xb_suspend, ++ .resume = tc6393xb_resume, ++ ++ .driver = { ++ .name = "tc6393xb", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init tc6393xb_init(void) ++{ ++ return platform_driver_register(&tc6393xb_driver); ++} ++ ++static void __exit tc6393xb_exit(void) ++{ ++ platform_driver_unregister(&tc6393xb_driver); ++} ++ ++module_init(tc6393xb_init); ++module_exit(tc6393xb_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Ian Molton, Dmitry Baryshkov and Dirk Opfer"); ++MODULE_DESCRIPTION("tc6393xb Toshiba Mobile IO Controller"); +diff --git a/include/linux/mfd/tc6393xb.h b/include/linux/mfd/tc6393xb.h +new file mode 100644 +index 0000000..e699294 +--- /dev/null ++++ b/include/linux/mfd/tc6393xb.h +@@ -0,0 +1,108 @@ ++/* ++ * Toshiba TC6393XB SoC support ++ * ++ * Copyright(c) 2005-2006 Chris Humbert ++ * Copyright(c) 2005 Dirk Opfer ++ * Copyright(c) 2005 Ian Molton <spyro@f2s.com> ++ * Copyright(c) 2007 Dmitry Baryshkov ++ * ++ * Based on code written by Sharp/Lineo for 2.4 kernels ++ * Based on locomo.c ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#ifndef TC6393XB_H ++#define TC6393XB_H ++ ++#include <linux/mfd-core.h> ++#include <linux/mfd/tmio.h> ++ ++union tc6393xb_scr_fer { ++ u8 raw; ++struct { ++ unsigned usben:1; /* D0 USB enable */ ++ unsigned lcdcven:1; /* D1 polysylicon TFT enable */ ++ unsigned slcden:1; /* D2 SLCD enable */ ++} __attribute__ ((packed)) bits; ++} __attribute__ ((packed)); ++ ++union tc6393xb_scr_ccr { ++ u16 raw; ++struct { ++ unsigned ck32ken:1; /* D0 SD host clock enable */ ++ unsigned usbcken:1; /* D1 USB host clock enable */ ++ unsigned x00:2; ++ unsigned sharp:1; /* D4 ??? set in Sharp's code */ ++ unsigned x01:3; ++ enum { disable = 0, ++ m12MHz = 1, ++ m24MHz = 2, ++ m48MHz = 3, ++ } mclksel:3; /* D10-D8 LCD controller clock */ ++ unsigned x02:1; ++ enum { h24MHz = 0, ++ h48MHz = 1, ++ } hclksel:2; /* D13-D12 host bus clock */ ++ unsigned x03:2; ++} __attribute__ ((packed)) bits; ++} __attribute__ ((packed)); ++ ++enum pincontrol { ++ opendrain = 0, ++ tristate = 1, ++ pushpull = 2, ++ /* reserved = 3, */ ++}; ++ ++union tc6393xb_scr_mcr { ++ u16 raw; ++struct { ++ enum pincontrol rdyst:2; /* D1-D0 HRDY control */ ++ unsigned x00:1; ++ unsigned aren:1; /* D3 HRDY pull up resistance cut off */ ++ enum pincontrol intst:2; /* D5-D4 #HINT control */ ++ unsigned x01:1; ++ unsigned aien:1; /* D7 #HINT pull up resitance cut off */ ++ unsigned x02:8; ++} __attribute__ ((packed)) bits; ++} __attribute__ ((packed)); ++ ++struct tc6393xb_platform_data { ++ u16 scr_pll2cr; /* PLL2 Control */ ++ union tc6393xb_scr_ccr scr_ccr; /* Clock Control */ ++ union tc6393xb_scr_mcr scr_mcr; /* Mode Control */ ++ u16 scr_gper; /* GP Enable */ ++ u32 scr_gpo_doecr; /* GPO Data OE Control */ ++ u32 scr_gpo_dsr; /* GPO Data Set */ ++ ++ int (*enable)(struct platform_device *dev); ++ int (*disable)(struct platform_device *dev); ++ int (*suspend)(struct platform_device *dev); ++ int (*resume)(struct platform_device *dev); ++ ++ int irq_base; /* a base for cascaded irq */ ++ ++ struct tmio_nand_data *nand_data; ++ struct tmio_fb_data *fb_data; ++}; ++ ++extern int tc6393xb_lcd_set_power(struct platform_device *fb_dev, bool on); ++extern int tc6393xb_lcd_mode(struct platform_device *fb_dev, ++ struct fb_videomode *mode); ++ ++ ++/* ++ * Relative to irq_base ++ */ ++#define IRQ_TC6393_NAND 0 ++#define IRQ_TC6393_MMC 1 ++#define IRQ_TC6393_OHCI 2 ++#define IRQ_TC6393_SERIAL 3 ++#define IRQ_TC6393_FB 4 ++ ++#define TC6393XB_NR_IRQS 8 ++ ++#endif +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0005-Add-support-for-tc6387xb-MFD-core.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0005-Add-support-for-tc6387xb-MFD-core.patch new file mode 100644 index 0000000000..7183e3af6d --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0005-Add-support-for-tc6387xb-MFD-core.patch @@ -0,0 +1,249 @@ +From a6a6faf1dbb90c950fe55a1719720457bfb5830a Mon Sep 17 00:00:00 2001 +From: Ian Molton <spyro@f2s.com> +Date: Sun, 16 Dec 2007 02:19:49 +0000 +Subject: [PATCH 05/64] Add support for tc6387xb MFD core + +--- + drivers/mfd/Kconfig | 6 ++ + drivers/mfd/Makefile | 1 + + drivers/mfd/tc6387xb.c | 163 ++++++++++++++++++++++++++++++++++++++++++ + include/linux/mfd/tc6387xb.h | 28 +++++++ + 4 files changed, 198 insertions(+), 0 deletions(-) + create mode 100644 drivers/mfd/tc6387xb.c + create mode 100644 include/linux/mfd/tc6387xb.h + +diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig +index 9903d0a..1575323 100644 +--- a/drivers/mfd/Kconfig ++++ b/drivers/mfd/Kconfig +@@ -9,6 +9,12 @@ config MFD_CORE + tristate + default n + ++config MFD_TC6387XB ++ bool "Support Toshiba TC6387XB" ++ select MFD_CORE ++ help ++ Support for Toshiba Mobile IO Controller TC6387XB ++ + config MFD_TC6393XB + bool "Support Toshiba TC6393XB" + select MFD_CORE +diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile +index ffd342e..41b2190 100644 +--- a/drivers/mfd/Makefile ++++ b/drivers/mfd/Makefile +@@ -6,6 +6,7 @@ obj-$(CONFIG_MFD_SM501) += sm501.o + + obj-$(CONFIG_MFD_CORE) += mfd-core.o + ++obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o + obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o + + obj-$(CONFIG_MCP) += mcp-core.o +diff --git a/drivers/mfd/tc6387xb.c b/drivers/mfd/tc6387xb.c +new file mode 100644 +index 0000000..c81fca2 +--- /dev/null ++++ b/drivers/mfd/tc6387xb.c +@@ -0,0 +1,163 @@ ++/* ++ * Toshiba TC6387XB support ++ * Copyright (c) 2005 Ian Molton ++ * ++ * 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 file contains TC6387XB base support. ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/init.h> ++#include <linux/delay.h> ++#include <linux/platform_device.h> ++ ++#include <asm/hardware.h> ++#include <asm/mach-types.h> ++ ++#include <linux/mfd-core.h> ++#include <linux/mfd/tc6387xb.h> ++ ++#ifdef CONFIG_PM ++static int tc6387xb_suspend(struct platform_device *dev, pm_message_t state) ++{ ++ struct tc6387xb_platform_data *pdata = platform_get_drvdata(dev); ++ ++ if (pdata && pdata->suspend) ++ pdata->suspend(dev); ++ ++ return 0; ++} ++ ++static int tc6387xb_resume(struct platform_device *dev) ++{ ++ struct tc6387xb_platform_data *pdata = platform_get_drvdata(dev); ++ ++ if (pdata && pdata->resume) ++ pdata->resume(dev); ++ ++ return 0; ++} ++#else ++#define tc6387xb_suspend NULL ++#define tc6387xb_resume NULL ++#endif ++ ++/*--------------------------------------------------------------------------*/ ++ ++static int tc6387xb_mmc_enable(struct platform_device *mmc) { ++ struct platform_device *dev = to_platform_device(mmc->dev.parent); ++ struct tc6387xb_platform_data *tc6387xb = dev->dev.platform_data; ++ ++ if(tc6387xb->enable_mmc_clock) ++ tc6387xb->enable_mmc_clock(dev); ++ ++ return 0; ++} ++ ++static int tc6387xb_mmc_disable(struct platform_device *mmc) { ++ struct platform_device *dev = to_platform_device(mmc->dev.parent); ++ struct tc6387xb_platform_data *tc6387xb = dev->dev.platform_data; ++ ++ if(tc6387xb->disable_mmc_clock) ++ tc6387xb->disable_mmc_clock(dev); ++ ++ return 0; ++} ++ ++ ++/*--------------------------------------------------------------------------*/ ++ ++static struct resource tc6387xb_mmc_resources[] = { ++ { ++ .name = TMIO_MMC_CONTROL, ++ .start = 0x800, ++ .end = 0x9ff, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .name = TMIO_MMC_CONFIG, ++ .start = 0x200, ++ .end = 0x2ff, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .name = TMIO_MMC_IRQ, ++ .start = 0, ++ .end = 0, ++ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_MFD_SUBDEVICE, ++ }, ++}; ++ ++static struct mfd_cell tc6387xb_cells[] = { ++ { ++ .name = "tmio-mmc", ++ .enable = tc6387xb_mmc_enable, ++ .disable = tc6387xb_mmc_disable, ++ .num_resources = ARRAY_SIZE(tc6387xb_mmc_resources), ++ .resources = tc6387xb_mmc_resources, ++ }, ++}; ++ ++static int tc6387xb_probe(struct platform_device *dev) ++{ ++ struct tc6387xb_platform_data *data = platform_get_drvdata(dev); ++ struct resource *iomem; ++ int irq; ++ ++ iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); ++ if (!iomem) ++ return -EINVAL; ++ ++ irq = platform_get_irq(dev, 0); ++ ++ if(data && data->enable) ++ data->enable(dev); ++ ++ printk(KERN_INFO "Toshiba tc6393xb initialised\n"); ++ ++ return mfd_add_devices(dev, tc6387xb_cells, ARRAY_SIZE(tc6387xb_cells), ++ iomem, 0, irq); ++} ++ ++static int tc6387xb_remove(struct platform_device *dev) ++{ ++ struct tc6387xb_platform_data *data = platform_get_drvdata(dev); ++ ++ if(data && data->disable) ++ data->disable(dev); ++ ++ return 0; ++} ++ ++ ++static struct platform_driver tc6387xb_platform_driver = { ++ .driver = { ++ .name = "tc6387xb", ++ }, ++ .probe = tc6387xb_probe, ++ .remove = tc6387xb_remove, ++ .suspend = tc6387xb_suspend, ++ .resume = tc6387xb_resume, ++}; ++ ++ ++static int __init tc6387xb_init(void) ++{ ++ return platform_driver_register (&tc6387xb_platform_driver); ++} ++ ++static void __exit tc6387xb_exit(void) ++{ ++ platform_driver_unregister(&tc6387xb_platform_driver); ++} ++ ++module_init(tc6387xb_init); ++module_exit(tc6387xb_exit); ++ ++MODULE_DESCRIPTION("Toshiba TC6387XB core driver"); ++MODULE_LICENSE("GPLv2"); ++MODULE_AUTHOR("Ian Molton"); +diff --git a/include/linux/mfd/tc6387xb.h b/include/linux/mfd/tc6387xb.h +new file mode 100644 +index 0000000..496770b +--- /dev/null ++++ b/include/linux/mfd/tc6387xb.h +@@ -0,0 +1,28 @@ ++/* ++ * linux/include/asm-arm/hardware/tc6387xb.h ++ * ++ * This file contains the definitions for the TC6393XB ++ * ++ * (C) Copyright 2005 Ian Molton <spyro@f2s.com> ++ * ++ * May be copied or modified under the terms of the GNU General Public ++ * License. See linux/COPYING for more information. ++ * ++ */ ++#ifndef MFD_T7L66XB_H ++#define MFD_T7L66XB_H ++ ++#include <linux/mfd-core.h> ++#include <linux/mfd/tmio.h> ++ ++struct tc6387xb_platform_data ++{ ++ int (*enable_mmc_clock)(struct platform_device *dev); ++ int (*disable_mmc_clock)(struct platform_device *dev); ++ int (*enable)(struct platform_device *dev); ++ int (*disable)(struct platform_device *dev); ++ int (*suspend)(struct platform_device *dev); ++ int (*resume)(struct platform_device *dev); ++}; ++ ++#endif +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0006-Add-support-for-t7l66xb-MFD-core.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0006-Add-support-for-t7l66xb-MFD-core.patch new file mode 100644 index 0000000000..e7aff2455b --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0006-Add-support-for-t7l66xb-MFD-core.patch @@ -0,0 +1,653 @@ +From 2e31fea352ca97988452f1f2c94809de2977ce40 Mon Sep 17 00:00:00 2001 +From: Ian Molton <spyro@f2s.com> +Date: Sat, 29 Dec 2007 15:08:52 +0000 +Subject: [PATCH 06/64] Add support for t7l66xb MFD core + +--- + drivers/mfd/Kconfig | 6 + + drivers/mfd/Makefile | 1 + + drivers/mfd/t7l66xb.c | 550 +++++++++++++++++++++++++++++++++++++++++++ + include/linux/mfd/t7l66xb.h | 45 ++++ + 4 files changed, 602 insertions(+), 0 deletions(-) + create mode 100644 drivers/mfd/t7l66xb.c + create mode 100644 include/linux/mfd/t7l66xb.h + +diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig +index 1575323..f79a969 100644 +--- a/drivers/mfd/Kconfig ++++ b/drivers/mfd/Kconfig +@@ -9,6 +9,12 @@ config MFD_CORE + tristate + default n + ++config MFD_T7L66XB ++ bool "Support Toshiba T7L66XB" ++ select MFD_CORE ++ help ++ Support for Toshiba Mobile IO Controller T7L66XB ++ + config MFD_TC6387XB + bool "Support Toshiba TC6387XB" + select MFD_CORE +diff --git a/drivers/mfd/Makefile b/drivers/mfd/Makefile +index 41b2190..b2037ae 100644 +--- a/drivers/mfd/Makefile ++++ b/drivers/mfd/Makefile +@@ -6,6 +6,7 @@ obj-$(CONFIG_MFD_SM501) += sm501.o + + obj-$(CONFIG_MFD_CORE) += mfd-core.o + ++obj-$(CONFIG_MFD_T7L66XB) += t7l66xb.o + obj-$(CONFIG_MFD_TC6387XB) += tc6387xb.o + obj-$(CONFIG_MFD_TC6393XB) += tc6393xb.o + +diff --git a/drivers/mfd/t7l66xb.c b/drivers/mfd/t7l66xb.c +new file mode 100644 +index 0000000..308776a +--- /dev/null ++++ b/drivers/mfd/t7l66xb.c +@@ -0,0 +1,550 @@ ++/* ++ * ++ * Toshiba T7L66XB core mfd support ++ * ++ * Copyright (c) 2005 Ian Molton ++ * Copyright (c) 2007 Ian Molton ++ * ++ * 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. ++ * ++ * T7L66 features: ++ * ++ * Supported in this driver: ++ * SD/MMC ++ * SM/NAND flash controller ++ * OHCI controller ++ * ++ * As yet not supported ++ * GPIO interface (on NAND pins) ++ * Serial interface ++ * TFT 'interface converter' ++ * PCMCIA interface logic ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/io.h> ++#include <linux/irq.h> ++#include <linux/platform_device.h> ++#include <linux/fb.h> ++#include <linux/mfd-core.h> ++#include <linux/mfd/tmio.h> ++#include <linux/mfd/t7l66xb.h> ++ ++union t7l66xb_dev_ctl { ++ u8 raw; ++struct { ++ unsigned usb_en:1; /* D0 USB enable */ ++ unsigned mmc_en:1; /* D1 MMC enable */ ++} __attribute__ ((packed)); ++} __attribute__ ((packed)); ++ ++ ++struct t7l66xb_scr { ++ u8 x00[8]; ++ u8 revid; /* 0x08 Revision ID */ ++ u8 x01[57]; ++ u8 imr; /* 0x42 Interrupt Mask */ ++ u8 x03[157]; ++ union t7l66xb_dev_ctl dev_ctl; /* 0xe0 Device control */ ++ u8 isr; /* 0xe1 Interrupt Status */ ++ u8 x04[14]; ++ u8 gpio_output_ctl; /* 0xf0 */ ++ u8 gpio_output_status; /* 0xf1 */ ++ u16 gpio_input_status; /* 0xf2 */ ++ u8 x05[4]; ++ u8 active_pullup_down_ctl; /* 0xf8 */ ++ u8 x06[7]; ++} __attribute__ ((packed)); ++ ++ ++/*--------------------------------------------------------------------------*/ ++ ++struct t7l66xb ++{ ++ struct t7l66xb_scr __iomem *scr; ++ spinlock_t lock; ++ ++ struct resource rscr; ++ struct resource *iomem; ++ int irq; ++}; ++ ++/*--------------------------------------------------------------------------*/ ++ ++static int t7l66xb_ohci_enable(struct platform_device *ohci) ++{ ++ struct platform_device *dev = to_platform_device(ohci->dev.parent); ++ struct t7l66xb *t7l66xb = platform_get_drvdata(dev); ++ struct t7l66xb_scr __iomem *scr = t7l66xb->scr; ++ unsigned long flags; ++ union t7l66xb_dev_ctl dev_ctl; ++ ++ spin_lock_irqsave(&t7l66xb->lock, flags); ++ ++ dev_ctl.raw = readb(&scr->dev_ctl); ++ dev_ctl.usb_en = 1; ++ writeb(dev_ctl.raw, &scr->dev_ctl); ++ ++ spin_unlock_irqrestore(&t7l66xb->lock, flags); ++ ++ return 0; ++} ++ ++static int t7l66xb_ohci_disable(struct platform_device *ohci) ++{ ++ struct platform_device *dev = to_platform_device(ohci->dev.parent); ++ struct t7l66xb *t7l66xb = platform_get_drvdata(dev); ++ struct t7l66xb_scr __iomem *scr = t7l66xb->scr; ++ unsigned long flags; ++ union t7l66xb_dev_ctl dev_ctl; ++ ++ spin_lock_irqsave(&t7l66xb->lock, flags); ++ ++ dev_ctl.raw = readb(&scr->dev_ctl); ++ dev_ctl.usb_en = 0; ++ writeb(dev_ctl.raw, &scr->dev_ctl); ++ ++ spin_unlock_irqrestore(&t7l66xb->lock, flags); ++ ++ return 0; ++} ++ ++/*--------------------------------------------------------------------------*/ ++ ++static int t7l66xb_mmc_enable(struct platform_device *ohci) ++{ ++ struct platform_device *dev = to_platform_device(ohci->dev.parent); ++ struct t7l66xb_platform_data *pdata = dev->dev.platform_data; ++ struct t7l66xb *t7l66xb = platform_get_drvdata(dev); ++ struct t7l66xb_scr __iomem *scr = t7l66xb->scr; ++ unsigned long flags; ++ union t7l66xb_dev_ctl dev_ctl; ++ ++ spin_lock_irqsave(&t7l66xb->lock, flags); ++ ++ if(pdata->enable_clk32k) ++ pdata->enable_clk32k(dev); ++ dev_ctl.raw = readb(&scr->dev_ctl); ++ dev_ctl.mmc_en = 1; ++ writeb(dev_ctl.raw, &scr->dev_ctl); ++ ++ spin_unlock_irqrestore(&t7l66xb->lock, flags); ++ ++ return 0; ++} ++ ++static int t7l66xb_mmc_disable(struct platform_device *ohci) ++{ ++ struct platform_device *dev = to_platform_device(ohci->dev.parent); ++ struct t7l66xb_platform_data *pdata = dev->dev.platform_data; ++ struct t7l66xb *t7l66xb = platform_get_drvdata(dev); ++ struct t7l66xb_scr __iomem *scr = t7l66xb->scr; ++ unsigned long flags; ++ union t7l66xb_dev_ctl dev_ctl; ++ ++ spin_lock_irqsave(&t7l66xb->lock, flags); ++ ++ dev_ctl.raw = readb(&scr->dev_ctl); ++ dev_ctl.mmc_en = 0; ++ writeb(dev_ctl.raw, &scr->dev_ctl); ++ if(pdata->disable_clk32k) ++ pdata->disable_clk32k(dev); ++ ++ spin_unlock_irqrestore(&t7l66xb->lock, flags); ++ ++ return 0; ++} ++ ++/*--------------------------------------------------------------------------*/ ++ ++static int t7l66xb_nand_disable(struct platform_device *nand) ++{ ++ struct platform_device *dev = to_platform_device(nand->dev.parent); ++ struct t7l66xb *t7l66xb = platform_get_drvdata(dev); ++ struct t7l66xb_scr __iomem *scr = t7l66xb->scr; ++ unsigned long flags; ++ union t7l66xb_dev_ctl dev_ctl; ++ ++ spin_lock_irqsave(&t7l66xb->lock, flags); ++ ++ dev_ctl.raw = readb(&scr->dev_ctl); ++// dev_ctl.nand_en = 0; ++ writeb(dev_ctl.raw, &scr->dev_ctl); ++ ++ spin_unlock_irqrestore(&t7l66xb->lock, flags); ++ ++ return 0; ++} ++ ++static int t7l66xb_nand_enable(struct platform_device *nand) ++{ ++ struct platform_device *dev = to_platform_device(nand->dev.parent); ++ struct t7l66xb *t7l66xb = platform_get_drvdata(dev); ++ struct t7l66xb_scr __iomem *scr = t7l66xb->scr; ++ unsigned long flags; ++ union t7l66xb_dev_ctl dev_ctl; ++ ++ spin_lock_irqsave(&t7l66xb->lock, flags); ++ ++ dev_ctl.raw = readb(&scr->dev_ctl); ++ // dev_ctl.nand_en = 1; ++ writeb(dev_ctl.raw, &scr->dev_ctl); ++ ++ spin_unlock_irqrestore(&t7l66xb->lock, flags); ++ ++ return 0; ++} ++ ++/*--------------------------------------------------------------------------*/ ++ ++const static struct resource t7l66xb_mmc_resources[] = { ++ { ++ .name = TMIO_MMC_CONTROL, ++ .start = 0x800, ++ .end = 0x9ff, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .name = TMIO_MMC_CONFIG, ++ .start = 0x200, ++ .end = 0x2ff, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .name = TMIO_MMC_IRQ, ++ .start = IRQ_T7L66XB_MMC, ++ .end = IRQ_T7L66XB_MMC, ++ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_MFD_SUBDEVICE, ++ }, ++}; ++ ++const static struct resource t7l66xb_ohci_resources[] = { ++ { ++ .name = TMIO_OHCI_CONFIG, ++ .start = 0x0300, ++ .end = 0x03ff, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .name = TMIO_OHCI_CONTROL, ++ .start = 0xa00, ++ .end = 0xbff, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .name = TMIO_OHCI_SRAM, ++ .start = 0x01000, ++ .end = 0x02fff, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .name = TMIO_OHCI_IRQ, ++ .start = IRQ_T7L66XB_OHCI, ++ .end = IRQ_T7L66XB_OHCI, ++ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_MFD_SUBDEVICE, ++ }, ++}; ++ ++const static struct resource t7l66xb_nand_resources[] = { ++ { ++ .name = TMIO_NAND_CONFIG, ++ .start = 0x0100, ++ .end = 0x01ff, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .name = TMIO_NAND_CONTROL, ++ .start = 0xc00, ++ .end = 0xc07, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .name = TMIO_NAND_IRQ, ++ .start = IRQ_T7L66XB_NAND, ++ .end = IRQ_T7L66XB_NAND, ++ .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_MFD_SUBDEVICE, ++ }, ++}; ++ ++static struct mfd_cell t7l66xb_cells[] = { ++ { ++ .name = "tmio-mmc", ++ .enable = t7l66xb_mmc_enable, ++ .disable = t7l66xb_mmc_disable, ++ .num_resources = ARRAY_SIZE(t7l66xb_mmc_resources), ++ .resources = t7l66xb_mmc_resources, ++ }, ++ { ++ .name = "tmio-ohci", ++ .enable = t7l66xb_ohci_enable, ++ .disable = t7l66xb_ohci_disable, ++ .num_resources = ARRAY_SIZE(t7l66xb_ohci_resources), ++ .resources = t7l66xb_ohci_resources, ++ }, ++ { ++ .name = "tmio-nand", ++ .enable = t7l66xb_nand_enable, ++ .disable = t7l66xb_nand_disable, ++ .num_resources = ARRAY_SIZE(t7l66xb_nand_resources), ++ .resources = t7l66xb_nand_resources, ++ }, ++}; ++ ++/*--------------------------------------------------------------------------*/ ++ ++/* Handle the T7L66XB interrupt mux */ ++static void t7l66xb_irq(unsigned int irq, struct irq_desc *desc) ++{ ++ struct platform_device *dev = get_irq_chip_data(irq); ++ struct t7l66xb_platform_data *tcpd = dev->dev.platform_data; ++ struct t7l66xb *t7l66xb = platform_get_drvdata(dev); ++ struct t7l66xb_scr __iomem *scr = t7l66xb->scr; ++ unsigned int isr; ++ unsigned int i; ++ ++ desc->chip->ack(irq); ++ while ((isr = readb(&scr->isr) & ~readb(&scr->imr))) ++ for (i = 0; i < T7L66XB_NR_IRQS; i++) ++ if (isr & (1 << i)) ++ desc_handle_irq(tcpd->irq_base + i, ++ irq_desc + tcpd->irq_base + i); ++} ++ ++static void t7l66xb_irq_mask(unsigned int irq) ++{ ++ struct platform_device *dev = get_irq_chip_data(irq); ++ struct t7l66xb_platform_data *tcpd = dev->dev.platform_data; ++ struct t7l66xb *t7l66xb = platform_get_drvdata(dev); ++ struct t7l66xb_scr __iomem *scr = t7l66xb->scr; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&t7l66xb->lock, flags); ++ iowrite8(ioread8(&scr->imr) | (1 << (irq - tcpd->irq_base)), ++ &scr->imr); ++ spin_unlock_irqrestore(&t7l66xb->lock, flags); ++} ++ ++static void t7l66xb_irq_unmask(unsigned int irq) ++{ ++ struct platform_device *dev = get_irq_chip_data(irq); ++ struct t7l66xb_platform_data *tcpd = dev->dev.platform_data; ++ struct t7l66xb *t7l66xb = platform_get_drvdata(dev); ++ struct t7l66xb_scr __iomem *scr = t7l66xb->scr; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&t7l66xb->lock, flags); ++ iowrite8(ioread8(&scr->imr) & ~(1 << (irq - tcpd->irq_base)), ++ &scr->imr); ++ spin_unlock_irqrestore(&t7l66xb->lock, flags); ++} ++ ++static struct irq_chip t7l66xb_chip = { ++ .name = "t7l66xb", ++ .ack = t7l66xb_irq_mask, ++ .mask = t7l66xb_irq_mask, ++ .unmask = t7l66xb_irq_unmask, ++}; ++ ++/*--------------------------------------------------------------------------*/ ++ ++/* Install the IRQ handler */ ++static void t7l66xb_attach_irq(struct platform_device *dev) ++{ ++ struct t7l66xb_platform_data *tcpd = dev->dev.platform_data; ++ struct t7l66xb *t7l66xb = platform_get_drvdata(dev); ++ unsigned int irq; ++ ++ for ( ++ irq = tcpd->irq_base; ++ irq <= tcpd->irq_base + T7L66XB_NR_IRQS; ++ irq++) { ++ set_irq_chip (irq, &t7l66xb_chip); ++ set_irq_chip_data (irq, dev); ++ set_irq_handler(irq, handle_level_irq); ++ set_irq_flags(irq, IRQF_VALID | IRQF_PROBE); ++ } ++ ++ set_irq_type (t7l66xb->irq, IRQT_FALLING); ++ set_irq_chip_data (t7l66xb->irq, dev); ++ set_irq_chained_handler (t7l66xb->irq, t7l66xb_irq); ++} ++ ++static void t7l66xb_detach_irq(struct platform_device *dev) ++{ ++ struct t7l66xb_platform_data *tcpd = dev->dev.platform_data; ++ struct t7l66xb *t7l66xb = platform_get_drvdata(dev); ++ unsigned int irq; ++ ++ set_irq_chained_handler(t7l66xb->irq, NULL); ++ set_irq_chip_data(t7l66xb->irq, NULL); ++ ++ for ( ++ irq = tcpd->irq_base; ++ irq <= tcpd->irq_base + T7L66XB_NR_IRQS; ++ irq++) { ++ set_irq_flags(irq, 0); ++ set_irq_chip(irq, NULL); ++ set_irq_chip_data(irq, NULL); ++ } ++} ++ ++/*--------------------------------------------------------------------------*/ ++ ++#ifdef CONFIG_PM ++static int t7l66xb_suspend(struct platform_device *dev, pm_message_t state) ++{ ++ struct t7l66xb_platform_data *pdata = dev->dev.platform_data; ++ ++ ++ if (pdata && pdata->suspend) ++ pdata->suspend(dev); ++ ++ return 0; ++} ++ ++static int t7l66xb_resume(struct platform_device *dev) ++{ ++ struct t7l66xb_platform_data *pdata = dev->dev.platform_data; ++ ++ if (pdata && pdata->resume) ++ pdata->resume(dev); ++ ++ return 0; ++} ++#else ++#define t7l66xb_suspend NULL ++#define t7l66xb_resume NULL ++#endif ++ ++/*--------------------------------------------------------------------------*/ ++ ++static int t7l66xb_probe(struct platform_device *dev) ++{ ++ struct t7l66xb_platform_data *pdata = dev->dev.platform_data; ++ struct t7l66xb *t7l66xb; ++ struct resource *iomem; ++ struct resource *rscr; ++ int retval = -ENOMEM; ++ ++ iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); ++ if (!iomem) ++ return -EINVAL; ++ ++ t7l66xb = kzalloc (sizeof *t7l66xb, GFP_KERNEL); ++ if (!t7l66xb) ++ goto err_kzalloc; ++ ++ spin_lock_init(&t7l66xb->lock); ++ ++ platform_set_drvdata(dev, t7l66xb); ++ t7l66xb->iomem = iomem; ++ t7l66xb->irq = platform_get_irq(dev, 0); ++ ++ rscr = &t7l66xb->rscr; ++ rscr->name = "t7l66xb-core"; ++ rscr->start = iomem->start; ++ rscr->end = iomem->start + 0xff; ++ rscr->flags = IORESOURCE_MEM; ++ ++ if((retval = request_resource(iomem, rscr))) ++ goto err_request_scr; ++ ++ t7l66xb->scr = ioremap(rscr->start, rscr->end - rscr->start + 1); ++ if (!t7l66xb->scr) { ++ retval = -ENOMEM; ++ goto err_ioremap; ++ } ++ ++ if (pdata && pdata->enable) ++ pdata->enable(dev); ++ ++ writeb(0xbf, &t7l66xb->scr->imr); /* Mask all interrupts */ ++ ++ printk(KERN_INFO "%s rev %d @ 0x%08lx, irq %d\n", ++ dev->name, readb(&t7l66xb->scr->revid), ++ (unsigned long)t7l66xb->scr, t7l66xb->irq); ++ ++ if(t7l66xb->irq) ++ t7l66xb_attach_irq(dev); ++ ++ t7l66xb_cells[2].driver_data = pdata->nand_data; ++ ++ if(!(retval = mfd_add_devices(dev, t7l66xb_cells, ++ ARRAY_SIZE(t7l66xb_cells), ++ iomem, 0, pdata->irq_base))) ++ return 0; ++ ++ if(t7l66xb->irq) ++ t7l66xb_detach_irq(dev); ++ ++ iounmap(t7l66xb->scr); ++err_ioremap: ++ release_resource(rscr); ++err_request_scr: ++ kfree(t7l66xb); ++err_kzalloc: ++ release_resource(iomem); ++ return retval; ++} ++ ++static int t7l66xb_remove(struct platform_device *dev) ++{ ++ struct t7l66xb_platform_data *pdata = dev->dev.platform_data; ++ struct t7l66xb *t7l66xb = platform_get_drvdata(dev); ++ int ret; ++ ++ if (t7l66xb->irq) ++ t7l66xb_detach_irq(dev); ++ ++ ret = pdata->disable(dev); ++ ++ iounmap(t7l66xb->scr); ++ release_resource(&t7l66xb->rscr); ++ release_resource(t7l66xb->iomem); ++ ++ mfd_remove_devices(dev); ++ ++ platform_set_drvdata(dev, NULL); ++ ++ kfree(t7l66xb); ++ ++ return ret; ++ ++} ++ ++static struct platform_driver t7l66xb_platform_driver = { ++ .driver = { ++ .name = "t7l66xb", ++ .owner = THIS_MODULE, ++ }, ++ .suspend = t7l66xb_suspend, ++ .resume = t7l66xb_resume, ++ .probe = t7l66xb_probe, ++ .remove = t7l66xb_remove, ++}; ++ ++/*--------------------------------------------------------------------------*/ ++ ++static int __init t7l66xb_init(void) ++{ ++ int retval = 0; ++ ++ retval = platform_driver_register (&t7l66xb_platform_driver); ++ return retval; ++} ++ ++static void __exit t7l66xb_exit(void) ++{ ++ platform_driver_unregister(&t7l66xb_platform_driver); ++} ++ ++module_init(t7l66xb_init); ++module_exit(t7l66xb_exit); ++ ++MODULE_DESCRIPTION("Toshiba T7L66XB core driver"); ++MODULE_LICENSE("GPLv2"); ++MODULE_AUTHOR("Ian Molton"); ++ +diff --git a/include/linux/mfd/t7l66xb.h b/include/linux/mfd/t7l66xb.h +new file mode 100644 +index 0000000..06b8de5 +--- /dev/null ++++ b/include/linux/mfd/t7l66xb.h +@@ -0,0 +1,45 @@ ++/* ++ * linux/include/asm-arm/hardware/t7l66xb.h ++ * ++ * This file contains the definitions for the T7L66XB ++ * ++ * (C) Copyright 2005 Ian Molton <spyro@f2s.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++#ifndef _ASM_ARCH_T7L66XB_SOC ++#define _ASM_ARCH_T7L66XB_SOC ++ ++#include <linux/mfd-core.h> ++#include <linux/mfd/tmio.h> ++ ++ ++struct t7l66xb_platform_data ++{ ++ int (*enable_clk32k)(struct platform_device *dev); ++ int (*disable_clk32k)(struct platform_device *dev); ++ ++ int (*enable)(struct platform_device *dev); ++ int (*disable)(struct platform_device *dev); ++ int (*suspend)(struct platform_device *dev); ++ int (*resume)(struct platform_device *dev); ++ ++ int irq_base; /* a base for cascaded irq */ ++ ++ struct tmio_nand_data *nand_data; ++}; ++ ++ ++#define T7L66XB_NAND_CNF_BASE (0x000100) ++#define T7L66XB_NAND_CTL_BASE (0x001000) ++ ++#define IRQ_T7L66XB_NAND (3) ++#define IRQ_T7L66XB_MMC (1) ++#define IRQ_T7L66XB_OHCI (2) ++ ++#define T7L66XB_NR_IRQS 8 ++ ++#endif +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0007-Common-headers-for-TMIO-MFD-subdevices.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0007-Common-headers-for-TMIO-MFD-subdevices.patch new file mode 100644 index 0000000000..2f5f11400c --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0007-Common-headers-for-TMIO-MFD-subdevices.patch @@ -0,0 +1,81 @@ +From d6e8b347dbcce9e0e8d2204b774c1c33cfcb483e Mon Sep 17 00:00:00 2001 +From: Ian Molton <spyro@f2s.com> +Date: Sat, 29 Dec 2007 15:27:43 +0000 +Subject: [PATCH 07/64] Common headers for TMIO MFD subdevices + +--- + include/linux/mfd/tmio.h | 62 ++++++++++++++++++++++++++++++++++++++++++++++ + 1 files changed, 62 insertions(+), 0 deletions(-) + create mode 100644 include/linux/mfd/tmio.h + +diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h +new file mode 100644 +index 0000000..b42a4c3 +--- /dev/null ++++ b/include/linux/mfd/tmio.h +@@ -0,0 +1,62 @@ ++#ifndef MFD_TMIO_H ++#define MFD_TMIO_H ++ ++#include <linux/io.h> ++#include <linux/platform_device.h> ++ ++struct fb_videomode; ++ ++/* ++ * data for the NAND controller ++ */ ++struct tmio_nand_data { ++ struct nand_bbt_descr *badblock_pattern; ++ struct mtd_partition *partition; ++ unsigned int num_partitions; ++}; ++ ++struct tmio_fb_data { ++ int (*lcd_set_power)(struct platform_device *fb_dev, ++ bool on); ++ int (*lcd_mode)(struct platform_device *fb_dev, ++ struct fb_videomode *mode); ++ int num_modes; ++ struct fb_videomode *modes; ++}; ++ ++static u32 __maybe_unused tmio_ioread32(const void __iomem *addr) ++{ ++ return ((u32) ioread16(addr)) | (((u32) ioread16(addr + 2)) << 16); ++} ++ ++static u32 __maybe_unused tmio_iowrite32(u32 val, const void __iomem *addr) ++{ ++ iowrite16(val, addr); ++ iowrite16(val >> 16, addr + 2); ++ return val; ++} ++ ++#define FBIO_TMIO_ACC_WRITE 0x7C639300 ++#define FBIO_TMIO_ACC_SYNC 0x7C639301 ++ ++#define TMIO_MMC_CONFIG "tmio-mmc-config" ++#define TMIO_MMC_CONTROL "tmio-mmc-control" ++#define TMIO_MMC_IRQ "tmio-mmc" ++ ++#define TMIO_NAND_CONFIG "tmio-nand-config" ++#define TMIO_NAND_CONTROL "tmio-nand-control" ++#define TMIO_NAND_IRQ "tmio-nand" ++ ++#define TMIO_FB_CONFIG "tmio-fb-config" ++#define TMIO_FB_CONTROL "tmio-fb-control" ++#define TMIO_FB_VRAM "tmio-fb-vram" ++#define TMIO_FB_IRQ "tmio-fb" ++ ++#define TMIO_OHCI_CONFIG "tmio-ohci-config" ++#define TMIO_OHCI_CONTROL "tmio-ohci-control" ++#define TMIO_OHCI_SRAM "tmio-ohci-sram" ++#define TMIO_OHCI_SRAM_ALIAS "tmio-ohci-sram-alias" ++#define TMIO_OHCI_IRQ "tmio-ohci" ++ ++#endif ++ +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0008-Nand-driver-for-TMIO-devices.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0008-Nand-driver-for-TMIO-devices.patch new file mode 100644 index 0000000000..48b8000ab7 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0008-Nand-driver-for-TMIO-devices.patch @@ -0,0 +1,608 @@ +From 917b3997a39396f5f51418930de7b933ad053bad Mon Sep 17 00:00:00 2001 +From: Ian Molton <spyro@f2s.com> +Date: Sat, 29 Dec 2007 15:14:23 +0000 +Subject: [PATCH 08/64] Nand driver for TMIO devices + +--- + drivers/mtd/nand/Kconfig | 7 + + drivers/mtd/nand/Makefile | 1 + + drivers/mtd/nand/tmio_nand.c | 557 ++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 565 insertions(+), 0 deletions(-) + create mode 100644 drivers/mtd/nand/tmio_nand.c + +diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig +index 246d451..43e489a 100644 +--- a/drivers/mtd/nand/Kconfig ++++ b/drivers/mtd/nand/Kconfig +@@ -284,6 +284,13 @@ config MTD_NAND_CM_X270 + depends on MTD_NAND && MACH_ARMCORE + + ++config MTD_NAND_TMIO ++ tristate "NAND Flash device on Toshiba Mobile IO Controller" ++ depends on MTD_NAND && MFD_CORE ++ help ++ Support for NAND flash connected to a Toshiba Mobile IO ++ Controller in some PDAs, including the Sharp SL6000x. ++ + config MTD_NAND_NANDSIM + tristate "Support for NAND Flash Simulator" + depends on MTD_PARTITIONS +diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile +index 3ad6c01..d839ebd 100644 +--- a/drivers/mtd/nand/Makefile ++++ b/drivers/mtd/nand/Makefile +@@ -27,6 +27,7 @@ obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o + obj-$(CONFIG_MTD_NAND_AT91) += at91_nand.o + obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o + obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o ++obj-$(CONFIG_MTD_NAND_TMIO) += tmio_nand.o + obj-$(CONFIG_MTD_NAND_PLATFORM) += plat_nand.o + obj-$(CONFIG_MTD_ALAUDA) += alauda.o + +diff --git a/drivers/mtd/nand/tmio_nand.c b/drivers/mtd/nand/tmio_nand.c +new file mode 100644 +index 0000000..450b4ec +--- /dev/null ++++ b/drivers/mtd/nand/tmio_nand.c +@@ -0,0 +1,557 @@ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/mfd-core.h> ++#include <linux/mfd/tmio.h> ++#include <linux/delay.h> ++#include <linux/io.h> ++#include <linux/irq.h> ++#include <linux/interrupt.h> ++#include <linux/ioport.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/nand.h> ++#include <linux/mtd/nand_ecc.h> ++#include <linux/mtd/partitions.h> ++ ++/*--------------------------------------------------------------------------*/ ++ ++/* tmio_nfcr.mode Register Command List */ ++#define FCR_MODE_DATA 0x94 /* Data Data_Mode */ ++#define FCR_MODE_COMMAND 0x95 /* Data Command_Mode */ ++#define FCR_MODE_ADDRESS 0x96 /* Data Address_Mode */ ++ ++#define FCR_MODE_HWECC_CALC 0xB4 /* HW-ECC Data */ ++#define FCR_MODE_HWECC_RESULT 0xD4 /* HW-ECC Calculation Result Read_Mode */ ++#define FCR_MODE_HWECC_RESET 0xF4 /* HW-ECC Reset */ ++ ++#define FCR_MODE_POWER_ON 0x0C /* Power Supply ON to SSFDC card */ ++#define FCR_MODE_POWER_OFF 0x08 /* Power Supply OFF to SSFDC card */ ++ ++#define FCR_MODE_LED_OFF 0x00 /* LED OFF */ ++#define FCR_MODE_LED_ON 0x04 /* LED ON */ ++ ++#define FCR_MODE_EJECT_ON 0x68 /* Ejection Demand from Penguin is Advanced */ ++#define FCR_MODE_EJECT_OFF 0x08 /* Ejection Demand from Penguin is Not Advanced */ ++ ++#define FCR_MODE_LOCK 0x6C /* Operates By Lock_Mode. Ejection Switch is Invalid */ ++#define FCR_MODE_UNLOCK 0x0C /* Operates By UnLock_Mode.Ejection Switch is Effective */ ++ ++#define FCR_MODE_CONTROLLER_ID 0x40 /* Controller ID Read */ ++#define FCR_MODE_STANDBY 0x00 /* SSFDC card Changes Standby State */ ++ ++#define FCR_MODE_WE 0x80 ++#define FCR_MODE_ECC1 0x40 ++#define FCR_MODE_ECC0 0x20 ++#define FCR_MODE_CE 0x10 ++#define FCR_MODE_PCNT1 0x08 ++#define FCR_MODE_PCNT0 0x04 ++#define FCR_MODE_ALE 0x02 ++#define FCR_MODE_CLE 0x01 ++ ++#define FCR_STATUS_BUSY 0x80 ++ ++/* ++ *NAND Flash Host Controller Configuration Register ++ */ ++struct tmio_nfhccr { ++ u8 x00[4]; ++ u16 command; /* 0x04 Command */ ++ u8 x01[0x0a]; ++ u16 base[2]; /* 0x10 NAND Flash Control Reg Base Addr*/ ++ u8 x02[0x29]; ++ u8 intp; /* 0x3d Interrupt Pin */ ++ u8 x03[0x0a]; ++ u8 inte; /* 0x48 Interrupt Enable */ ++ u8 x04; ++ u8 ec; /* 0x4a Event Control */ ++ u8 x05; ++ u8 icc; /* 0x4c Internal Clock Control */ ++ u8 x06[0x0e]; ++ u8 eccc; /* 0x5b ECC Control */ ++ u8 x07[4]; ++ u8 nftc; /* 0x60 NAND Flash Transaction Control */ ++ u8 nfm; /* 0x61 NAND Flash Monitor */ ++ u8 nfpsc; /* 0x62 NAND Flash Power Supply Control */ ++ u8 nfdc; /* 0x63 NAND Flash Detect Control */ ++ u8 x08[0x9c]; ++} __attribute__ ((packed)); ++ ++/* ++ *NAND Flash Control Register ++ */ ++struct tmio_nfcr { ++union { ++ u8 u8; /* 0x00 Data Register */ ++ u16 u16; ++ u32 u32; ++} __attribute__ ((packed)); ++ u8 mode; /* 0x04 Mode Register */ ++ u8 status; /* 0x05 Status Register */ ++ u8 isr; /* 0x06 Interrupt Status Register */ ++ u8 imr; /* 0x07 Interrupt Mask Register */ ++} __attribute__ ((packed)); ++ ++struct tmio_nand { ++ struct mtd_info mtd; ++ struct nand_chip chip; ++ ++ struct platform_device *dev; ++ ++ struct tmio_nfhccr __iomem *ccr; ++ struct tmio_nfcr __iomem *fcr; ++ ++ unsigned int irq; ++ ++ /* for tmio_nand_read_byte */ ++ u8 read; ++ unsigned read_good:1; ++}; ++ ++#define mtd_to_tmio(m) container_of(m, struct tmio_nand, mtd) ++ ++#ifdef CONFIG_MTD_CMDLINE_PARTS ++static const char *part_probes[] = { "cmdlinepart", NULL }; ++#endif ++ ++/*--------------------------------------------------------------------------*/ ++ ++static void tmio_nand_hwcontrol(struct mtd_info *mtd, int cmd, ++ unsigned int ctrl) ++{ ++ struct tmio_nand *tmio = mtd_to_tmio(mtd); ++ struct tmio_nfcr __iomem *fcr = tmio->fcr; ++ struct nand_chip *chip = mtd->priv; ++ ++ if (ctrl & NAND_CTRL_CHANGE) { ++ u8 mode; ++ ++ if (ctrl & NAND_NCE) { ++ mode = FCR_MODE_DATA; ++ ++ if (ctrl & NAND_CLE) ++ mode |= FCR_MODE_CLE; ++ else ++ mode &= ~FCR_MODE_CLE; ++ ++ if (ctrl & NAND_ALE) ++ mode |= FCR_MODE_ALE; ++ else ++ mode &= ~FCR_MODE_ALE; ++ } else { ++ mode = FCR_MODE_STANDBY; ++ } ++ ++ iowrite8(mode, &fcr->mode); ++ tmio->read_good = 0; ++ } ++ ++ if (cmd != NAND_CMD_NONE) ++ writeb(cmd, chip->IO_ADDR_W); ++} ++ ++static int tmio_nand_dev_ready(struct mtd_info *mtd) ++{ ++ struct tmio_nand *tmio = mtd_to_tmio(mtd); ++ struct tmio_nfcr __iomem *fcr = tmio->fcr; ++ ++ return !(ioread8(&fcr->status) & FCR_STATUS_BUSY); ++} ++ ++static irqreturn_t tmio_irq(int irq, void *__dev) ++{ ++ struct platform_device *dev = __dev; ++ struct tmio_nand *tmio = platform_get_drvdata(dev); ++ struct nand_chip *nand_chip = &tmio->chip; ++ struct tmio_nfcr __iomem *fcr = tmio->fcr; ++ ++ /* disable RDYREQ interrupt */ ++ iowrite8(0x00, &fcr->imr); ++ ++ if (unlikely(!waitqueue_active(&nand_chip->controller->wq))) ++ dev_warn(&dev->dev, "spurious interrupt\n"); ++ ++ wake_up(&nand_chip->controller->wq); ++ return IRQ_HANDLED; ++} ++ ++/* ++ *The TMIO core has a RDYREQ interrupt on the posedge of #SMRB. ++ *This interrupt is normally disabled, but for long operations like ++ *erase and write, we enable it to wake us up. The irq handler ++ *disables the interrupt. ++ */ ++static int ++tmio_nand_wait(struct mtd_info *mtd, struct nand_chip *nand_chip) ++{ ++ struct tmio_nand *tmio = mtd_to_tmio(mtd); ++ struct tmio_nfcr __iomem *fcr = tmio->fcr; ++ long timeout; ++ ++ /* enable RDYREQ interrupt */ ++ iowrite8(0x0f, &fcr->isr); ++ iowrite8(0x81, &fcr->imr); ++ ++ timeout = wait_event_timeout(nand_chip->controller->wq, tmio_nand_dev_ready(mtd), ++ msecs_to_jiffies(nand_chip->state == FL_ERASING ? 400 : 20)); ++ ++ if (unlikely(!tmio_nand_dev_ready(mtd))) { ++ iowrite8(0x00, &fcr->imr); ++ dev_warn(&tmio->dev->dev, "still busy with %s after %d ms\n", ++ nand_chip->state == FL_ERASING ? "erase" : "program", ++ nand_chip->state == FL_ERASING ? 400 : 20); ++ ++ } else if (unlikely(!timeout)) { ++ iowrite8(0x00, &fcr->imr); ++ dev_warn(&tmio->dev->dev, "timeout waiting for interrupt\n"); ++ } ++ ++ nand_chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); ++ return nand_chip->read_byte(mtd); ++} ++ ++/* ++ *The TMIO controller combines two 8-bit data bytes into one 16-bit ++ *word. This function separates them so nand_base.c works as expected, ++ *especially its NAND_CMD_READID routines. ++ * ++ *To prevent stale data from being read, tmio_nand_hwcontrol() clears ++ *tmio->read_good. ++ */ ++static u_char tmio_nand_read_byte(struct mtd_info *mtd) ++{ ++ struct tmio_nand *tmio = mtd_to_tmio(mtd); ++ struct tmio_nfcr __iomem *fcr = tmio->fcr; ++ unsigned int data; ++ ++ if (tmio->read_good--) ++ return tmio->read; ++ ++ data = ioread16(&fcr->u16); ++ tmio->read = data >> 8; ++ return data; ++} ++ ++/* ++ *The TMIO controller converts an 8-bit NAND interface to a 16-bit ++ *bus interface, so all data reads and writes must be 16-bit wide. ++ *Thus, we implement 16-bit versions of the read, write, and verify ++ *buffer functions. ++ */ ++static void ++tmio_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ struct tmio_nand *tmio = mtd_to_tmio(mtd); ++ struct tmio_nfcr __iomem *fcr = tmio->fcr; ++ ++ iowrite16_rep(&fcr->u16, buf, len >> 1); ++} ++ ++static void tmio_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ struct tmio_nand *tmio = mtd_to_tmio(mtd); ++ struct tmio_nfcr __iomem *fcr = tmio->fcr; ++ ++ ioread16_rep(&fcr->u16, buf, len >> 1); ++} ++ ++static int ++tmio_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ struct tmio_nand *tmio = mtd_to_tmio(mtd); ++ struct tmio_nfcr __iomem *fcr = tmio->fcr; ++ u16 *p = (u16 *) buf; ++ ++ for (len >>= 1; len; len--) ++ if (*(p++) != ioread16(&fcr->u16)) ++ return -EFAULT; ++ return 0; ++} ++ ++static void tmio_nand_enable_hwecc(struct mtd_info *mtd, int mode) ++{ ++ struct tmio_nand *tmio = mtd_to_tmio(mtd); ++ struct tmio_nfcr __iomem *fcr = tmio->fcr; ++ ++ iowrite8(FCR_MODE_HWECC_RESET, &fcr->mode); ++ ioread8(&fcr->u8); /* dummy read */ ++ iowrite8(FCR_MODE_HWECC_CALC, &fcr->mode); ++} ++ ++static int tmio_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, ++ u_char *ecc_code) ++{ ++ struct tmio_nand *tmio = mtd_to_tmio(mtd); ++ struct tmio_nfcr __iomem *fcr = tmio->fcr; ++ unsigned int ecc; ++ ++ iowrite8(FCR_MODE_HWECC_RESULT, &fcr->mode); ++ ++ ecc = ioread16(&fcr->u16); ++ ecc_code[1] = ecc; /* 000-255 LP7-0 */ ++ ecc_code[0] = ecc >> 8; /* 000-255 LP15-8 */ ++ ecc = ioread16(&fcr->u16); ++ ecc_code[2] = ecc; /* 000-255 CP5-0,11b */ ++ ecc_code[4] = ecc >> 8; /* 256-511 LP7-0 */ ++ ecc = ioread16(&fcr->u16); ++ ecc_code[3] = ecc; /* 256-511 LP15-8 */ ++ ecc_code[5] = ecc >> 8; /* 256-511 CP5-0,11b */ ++ ++ iowrite8(FCR_MODE_DATA, &fcr->mode); ++ return 0; ++} ++ ++static int tmio_hw_init(struct platform_device *dev, struct tmio_nand *tmio) ++{ ++ struct mfd_cell *cell = mfd_get_cell(dev); ++ const struct resource *nfcr = NULL; ++ struct tmio_nfhccr __iomem *ccr = tmio->ccr; ++ struct tmio_nfcr __iomem *fcr = tmio->fcr; ++ unsigned long base; ++ int i; ++ ++ for (i = 0; i < cell->num_resources; i++) ++ if (!strcmp((cell->resources+i)->name, TMIO_NAND_CONTROL)) ++ nfcr = &cell->resources[i]; ++ ++ if (nfcr == NULL) ++ return -ENOMEM; ++ ++ if (!cell->enable) { ++ printk(KERN_ERR "null cell enable!"); ++ return -EINVAL; ++ } ++ ++ cell->enable(dev); ++ ++ /* (4Ch) CLKRUN Enable 1st spcrunc */ ++ iowrite8(0x81, &ccr->icc); ++ ++ /* (10h)BaseAddress 0x1000 spba.spba2 */ ++ base = nfcr->start; ++ iowrite16(base, ccr->base + 0); ++ iowrite16(base >> 16, ccr->base + 1); ++ ++ /* (04h)Command Register I/O spcmd */ ++ iowrite8(0x02, &ccr->command); ++ ++ /* (62h) Power Supply Control ssmpwc */ ++ /* HardPowerOFF - SuspendOFF - PowerSupplyWait_4MS */ ++ iowrite8(0x02, &ccr->nfpsc); ++ ++ /* (63h) Detect Control ssmdtc */ ++ iowrite8(0x02, &ccr->nfdc); ++ ++ /* Interrupt status register clear sintst */ ++ iowrite8(0x0f, &fcr->isr); ++ ++ /* After power supply, Media are reset smode */ ++ iowrite8(FCR_MODE_POWER_ON, &fcr->mode); ++ iowrite8(FCR_MODE_COMMAND, &fcr->mode); ++ iowrite8(NAND_CMD_RESET, &fcr->u8); ++ ++ /* Standby Mode smode */ ++ iowrite8(FCR_MODE_STANDBY, &fcr->mode); ++ ++ mdelay(5); ++ ++ return 0; ++} ++ ++static void tmio_hw_stop(struct platform_device *dev, struct tmio_nand *tmio) ++{ ++ struct mfd_cell *cell = mfd_get_cell(dev); ++ struct tmio_nfcr __iomem *fcr = tmio->fcr; ++ ++ iowrite8(FCR_MODE_POWER_OFF, &fcr->mode); ++ cell->disable(dev); ++} ++ ++static int tmio_probe(struct platform_device *dev) ++{ ++ struct mfd_cell *cell = mfd_get_cell(dev); ++ struct tmio_nand_data *data = cell->driver_data; ++ struct resource *ccr = platform_get_resource_byname(dev, IORESOURCE_MEM, TMIO_NAND_CONFIG); ++ struct resource *fcr = platform_get_resource_byname(dev, IORESOURCE_MEM, TMIO_NAND_CONTROL); ++ int irq = platform_get_irq(dev, 0); ++ struct tmio_nand *tmio; ++ struct mtd_info *mtd; ++ struct nand_chip *nand_chip; ++ struct mtd_partition *parts; ++ int nbparts = 0; ++ int retval; ++ ++ if (data == NULL) { ++ dev_err(&dev->dev, "NULL platform data!\n"); ++ return -EINVAL; ++ } ++ ++ tmio = kzalloc(sizeof *tmio, GFP_KERNEL); ++ if (!tmio) { ++ retval = -ENOMEM; ++ goto err_kzalloc; ++ } ++ ++ tmio->dev = dev; ++ ++ platform_set_drvdata(dev, tmio); ++ mtd = &tmio->mtd; ++ nand_chip = &tmio->chip; ++ mtd->priv = nand_chip; ++ mtd->name = "tmio-nand"; ++ ++ tmio->ccr = ioremap(ccr->start, ccr->end - ccr->start + 1); ++ if (!tmio->ccr) { ++ retval = -EIO; ++ goto err_iomap_ccr; ++ } ++ ++ tmio->fcr = ioremap(fcr->start, fcr->end - fcr->start + 1); ++ if (!tmio->fcr) { ++ retval = -EIO; ++ goto err_iomap_fcr; ++ } ++ ++ retval = tmio_hw_init(dev, tmio); ++ if (retval) ++ goto err_hwinit; ++ ++ /* Set address of NAND IO lines */ ++ nand_chip->IO_ADDR_R = tmio->fcr; ++ nand_chip->IO_ADDR_W = tmio->fcr; ++ ++ /* Set address of hardware control function */ ++ nand_chip->cmd_ctrl = tmio_nand_hwcontrol; ++ nand_chip->dev_ready = tmio_nand_dev_ready; ++ nand_chip->read_byte = tmio_nand_read_byte; ++ nand_chip->write_buf = tmio_nand_write_buf; ++ nand_chip->read_buf = tmio_nand_read_buf; ++ nand_chip->verify_buf = tmio_nand_verify_buf; ++ ++ /* set eccmode using hardware ECC */ ++ nand_chip->ecc.mode = NAND_ECC_HW; ++ nand_chip->ecc.size = 512; ++ nand_chip->ecc.bytes = 6; ++ nand_chip->ecc.hwctl = tmio_nand_enable_hwecc; ++ nand_chip->ecc.calculate = tmio_nand_calculate_ecc; ++ nand_chip->ecc.correct = nand_correct_data; ++ nand_chip->badblock_pattern = data->badblock_pattern; ++ ++ /* 15 us command delay time */ ++ nand_chip->chip_delay = 15; ++ ++ retval = request_irq(irq, &tmio_irq, ++ IRQF_DISABLED, dev->dev.bus_id, dev); ++ if (retval) { ++ dev_err(&dev->dev, "request_irq error %d\n", retval); ++ goto err_irq; ++ } ++ ++ tmio->irq = irq; ++ nand_chip->waitfunc = tmio_nand_wait; ++ ++ /* Scan to find existence of the device */ ++ if (nand_scan(mtd, 1)) { ++ retval = -ENODEV; ++ goto err_scan; ++ } ++ /* Register the partitions */ ++#ifdef CONFIG_MTD_PARTITIONS ++#ifdef CONFIG_MTD_CMDLINE_PARTS ++ nbparts = parse_mtd_partitions(mtd, part_probes, &parts, 0); ++#endif ++ if (nbparts <= 0) { ++ parts = data->partition; ++ nbparts = data->num_partitions; ++ } ++ ++ retval = add_mtd_partitions(mtd, parts, nbparts); ++#else ++ retval = add_mtd_device(mtd); ++#endif ++ ++ if (!retval) ++ return retval; ++ ++ nand_release(mtd); ++ ++err_scan: ++ if (tmio->irq) ++ free_irq(tmio->irq, dev); ++err_irq: ++ tmio_hw_stop(dev, tmio); ++err_hwinit: ++ iounmap(tmio->fcr); ++err_iomap_fcr: ++ iounmap(tmio->ccr); ++err_iomap_ccr: ++ kfree(tmio); ++err_kzalloc: ++ return retval; ++} ++ ++static int tmio_remove(struct platform_device *dev) ++{ ++ struct tmio_nand *tmio = platform_get_drvdata(dev); ++ ++ nand_release(&tmio->mtd); ++ if (tmio->irq) ++ free_irq(tmio->irq, tmio); ++ tmio_hw_stop(dev, tmio); ++ iounmap(tmio->fcr); ++ iounmap(tmio->ccr); ++ kfree(tmio); ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int tmio_suspend(struct platform_device *dev, pm_message_t state) ++{ ++ struct mfd_cell *cell = mfd_get_cell(dev); ++ ++ if (cell->suspend) ++ cell->suspend(dev); ++ ++ tmio_hw_stop(dev, platform_get_drvdata(dev)); ++ return 0; ++} ++ ++static int tmio_resume(struct platform_device *dev) ++{ ++ struct mfd_cell *cell = mfd_get_cell(dev); ++ ++ tmio_hw_init(dev, platform_get_drvdata(dev)); ++ ++ if (cell->resume) ++ cell->resume(dev); ++ ++ return 0; ++} ++#endif ++ ++static struct platform_driver tmio_driver = { ++ .driver.name = "tmio-nand", ++ .driver.owner = THIS_MODULE, ++ .probe = tmio_probe, ++ .remove = tmio_remove, ++#ifdef CONFIG_PM ++ .suspend = tmio_suspend, ++ .resume = tmio_resume, ++#endif ++}; ++ ++static int __init tmio_init(void) ++{ ++ return platform_driver_register(&tmio_driver); ++} ++ ++static void __exit tmio_exit(void) ++{ ++ platform_driver_unregister(&tmio_driver); ++} ++ ++module_init(tmio_init); ++module_exit(tmio_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Dirk Opfer, Chris Humbert, Dmitry Baryshkov"); ++MODULE_DESCRIPTION("NAND flash driver on Toshiba Mobile IO controller"); +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0009-FB-driver-for-TMIO-devices.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0009-FB-driver-for-TMIO-devices.patch new file mode 100644 index 0000000000..5fc96f8973 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0009-FB-driver-for-TMIO-devices.patch @@ -0,0 +1,1128 @@ +From 519d015892ab0a7cad1f6b26fcd38117171384ce Mon Sep 17 00:00:00 2001 +From: Ian Molton <spyro@f2s.com> +Date: Tue, 1 Jan 2008 21:22:23 +0000 +Subject: [PATCH 09/64] FB driver for TMIO devices + +--- + drivers/video/Kconfig | 22 + + drivers/video/Makefile | 1 + + drivers/video/tmiofb.c | 1062 ++++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 1085 insertions(+), 0 deletions(-) + create mode 100644 drivers/video/tmiofb.c + +diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig +index 5b3dbcf..6d0df58 100644 +--- a/drivers/video/Kconfig ++++ b/drivers/video/Kconfig +@@ -1782,6 +1782,28 @@ config FB_W100 + + If unsure, say N. + ++config FB_TMIO ++ tristate "Toshiba Mobice IO FrameBuffer support" ++ depends on FB && MFD_CORE ++ select FB_CFB_FILLRECT ++ select FB_CFB_COPYAREA ++ select FB_CFB_IMAGEBLIT ++ ---help--- ++ Frame buffer driver for the Toshiba Mobile IO integrated as found ++ on the Sharp SL-6000 series ++ ++ This driver is also available as a module ( = code which can be ++ inserted and removed from the running kernel whenever you want). The ++ module will be called tmiofb. If you want to compile it as a module, ++ say M here and read <file:Documentation/kbuild/modules.txt>. ++ ++ If unsure, say N. ++ ++config FB_TMIO_ACCELL ++ bool "tmiofb acceleration" ++ depends on FB_TMIO ++ default y ++ + config FB_S3C2410 + tristate "S3C2410 LCD framebuffer support" + depends on FB && ARCH_S3C2410 +diff --git a/drivers/video/Makefile b/drivers/video/Makefile +index 83e02b3..74e9384 100644 +--- a/drivers/video/Makefile ++++ b/drivers/video/Makefile +@@ -97,6 +97,7 @@ obj-$(CONFIG_FB_CIRRUS) += cirrusfb.o + obj-$(CONFIG_FB_ASILIANT) += asiliantfb.o + obj-$(CONFIG_FB_PXA) += pxafb.o + obj-$(CONFIG_FB_W100) += w100fb.o ++obj-$(CONFIG_FB_TMIO) += tmiofb.o + obj-$(CONFIG_FB_AU1100) += au1100fb.o + obj-$(CONFIG_FB_AU1200) += au1200fb.o + obj-$(CONFIG_FB_PMAG_AA) += pmag-aa-fb.o +diff --git a/drivers/video/tmiofb.c b/drivers/video/tmiofb.c +new file mode 100644 +index 0000000..6b963a1 +--- /dev/null ++++ b/drivers/video/tmiofb.c +@@ -0,0 +1,1062 @@ ++/* ++ * Frame Buffer Device for Toshiba Mobile IO(TMIO) controller ++ * ++ * Copyright(C) 2005-2006 Chris Humbert ++ * Copyright(C) 2005 Dirk Opfer ++ * ++ * Based on: ++ * drivers/video/w100fb.c ++ * code written by Sharp/Lineo for 2.4 kernels ++ * ++ * 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. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/mfd-core.h> ++#include <linux/mfd/tmio.h> ++#include <linux/fb.h> ++#include <linux/interrupt.h> ++#include <linux/delay.h> ++/* Why should fb driver call console functions? because acquire_console_sem() */ ++#include <linux/console.h> ++#include <linux/uaccess.h> ++#include <linux/vmalloc.h> ++ ++/* ++ * accelerator commands ++ */ ++#define TMIOFB_ACC_CSADR(x) (0x00000000 | ((x) & 0x001ffffe)) ++#define TMIOFB_ACC_CHPIX(x) (0x01000000 | ((x) & 0x000003ff)) ++#define TMIOFB_ACC_CVPIX(x) (0x02000000 | ((x) & 0x000003ff)) ++#define TMIOFB_ACC_PSADR(x) (0x03000000 | ((x) & 0x00fffffe)) ++#define TMIOFB_ACC_PHPIX(x) (0x04000000 | ((x) & 0x000003ff)) ++#define TMIOFB_ACC_PVPIX(x) (0x05000000 | ((x) & 0x000003ff)) ++#define TMIOFB_ACC_PHOFS(x) (0x06000000 | ((x) & 0x000003ff)) ++#define TMIOFB_ACC_PVOFS(x) (0x07000000 | ((x) & 0x000003ff)) ++#define TMIOFB_ACC_POADR(x) (0x08000000 | ((x) & 0x00fffffe)) ++#define TMIOFB_ACC_RSTR(x) (0x09000000 | ((x) & 0x000000ff)) ++#define TMIOFB_ACC_TCLOR(x) (0x0A000000 | ((x) & 0x0000ffff)) ++#define TMIOFB_ACC_FILL(x) (0x0B000000 | ((x) & 0x0000ffff)) ++#define TMIOFB_ACC_DSADR(x) (0x0C000000 | ((x) & 0x00fffffe)) ++#define TMIOFB_ACC_SSADR(x) (0x0D000000 | ((x) & 0x00fffffe)) ++#define TMIOFB_ACC_DHPIX(x) (0x0E000000 | ((x) & 0x000003ff)) ++#define TMIOFB_ACC_DVPIX(x) (0x0F000000 | ((x) & 0x000003ff)) ++#define TMIOFB_ACC_SHPIX(x) (0x10000000 | ((x) & 0x000003ff)) ++#define TMIOFB_ACC_SVPIX(x) (0x11000000 | ((x) & 0x000003ff)) ++#define TMIOFB_ACC_LBINI(x) (0x12000000 | ((x) & 0x0000ffff)) ++#define TMIOFB_ACC_LBK2(x) (0x13000000 | ((x) & 0x0000ffff)) ++#define TMIOFB_ACC_SHBINI(x) (0x14000000 | ((x) & 0x0000ffff)) ++#define TMIOFB_ACC_SHBK2(x) (0x15000000 | ((x) & 0x0000ffff)) ++#define TMIOFB_ACC_SVBINI(x) (0x16000000 | ((x) & 0x0000ffff)) ++#define TMIOFB_ACC_SVBK2(x) (0x17000000 | ((x) & 0x0000ffff)) ++ ++#define TMIOFB_ACC_CMGO 0x20000000 ++#define TMIOFB_ACC_CMGO_CEND 0x00000001 ++#define TMIOFB_ACC_CMGO_INT 0x00000002 ++#define TMIOFB_ACC_CMGO_CMOD 0x00000010 ++#define TMIOFB_ACC_CMGO_CDVRV 0x00000020 ++#define TMIOFB_ACC_CMGO_CDHRV 0x00000040 ++#define TMIOFB_ACC_CMGO_RUND 0x00008000 ++#define TMIOFB_ACC_SCGO 0x21000000 ++#define TMIOFB_ACC_SCGO_CEND 0x00000001 ++#define TMIOFB_ACC_SCGO_INT 0x00000002 ++#define TMIOFB_ACC_SCGO_ROP3 0x00000004 ++#define TMIOFB_ACC_SCGO_TRNS 0x00000008 ++#define TMIOFB_ACC_SCGO_DVRV 0x00000010 ++#define TMIOFB_ACC_SCGO_DHRV 0x00000020 ++#define TMIOFB_ACC_SCGO_SVRV 0x00000040 ++#define TMIOFB_ACC_SCGO_SHRV 0x00000080 ++#define TMIOFB_ACC_SCGO_DSTXY 0x00008000 ++#define TMIOFB_ACC_SBGO 0x22000000 ++#define TMIOFB_ACC_SBGO_CEND 0x00000001 ++#define TMIOFB_ACC_SBGO_INT 0x00000002 ++#define TMIOFB_ACC_SBGO_DVRV 0x00000010 ++#define TMIOFB_ACC_SBGO_DHRV 0x00000020 ++#define TMIOFB_ACC_SBGO_SVRV 0x00000040 ++#define TMIOFB_ACC_SBGO_SHRV 0x00000080 ++#define TMIOFB_ACC_SBGO_SBMD 0x00000100 ++#define TMIOFB_ACC_FLGO 0x23000000 ++#define TMIOFB_ACC_FLGO_CEND 0x00000001 ++#define TMIOFB_ACC_FLGO_INT 0x00000002 ++#define TMIOFB_ACC_FLGO_ROP3 0x00000004 ++#define TMIOFB_ACC_LDGO 0x24000000 ++#define TMIOFB_ACC_LDGO_CEND 0x00000001 ++#define TMIOFB_ACC_LDGO_INT 0x00000002 ++#define TMIOFB_ACC_LDGO_ROP3 0x00000004 ++#define TMIOFB_ACC_LDGO_ENDPX 0x00000008 ++#define TMIOFB_ACC_LDGO_LVRV 0x00000010 ++#define TMIOFB_ACC_LDGO_LHRV 0x00000020 ++#define TMIOFB_ACC_LDGO_LDMOD 0x00000040 ++ ++/* a FIFO is always allocated, even if acceleration is not used */ ++#define TMIOFB_FIFO_SIZE 512 ++ ++/* ++ * LCD Host Controller Configuration Register ++ * ++ * This iomem area supports only 16-bit IO. ++ */ ++struct tmio_lhccr { ++ u16 x00[2]; ++ u16 cmd; /* 0x04 Command */ ++ u16 x01; ++ u16 revid; /* 0x08 Revision ID */ ++ u16 x02[3]; ++ u16 basel; /* 0x10 LCD Control Reg Base Addr Low */ ++ u16 baseh; /* 0x12 LCD Control Reg Base Addr High */ ++ u16 x03[0x16]; ++ u16 ugcc; /* 0x40 Unified Gated Clock Control */ ++ u16 gcc; /* 0x42 Gated Clock Control */ ++ u16 x04[6]; ++ u16 usc; /* 0x50 Unified Software Clear */ ++ u16 x05[7]; ++ u16 vramrtc; /* 0x60 VRAM Timing Control */ ++ /* 0x61 VRAM Refresh Control */ ++ u16 vramsac; /* 0x62 VRAM Access Control */ ++ /* 0x63 VRAM Status */ ++ u16 vrambc; /* 0x64 VRAM Block Control */ ++ u16 x06[0x4d]; ++}; ++ ++/* ++ * LCD Control Register ++ * ++ * This iomem area supports only 16-bit IO. ++ */ ++struct tmio_lcr { ++ u16 uis; /* 0x000 Unified Interrupt Status */ ++ u16 x00[3]; ++ u16 vhpn; /* 0x008 VRAM Horizontal Pixel Number */ ++ u16 cfsal; /* 0x00a Command FIFO Start Address Low */ ++ u16 cfsah; /* 0x00c Command FIFO Start Address High */ ++ u16 cfs; /* 0x00e Command FIFO Size */ ++ u16 cfws; /* 0x010 Command FIFO Writeable Size */ ++ u16 bbie; /* 0x012 BitBLT Interrupt Enable */ ++ u16 bbisc; /* 0x014 BitBLT Interrupt Status and Clear */ ++ u16 ccs; /* 0x016 Command Count Status */ ++ u16 bbes; /* 0x018 BitBLT Execution Status */ ++ u16 x01; ++ u16 cmdl; /* 0x01c Command Low */ ++ u16 cmdh; /* 0x01e Command High */ ++ u16 x02; ++ u16 cfc; /* 0x022 Command FIFO Clear */ ++ u16 ccifc; /* 0x024 CMOS Camera IF Control */ ++ u16 hwt; /* 0x026 Hardware Test */ ++ u16 x03[0x6c]; ++ u16 lcdccrc;/* 0x100 LCDC Clock and Reset Control */ ++ u16 lcdcc; /* 0x102 LCDC Control */ ++ u16 lcdcopc;/* 0x104 LCDC Output Pin Control */ ++ u16 x04; ++ u16 lcdis; /* 0x108 LCD Interrupt Status */ ++ u16 lcdim; /* 0x10a LCD Interrupt Mask */ ++ u16 lcdie; /* 0x10c LCD Interrupt Enable */ ++ u16 x05[10]; ++ u16 gdsal; /* 0x122 Graphics Display Start Address Low */ ++ u16 gdsah; /* 0x124 Graphics Display Start Address High */ ++ u16 x06[2]; ++ u16 vhpcl; /* 0x12a VRAM Horizontal Pixel Count Low */ ++ u16 vhpch; /* 0x12c VRAM Horizontal Pixel Count High */ ++ u16 gm; /* 0x12e Graphic Mode(VRAM access enable) */ ++ u16 x07[8]; ++ u16 ht; /* 0x140 Horizontal Total */ ++ u16 hds; /* 0x142 Horizontal Display Start */ ++ u16 hss; /* 0x144 H-Sync Start */ ++ u16 hse; /* 0x146 H-Sync End */ ++ u16 x08[2]; ++ u16 hnp; /* 0x14c Horizontal Number of Pixels */ ++ u16 x09; ++ u16 vt; /* 0x150 Vertical Total */ ++ u16 vds; /* 0x152 Vertical Display Start */ ++ u16 vss; /* 0x154 V-Sync Start */ ++ u16 vse; /* 0x156 V-Sync End */ ++ u16 x0a[4]; ++ u16 cdln; /* 0x160 Current Display Line Number */ ++ u16 iln; /* 0x162 Interrupt Line Number */ ++ u16 sp; /* 0x164 Sync Polarity */ ++ u16 misc; /* 0x166 MISC(RGB565 mode) */ ++ u16 x0b; ++ u16 vihss; /* 0x16a Video Interface H-Sync Start */ ++ u16 vivs; /* 0x16c Video Interface Vertical Start */ ++ u16 vive; /* 0x16e Video Interface Vertical End */ ++ u16 vivss; /* 0x170 Video Interface V-Sync Start */ ++ u16 x0c[6]; ++ u16 vccis; /* 0x17e Video / CMOS Camera Interface Select */ ++ u16 vidwsal;/* 0x180 VI Data Write Start Address Low */ ++ u16 vidwsah;/* 0x182 VI Data Write Start Address High */ ++ u16 vidrsal;/* 0x184 VI Data Read Start Address Low */ ++ u16 vidrsah;/* 0x186 VI Data Read Start Address High */ ++ u16 vipddst;/* 0x188 VI Picture Data Display Start Timing */ ++ u16 vipddet;/* 0x186 VI Picture Data Display End Timing */ ++ u16 vie; /* 0x18c Video Interface Enable */ ++ u16 vcs; /* 0x18e Video/Camera Select */ ++ u16 x0d[2]; ++ u16 vphwc; /* 0x194 Video Picture Horizontal Wait Count */ ++ u16 vphs; /* 0x196 Video Picture Horizontal Size */ ++ u16 vpvwc; /* 0x198 Video Picture Vertical Wait Count */ ++ u16 vpvs; /* 0x19a Video Picture Vertical Size */ ++ u16 x0e[2]; ++ u16 plhpix; /* 0x1a0 PLHPIX */ ++ u16 xs; /* 0x1a2 XStart */ ++ u16 xckhw; /* 0x1a4 XCK High Width */ ++ u16 x0f; ++ u16 sths; /* 0x1a8 STH Start */ ++ u16 vt2; /* 0x1aa Vertical Total */ ++ u16 ycksw; /* 0x1ac YCK Start Wait */ ++ u16 ysts; /* 0x1ae YST Start */ ++ u16 ppols; /* 0x1b0 #PPOL Start */ ++ u16 precw; /* 0x1b2 PREC Width */ ++ u16 vclkhw; /* 0x1b4 VCLK High Width */ ++ u16 oc; /* 0x1b6 Output Control */ ++ u16 x10[0x24]; ++}; ++static char *mode_option __devinitdata; ++ ++struct tmiofb_par { ++ u32 pseudo_palette[16]; ++ ++#ifdef CONFIG_FB_TMIO_ACCELL ++ wait_queue_head_t wait_acc; ++ bool use_polling; ++#endif ++ ++ struct tmio_lhccr __iomem *ccr; ++ struct tmio_lcr __iomem *lcr; ++ void __iomem *vram; ++}; ++ ++/*--------------------------------------------------------------------------*/ ++ ++static irqreturn_t tmiofb_irq(int irq, void *__info); ++ ++/*--------------------------------------------------------------------------*/ ++ ++ ++/* ++ * Turns off the LCD controller and LCD host controller. ++ */ ++static int tmiofb_hw_stop(struct platform_device *dev) ++{ ++ struct mfd_cell *cell = mfd_get_cell(dev); ++ struct tmio_fb_data *data = cell->driver_data; ++ struct fb_info *info = platform_get_drvdata(dev); ++ struct tmiofb_par *par = info->par; ++ struct tmio_lhccr __iomem *ccr = par->ccr; ++ struct tmio_lcr __iomem *lcr = par->lcr; ++ ++ iowrite16(0, &ccr->ugcc); ++ iowrite16(0, &lcr->gm); ++ data->lcd_set_power(dev, 0); ++ iowrite16(0x0010, &lcr->lcdccrc); ++ ++ return 0; ++} ++ ++/* ++ * Initializes the LCD host controller. ++ */ ++static int tmiofb_hw_init(struct platform_device *dev) ++{ ++ struct mfd_cell *cell = mfd_get_cell(dev); ++ struct tmio_fb_data *data = cell->driver_data; ++ struct fb_info *info = platform_get_drvdata(dev); ++ struct tmiofb_par *par = info->par; ++ struct tmio_lhccr __iomem *ccr = par->ccr; ++ struct tmio_lcr __iomem *lcr = par->lcr; ++ const struct resource *nlcr = NULL; ++ const struct resource *vram = NULL; ++ unsigned long base; ++ int i; ++ ++ for (i = 0; i < cell->num_resources; i++) { ++ if (!strcmp((cell->resources+i)->name, TMIO_FB_CONTROL)) ++ nlcr = &cell->resources[i]; ++ if (!strcmp((cell->resources+i)->name, TMIO_FB_VRAM)) ++ vram = &cell->resources[i]; ++ } ++ ++ if (nlcr == NULL || vram == NULL) ++ return -EINVAL; ++ ++ base = nlcr->start; ++ ++ if (info->mode == NULL) { ++ printk(KERN_ERR "tmio-fb: null info->mode\n"); ++ info->mode = data->modes; ++ } ++ ++ data->lcd_mode(dev, info->mode); ++ ++ iowrite16(0x003a, &ccr->ugcc); ++ iowrite16(0x003a, &ccr->gcc); ++ iowrite16(0x3f00, &ccr->usc); ++ ++ data->lcd_set_power(dev, 1); ++ mdelay(2); ++ ++ iowrite16(0x0000, &ccr->usc); ++ iowrite16(base >> 16, &ccr->baseh); ++ iowrite16(base, &ccr->basel); ++ iowrite16(0x0002, &ccr->cmd); /* base address enable */ ++ iowrite16(0x40a8, &ccr->vramrtc); /* VRAMRC, VRAMTC */ ++ iowrite16(0x0018, &ccr->vramsac); /* VRAMSTS, VRAMAC */ ++ iowrite16(0x0002, &ccr->vrambc); ++ mdelay(2); ++ iowrite16(0x000b, &ccr->vrambc); ++ ++ base = vram->start + info->screen_size; ++ iowrite16(base >> 16, &lcr->cfsah); ++ iowrite16(base, &lcr->cfsal); ++ iowrite16(TMIOFB_FIFO_SIZE - 1, &lcr->cfs); ++ iowrite16(1, &lcr->cfc); ++ iowrite16(1, &lcr->bbie); ++ iowrite16(0, &lcr->cfws); ++ ++ return 0; ++} ++ ++/* ++ * Sets the LCD controller's output resolution and pixel clock ++ */ ++static void tmiofb_hw_mode(struct platform_device *dev) ++{ ++ struct mfd_cell *cell = mfd_get_cell(dev); ++ struct tmio_fb_data *data = cell->driver_data; ++ struct fb_info *info = platform_get_drvdata(dev); ++ struct fb_videomode *mode = info->mode; ++ struct tmiofb_par *par = info->par; ++ struct tmio_lcr __iomem *lcr = par->lcr; ++ unsigned int i; ++ ++ iowrite16(0, &lcr->gm); ++ data->lcd_set_power(dev, 0); ++ iowrite16(0x0010, &lcr->lcdccrc); ++ data->lcd_mode(dev, mode); ++ data->lcd_set_power(dev, 1); ++ ++ iowrite16(i = mode->xres * 2, &lcr->vhpn); ++ iowrite16(0, &lcr->gdsah); ++ iowrite16(0, &lcr->gdsal); ++ iowrite16(i >> 16, &lcr->vhpch); ++ iowrite16(i, &lcr->vhpcl); ++ iowrite16(i = 0, &lcr->hss); ++ iowrite16(i += mode->hsync_len, &lcr->hse); ++ iowrite16(i += mode->left_margin, &lcr->hds); ++ iowrite16(i += mode->xres + mode->right_margin, &lcr->ht); ++ iowrite16(mode->xres, &lcr->hnp); ++ iowrite16(i = 0, &lcr->vss); ++ iowrite16(i += mode->vsync_len, &lcr->vse); ++ iowrite16(i += mode->upper_margin, &lcr->vds); ++ iowrite16(i += mode->yres, &lcr->iln); ++ iowrite16(i += mode->lower_margin, &lcr->vt); ++ iowrite16(3, /* RGB565 mode */ &lcr->misc); ++ iowrite16(1, /* VRAM enable */ &lcr->gm); ++ iowrite16(0x4007, &lcr->lcdcc); ++ iowrite16(3, /* sync polarity */ &lcr->sp); ++ ++ iowrite16(0x0010, &lcr->lcdccrc); ++ mdelay(5); ++ iowrite16(0x0014, &lcr->lcdccrc); /* STOP_CKP */ ++ mdelay(5); ++ iowrite16(0x0015, &lcr->lcdccrc); /* STOP_CKP | SOFT_RESET */ ++ iowrite16(0xfffa, &lcr->vcs); ++} ++ ++/*--------------------------------------------------------------------------*/ ++ ++#ifdef CONFIG_FB_TMIO_ACCELL ++static int __must_check ++tmiofb_acc_wait(struct fb_info *info, unsigned int ccs) ++{ ++ struct tmiofb_par *par = info->par; ++ struct tmio_lcr __iomem *lcr = par->lcr; ++ if (in_atomic() || par->use_polling) { ++ int i = 0; ++ while (ioread16(&lcr->ccs) > ccs) { ++ udelay(1); ++ i++; ++ if (i > 10000) { ++ printk(KERN_ERR "tmiofb: timeout waiting for %d\n", ccs); ++ return -ETIMEDOUT; ++ } ++ tmiofb_irq(-1, info); ++ } ++ } else { ++ if (!wait_event_interruptible_timeout(par->wait_acc, ++ ioread16(&par->lcr->ccs) <= ccs, 1000)) { ++ printk(KERN_ERR "tmiofb: timeout waiting for %d\n", ccs); ++ return -ETIMEDOUT; ++ } ++ } ++ ++ return 0; ++} ++ ++/* ++ * Writes an accelerator command to the accelerator's FIFO. ++ */ ++static int ++tmiofb_acc_write(struct fb_info *info, const u32 *cmd, unsigned int count) ++{ ++ struct tmiofb_par *par = info->par; ++ struct tmio_lcr __iomem *lcr = par->lcr; ++ int ret; ++ ++ ret = tmiofb_acc_wait(info, TMIOFB_FIFO_SIZE - count); ++ if (ret) ++ return ret; ++ ++ for (; count; count--, cmd++) { ++ iowrite16(*cmd >> 16, &lcr->cmdh); ++ iowrite16(*cmd, &lcr->cmdl); ++ } ++ ++ return ret; ++} ++ ++/* ++ * Wait for the accelerator to finish its operations before writing ++ * to the framebuffer for consistent display output. ++ */ ++static int tmiofb_sync(struct fb_info *fbi) ++{ ++ struct tmiofb_par *par = fbi->par; ++ ++ int ret; ++ int i = 0; ++ ++ ret = tmiofb_acc_wait(fbi, 0); ++ ++ while (ioread16(&par->lcr->bbes) & 2) { /* blit active */ ++ udelay(1); ++ i++ ; ++ if (i > 10000) { ++ printk(KERN_ERR "timeout waiting for blit to end!\n"); ++ return -ETIMEDOUT; ++ } ++ } ++ ++ return ret; ++} ++ ++static void ++tmiofb_fillrect(struct fb_info *fbi, const struct fb_fillrect *rect) ++{ ++ const u32 cmd [] = { ++ TMIOFB_ACC_DSADR((rect->dy * fbi->mode->xres + rect->dx) * 2), ++ TMIOFB_ACC_DHPIX(rect->width - 1), ++ TMIOFB_ACC_DVPIX(rect->height - 1), ++ TMIOFB_ACC_FILL(rect->color), ++ TMIOFB_ACC_FLGO, ++ }; ++ ++ if (fbi->state != FBINFO_STATE_RUNNING || ++ fbi->flags & FBINFO_HWACCEL_DISABLED) { ++ cfb_fillrect(fbi, rect); ++ return; ++ } ++ ++ tmiofb_acc_write(fbi, cmd, ARRAY_SIZE(cmd)); ++} ++ ++static void ++tmiofb_copyarea(struct fb_info *fbi, const struct fb_copyarea *area) ++{ ++ const u32 cmd [] = { ++ TMIOFB_ACC_DSADR((area->dy * fbi->mode->xres + area->dx) * 2), ++ TMIOFB_ACC_DHPIX(area->width - 1), ++ TMIOFB_ACC_DVPIX(area->height - 1), ++ TMIOFB_ACC_SSADR((area->sy * fbi->mode->xres + area->sx) * 2), ++ TMIOFB_ACC_SCGO, ++ }; ++ ++ if (fbi->state != FBINFO_STATE_RUNNING || ++ fbi->flags & FBINFO_HWACCEL_DISABLED) { ++ cfb_copyarea(fbi, area); ++ return; ++ } ++ ++ tmiofb_acc_write(fbi, cmd, ARRAY_SIZE(cmd)); ++} ++#endif ++ ++static void tmiofb_clearscreen(struct fb_info *info) ++{ ++ const struct fb_fillrect rect = { ++ .dx = 0, ++ .dy = 0, ++ .width = info->mode->xres, ++ .height = info->mode->yres, ++ .color = 0, ++ }; ++ ++ info->fbops->fb_fillrect(info, &rect); ++} ++ ++static int tmiofb_vblank(struct fb_info *fbi, struct fb_vblank *vblank) ++{ ++ struct tmiofb_par *par = fbi->par; ++ struct fb_videomode *mode = fbi->mode; ++ unsigned int vcount = ioread16(&par->lcr->cdln); ++ unsigned int vds = mode->vsync_len + mode->upper_margin; ++ ++ vblank->vcount = vcount; ++ vblank->flags = FB_VBLANK_HAVE_VBLANK | FB_VBLANK_HAVE_VCOUNT ++ | FB_VBLANK_HAVE_VSYNC; ++ ++ if (vcount < mode->vsync_len) ++ vblank->flags |= FB_VBLANK_VSYNCING; ++ ++ if (vcount < vds || vcount > vds + mode->yres) ++ vblank->flags |= FB_VBLANK_VBLANKING; ++ ++ return 0; ++} ++ ++ ++static int tmiofb_ioctl(struct fb_info *fbi, ++ unsigned int cmd, unsigned long arg) ++{ ++ switch (cmd) { ++ case FBIOGET_VBLANK: { ++ struct fb_vblank vblank = {0}; ++ void __user *argp = (void __user *) arg; ++ ++ tmiofb_vblank(fbi, &vblank); ++ if (copy_to_user(argp, &vblank, sizeof vblank)) ++ return -EFAULT; ++ return 0; ++ } ++ ++#ifdef CONFIG_FB_TMIO_ACCELL ++ case FBIO_TMIO_ACC_SYNC: ++ tmiofb_sync(fbi); ++ return 0; ++ ++ case FBIO_TMIO_ACC_WRITE: { ++ u32 __user *argp = (void __user *) arg; ++ u32 len; ++ u32 acc [16]; ++ ++ if (copy_from_user(&len, argp, sizeof(u32))) ++ return -EFAULT; ++ if (len > ARRAY_SIZE(acc)) ++ return -EINVAL; ++ if (copy_from_user(acc, argp + 1, sizeof(u32) * len)) ++ return -EFAULT; ++ ++ return tmiofb_acc_write(fbi, acc, len); ++ } ++#endif ++ } ++ ++ return -EINVAL; ++} ++ ++/*--------------------------------------------------------------------------*/ ++ ++/* Select the smallest mode that allows the desired resolution to be ++ * displayed. If desired, the x and y parameters can be rounded up to ++ * match the selected mode. ++ */ ++static struct fb_videomode* ++tmiofb_find_mode(struct fb_info *info, struct fb_var_screeninfo *var) ++{ ++ struct mfd_cell *cell = mfd_get_cell(to_platform_device(info->device)); ++ struct tmio_fb_data *data = cell->driver_data; ++ struct fb_videomode *best = NULL; ++ int i; ++ ++ for (i = 0; i < data->num_modes; i++) { ++ struct fb_videomode *mode = data->modes + i; ++ ++ if (mode->xres >= var->xres && mode->yres >= var->yres ++ && (!best || (mode->xres < best->xres ++ && mode->yres < best->yres))) ++ best = mode; ++ } ++ ++ return best; ++} ++ ++static int tmiofb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) ++{ ++ ++ struct fb_videomode *mode; ++ ++ mode = tmiofb_find_mode(info, var); ++ if (!mode || var->bits_per_pixel > 16) ++ return -EINVAL; ++ ++ fb_videomode_to_var(var, mode); ++ ++ var->xres_virtual = mode->xres; ++ var->yres_virtual = info->screen_size / (mode->xres * 2); ++ var->xoffset = 0; ++ var->yoffset = 0; ++ var->bits_per_pixel = 16; ++ var->grayscale = 0; ++ var->red.offset = 11; var->red.length = 5; ++ var->green.offset = 5; var->green.length = 6; ++ var->blue.offset = 0; var->blue.length = 5; ++ var->transp.offset = 0; var->transp.length = 0; ++ var->nonstd = 0; ++ var->height = 82; /* mm */ ++ var->width = 60; /* mm */ ++ var->rotate = 0; ++ return 0; ++} ++ ++static int tmiofb_set_par(struct fb_info *info) ++{ ++/* struct fb_var_screeninfo *var = &info->var; ++ struct fb_videomode *mode; ++ ++ mode = tmiofb_find_mode(info, var); ++ if (!mode) ++ return -EINVAL; ++ ++ if (info->mode == mode) ++ return 0; ++ ++ info->mode = mode; */ ++ info->fix.line_length = info->mode->xres * 2; ++ ++ tmiofb_hw_mode(to_platform_device(info->device)); ++ tmiofb_clearscreen(info); ++ return 0; ++} ++ ++static int tmiofb_setcolreg(unsigned regno, unsigned red, unsigned green, ++ unsigned blue, unsigned transp, ++ struct fb_info *info) ++{ ++ struct tmiofb_par *par = info->par; ++ ++ if (regno < ARRAY_SIZE(par->pseudo_palette)) { ++ par->pseudo_palette [regno] = ++ ((red & 0xf800)) | ++ ((green & 0xfc00) >> 5) | ++ ((blue & 0xf800) >> 11); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++static struct fb_ops tmiofb_ops = { ++ .owner = THIS_MODULE, ++ ++ .fb_ioctl = tmiofb_ioctl, ++ .fb_check_var = tmiofb_check_var, ++ .fb_set_par = tmiofb_set_par, ++ .fb_setcolreg = tmiofb_setcolreg, ++ .fb_imageblit = cfb_imageblit, ++#ifdef CONFIG_FB_TMIO_ACCELL ++ .fb_sync = tmiofb_sync, ++ .fb_fillrect = tmiofb_fillrect, ++ .fb_copyarea = tmiofb_copyarea, ++#else ++ .fb_fillrect = cfb_fillrect, ++ .fb_copyarea = cfb_copyarea, ++#endif ++}; ++ ++/*--------------------------------------------------------------------------*/ ++ ++/* ++ * reasons for an interrupt: ++ * uis bbisc lcdis ++ * 0100 0001 accelerator command completed ++ * 2000 0001 vsync start ++ * 2000 0002 display start ++ * 2000 0004 line number match(0x1ff mask???) ++ */ ++static irqreturn_t tmiofb_irq(int irq, void *__info) ++{ ++ struct fb_info *info = __info; ++ struct tmiofb_par *par = info->par; ++ struct tmio_lcr __iomem *lcr = par->lcr; ++ unsigned int bbisc = ioread16(&lcr->bbisc); ++ ++ ++ if (unlikely(par->use_polling && irq != -1)) { ++ printk(KERN_INFO "tmiofb: switching to waitq\n"); ++ par->use_polling = false; ++ } ++ ++ iowrite16(bbisc, &lcr->bbisc); ++ ++#ifdef CONFIG_FB_TMIO_ACCELL ++ if (bbisc & 1) ++ wake_up(&par->wait_acc); ++#endif ++ ++ return IRQ_HANDLED; ++} ++ ++static int tmiofb_probe(struct platform_device *dev) ++{ ++ struct mfd_cell *cell = mfd_get_cell(dev); ++ struct tmio_fb_data *data = cell->driver_data; ++ struct resource *ccr = platform_get_resource_byname(dev, IORESOURCE_MEM, TMIO_FB_CONFIG); ++ struct resource *lcr = platform_get_resource_byname(dev, IORESOURCE_MEM, TMIO_FB_CONTROL); ++ struct resource *vram = platform_get_resource_byname(dev, IORESOURCE_MEM, TMIO_FB_VRAM); ++ int irq = platform_get_irq(dev, 0); ++ struct fb_info *info; ++ struct tmiofb_par *par; ++ int retval; ++ ++ if (data == NULL) { ++ dev_err(&dev->dev, "NULL platform data!\n"); ++ return -EINVAL; ++ } ++ ++ info = framebuffer_alloc(sizeof(struct tmiofb_par), &dev->dev); ++ ++ if (!info) { ++ retval = -ENOMEM; ++ goto err_framebuffer_alloc; ++ } ++ ++ par = info->par; ++ platform_set_drvdata(dev, info); ++ ++#ifdef CONFIG_FB_TMIO_ACCELL ++ init_waitqueue_head(&par->wait_acc); ++ ++ par->use_polling = true; ++ ++ info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_COPYAREA ++ | FBINFO_HWACCEL_FILLRECT; ++#else ++ info->flags = FBINFO_DEFAULT; ++#endif ++ ++ info->fbops = &tmiofb_ops; ++ ++ strcpy(info->fix.id, "tmio-fb"); ++ info->fix.smem_start = vram->start; ++ info->fix.smem_len = vram->end - vram->start + 1; ++ info->fix.type = FB_TYPE_PACKED_PIXELS; ++ info->fix.visual = FB_VISUAL_TRUECOLOR; ++ info->fix.mmio_start = lcr->start; ++ info->fix.mmio_len = lcr->end - lcr->start + 1; ++ info->fix.accel = FB_ACCEL_NONE; ++ info->screen_size = info->fix.smem_len - (4 * TMIOFB_FIFO_SIZE); ++ info->pseudo_palette = par->pseudo_palette; ++ ++ par->ccr = ioremap(ccr->start, ccr->end - ccr->start + 1); ++ if (!par->ccr) { ++ retval = -ENOMEM; ++ goto err_ioremap_ccr; ++ } ++ ++ par->lcr = ioremap(info->fix.mmio_start, info->fix.mmio_len); ++ if (!par->lcr) { ++ retval = -ENOMEM; ++ goto err_ioremap_lcr; ++ } ++ ++ par->vram = ioremap(info->fix.smem_start, info->fix.smem_len); ++ if (!par->vram) { ++ retval = -ENOMEM; ++ goto err_ioremap_vram; ++ } ++ info->screen_base = par->vram; ++ ++ retval = request_irq(irq, &tmiofb_irq, IRQF_DISABLED, ++ dev->dev.bus_id, info); ++ ++ if (retval) ++ goto err_request_irq; ++ ++ retval = fb_find_mode(&info->var, info, mode_option, ++ data->modes, data->num_modes, ++ data->modes, 16); ++ if (!retval) { ++ retval = -EINVAL; ++ goto err_find_mode; ++ } ++ ++ retval = cell->enable(dev); ++ if (retval) ++ goto err_enable; ++ ++ retval = tmiofb_hw_init(dev); ++ if (retval) ++ goto err_hw_init; ++ ++/* retval = tmiofb_set_par(info); ++ if (retval) ++ goto err_set_par;*/ ++ ++ retval = register_framebuffer(info); ++ if (retval < 0) ++ goto err_register_framebuffer; ++ ++ printk(KERN_INFO "fb%d: %s frame buffer device\n", ++ info->node, info->fix.id); ++ ++ return 0; ++ ++err_register_framebuffer: ++/*err_set_par:*/ ++ tmiofb_hw_stop(dev); ++err_hw_init: ++ cell->disable(dev); ++err_enable: ++err_find_mode: ++ free_irq(irq, info); ++err_request_irq: ++ iounmap(par->vram); ++err_ioremap_vram: ++ iounmap(par->lcr); ++err_ioremap_lcr: ++ iounmap(par->ccr); ++err_ioremap_ccr: ++ platform_set_drvdata(dev, NULL); ++ framebuffer_release(info); ++err_framebuffer_alloc: ++ return retval; ++} ++ ++static int __devexit tmiofb_remove(struct platform_device *dev) ++{ ++ struct mfd_cell *cell = mfd_get_cell(dev); ++ struct fb_info *info = platform_get_drvdata(dev); ++ int irq = platform_get_irq(dev, 0); ++ struct tmiofb_par *par; ++ ++ if (info) { ++ par = info->par; ++ unregister_framebuffer(info); ++ ++ tmiofb_hw_stop(dev); ++ ++ cell->disable(dev); ++ ++ free_irq(irq, info); ++ ++ iounmap(par->vram); ++ iounmap(par->lcr); ++ iounmap(par->ccr); ++ ++ framebuffer_release(info); ++ platform_set_drvdata(dev, NULL); ++ } ++ ++ return 0; ++} ++ ++#if 0 ++static void tmiofb_dump_regs(struct platform_device *dev) ++{ ++ struct fb_info *info = platform_get_drvdata(dev); ++ struct tmiofb_par *par = info->par; ++ struct tmio_lhccr __iomem *ccr = par->ccr; ++ struct tmio_lcr __iomem *lcr = par->lcr; ++ ++ printk("lhccr:\n"); ++#define CCR_PR(n) printk("\t" #n " = \t%04x\n", ioread16(&ccr->n)); ++ CCR_PR(cmd); ++ CCR_PR(revid); ++ CCR_PR(basel); ++ CCR_PR(baseh); ++ CCR_PR(ugcc); ++ CCR_PR(gcc); ++ CCR_PR(usc); ++ CCR_PR(vramrtc); ++ CCR_PR(vramsac); ++ CCR_PR(vrambc); ++#undef CCR_PR ++ ++ printk("lcr: \n"); ++#define LCR_PR(n) printk("\t" #n " = \t%04x\n", ioread16(&lcr->n)); ++ LCR_PR(uis); ++ LCR_PR(vhpn); ++ LCR_PR(cfsal); ++ LCR_PR(cfsah); ++ LCR_PR(cfs); ++ LCR_PR(cfws); ++ LCR_PR(bbie); ++ LCR_PR(bbisc); ++ LCR_PR(ccs); ++ LCR_PR(bbes); ++ LCR_PR(cmdl); ++ LCR_PR(cmdh); ++ LCR_PR(cfc); ++ LCR_PR(ccifc); ++ LCR_PR(hwt); ++ LCR_PR(lcdccrc); ++ LCR_PR(lcdcc); ++ LCR_PR(lcdcopc); ++ LCR_PR(lcdis); ++ LCR_PR(lcdim); ++ LCR_PR(lcdie); ++ LCR_PR(gdsal); ++ LCR_PR(gdsah); ++ LCR_PR(vhpcl); ++ LCR_PR(vhpch); ++ LCR_PR(gm); ++ LCR_PR(ht); ++ LCR_PR(hds); ++ LCR_PR(hss); ++ LCR_PR(hse); ++ LCR_PR(hnp); ++ LCR_PR(vt); ++ LCR_PR(vds); ++ LCR_PR(vss); ++ LCR_PR(vse); ++ LCR_PR(cdln); ++ LCR_PR(iln); ++ LCR_PR(sp); ++ LCR_PR(misc); ++ LCR_PR(vihss); ++ LCR_PR(vivs); ++ LCR_PR(vive); ++ LCR_PR(vivss); ++ LCR_PR(vccis); ++ LCR_PR(vidwsal); ++ LCR_PR(vidwsah); ++ LCR_PR(vidrsal); ++ LCR_PR(vidrsah); ++ LCR_PR(vipddst); ++ LCR_PR(vipddet); ++ LCR_PR(vie); ++ LCR_PR(vcs); ++ LCR_PR(vphwc); ++ LCR_PR(vphs); ++ LCR_PR(vpvwc); ++ LCR_PR(vpvs); ++ LCR_PR(plhpix); ++ LCR_PR(xs); ++ LCR_PR(xckhw); ++ LCR_PR(sths); ++ LCR_PR(vt2); ++ LCR_PR(ycksw); ++ LCR_PR(ysts); ++ LCR_PR(ppols); ++ LCR_PR(precw); ++ LCR_PR(vclkhw); ++ LCR_PR(oc); ++#undef LCR_PR ++} ++#endif ++ ++#ifdef CONFIG_PM ++static int tmiofb_suspend(struct platform_device *dev, pm_message_t state) ++{ ++ struct fb_info *info = platform_get_drvdata(dev); ++ struct tmiofb_par *par = info->par; ++ struct mfd_cell *cell = mfd_get_cell(dev); ++ int retval = 0; ++ ++ acquire_console_sem(); ++ ++ fb_set_suspend(info, 1); ++ ++ if (info->fbops->fb_sync) ++ info->fbops->fb_sync(info); ++ ++ ++ printk(KERN_INFO "tmiofb: switching to polling\n"); ++ par->use_polling = true; ++ tmiofb_hw_stop(dev); ++ ++ if (cell->suspend) ++ retval = cell->suspend(dev); ++ ++ release_console_sem(); ++ ++ return retval; ++} ++ ++static int tmiofb_resume(struct platform_device *dev) ++{ ++ struct fb_info *info = platform_get_drvdata(dev); ++ struct mfd_cell *cell = mfd_get_cell(dev); ++ int retval; ++ ++ acquire_console_sem(); ++ ++ if (cell->resume) { ++ retval = cell->resume(dev); ++ if (retval) ++ return retval; ++ } ++ ++ tmiofb_irq(-1, info); ++ ++ tmiofb_hw_init(dev); ++ ++ tmiofb_hw_mode(dev); ++ ++ fb_set_suspend(info, 0); ++ release_console_sem(); ++ return 0; ++} ++#endif ++ ++static struct platform_driver tmiofb_driver = { ++ .driver.name = "tmio-fb", ++ .driver.owner = THIS_MODULE, ++ .probe = tmiofb_probe, ++ .remove = __devexit_p(tmiofb_remove), ++#ifdef CONFIG_PM ++ .suspend = tmiofb_suspend, ++ .resume = tmiofb_resume, ++#endif ++}; ++ ++/*--------------------------------------------------------------------------*/ ++ ++#ifndef MODULE ++static void __init tmiofb_setup(char *options) ++{ ++ char *this_opt; ++ ++ if (!options || !*options) ++ return; ++ ++ while ((this_opt = strsep(&options, ",")) != NULL) { ++ if (!*this_opt) continue; ++ /* ++ * FIXME ++ */ ++ } ++} ++#endif ++ ++static int __init tmiofb_init(void) ++{ ++#ifndef MODULE ++ char *option = NULL; ++ ++ if (fb_get_options("tmiofb", &option)) ++ return -ENODEV; ++ tmiofb_setup(option); ++#endif ++ return platform_driver_register(&tmiofb_driver); ++} ++ ++static void __exit tmiofb_cleanup(void) ++{ ++ platform_driver_unregister(&tmiofb_driver); ++} ++ ++module_init(tmiofb_init); ++module_exit(tmiofb_cleanup); ++ ++MODULE_DESCRIPTION("TMIO framebuffer driver"); ++MODULE_AUTHOR("Chris Humbert, Dirk Opfer, Dmitry Baryshkov"); ++MODULE_LICENSE("GPL"); +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0010-OHCI-driver-for-TMIO-devices.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0010-OHCI-driver-for-TMIO-devices.patch new file mode 100644 index 0000000000..f358c069d0 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0010-OHCI-driver-for-TMIO-devices.patch @@ -0,0 +1,431 @@ +From e5f06830bc8d3ef4792c9c0569825d0347b39852 Mon Sep 17 00:00:00 2001 +From: Ian Molton <spyro@f2s.com> +Date: Fri, 4 Jan 2008 18:43:31 +0000 +Subject: [PATCH 10/64] OHCI driver for TMIO devices + +--- + drivers/usb/Kconfig | 1 + + drivers/usb/host/Kconfig | 1 + + drivers/usb/host/ohci-hcd.c | 5 + + drivers/usb/host/ohci-tmio.c | 369 ++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 376 insertions(+), 0 deletions(-) + create mode 100644 drivers/usb/host/ohci-tmio.c + +diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig +index 7580aa5..8912042 100644 +--- a/drivers/usb/Kconfig ++++ b/drivers/usb/Kconfig +@@ -36,6 +36,7 @@ config USB_ARCH_HAS_OHCI + default y if ARCH_EP93XX + default y if ARCH_AT91 + default y if ARCH_PNX4008 ++ default y if MFD_TC6393XB + # PPC: + default y if STB03xxx + default y if PPC_MPC52xx +diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig +index 49a91c5..5ae3589 100644 +--- a/drivers/usb/host/Kconfig ++++ b/drivers/usb/host/Kconfig +@@ -101,6 +101,7 @@ config USB_OHCI_HCD + depends on USB && USB_ARCH_HAS_OHCI + select ISP1301_OMAP if MACH_OMAP_H2 || MACH_OMAP_H3 + select I2C if ARCH_PNX4008 ++ select DMABOUNCE if MFD_TC6393XB + ---help--- + The Open Host Controller Interface (OHCI) is a standard for accessing + USB 1.1 host controller hardware. It does more in hardware than Intel's +diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c +index ecfe800..77abf3e 100644 +--- a/drivers/usb/host/ohci-hcd.c ++++ b/drivers/usb/host/ohci-hcd.c +@@ -1043,6 +1043,11 @@ MODULE_LICENSE ("GPL"); + #define PS3_SYSTEM_BUS_DRIVER ps3_ohci_driver + #endif + ++#ifdef CONFIG_MFD_TC6393XB ++#include "ohci-tmio.c" ++#define PLATFORM_DRIVER ohci_hcd_tmio_driver ++#endif ++ + #ifdef CONFIG_USB_OHCI_HCD_SSB + #include "ohci-ssb.c" + #define SSB_OHCI_DRIVER ssb_ohci_driver +diff --git a/drivers/usb/host/ohci-tmio.c b/drivers/usb/host/ohci-tmio.c +new file mode 100644 +index 0000000..be609f3 +--- /dev/null ++++ b/drivers/usb/host/ohci-tmio.c +@@ -0,0 +1,369 @@ ++/* ++ * OHCI HCD(Host Controller Driver) for USB. ++ * ++ *(C) Copyright 1999 Roman Weissgaerber <weissg@vienna.at> ++ *(C) Copyright 2000-2002 David Brownell <dbrownell@users.sourceforge.net> ++ *(C) Copyright 2002 Hewlett-Packard Company ++ * ++ * Bus glue for Toshiba Mobile IO(TMIO) Controller's OHCI core ++ *(C) Copyright 2005 Chris Humbert <mahadri-usb@drigon.com> ++ * ++ * This is known to work with the following variants: ++ * TC6393XB revision 3 (32kB SRAM) ++ * ++ * The TMIO's OHCI core DMAs through a small internal buffer that ++ * is directly addressable by the CPU. dma_declare_coherent_memory ++ * and DMA bounce buffers allow the higher-level OHCI host driver to ++ * work. However, the dma API doesn't handle dma mapping failures ++ * well(dma_sg_map() is a prime example), so it is unusable. ++ * ++ * This HC pretends be a PIO-ish controller and uses the kernel's ++ * generic allocator for the entire SRAM. Using the USB core's ++ * usb_operations, we provide hcd_buffer_alloc/free. Using the OHCI's ++ * ohci_ops, we provide memory management for OHCI's TDs and EDs. We ++ * internally queue a URB's TDs until enough dma memory is available ++ * to enqueue them with the HC. ++ * ++ * Written from sparse documentation from Toshiba and Sharp's driver ++ * for the 2.4 kernel, ++ * usb-ohci-tc6393.c(C) Copyright 2004 Lineo Solutions, 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 <linux/fs.h> ++#include <linux/mount.h> ++#include <linux/pagemap.h> ++#include <linux/init.h> ++#include <linux/namei.h> ++#include <linux/sched.h>*/ ++#include <linux/platform_device.h> ++#include <linux/mfd-core.h> ++#include <linux/mfd/tmio.h> ++#include <linux/dma-mapping.h> ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * USB Host Controller Configuration Register ++ */ ++struct tmio_uhccr { ++ u8 x00[8]; ++ u8 revid; /* 0x08 Revision ID */ ++ u8 x01[7]; ++ u16 basel; /* 0x10 USB Control Register Base Address Low */ ++ u16 baseh; /* 0x12 USB Control Register Base Address High */ ++ u8 x02[0x2c]; ++ u8 ilme; /* 0x40 Internal Local Memory Enable */ ++ u8 x03[0x0b]; ++ u16 pm; /* 0x4c Power Management */ ++ u8 x04[2]; ++ u8 intc; /* 0x50 INT Control */ ++ u8 x05[3]; ++ u16 lmw1l; /* 0x54 Local Memory Window 1 LMADRS Low */ ++ u16 lmw1h; /* 0x56 Local Memory Window 1 LMADRS High */ ++ u16 lmw1bl; /* 0x58 Local Memory Window 1 Base Address Low */ ++ u16 lmw1bh; /* 0x5A Local Memory Window 1 Base Address High */ ++ u16 lmw2l; /* 0x5C Local Memory Window 2 LMADRS Low */ ++ u16 lmw2h; /* 0x5E Local Memory Window 2 LMADRS High */ ++ u16 lmw2bl; /* 0x60 Local Memory Window 2 Base Address Low */ ++ u16 lmw2bh; /* 0x62 Local Memory Window 2 Base Address High */ ++ u8 x06[0x98]; ++ u8 misc; /* 0xFC MISC */ ++ u8 x07[3]; ++} __attribute__((packed)); ++ ++#define UHCCR_PM_GKEN 0x0001 ++#define UHCCR_PM_CKRNEN 0x0002 ++#define UHCCR_PM_USBPW1 0x0004 ++#define UHCCR_PM_USBPW2 0x0008 ++#define UHCCR_PM_PMEE 0x0100 ++#define UHCCR_PM_PMES 0x8000 ++ ++/*-------------------------------------------------------------------------*/ ++ ++struct tmio_hcd { ++ struct tmio_uhccr __iomem *ccr; ++}; ++ ++#define hcd_to_tmio(hcd) ((struct tmio_hcd *)(hcd_to_ohci(hcd) + 1)) ++#define ohci_to_tmio(ohci) ((struct tmio_hcd *)(ohci + 1)) ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void tmio_stop_hc(struct platform_device *dev) ++{ ++ struct mfd_cell *cell = mfd_get_cell(dev); ++ struct usb_hcd *hcd = platform_get_drvdata(dev); ++ struct tmio_hcd *tmio = hcd_to_tmio(hcd); ++ struct tmio_uhccr __iomem *ccr = tmio->ccr; ++ u16 pm; ++ ++ pm = UHCCR_PM_GKEN | UHCCR_PM_CKRNEN | UHCCR_PM_USBPW1 | UHCCR_PM_USBPW2; ++ iowrite8(0, &ccr->intc); ++ iowrite8(0, &ccr->ilme); ++ iowrite16(0, &ccr->basel); ++ iowrite16(0, &ccr->baseh); ++ iowrite16(pm, &ccr->pm); ++ ++ cell->disable(dev); ++} ++ ++static void tmio_start_hc(struct platform_device *dev) ++{ ++ struct mfd_cell *cell = mfd_get_cell(dev); ++ struct usb_hcd *hcd = platform_get_drvdata(dev); ++ struct tmio_hcd *tmio = hcd_to_tmio(hcd); ++ struct tmio_uhccr __iomem *ccr = tmio->ccr; ++ u16 pm; ++ unsigned long base = hcd->rsrc_start; ++ ++ pm = UHCCR_PM_CKRNEN | UHCCR_PM_GKEN | UHCCR_PM_PMEE | UHCCR_PM_PMES; ++ cell->enable(dev); ++ ++ iowrite16(pm, &ccr->pm); ++ iowrite16(base, &ccr->basel); ++ iowrite16(base >> 16, &ccr->baseh); ++ iowrite8(1, &ccr->ilme); ++ iowrite8(2, &ccr->intc); ++ ++ dev_info(&dev->dev, "revision %d @ 0x%08llx, irq %d\n", ++ ioread8(&ccr->revid), hcd->rsrc_start, hcd->irq); ++} ++ ++static int usb_hcd_tmio_probe(const struct hc_driver *driver, ++ struct platform_device *dev) ++{ ++ struct resource *config = platform_get_resource_byname(dev, IORESOURCE_MEM, TMIO_OHCI_CONFIG); ++ struct resource *regs = platform_get_resource_byname(dev, IORESOURCE_MEM, TMIO_OHCI_CONTROL); ++ struct resource *sram = platform_get_resource_byname(dev, IORESOURCE_MEM, TMIO_OHCI_SRAM); ++ int irq = platform_get_irq(dev, 0); ++ struct tmio_hcd *tmio; ++ struct ohci_hcd *ohci; ++ struct usb_hcd *hcd; ++ int retval; ++ ++ if (usb_disabled()) ++ return -ENODEV; ++ ++ hcd = usb_create_hcd(driver, &dev->dev, dev->dev.bus_id); ++ if (!hcd) { ++ retval = -ENOMEM; ++ goto err_usb_create_hcd; ++ } ++ ++ hcd->rsrc_start = regs->start; ++ hcd->rsrc_len = regs->end - regs->start + 1; ++ ++ tmio = hcd_to_tmio(hcd); ++ ++ tmio->ccr = ioremap(config->start, config->end - config->start + 1); ++ if (!tmio->ccr) { ++ retval = -ENOMEM; ++ goto err_ioremap_ccr; ++ } ++ ++ hcd->regs = ioremap(hcd->rsrc_start, hcd->rsrc_len); ++ if (!hcd->regs) { ++ retval = -ENOMEM; ++ goto err_ioremap_regs; ++ } ++ ++ if (dma_declare_coherent_memory(&dev->dev, sram->start, ++ sram->start, ++ sram->end - sram->start + 1, ++ DMA_MEMORY_MAP) != DMA_MEMORY_MAP) { ++ retval = -EBUSY; ++ goto err_dma_declare; ++ } ++ ++ retval = dmabounce_register_dev(&dev->dev, 512, 4096); ++ if (retval) ++ goto err_dmabounce_register_dev; ++ ++ tmio_start_hc(dev); ++ ohci = hcd_to_ohci(hcd); ++ ohci_hcd_init(ohci); ++ ++ retval = usb_add_hcd(hcd, irq, IRQF_DISABLED); ++ ++ if (retval == 0) ++ return retval; ++ ++ tmio_stop_hc(dev); ++ ++ dmabounce_unregister_dev(&dev->dev); ++err_dmabounce_register_dev: ++ dma_release_declared_memory(&dev->dev); ++err_dma_declare: ++ iounmap(hcd->regs); ++err_ioremap_regs: ++ iounmap(tmio->ccr); ++err_ioremap_ccr: ++ usb_put_hcd(hcd); ++err_usb_create_hcd: ++ ++ return retval; ++} ++ ++static void usb_hcd_tmio_remove(struct usb_hcd *hcd, struct platform_device *dev) ++{ ++ struct tmio_hcd *tmio = hcd_to_tmio(hcd); ++ ++ usb_remove_hcd(hcd); ++ tmio_stop_hc(dev); ++ dmabounce_unregister_dev(&dev->dev); ++ dma_release_declared_memory(&dev->dev); ++ iounmap(hcd->regs); ++ iounmap(tmio->ccr); ++ usb_put_hcd(hcd); ++} ++ ++static int __devinit ++ohci_tmio_start(struct usb_hcd *hcd) ++{ ++ struct ohci_hcd *ohci = hcd_to_ohci(hcd); ++ int retval; ++ ++ if ((retval = ohci_init(ohci)) < 0) ++ return retval; ++ ++ if ((retval = ohci_run(ohci)) < 0) { ++ err("can't start %s", hcd->self.bus_name); ++ ohci_stop(hcd); ++ return retval; ++ } ++ ++ return 0; ++} ++ ++static const struct hc_driver ohci_tmio_hc_driver = { ++ .description = hcd_name, ++ .product_desc = "TMIO OHCI USB Host Controller", ++ .hcd_priv_size = sizeof(struct ohci_hcd) + sizeof (struct tmio_hcd), ++ ++ /* generic hardware linkage */ ++ .irq = ohci_irq, ++ .flags = HCD_USB11 | HCD_MEMORY, ++ ++ /* basic lifecycle operations */ ++ .start = ohci_tmio_start, ++ .stop = ohci_stop, ++ .shutdown = ohci_shutdown, ++ ++ /* 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, ++ .hub_irq_enable = ohci_rhsc_enable, ++#ifdef CONFIG_PM ++ .bus_suspend = ohci_bus_suspend, ++ .bus_resume = ohci_bus_resume, ++#endif ++ .start_port_reset = ohci_start_port_reset, ++}; ++ ++/*-------------------------------------------------------------------------*/ ++static struct platform_driver ohci_hcd_tmio_driver; ++ ++static int ++tmio_dmabounce_check(struct device *dev, dma_addr_t dma, size_t size, void *data) ++{ ++ struct resource *sram = data; ++#ifdef DEBUG ++ printk(KERN_ERR "tmio_dmabounce_check: %08x %d\n", dma, size); ++#endif ++ ++ if (dev->driver != &ohci_hcd_tmio_driver.driver) ++ return 0; ++ ++ if (sram->start <= dma && dma + size <= sram->end) ++ return 0; ++ ++ return 1; ++} ++ ++static u64 dma_mask = DMA_32BIT_MASK; ++ ++static int ohci_hcd_tmio_drv_probe(struct platform_device *dev) ++{ ++ struct resource *sram = platform_get_resource_byname(dev, IORESOURCE_MEM, TMIO_OHCI_SRAM); ++ ++ dev->dev.dma_mask = &dma_mask; ++ dev->dev.coherent_dma_mask = DMA_32BIT_MASK; ++ ++ dmabounce_register_checker(tmio_dmabounce_check, sram); ++ ++ return usb_hcd_tmio_probe(&ohci_tmio_hc_driver, dev); ++} ++ ++static int ohci_hcd_tmio_drv_remove(struct platform_device *dev) ++{ ++ struct usb_hcd *hcd = platform_get_drvdata(dev); ++ struct resource *sram = platform_get_resource_byname(dev, IORESOURCE_MEM, TMIO_OHCI_SRAM); ++ ++ usb_hcd_tmio_remove(hcd, dev); ++ ++ platform_set_drvdata(dev, NULL); ++ ++ dmabounce_remove_checker(tmio_dmabounce_check, sram); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int ohci_hcd_tmio_drv_suspend(struct platform_device *dev, pm_message_t state) ++{ ++ struct usb_hcd *hcd = platform_get_drvdata(dev); ++ struct ohci_hcd *ohci = hcd_to_ohci(hcd); ++ ++ if (time_before(jiffies, ohci->next_statechange)) ++ msleep(5); ++ ohci->next_statechange = jiffies; ++ ++ tmio_stop_hc(dev); ++ hcd->state = HC_STATE_SUSPENDED; ++ dev->dev.power.power_state = PMSG_SUSPEND; ++ ++ return 0; ++} ++ ++static int ohci_hcd_tmio_drv_resume(struct platform_device *dev) ++{ ++ struct usb_hcd *hcd = platform_get_drvdata(dev); ++ struct ohci_hcd *ohci = hcd_to_ohci(hcd); ++ ++ if (time_before(jiffies, ohci->next_statechange)) ++ msleep(5); ++ ohci->next_statechange = jiffies; ++ ++ tmio_start_hc(dev); ++ ++ dev->dev.power.power_state = PMSG_ON; ++ usb_hcd_resume_root_hub(hcd); ++ ++ return 0; ++} ++#endif ++ ++static struct platform_driver ohci_hcd_tmio_driver = { ++ .probe = ohci_hcd_tmio_drv_probe, ++ .remove = ohci_hcd_tmio_drv_remove, ++ .shutdown = usb_hcd_platform_shutdown, ++#ifdef CONFIG_PM ++ .suspend = ohci_hcd_tmio_drv_suspend, ++ .resume = ohci_hcd_tmio_drv_resume, ++#endif ++ .driver = { ++ .name = "tmio-ohci", ++ }, ++}; +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0011-MMC-driver-for-TMIO-devices.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0011-MMC-driver-for-TMIO-devices.patch new file mode 100644 index 0000000000..6ff752d1ff --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0011-MMC-driver-for-TMIO-devices.patch @@ -0,0 +1,891 @@ +From b358a64c1fdd1eb80da57f919c893d910db95e37 Mon Sep 17 00:00:00 2001 +From: Ian Molton <spyro@f2s.com> +Date: Sat, 29 Dec 2007 15:26:19 +0000 +Subject: [PATCH 11/64] MMC driver for TMIO devices + +--- + drivers/mmc/host/Kconfig | 6 + + drivers/mmc/host/Makefile | 1 + + drivers/mmc/host/tmio_mmc.c | 633 +++++++++++++++++++++++++++++++++++++++++++ + drivers/mmc/host/tmio_mmc.h | 205 ++++++++++++++ + 4 files changed, 845 insertions(+), 0 deletions(-) + create mode 100644 drivers/mmc/host/tmio_mmc.c + create mode 100644 drivers/mmc/host/tmio_mmc.h + +diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig +index 5fef678..f8f9b7e 100644 +--- a/drivers/mmc/host/Kconfig ++++ b/drivers/mmc/host/Kconfig +@@ -130,3 +130,9 @@ config MMC_SPI + + If unsure, or if your system has no SPI master driver, say N. + ++config MMC_TMIO ++ tristate "Toshiba Mobile IO Controller (TMIO) MMC/SD function support" ++ depends on MMC ++ help ++ This provides support for the SD/MMC cell found in TC6393XB, ++ T7L66XB and also ipaq ASIC3 +diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile +index 3877c87..7ac956b 100644 +--- a/drivers/mmc/host/Makefile ++++ b/drivers/mmc/host/Makefile +@@ -17,4 +17,5 @@ obj-$(CONFIG_MMC_OMAP) += omap.o + obj-$(CONFIG_MMC_AT91) += at91_mci.o + obj-$(CONFIG_MMC_TIFM_SD) += tifm_sd.o + obj-$(CONFIG_MMC_SPI) += mmc_spi.o ++obj-$(CONFIG_MMC_TMIO) += tmio_mmc.o + +diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c +new file mode 100644 +index 0000000..735c386 +--- /dev/null ++++ b/drivers/mmc/host/tmio_mmc.c +@@ -0,0 +1,633 @@ ++/* ++ * linux/drivers/mmc/tmio_mmc.c ++ * ++ * Copyright (C) 2004 Ian Molton ++ * Copyright (C) 2007 Ian Molton ++ * ++ * 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. ++ * ++ * Driver for the MMC / SD / SDIO cell found in: ++ * ++ * TC6393XB TC6391XB TC6387XB T7L66XB ++ * ++ * This driver draws mainly on scattered spec sheets, Reverse engineering ++ * of the toshiba e800 SD driver and some parts of the 2.4 ASIC3 driver (4 bit ++ * support). (Further 4 bit support from a later datasheet). ++ * ++ * TODO: ++ * Investigate using a workqueue for PIO transfers ++ * Eliminate FIXMEs ++ * SDIO support ++ * Better Power management ++ * Handle MMC errors better ++ * double buffer support ++ * ++ */ ++#include <linux/module.h> ++#include <linux/irq.h> ++#include <linux/device.h> ++#include <linux/delay.h> ++#include <linux/mmc/mmc.h> ++#include <linux/mmc/host.h> ++#include <linux/mfd-core.h> ++#include <linux/mfd/tmio.h> ++ ++#include "tmio_mmc.h" ++ ++/* ++ * Fixme - documentation conflicts on what the clock values are for the ++ * various dividers. ++ * One document I have says that its a divisor of a 24MHz clock, another 33. ++ * This probably depends on HCLK for a given platform, so we may need to ++ * require HCLK be passed to us from the MFD core. ++ * ++ */ ++ ++static void tmio_mmc_set_clock (struct tmio_mmc_host *host, int new_clock) { ++ struct tmio_mmc_cnf __iomem *cnf = host->cnf; ++ struct tmio_mmc_ctl __iomem *ctl = host->ctl; ++ u32 clk = 0, clock; ++ ++ if (new_clock) { ++ for(clock = 46875, clk = 0x100; new_clock >= (clock<<1); ){ ++ clock <<= 1; ++ clk >>= 1; ++ } ++ if(clk & 0x1) ++ clk = 0x20000; ++ ++ clk >>= 2; ++ if(clk & 0x8000) /* For full speed we disable the divider. */ ++ writeb(0, &cnf->sd_clk_mode); ++ else ++ writeb(1, &cnf->sd_clk_mode); ++ clk |= 0x100; ++ } ++ ++ writew(clk, &ctl->sd_card_clk_ctl); ++} ++ ++static void tmio_mmc_clk_stop (struct tmio_mmc_host *host) { ++ struct tmio_mmc_ctl __iomem *ctl = host->ctl; ++ ++ writew(0x0000, &ctl->clk_and_wait_ctl); ++ msleep(10); ++ writew(readw(&ctl->sd_card_clk_ctl) & ~0x0100, &ctl->sd_card_clk_ctl); ++ msleep(10); ++} ++ ++static void tmio_mmc_clk_start (struct tmio_mmc_host *host) { ++ struct tmio_mmc_ctl __iomem *ctl = host->ctl; ++ ++ writew(readw(&ctl->sd_card_clk_ctl) | 0x0100, &ctl->sd_card_clk_ctl); ++ msleep(10); ++ writew(0x0100, &ctl->clk_and_wait_ctl); ++ msleep(10); ++} ++ ++static void reset(struct tmio_mmc_host *host) { ++ struct tmio_mmc_ctl __iomem *ctl = host->ctl; ++ ++ /* FIXME - should we set stop clock reg here */ ++ writew(0x0000, &ctl->reset_sd); ++ writew(0x0000, &ctl->reset_sdio); ++ msleep(10); ++ writew(0x0001, &ctl->reset_sd); ++ writew(0x0001, &ctl->reset_sdio); ++ msleep(10); ++} ++ ++static void ++tmio_mmc_finish_request(struct tmio_mmc_host *host) ++{ ++ struct mmc_request *mrq = host->mrq; ++ ++ host->mrq = NULL; ++ host->cmd = NULL; ++ host->data = NULL; ++ ++ mmc_request_done(host->mmc, mrq); ++} ++ ++/* These are the bitmasks the tmio chip requires to implement the MMC response ++ * types. Note that R1 and R6 are the same in this scheme. */ ++#define APP_CMD 0x0040 ++#define RESP_NONE 0x0300 ++#define RESP_R1 0x0400 ++#define RESP_R1B 0x0500 ++#define RESP_R2 0x0600 ++#define RESP_R3 0x0700 ++#define DATA_PRESENT 0x0800 ++#define TRANSFER_READ 0x1000 ++#define TRANSFER_MULTI 0x2000 ++#define SECURITY_CMD 0x4000 ++ ++static void ++tmio_mmc_start_command(struct tmio_mmc_host *host, struct mmc_command *cmd) ++{ ++ struct tmio_mmc_ctl __iomem *ctl = host->ctl; ++ struct mmc_data *data = host->data; ++ int c = cmd->opcode; ++ ++ if(cmd->opcode == MMC_STOP_TRANSMISSION) { ++ writew(0x001, &ctl->stop_internal_action); ++ return; ++ } ++ ++ switch(mmc_resp_type(cmd)) { ++ case MMC_RSP_NONE: c |= RESP_NONE; break; ++ case MMC_RSP_R1: c |= RESP_R1; break; ++ case MMC_RSP_R1B: c |= RESP_R1B; break; ++ case MMC_RSP_R2: c |= RESP_R2; break; ++ case MMC_RSP_R3: c |= RESP_R3; break; ++ default: ++ DBG("Unknown response type %d\n", mmc_resp_type(cmd)); ++ } ++ ++ host->cmd = cmd; ++ ++/* FIXME - this seems to be ok comented out but the spec suggest this bit should ++ * be set when issuing app commands. ++ * if(cmd->flags & MMC_FLAG_ACMD) ++ * c |= APP_CMD; ++ */ ++ if(data) { ++ c |= DATA_PRESENT; ++ if(data->blocks > 1) { ++ writew(0x100, &ctl->stop_internal_action); ++ c |= TRANSFER_MULTI; ++ } ++ if(data->flags & MMC_DATA_READ) ++ c |= TRANSFER_READ; ++ } ++ ++ enable_mmc_irqs(ctl, TMIO_MASK_CMD); ++ ++ /* Fire off the command */ ++ tmio_iowrite32(cmd->arg, ctl->arg_reg); ++ writew(c, &ctl->sd_cmd); ++} ++ ++/* This chip always returns (at least?) as much data as you ask for. ++ * Im unsure what happens if you ask for less than a block. This should be ++ * looked into to ensure that a funny length read doesnt hose the controller. ++ * ++ * FIXME - this chip cannot do 1 and 2 byte data requests in 4 bit mode ++ */ ++static inline void tmio_mmc_pio_irq(struct tmio_mmc_host *host) { ++ struct tmio_mmc_ctl __iomem *ctl = host->ctl; ++ struct mmc_data *data = host->data; ++ unsigned short *buf; ++ unsigned int count; ++ unsigned long flags; ++ ++ if(!data){ ++ DBG("Spurious PIO IRQ\n"); ++ return; ++ } ++ ++ buf = (unsigned short *)(tmio_mmc_kmap_atomic(host, &flags) + ++ host->sg_off); ++ ++ /* Ensure we dont read more than one block. The chip will interrupt us ++ * When the next block is available. ++ * FIXME - this is probably not true now IRQ handling is fixed ++ */ ++ count = host->sg_ptr->length - host->sg_off; ++ if(count > data->blksz) ++ count = data->blksz; ++ ++ DBG("count: %08x offset: %08x flags %08x\n", ++ count, host->sg_off, data->flags); ++ ++ /* Transfer the data */ ++ if(data->flags & MMC_DATA_READ) ++ readsw(&ctl->sd_data_port[0], buf, count >> 1); ++ else ++ writesw(&ctl->sd_data_port[0], buf, count >> 1); ++ ++ host->sg_off += count; ++ ++ tmio_mmc_kunmap_atomic(host, &flags); ++ ++ if(host->sg_off == host->sg_ptr->length) ++ tmio_mmc_next_sg(host); ++ ++ return; ++} ++ ++static inline void tmio_mmc_data_irq(struct tmio_mmc_host *host) { ++ struct tmio_mmc_ctl __iomem *ctl = host->ctl; ++ struct mmc_data *data = host->data; ++ ++ host->data = NULL; ++ ++ if(!data){ ++ DBG("Spurious data end IRQ\n"); ++ return; ++ } ++ ++ /* FIXME - return correct transfer count on errors */ ++ if (!data->error) ++ data->bytes_xfered = data->blocks * data->blksz; ++ else ++ data->bytes_xfered = 0; ++ ++ DBG("Completed data request\n"); ++ ++ /*FIXME - other drivers allow an optional stop command of any given type ++ * which we dont do, as the chip can auto generate them. ++ * Perhaps we can be smarter about when to use auto CMD12 and ++ * only issue the auto request when we know this is the desired ++ * stop command, allowing fallback to the stop command the ++ * upper layers expect. For now, we do what works. ++ */ ++ ++ writew(0x000, &ctl->stop_internal_action); ++ ++ if(data->flags & MMC_DATA_READ) ++ disable_mmc_irqs(ctl, TMIO_MASK_READOP); ++ else ++ disable_mmc_irqs(ctl, TMIO_MASK_WRITEOP); ++ ++ tmio_mmc_finish_request(host); ++} ++ ++static inline void tmio_mmc_cmd_irq(struct tmio_mmc_host *host, unsigned int stat) { ++ struct tmio_mmc_ctl __iomem *ctl = host->ctl; ++ struct mmc_command *cmd = host->cmd; ++ ++ if(!host->cmd) { ++ DBG("Spurious CMD irq\n"); ++ return; ++ } ++ ++ host->cmd = NULL; ++ ++ /* This controller is sicker than the PXA one. not only do we need to ++ * drop the top 8 bits of the first response word, we also need to ++ * modify the order of the response for short response command types. ++ */ ++ ++ /* FIXME - this works but readl is wrong and will break on asic3... */ ++ cmd->resp[3] = tmio_ioread32(&ctl->response[0]); ++ cmd->resp[2] = tmio_ioread32(&ctl->response[2]); ++ cmd->resp[1] = tmio_ioread32(&ctl->response[4]); ++ cmd->resp[0] = tmio_ioread32(&ctl->response[6]); ++ ++ if(cmd->flags & MMC_RSP_136) { ++ cmd->resp[0] = (cmd->resp[0] <<8) | (cmd->resp[1] >>24); ++ cmd->resp[1] = (cmd->resp[1] <<8) | (cmd->resp[2] >>24); ++ cmd->resp[2] = (cmd->resp[2] <<8) | (cmd->resp[3] >>24); ++ cmd->resp[3] <<= 8; ++ } ++ else if(cmd->flags & MMC_RSP_R3) { ++ cmd->resp[0] = cmd->resp[3]; ++ } ++ ++ if (stat & TMIO_STAT_CMDTIMEOUT) ++ cmd->error = -ETIMEDOUT; ++ else if (stat & TMIO_STAT_CRCFAIL && cmd->flags & MMC_RSP_CRC) ++ cmd->error = -EILSEQ; ++ ++ /* If there is data to handle we enable data IRQs here, and ++ * we will ultimatley finish the request in the data_end handler. ++ * If theres no data or we encountered an error, finish now. ++ */ ++ if(host->data && !cmd->error){ ++ if(host->data->flags & MMC_DATA_READ) ++ enable_mmc_irqs(ctl, TMIO_MASK_READOP); ++ else ++ enable_mmc_irqs(ctl, TMIO_MASK_WRITEOP); ++ } ++ else { ++ tmio_mmc_finish_request(host); ++ } ++ ++ return; ++} ++ ++ ++static irqreturn_t tmio_mmc_irq(int irq, void *devid) ++{ ++ struct tmio_mmc_host *host = devid; ++ struct tmio_mmc_ctl __iomem *ctl = host->ctl; ++ unsigned int ireg, irq_mask, status; ++ ++ DBG("MMC IRQ begin\n"); ++ ++ status = tmio_ioread32(ctl->status); ++ irq_mask = tmio_ioread32(ctl->irq_mask); ++ ireg = status & TMIO_MASK_IRQ & ~irq_mask; ++ ++#ifdef CONFIG_MMC_DEBUG ++ debug_status(status); ++ debug_status(ireg); ++#endif ++ if (!ireg) { ++ disable_mmc_irqs(ctl, status & ~irq_mask); ++#ifdef CONFIG_MMC_DEBUG ++ WARN("tmio_mmc: Spurious MMC irq, disabling! 0x%08x 0x%08x 0x%08x\n", status, irq_mask, ireg); ++ debug_status(status); ++#endif ++ goto out; ++ } ++ ++ while (ireg) { ++ /* Card insert / remove attempts */ ++ if (ireg & (TMIO_STAT_CARD_INSERT | TMIO_STAT_CARD_REMOVE)){ ++ ack_mmc_irqs(ctl, TMIO_STAT_CARD_INSERT | TMIO_STAT_CARD_REMOVE); ++ mmc_detect_change(host->mmc,0); ++ } ++ ++ /* CRC and other errors */ ++/* if (ireg & TMIO_STAT_ERR_IRQ) ++ * handled |= tmio_error_irq(host, irq, stat); ++ */ ++ ++ /* Command completion */ ++ if (ireg & TMIO_MASK_CMD) { ++ tmio_mmc_cmd_irq(host, status); ++ ack_mmc_irqs(ctl, TMIO_MASK_CMD); ++ } ++ ++ /* Data transfer */ ++ if (ireg & (TMIO_STAT_RXRDY | TMIO_STAT_TXRQ)) { ++ ack_mmc_irqs(ctl, TMIO_STAT_RXRDY | TMIO_STAT_TXRQ); ++ tmio_mmc_pio_irq(host); ++ } ++ ++ /* Data transfer completion */ ++ if (ireg & TMIO_STAT_DATAEND) { ++ tmio_mmc_data_irq(host); ++ ack_mmc_irqs(ctl, TMIO_STAT_DATAEND); ++ } ++ ++ /* Check status - keep going until we've handled it all */ ++ status = tmio_ioread32(ctl->status); ++ irq_mask = tmio_ioread32(ctl->irq_mask); ++ ireg = status & TMIO_MASK_IRQ & ~irq_mask; ++ ++#ifdef CONFIG_MMC_DEBUG ++ DBG("Status at end of loop: %08x\n", status); ++ debug_status(status); ++#endif ++ } ++ DBG("MMC IRQ end\n"); ++ ++out: ++ return IRQ_HANDLED; ++} ++ ++static void tmio_mmc_start_data(struct tmio_mmc_host *host, struct mmc_data *data) ++{ ++ struct tmio_mmc_ctl __iomem *ctl = host->ctl; ++ ++ DBG("setup data transfer: blocksize %08x nr_blocks %d\n", ++ data->blksz, data->blocks); ++ ++ tmio_mmc_init_sg(host, data); ++ host->data = data; ++ ++ /* Set transfer length / blocksize */ ++ writew(data->blksz, &ctl->sd_xfer_len); ++ writew(data->blocks, &ctl->xfer_blk_count); ++} ++ ++/* Process requests from the MMC layer */ ++static void tmio_mmc_request(struct mmc_host *mmc, struct mmc_request *mrq) ++{ ++ struct tmio_mmc_host *host = mmc_priv(mmc); ++ ++ WARN_ON(host->mrq != NULL); ++ ++ host->mrq = mrq; ++ ++ /* If we're performing a data request we need to setup some ++ extra information */ ++ if (mrq->data) ++ tmio_mmc_start_data(host, mrq->data); ++ ++ tmio_mmc_start_command(host, mrq->cmd); ++} ++ ++/* Set MMC clock / power. ++ * Note: This controller uses a simple divider scheme therefore it cannot ++ * run a MMC card at full speed (20MHz). The max clock is 24MHz on SD, but as ++ * MMC wont run that fast, it has to be clocked at 12MHz which is the next ++ * slowest setting. ++ */ ++static void tmio_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ++{ ++ struct tmio_mmc_host *host = mmc_priv(mmc); ++ struct tmio_mmc_cnf __iomem *cnf = host->cnf; ++ struct tmio_mmc_ctl __iomem *ctl = host->ctl; ++ ++ if(ios->clock) ++ tmio_mmc_set_clock (host, ios->clock); ++ ++ /* Power sequence - OFF -> ON -> UP */ ++ switch (ios->power_mode) { ++ case MMC_POWER_OFF: ++ writeb(0x00, &cnf->pwr_ctl[1]); /* power down SD bus */ ++ tmio_mmc_clk_stop(host); ++ break; ++ case MMC_POWER_ON: ++ writeb(0x02, &cnf->pwr_ctl[1]); /* power up SD bus */ ++ break; ++ case MMC_POWER_UP: ++ tmio_mmc_clk_start(host); /* start bus clock */ ++ break; ++ } ++ ++ switch (ios->bus_width) { ++ case MMC_BUS_WIDTH_1: ++ writew(0x80e0, &ctl->sd_mem_card_opt); ++ break; ++ case MMC_BUS_WIDTH_4: ++ writew(0x00e0, &ctl->sd_mem_card_opt); ++ break; ++ } ++ ++ /* Potentially we may need a 140us pause here. FIXME */ ++ udelay(140); ++} ++ ++static int tmio_mmc_get_ro(struct mmc_host *mmc) { ++ struct tmio_mmc_host *host = mmc_priv(mmc); ++ struct tmio_mmc_ctl __iomem *ctl = host->ctl; ++ ++ return (readw(&ctl->status[0]) & TMIO_STAT_WRPROTECT)?0:1; ++} ++ ++static struct mmc_host_ops tmio_mmc_ops = { ++ .request = tmio_mmc_request, ++ .set_ios = tmio_mmc_set_ios, ++ .get_ro = tmio_mmc_get_ro, ++}; ++ ++static int tmio_mmc_suspend(struct platform_device *dev, pm_message_t state) { ++ struct mfd_cell *cell = mfd_get_cell(dev); ++ struct mmc_host *mmc = platform_get_drvdata(dev); ++ int ret; ++ ++ ret = mmc_suspend_host(mmc, state); ++ ++ /* Tell MFD core it can disable us now.*/ ++ if(!ret && cell->disable) ++ cell->disable(dev); ++ ++ return ret; ++} ++ ++static int tmio_mmc_resume(struct platform_device *dev) { ++ struct mfd_cell *cell = mfd_get_cell(dev); ++ struct mmc_host *mmc = platform_get_drvdata(dev); ++ struct tmio_mmc_host *host = mmc_priv(mmc); ++ struct tmio_mmc_cnf __iomem *cnf = host->cnf; ++ ++ /* Enable the MMC/SD Control registers */ ++ writew(SDCREN, &cnf->cmd); ++ writel(dev->resource[0].start & 0xfffe, &cnf->ctl_base); ++ ++ /* Tell the MFD core we are ready to be enabled */ ++ if(cell->enable) ++ cell->enable(dev); ++ ++ mmc_resume_host(mmc); ++ ++ return 0; ++} ++ ++static int __devinit tmio_mmc_probe(struct platform_device *dev) ++{ ++ struct mfd_cell *cell = mfd_get_cell(dev); ++ struct tmio_mmc_cnf __iomem *cnf; ++ struct tmio_mmc_ctl __iomem *ctl; ++ struct tmio_mmc_host *host; ++ struct mmc_host *mmc; ++ int ret = -ENOMEM; ++ ++ mmc = mmc_alloc_host(sizeof(struct tmio_mmc_host), &dev->dev); ++ if (!mmc) { ++ goto out; ++ } ++ ++ host = mmc_priv(mmc); ++ host->mmc = mmc; ++ platform_set_drvdata(dev, mmc); /* Used so we can de-init safely. */ ++ ++ host->cnf = cnf = ioremap((unsigned long)dev->resource[1].start, ++ (unsigned long)dev->resource[1].end - ++ (unsigned long)dev->resource[1].start); ++ if(!host->cnf) ++ goto host_free; ++ ++ host->ctl = ctl = ioremap((unsigned long)dev->resource[0].start, ++ (unsigned long)dev->resource[0].end - ++ (unsigned long)dev->resource[0].start); ++ if (!host->ctl) { ++ goto unmap_cnf; ++ } ++ ++ mmc->ops = &tmio_mmc_ops; ++ mmc->caps = MMC_CAP_4_BIT_DATA; ++ mmc->f_min = 46875; ++ mmc->f_max = 24000000; ++ mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; ++ ++ /* Enable the MMC/SD Control registers */ ++ writew(SDCREN, &cnf->cmd); ++ writel(dev->resource[0].start & 0xfffe, &cnf->ctl_base); ++ ++ /* Tell the MFD core we are ready to be enabled */ ++ if(cell->enable) ++ cell->enable(dev); ++ ++ writeb(0x01,&cnf->pwr_ctl[2]); /* Disable SD power during suspend */ ++ writeb(0x1f, &cnf->stop_clk_ctl); /* Route clock to SDIO??? FIXME */ ++ writeb(0x0, &cnf->pwr_ctl[1]); /* Power down SD bus*/ ++ tmio_mmc_clk_stop(host); /* Stop bus clock */ ++ reset(host); /* Reset MMC HC */ ++ ++ host->irq = (unsigned long)dev->resource[2].start; ++ ret = request_irq(host->irq, tmio_mmc_irq, IRQF_DISABLED, "tmio-mmc", host); ++ if (ret){ ++ ret = -ENODEV; ++ DBG("Failed to allocate IRQ.\n"); ++ goto unmap_ctl; ++ } ++ set_irq_type(host->irq, IRQT_FALLING); ++ ++ mmc_add_host(mmc); ++ ++ printk(KERN_INFO "%s at 0x%08lx irq %d\n", mmc_hostname(host->mmc), ++ (unsigned long)host->ctl, host->irq); ++ ++ /* Lets unmask the IRQs we want to know about */ ++ disable_mmc_irqs(ctl, TMIO_MASK_ALL); ++ enable_mmc_irqs(ctl, TMIO_MASK_IRQ); ++ ++ return 0; ++ ++unmap_ctl: ++ iounmap(host->ctl); ++unmap_cnf: ++ iounmap(host->cnf); ++host_free: ++ mmc_free_host(mmc); ++out: ++ return ret; ++} ++ ++static int __devexit tmio_mmc_remove(struct platform_device *dev) ++{ ++ struct mmc_host *mmc = platform_get_drvdata(dev); ++ ++ platform_set_drvdata(dev, NULL); ++ ++ if (mmc) { ++ struct tmio_mmc_host *host = mmc_priv(mmc); ++ mmc_remove_host(mmc); ++ free_irq(host->irq, host); ++ /* FIXME - we might want to consider stopping the chip here. */ ++ iounmap(host->ctl); ++ iounmap(host->cnf); ++ mmc_free_host(mmc); /* FIXME - why does this call hang ? */ ++ } ++ return 0; ++} ++ ++/* ------------------- device registration ----------------------- */ ++ ++static struct platform_driver tmio_mmc_driver = { ++ .driver = { ++ .name = "tmio-mmc", ++ }, ++ .probe = tmio_mmc_probe, ++ .remove = __devexit_p(tmio_mmc_remove), ++#ifdef CONFIG_PM ++ .suspend = tmio_mmc_suspend, ++ .resume = tmio_mmc_resume, ++#endif ++}; ++ ++ ++static int __init tmio_mmc_init(void) ++{ ++ return platform_driver_register (&tmio_mmc_driver); ++} ++ ++static void __exit tmio_mmc_exit(void) ++{ ++ platform_driver_unregister (&tmio_mmc_driver); ++} ++ ++module_init(tmio_mmc_init); ++module_exit(tmio_mmc_exit); ++ ++MODULE_DESCRIPTION("Toshiba TMIO SD/MMC driver"); ++MODULE_AUTHOR("Ian Molton <spyro@f2s.com>"); ++MODULE_LICENSE("GPLv2"); +diff --git a/drivers/mmc/host/tmio_mmc.h b/drivers/mmc/host/tmio_mmc.h +new file mode 100644 +index 0000000..d4d9f8f +--- /dev/null ++++ b/drivers/mmc/host/tmio_mmc.h +@@ -0,0 +1,205 @@ ++/* Definitons for use with the tmio_mmc.c ++ * ++ * (c) 2005 Ian Molton <spyro@f2s.com> ++ * (c) 2007 Ian Molton <spyro@f2s.com> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++struct tmio_mmc_cnf { ++ u8 x00[4]; ++ u16 cmd; ++ u8 x01[10]; ++ u32 ctl_base; ++ u8 x02[41]; ++ u8 int_pin; ++ u8 x03[2]; ++ u8 stop_clk_ctl; ++ u8 gclk_ctl; /* Gated Clock Control */ ++ u8 sd_clk_mode; /* 0x42 */ ++ u8 x04; ++ u16 pin_status; ++ u8 x05[2]; ++ u8 pwr_ctl[3]; ++ u8 x06; ++ u8 card_detect_mode; ++ u8 x07[3]; ++ u8 sd_slot; ++ u8 x08[159]; ++ u8 ext_gclk_ctl_1; /* Extended Gated Clock Control 1 */ ++ u8 ext_gclk_ctl_2; /* Extended Gated Clock Control 2 */ ++ u8 x09[7]; ++ u8 ext_gclk_ctl_3; /* Extended Gated Clock Control 3 */ ++ u8 sd_led_en_1; ++ u8 x10[3]; ++ u8 sd_led_en_2; ++ u8 x11; ++} __attribute__ ((packed)); ++ ++#define SDCREN 0x2 /* Enable access to MMC CTL regs. (flag in COMMAND_REG)*/ ++ ++struct tmio_mmc_ctl { ++ u16 sd_cmd; ++ u16 x00; ++ u16 arg_reg[2]; ++ u16 stop_internal_action; ++ u16 xfer_blk_count; ++ u16 response[8]; ++ u16 status[2]; ++ u16 irq_mask[2]; ++ u16 sd_card_clk_ctl; ++ u16 sd_xfer_len; ++ u16 sd_mem_card_opt; ++ u16 x01; ++ u16 sd_error_detail_status[2]; ++ u16 sd_data_port[2]; ++ u16 transaction_ctl; ++ u16 x02[85]; ++ u16 reset_sd; ++ u16 x03[15]; ++ u16 sdio_regs[28]; ++ u16 clk_and_wait_ctl; ++ u16 x04[83]; ++ u16 reset_sdio; ++ u16 x05[15]; ++} __attribute__ ((packed)); ++ ++/* Definitions for values the CTRL_STATUS register can take. */ ++#define TMIO_STAT_CMDRESPEND 0x00000001 ++#define TMIO_STAT_DATAEND 0x00000004 ++#define TMIO_STAT_CARD_REMOVE 0x00000008 ++#define TMIO_STAT_CARD_INSERT 0x00000010 ++#define TMIO_STAT_SIGSTATE 0x00000020 ++#define TMIO_STAT_WRPROTECT 0x00000080 ++#define TMIO_STAT_CARD_REMOVE_A 0x00000100 ++#define TMIO_STAT_CARD_INSERT_A 0x00000200 ++#define TMIO_STAT_SIGSTATE_A 0x00000400 ++#define TMIO_STAT_CMD_IDX_ERR 0x00010000 ++#define TMIO_STAT_CRCFAIL 0x00020000 ++#define TMIO_STAT_STOPBIT_ERR 0x00040000 ++#define TMIO_STAT_DATATIMEOUT 0x00080000 ++#define TMIO_STAT_RXOVERFLOW 0x00100000 ++#define TMIO_STAT_TXUNDERRUN 0x00200000 ++#define TMIO_STAT_CMDTIMEOUT 0x00400000 ++#define TMIO_STAT_RXRDY 0x01000000 ++#define TMIO_STAT_TXRQ 0x02000000 ++#define TMIO_STAT_ILL_FUNC 0x20000000 ++#define TMIO_STAT_CMD_BUSY 0x40000000 ++#define TMIO_STAT_ILL_ACCESS 0x80000000 ++ ++/* Define some IRQ masks */ ++/* This is the mask used at reset by the chip */ ++#define TMIO_MASK_ALL 0x837f031d ++#define TMIO_MASK_READOP (TMIO_STAT_RXRDY | TMIO_STAT_DATAEND | \ ++ TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT) ++#define TMIO_MASK_WRITEOP (TMIO_STAT_TXRQ | TMIO_STAT_DATAEND | \ ++ TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT) ++#define TMIO_MASK_CMD (TMIO_STAT_CMDRESPEND | TMIO_STAT_CMDTIMEOUT | \ ++ TMIO_STAT_CARD_REMOVE | TMIO_STAT_CARD_INSERT) ++#define TMIO_MASK_IRQ (TMIO_MASK_READOP | TMIO_MASK_WRITEOP | TMIO_MASK_CMD) ++ ++#define enable_mmc_irqs(ctl, i) \ ++ do { \ ++ u32 mask;\ ++ mask = tmio_ioread32((ctl)->irq_mask); \ ++ mask &= ~((i) & TMIO_MASK_IRQ); \ ++ tmio_iowrite32(mask, (ctl)->irq_mask); \ ++ } while (0) ++ ++#define disable_mmc_irqs(ctl, i) \ ++ do { \ ++ u32 mask;\ ++ mask = tmio_ioread32((ctl)->irq_mask); \ ++ mask |= ((i) & TMIO_MASK_IRQ); \ ++ tmio_iowrite32(mask, (ctl)->irq_mask); \ ++ } while (0) ++ ++#define ack_mmc_irqs(ctl, i) \ ++ do { \ ++ u32 mask;\ ++ mask = tmio_ioread32((ctl)->status); \ ++ mask &= ~((i) & TMIO_MASK_IRQ); \ ++ tmio_iowrite32(mask, (ctl)->status); \ ++ } while (0) ++ ++ ++struct tmio_mmc_host { ++ struct tmio_mmc_cnf __iomem *cnf; ++ struct tmio_mmc_ctl __iomem *ctl; ++ struct mmc_command *cmd; ++ struct mmc_request *mrq; ++ struct mmc_data *data; ++ struct mmc_host *mmc; ++ int irq; ++ ++ /* pio related stuff */ ++ struct scatterlist *sg_ptr; ++ unsigned int sg_len; ++ unsigned int sg_off; ++}; ++ ++#include <linux/scatterlist.h> ++#include <linux/blkdev.h> ++ ++static inline void tmio_mmc_init_sg(struct tmio_mmc_host *host, struct mmc_data *data) ++{ ++ host->sg_len = data->sg_len; ++ host->sg_ptr = data->sg; ++ host->sg_off = 0; ++} ++ ++static inline int tmio_mmc_next_sg(struct tmio_mmc_host *host) ++{ ++ host->sg_ptr++; ++ host->sg_off = 0; ++ return --host->sg_len; ++} ++ ++static inline char *tmio_mmc_kmap_atomic(struct tmio_mmc_host *host, unsigned long *flags) ++{ ++ struct scatterlist *sg = host->sg_ptr; ++ ++ local_irq_save(*flags); ++ return kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset; ++} ++ ++static inline void tmio_mmc_kunmap_atomic(struct tmio_mmc_host *host, unsigned long *flags) ++{ ++ kunmap_atomic(sg_page(host->sg_ptr), KM_BIO_SRC_IRQ); ++ local_irq_restore(*flags); ++} ++ ++#ifdef CONFIG_MMC_DEBUG ++#define DBG(args...) printk(args) ++ ++void debug_status(u32 status){ ++ printk("status: %08x = ", status); ++ if(status & TMIO_STAT_CARD_REMOVE) printk("Card_removed "); ++ if(status & TMIO_STAT_CARD_INSERT) printk("Card_insert "); ++ if(status & TMIO_STAT_SIGSTATE) printk("Sigstate "); ++ if(status & TMIO_STAT_WRPROTECT) printk("Write_protect "); ++ if(status & TMIO_STAT_CARD_REMOVE_A) printk("Card_remove_A "); ++ if(status & TMIO_STAT_CARD_INSERT_A) printk("Card_insert_A "); ++ if(status & TMIO_STAT_SIGSTATE_A) printk("Sigstate_A "); ++ if(status & TMIO_STAT_CMD_IDX_ERR) printk("Cmd_IDX_Err "); ++ if(status & TMIO_STAT_STOPBIT_ERR) printk("Stopbit_ERR "); ++ if(status & TMIO_STAT_ILL_FUNC) printk("ILLEGAL_FUNC "); ++ if(status & TMIO_STAT_CMD_BUSY) printk("CMD_BUSY "); ++ if(status & TMIO_STAT_CMDRESPEND) printk("Response_end "); ++ if(status & TMIO_STAT_DATAEND) printk("Data_end "); ++ if(status & TMIO_STAT_CRCFAIL) printk("CRC_failure "); ++ if(status & TMIO_STAT_DATATIMEOUT) printk("Data_timeout "); ++ if(status & TMIO_STAT_CMDTIMEOUT) printk("Command_timeout "); ++ if(status & TMIO_STAT_RXOVERFLOW) printk("RX_OVF "); ++ if(status & TMIO_STAT_TXUNDERRUN) printk("TX_UND "); ++ if(status & TMIO_STAT_RXRDY) printk("RX_rdy "); ++ if(status & TMIO_STAT_TXRQ) printk("TX_req "); ++ if(status & TMIO_STAT_ILL_ACCESS) printk("ILLEGAL_ACCESS "); ++ printk("\n"); ++} ++#else ++#define DBG(fmt,args...) do { } while (0) ++#endif +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0012-Tosa-keyboard-support.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0012-Tosa-keyboard-support.patch new file mode 100644 index 0000000000..0fa10ebd4c --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0012-Tosa-keyboard-support.patch @@ -0,0 +1,593 @@ +From 6d377e8f80ce421e6842ac5f42081345fbc70002 Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Wed, 9 Jan 2008 01:27:41 +0300 +Subject: [PATCH 12/64] Tosa keyboard support + +Support keyboard on tosa (Sharp Zaurus SL-6000x). +Largely based on patches by Dirk Opfer. + +Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> +--- + arch/arm/mach-pxa/tosa.c | 43 ++++ + drivers/input/keyboard/Kconfig | 21 ++ + drivers/input/keyboard/Makefile | 1 + + drivers/input/keyboard/tosakbd.c | 415 ++++++++++++++++++++++++++++++++++++++ + include/asm-arm/arch-pxa/tosa.h | 30 +++ + 5 files changed, 510 insertions(+), 0 deletions(-) + create mode 100644 drivers/input/keyboard/tosakbd.c + +diff --git a/arch/arm/mach-pxa/tosa.c b/arch/arm/mach-pxa/tosa.c +index 240fd04..e7e0f52 100644 +--- a/arch/arm/mach-pxa/tosa.c ++++ b/arch/arm/mach-pxa/tosa.c +@@ -21,6 +21,8 @@ + #include <linux/mmc/host.h> + #include <linux/pm.h> + #include <linux/delay.h> ++#include <linux/gpio_keys.h> ++#include <linux/input.h> + + #include <asm/setup.h> + #include <asm/memory.h> +@@ -253,6 +255,46 @@ static struct platform_device tosakbd_device = { + .id = -1, + }; + ++static struct gpio_keys_button tosa_gpio_keys[] = { ++ { ++ .type = EV_PWR, ++ .code = KEY_SUSPEND, ++ .gpio = TOSA_GPIO_ON_KEY, ++ .desc = "On key", ++ .wakeup = 1, ++ .active_low = 1, ++ }, ++ { ++ .type = EV_KEY, ++ .code = TOSA_KEY_RECORD, ++ .gpio = TOSA_GPIO_RECORD_BTN, ++ .desc = "Record Button", ++ .wakeup = 1, ++ .active_low = 1, ++ }, ++ { ++ .type = EV_KEY, ++ .code = TOSA_KEY_SYNC, ++ .gpio = TOSA_GPIO_SYNC, ++ .desc = "Sync Button", ++ .wakeup = 1, ++ .active_low = 1, ++ }, ++}; ++ ++static struct gpio_keys_platform_data tosa_gpio_keys_platform_data = { ++ .buttons = tosa_gpio_keys, ++ .nbuttons = ARRAY_SIZE(tosa_gpio_keys), ++}; ++ ++static struct platform_device tosa_gpio_keys_device = { ++ .name = "gpio-keys", ++ .id = -1, ++ .dev = { ++ .platform_data = &tosa_gpio_keys_platform_data, ++ }, ++}; ++ + /* + * Tosa LEDs + */ +@@ -265,6 +307,7 @@ static struct platform_device *devices[] __initdata = { + &tosascoop_device, + &tosascoop_jc_device, + &tosakbd_device, ++ &tosa_gpio_keys_device, + &tosaled_device, + }; + +diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig +index 086d58c..0c32762 100644 +--- a/drivers/input/keyboard/Kconfig ++++ b/drivers/input/keyboard/Kconfig +@@ -154,6 +154,27 @@ config KEYBOARD_SPITZ + To compile this driver as a module, choose M here: the + module will be called spitzkbd. + ++config KEYBOARD_TOSA ++ tristate "Tosa keyboard" ++ depends on MACH_TOSA ++ default y ++ help ++ Say Y here to enable the keyboard on the Sharp Zaurus SL-6000x (Tosa) ++ ++ To compile this driver as a module, choose M here: the ++ module will be called tosakbd. ++ ++config KEYBOARD_TOSA_USE_EXT_KEYCODES ++ bool "Tosa keyboard: use extended keycodes" ++ depends on KEYBOARD_TOSA ++ default n ++ help ++ Say Y here to enable the tosa keyboard driver to generate extended ++ (>= 127) keycodes. Be aware, that they can't be correctly interpreted ++ by either console keyboard driver or by Kdrive keybd driver. ++ ++ Say Y only if you know, what you are doing! ++ + config KEYBOARD_AMIGA + tristate "Amiga keyboard" + depends on AMIGA +diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile +index e97455f..6caa065 100644 +--- a/drivers/input/keyboard/Makefile ++++ b/drivers/input/keyboard/Makefile +@@ -15,6 +15,7 @@ obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o + obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o + obj-$(CONFIG_KEYBOARD_CORGI) += corgikbd.o + obj-$(CONFIG_KEYBOARD_SPITZ) += spitzkbd.o ++obj-$(CONFIG_KEYBOARD_TOSA) += tosakbd.o + obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o + obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o + obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o +diff --git a/drivers/input/keyboard/tosakbd.c b/drivers/input/keyboard/tosakbd.c +new file mode 100644 +index 0000000..3884d1e +--- /dev/null ++++ b/drivers/input/keyboard/tosakbd.c +@@ -0,0 +1,415 @@ ++/* ++ * Keyboard driver for Sharp Tosa models (SL-6000x) ++ * ++ * Copyright (c) 2005 Dirk Opfer ++ * Copyright (c) 2007 Dmitry Baryshkov ++ * ++ * Based on xtkbd.c/locomkbd.c/corgikbd.c ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/input.h> ++#include <linux/delay.h> ++#include <linux/interrupt.h> ++ ++#include <asm/arch/gpio.h> ++#include <asm/arch/tosa.h> ++ ++#define KB_ROWMASK(r) (1 << (r)) ++#define SCANCODE(r, c) (((r)<<4) + (c) + 1) ++#define NR_SCANCODES SCANCODE(TOSA_KEY_SENSE_NUM - 1, TOSA_KEY_STROBE_NUM - 1) + 1 ++ ++#define SCAN_INTERVAL (HZ/10) ++ ++#define KB_DISCHARGE_DELAY 10 ++#define KB_ACTIVATE_DELAY 10 ++ ++static unsigned int tosakbd_keycode[NR_SCANCODES] = { ++0, ++0, KEY_W, 0, 0, 0, KEY_K, KEY_BACKSPACE, KEY_P, ++0, 0, 0, 0, 0, 0, 0, 0, ++KEY_Q, KEY_E, KEY_T, KEY_Y, 0, KEY_O, KEY_I, KEY_COMMA, ++0, 0, 0, 0, 0, 0, 0, 0, ++KEY_A, KEY_D, KEY_G, KEY_U, 0, KEY_L, KEY_ENTER, KEY_DOT, ++0, 0, 0, 0, 0, 0, 0, 0, ++KEY_Z, KEY_C, KEY_V, KEY_J, TOSA_KEY_ADDRESSBOOK, TOSA_KEY_CANCEL, TOSA_KEY_CENTER, TOSA_KEY_OK, ++KEY_LEFTSHIFT, 0, 0, 0, 0, 0, 0, 0, ++KEY_S, KEY_R, KEY_B, KEY_N, TOSA_KEY_CALENDAR, TOSA_KEY_HOMEPAGE, KEY_LEFTCTRL, TOSA_KEY_LIGHT, ++0, KEY_RIGHTSHIFT, 0, 0, 0, 0, 0, 0, ++KEY_TAB, KEY_SLASH, KEY_H, KEY_M, TOSA_KEY_MENU, 0, KEY_UP, 0, ++0, 0, TOSA_KEY_FN, 0, 0, 0, 0, 0, ++KEY_X, KEY_F, KEY_SPACE, KEY_APOSTROPHE, TOSA_KEY_MAIL, KEY_LEFT, KEY_DOWN, KEY_RIGHT, ++0, 0, 0, ++}; ++ ++struct tosakbd { ++ unsigned int keycode[ARRAY_SIZE(tosakbd_keycode)]; ++ struct input_dev *input; ++ ++ spinlock_t lock; /* protect kbd scanning */ ++ struct timer_list timer; ++}; ++ ++ ++/* Helper functions for reading the keyboard matrix ++ * Note: We should really be using pxa_gpio_mode to alter GPDR but it ++ * requires a function call per GPIO bit which is excessive ++ * when we need to access 12 bits at once, multiple times. ++ * These functions must be called within local_irq_save()/local_irq_restore() ++ * or similar. ++ */ ++#define GET_ROWS_STATUS(c) ((GPLR2 & TOSA_GPIO_ALL_SENSE_BIT) >> TOSA_GPIO_ALL_SENSE_RSHIFT) ++ ++static inline void tosakbd_discharge_all(void) ++{ ++ /* STROBE All HiZ */ ++ GPCR1 = TOSA_GPIO_HIGH_STROBE_BIT; ++ GPDR1 &= ~TOSA_GPIO_HIGH_STROBE_BIT; ++ GPCR2 = TOSA_GPIO_LOW_STROBE_BIT; ++ GPDR2 &= ~TOSA_GPIO_LOW_STROBE_BIT; ++} ++ ++static inline void tosakbd_activate_all(void) ++{ ++ /* STROBE ALL -> High */ ++ GPSR1 = TOSA_GPIO_HIGH_STROBE_BIT; ++ GPDR1 |= TOSA_GPIO_HIGH_STROBE_BIT; ++ GPSR2 = TOSA_GPIO_LOW_STROBE_BIT; ++ GPDR2 |= TOSA_GPIO_LOW_STROBE_BIT; ++ ++ udelay(KB_DISCHARGE_DELAY); ++ ++ /* STATE CLEAR */ ++ GEDR2 |= TOSA_GPIO_ALL_SENSE_BIT; ++} ++ ++static inline void tosakbd_activate_col(int col) ++{ ++ if (col <= 5) { ++ /* STROBE col -> High, not col -> HiZ */ ++ GPSR1 = TOSA_GPIO_STROBE_BIT(col); ++ GPDR1 = (GPDR1 & ~TOSA_GPIO_HIGH_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col); ++ } else { ++ /* STROBE col -> High, not col -> HiZ */ ++ GPSR2 = TOSA_GPIO_STROBE_BIT(col); ++ GPDR2 = (GPDR2 & ~TOSA_GPIO_LOW_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col); ++ } ++} ++ ++static inline void tosakbd_reset_col(int col) ++{ ++ if (col <= 5) { ++ /* STROBE col -> Low */ ++ GPCR1 = TOSA_GPIO_STROBE_BIT(col); ++ /* STROBE col -> out, not col -> HiZ */ ++ GPDR1 = (GPDR1 & ~TOSA_GPIO_HIGH_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col); ++ } else { ++ /* STROBE col -> Low */ ++ GPCR2 = TOSA_GPIO_STROBE_BIT(col); ++ /* STROBE col -> out, not col -> HiZ */ ++ GPDR2 = (GPDR2 & ~TOSA_GPIO_LOW_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col); ++ } ++} ++/* ++ * The tosa keyboard only generates interrupts when a key is pressed. ++ * So when a key is pressed, we enable a timer. This timer scans the ++ * keyboard, and this is how we detect when the key is released. ++ */ ++ ++/* Scan the hardware keyboard and push any changes up through the input layer */ ++static void tosakbd_scankeyboard(struct platform_device *dev) ++{ ++ struct tosakbd *tosakbd = platform_get_drvdata(dev); ++ unsigned int row, col, rowd; ++ unsigned long flags; ++ unsigned int num_pressed = 0; ++ ++ spin_lock_irqsave(&tosakbd->lock, flags); ++ ++ for (col = 0; col < TOSA_KEY_STROBE_NUM; col++) { ++ /* ++ * Discharge the output driver capacitatance ++ * in the keyboard matrix. (Yes it is significant..) ++ */ ++ tosakbd_discharge_all(); ++ udelay(KB_DISCHARGE_DELAY); ++ ++ tosakbd_activate_col(col); ++ udelay(KB_ACTIVATE_DELAY); ++ ++ rowd = GET_ROWS_STATUS(col); ++ ++ for (row = 0; row < TOSA_KEY_SENSE_NUM; row++) { ++ unsigned int scancode, pressed; ++ scancode = SCANCODE(row, col); ++ pressed = rowd & KB_ROWMASK(row); ++ ++ if (pressed && !tosakbd->keycode[scancode]) ++ dev_warn(&dev->dev, ++ "unhandled scancode: 0x%02x\n", ++ scancode); ++ ++ input_report_key(tosakbd->input, ++ tosakbd->keycode[scancode], ++ pressed); ++ if (pressed) ++ num_pressed++; ++ } ++ ++ tosakbd_reset_col(col); ++ } ++ ++ tosakbd_activate_all(); ++ ++ input_sync(tosakbd->input); ++ ++ /* if any keys are pressed, enable the timer */ ++ if (num_pressed) ++ mod_timer(&tosakbd->timer, jiffies + SCAN_INTERVAL); ++ ++ spin_unlock_irqrestore(&tosakbd->lock, flags); ++} ++ ++/* ++ * tosa keyboard interrupt handler. ++ */ ++static irqreturn_t tosakbd_interrupt(int irq, void *__dev) ++{ ++ struct platform_device *dev = __dev; ++ struct tosakbd *tosakbd = platform_get_drvdata(dev); ++ ++ if (!timer_pending(&tosakbd->timer)) { ++ /** wait chattering delay **/ ++ udelay(20); ++ tosakbd_scankeyboard(dev); ++ } ++ ++ return IRQ_HANDLED; ++} ++ ++/* ++ * tosa timer checking for released keys ++ */ ++static void tosakbd_timer_callback(unsigned long __dev) ++{ ++ struct platform_device *dev = (struct platform_device *)__dev; ++ tosakbd_scankeyboard(dev); ++} ++ ++#ifdef CONFIG_PM ++static int tosakbd_suspend(struct platform_device *dev, pm_message_t state) ++{ ++ struct tosakbd *tosakbd = platform_get_drvdata(dev); ++ ++ del_timer_sync(&tosakbd->timer); ++ ++ return 0; ++} ++ ++static int tosakbd_resume(struct platform_device *dev) ++{ ++ tosakbd_scankeyboard(dev); ++ ++ return 0; ++} ++#else ++#define tosakbd_suspend NULL ++#define tosakbd_resume NULL ++#endif ++ ++static int __devinit tosakbd_probe(struct platform_device *pdev) { ++ ++ int i; ++ struct tosakbd *tosakbd; ++ struct input_dev *input_dev; ++ int error; ++ ++ tosakbd = kzalloc(sizeof(struct tosakbd), GFP_KERNEL); ++ if (!tosakbd) ++ return -ENOMEM; ++ ++ input_dev = input_allocate_device(); ++ if (!input_dev) { ++ kfree(tosakbd); ++ return -ENOMEM; ++ } ++ ++ platform_set_drvdata(pdev, tosakbd); ++ ++ spin_lock_init(&tosakbd->lock); ++ ++ /* Init Keyboard rescan timer */ ++ init_timer(&tosakbd->timer); ++ tosakbd->timer.function = tosakbd_timer_callback; ++ tosakbd->timer.data = (unsigned long) pdev; ++ ++ tosakbd->input = input_dev; ++ ++ input_set_drvdata(input_dev, tosakbd); ++ input_dev->name = "Tosa Keyboard"; ++ input_dev->phys = "tosakbd/input0"; ++ input_dev->dev.parent = &pdev->dev; ++ ++ input_dev->id.bustype = BUS_HOST; ++ input_dev->id.vendor = 0x0001; ++ input_dev->id.product = 0x0001; ++ input_dev->id.version = 0x0100; ++ ++ input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP); ++ input_dev->keycode = tosakbd->keycode; ++ input_dev->keycodesize = sizeof(unsigned int); ++ input_dev->keycodemax = ARRAY_SIZE(tosakbd_keycode); ++ ++ memcpy(tosakbd->keycode, tosakbd_keycode, sizeof(tosakbd_keycode)); ++ ++ for (i = 0; i < ARRAY_SIZE(tosakbd_keycode); i++) ++ __set_bit(tosakbd->keycode[i], input_dev->keybit); ++ clear_bit(0, input_dev->keybit); ++ ++ /* Setup sense interrupts - RisingEdge Detect, sense lines as inputs */ ++ for (i = 0; i < TOSA_KEY_SENSE_NUM; i++) { ++ int gpio = TOSA_GPIO_KEY_SENSE(i); ++ int irq; ++ error = gpio_request(gpio, "tosakbd"); ++ if (error < 0) { ++ printk(KERN_ERR "tosakbd: failed to request GPIO %d, " ++ " error %d\n", gpio, error); ++ goto fail; ++ } ++ ++ error = gpio_direction_input(TOSA_GPIO_KEY_SENSE(i)); ++ if (error < 0) { ++ printk(KERN_ERR "tosakbd: failed to configure input" ++ " direction for GPIO %d, error %d\n", ++ gpio, error); ++ gpio_free(gpio); ++ goto fail; ++ } ++ ++ irq = gpio_to_irq(gpio); ++ if (irq < 0) { ++ error = irq; ++ printk(KERN_ERR "gpio-keys: Unable to get irq number" ++ " for GPIO %d, error %d\n", ++ gpio, error); ++ gpio_free(gpio); ++ goto fail; ++ } ++ ++ error = request_irq(irq, tosakbd_interrupt, ++ IRQF_DISABLED | IRQF_TRIGGER_RISING, ++ "tosakbd", pdev); ++ ++ if (error) { ++ printk("tosakbd: Can't get IRQ: %d: error %d!\n", ++ irq, error); ++ gpio_free(gpio); ++ goto fail; ++ } ++ } ++ ++ /* Set Strobe lines as outputs - set high */ ++ for (i = 0; i < TOSA_KEY_STROBE_NUM; i++) { ++ int gpio = TOSA_GPIO_KEY_STROBE(i); ++ error = gpio_request(gpio, "tosakbd"); ++ if (error < 0) { ++ printk(KERN_ERR "tosakbd: failed to request GPIO %d, " ++ " error %d\n", gpio, error); ++ goto fail2; ++ } ++ ++ error = gpio_direction_output(gpio, 1); ++ if (error < 0) { ++ printk(KERN_ERR "tosakbd: failed to configure input" ++ " direction for GPIO %d, error %d\n", ++ gpio, error); ++ gpio_free(gpio); ++ goto fail; ++ } ++ ++ } ++ ++ error = input_register_device(input_dev); ++ if (error) { ++ printk(KERN_ERR "tosakbd: Unable to register input device, " ++ "error: %d\n", error); ++ goto fail; ++ } ++ ++ printk(KERN_INFO "input: Tosa Keyboard Registered\n"); ++ ++ return 0; ++ ++fail2: ++ while (--i >= 0) ++ gpio_free(TOSA_GPIO_KEY_STROBE(i)); ++ ++ i = TOSA_KEY_SENSE_NUM; ++fail: ++ while (--i >= 0) { ++ free_irq(gpio_to_irq(TOSA_GPIO_KEY_SENSE(i)), pdev); ++ gpio_free(TOSA_GPIO_KEY_SENSE(i)); ++ } ++ ++ platform_set_drvdata(pdev, NULL); ++ input_free_device(input_dev); ++ kfree(tosakbd); ++ ++ return error; ++} ++ ++static int __devexit tosakbd_remove(struct platform_device *dev) { ++ ++ int i; ++ struct tosakbd *tosakbd = platform_get_drvdata(dev); ++ ++ for (i = 0; i < TOSA_KEY_STROBE_NUM; i++) ++ gpio_free(TOSA_GPIO_KEY_STROBE(i)); ++ ++ for (i = 0; i < TOSA_KEY_SENSE_NUM; i++) { ++ free_irq(gpio_to_irq(TOSA_GPIO_KEY_SENSE(i)), dev); ++ gpio_free(TOSA_GPIO_KEY_SENSE(i)); ++ } ++ ++ del_timer_sync(&tosakbd->timer); ++ ++ input_unregister_device(tosakbd->input); ++ ++ kfree(tosakbd); ++ ++ return 0; ++} ++ ++static struct platform_driver tosakbd_driver = { ++ .probe = tosakbd_probe, ++ .remove = __devexit_p(tosakbd_remove), ++ .suspend = tosakbd_suspend, ++ .resume = tosakbd_resume, ++ .driver = { ++ .name = "tosa-keyboard", ++ }, ++}; ++ ++static int __devinit tosakbd_init(void) ++{ ++ return platform_driver_register(&tosakbd_driver); ++} ++ ++static void __exit tosakbd_exit(void) ++{ ++ platform_driver_unregister(&tosakbd_driver); ++} ++ ++module_init(tosakbd_init); ++module_exit(tosakbd_exit); ++ ++MODULE_AUTHOR("Dirk Opfer <Dirk@Opfer-Online.de>"); ++MODULE_DESCRIPTION("Tosa Keyboard Driver"); ++MODULE_LICENSE("GPL v2"); +diff --git a/include/asm-arm/arch-pxa/tosa.h b/include/asm-arm/arch-pxa/tosa.h +index c3364a2..c05e4fa 100644 +--- a/include/asm-arm/arch-pxa/tosa.h ++++ b/include/asm-arm/arch-pxa/tosa.h +@@ -163,4 +163,34 @@ + + extern struct platform_device tosascoop_jc_device; + extern struct platform_device tosascoop_device; ++ ++#define TOSA_KEY_SYNC KEY_102ND /* ??? */ ++ ++ ++#ifndef CONFIG_KEYBOARD_TOSA_USE_EXT_KEYCODES ++#define TOSA_KEY_RECORD KEY_YEN ++#define TOSA_KEY_ADDRESSBOOK KEY_KATAKANA ++#define TOSA_KEY_CANCEL KEY_ESC ++#define TOSA_KEY_CENTER KEY_HIRAGANA ++#define TOSA_KEY_OK KEY_HENKAN ++#define TOSA_KEY_CALENDAR KEY_KATAKANAHIRAGANA ++#define TOSA_KEY_HOMEPAGE KEY_HANGEUL ++#define TOSA_KEY_LIGHT KEY_MUHENKAN ++#define TOSA_KEY_MENU KEY_HANJA ++#define TOSA_KEY_FN KEY_RIGHTALT ++#define TOSA_KEY_MAIL KEY_ZENKAKUHANKAKU ++#else ++#define TOSA_KEY_RECORD KEY_RECORD ++#define TOSA_KEY_ADDRESSBOOK KEY_ADDRESSBOOK ++#define TOSA_KEY_CANCEL KEY_CANCEL ++#define TOSA_KEY_CENTER KEY_SELECT /* ??? */ ++#define TOSA_KEY_OK KEY_OK ++#define TOSA_KEY_CALENDAR KEY_CALENDAR ++#define TOSA_KEY_HOMEPAGE KEY_HOMEPAGE ++#define TOSA_KEY_LIGHT KEY_KBDILLUMTOGGLE ++#define TOSA_KEY_MENU KEY_MENU ++#define TOSA_KEY_FN KEY_FN ++#define TOSA_KEY_MAIL KEY_MAIL ++#endif ++ + #endif /* _ASM_ARCH_TOSA_H_ */ +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0013-USB-gadget-pxa2xx_udc-supports-inverted-vbus.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0013-USB-gadget-pxa2xx_udc-supports-inverted-vbus.patch new file mode 100644 index 0000000000..082a2c72b8 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0013-USB-gadget-pxa2xx_udc-supports-inverted-vbus.patch @@ -0,0 +1,61 @@ +From 18c1a92a09faf75ebdac7ac471c741a6622cf3e2 Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Wed, 9 Jan 2008 01:27:49 +0300 +Subject: [PATCH 13/64] USB: gadget: pxa2xx_udc supports inverted vbus + +Some boards (like e.g. Tosa) invert the VBUS-detection signal: +it's low when a host is supplying VBUS, and high otherwise. +Allow specifying whether gpio_vbus value is inverted. + +Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> +Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> +Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de> +--- + drivers/usb/gadget/pxa2xx_udc.c | 9 +++++++-- + include/asm-arm/mach/udc_pxa2xx.h | 2 ++ + 2 files changed, 9 insertions(+), 2 deletions(-) + +diff --git a/drivers/usb/gadget/pxa2xx_udc.c b/drivers/usb/gadget/pxa2xx_udc.c +index 3173b39..4f7d4ef 100644 +--- a/drivers/usb/gadget/pxa2xx_udc.c ++++ b/drivers/usb/gadget/pxa2xx_udc.c +@@ -127,8 +127,10 @@ static int is_vbus_present(void) + { + struct pxa2xx_udc_mach_info *mach = the_controller->mach; + +- if (mach->gpio_vbus) +- return gpio_get_value(mach->gpio_vbus); ++ if (mach->gpio_vbus) { ++ int value = gpio_get_value(mach->gpio_vbus); ++ return mach->gpio_vbus_inverted ? !value : value; ++ } + if (mach->udc_is_connected) + return mach->udc_is_connected(); + return 1; +@@ -1397,6 +1399,9 @@ static irqreturn_t udc_vbus_irq(int irq, void *_dev) + struct pxa2xx_udc *dev = _dev; + int vbus = gpio_get_value(dev->mach->gpio_vbus); + ++ if (dev->mach->gpio_vbus_inverted) ++ vbus = !vbus; ++ + pxa2xx_udc_vbus_session(&dev->gadget, vbus); + return IRQ_HANDLED; + } +diff --git a/include/asm-arm/mach/udc_pxa2xx.h b/include/asm-arm/mach/udc_pxa2xx.h +index ff0a957..f191e14 100644 +--- a/include/asm-arm/mach/udc_pxa2xx.h ++++ b/include/asm-arm/mach/udc_pxa2xx.h +@@ -19,7 +19,9 @@ struct pxa2xx_udc_mach_info { + * with on-chip GPIOs not Lubbock's wierd hardware, can have a sane + * VBUS IRQ and omit the methods above. Store the GPIO number + * here; for GPIO 0, also mask in one of the pxa_gpio_mode() bits. ++ * Note that sometimes the signals go through inverters... + */ ++ bool gpio_vbus_inverted; + u16 gpio_vbus; /* high == vbus present */ + u16 gpio_pullup; /* high == pullup activated */ + }; +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0014-tosa_udc_use_gpio_vbus.patch.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0014-tosa_udc_use_gpio_vbus.patch.patch new file mode 100644 index 0000000000..98783efea0 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0014-tosa_udc_use_gpio_vbus.patch.patch @@ -0,0 +1,38 @@ +From 932ff38b17c7847c43e2bad01b510b64c27f9810 Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Wed, 9 Jan 2008 01:27:59 +0300 +Subject: [PATCH 14/64] tosa_udc_use_gpio_vbus.patch + +Use gpio_vbus instead of udc_is_connected for udc on tosa. + +Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> +Acked-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + arch/arm/mach-pxa/tosa.c | 9 ++------- + 1 files changed, 2 insertions(+), 7 deletions(-) + +diff --git a/arch/arm/mach-pxa/tosa.c b/arch/arm/mach-pxa/tosa.c +index e7e0f52..5268e94 100644 +--- a/arch/arm/mach-pxa/tosa.c ++++ b/arch/arm/mach-pxa/tosa.c +@@ -159,15 +159,10 @@ static void tosa_udc_command(int cmd) + } + } + +-static int tosa_udc_is_connected(void) +-{ +- return ((GPLR(TOSA_GPIO_USB_IN) & GPIO_bit(TOSA_GPIO_USB_IN)) == 0); +-} +- +- + static struct pxa2xx_udc_mach_info udc_info __initdata = { + .udc_command = tosa_udc_command, +- .udc_is_connected = tosa_udc_is_connected, ++ .gpio_vbus = TOSA_GPIO_USB_IN, ++ .gpio_vbus_inverted = 1, + }; + + /* +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0015-sharpsl-export-params.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0015-sharpsl-export-params.patch new file mode 100644 index 0000000000..f8e57e8306 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0015-sharpsl-export-params.patch @@ -0,0 +1,32 @@ +From bba216220d17d1091413e82c9924ac5614402c05 Mon Sep 17 00:00:00 2001 +From: Ian Molton <spyro@f2s.com> +Date: Wed, 9 Jan 2008 01:28:06 +0300 +Subject: [PATCH 15/64] sharpsl export params + +--- + arch/arm/common/sharpsl_param.c | 2 ++ + 1 files changed, 2 insertions(+), 0 deletions(-) + +diff --git a/arch/arm/common/sharpsl_param.c b/arch/arm/common/sharpsl_param.c +index aad4d94..d56c932 100644 +--- a/arch/arm/common/sharpsl_param.c ++++ b/arch/arm/common/sharpsl_param.c +@@ -12,6 +12,7 @@ + */ + + #include <linux/kernel.h> ++#include <linux/module.h> + #include <linux/string.h> + #include <asm/mach/sharpsl_param.h> + +@@ -36,6 +37,7 @@ + #define PHAD_MAGIC MAGIC_CHG('P','H','A','D') + + struct sharpsl_param_info sharpsl_param; ++EXPORT_SYMBOL(sharpsl_param); + + void sharpsl_save_param(void) + { +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0016-This-patch-fixes-the-pxa25x-clocks-definitions-to-ad.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0016-This-patch-fixes-the-pxa25x-clocks-definitions-to-ad.patch new file mode 100644 index 0000000000..d73de0698c --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0016-This-patch-fixes-the-pxa25x-clocks-definitions-to-ad.patch @@ -0,0 +1,44 @@ +From 0fe7b491b70efafbd41185f8e95a3eada65984a1 Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Mon, 28 Jan 2008 01:49:28 +0300 +Subject: [PATCH 16/64] This patch fixes the pxa25x clocks definitions to add hwuart. + +Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> +--- + arch/arm/mach-pxa/pxa25x.c | 9 ++++++++- + 1 files changed, 8 insertions(+), 1 deletions(-) + +diff --git a/arch/arm/mach-pxa/pxa25x.c b/arch/arm/mach-pxa/pxa25x.c +index 9732d5d..006a6e0 100644 +--- a/arch/arm/mach-pxa/pxa25x.c ++++ b/arch/arm/mach-pxa/pxa25x.c +@@ -111,11 +111,14 @@ static const struct clkops clk_pxa25x_lcd_ops = { + * 95.842MHz -> MMC 19.169MHz, I2C 31.949MHz, FICP 47.923MHz, USB 47.923MHz + * 147.456MHz -> UART 14.7456MHz, AC97 12.288MHz, I2S 5.672MHz (allegedly) + */ ++static struct clk pxa25x_hwuart_clk = ++ INIT_CKEN("UARTCLK", HWUART, 14745600, 1, &pxa_device_hwuart.dev) ++; ++ + static struct clk pxa25x_clks[] = { + INIT_CK("LCDCLK", LCD, &clk_pxa25x_lcd_ops, &pxa_device_fb.dev), + INIT_CKEN("UARTCLK", FFUART, 14745600, 1, &pxa_device_ffuart.dev), + INIT_CKEN("UARTCLK", BTUART, 14745600, 1, &pxa_device_btuart.dev), +- INIT_CKEN("UARTCLK", BTUART, 14745600, 1, &pxa_device_btuart.dev), + INIT_CKEN("UARTCLK", STUART, 14745600, 1, NULL), + INIT_CKEN("UDCCLK", USB, 47923000, 5, &pxa_device_udc.dev), + INIT_CKEN("MMCCLK", MMC, 19169000, 0, &pxa_device_mci.dev), +@@ -303,6 +306,10 @@ static int __init pxa25x_init(void) + { + int ret = 0; + ++ /* Only add HWUART for PXA255/26x; PXA210/250/27x do not have it. */ ++ if (cpu_is_pxa25x()) ++ clks_register(&pxa25x_hwuart_clk, 1); ++ + if (cpu_is_pxa21x() || cpu_is_pxa25x()) { + clks_register(pxa25x_clks, ARRAY_SIZE(pxa25x_clks)); + +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0017-Convert-pxa2xx-UDC-to-use-debugfs.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0017-Convert-pxa2xx-UDC-to-use-debugfs.patch new file mode 100644 index 0000000000..5163361da3 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0017-Convert-pxa2xx-UDC-to-use-debugfs.patch @@ -0,0 +1,280 @@ +From 71857e8f6c4a8d2d3eac3037f02e0c30c6fdb37e Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Wed, 9 Jan 2008 01:43:28 +0300 +Subject: [PATCH 17/64] Convert pxa2xx UDC to use debugfs + +Use debugfs instead of /proc/driver/udc + +Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> +--- + drivers/usb/gadget/pxa2xx_udc.c | 100 +++++++++++++++++---------------------- + drivers/usb/gadget/pxa2xx_udc.h | 10 +++- + 2 files changed, 51 insertions(+), 59 deletions(-) + +diff --git a/drivers/usb/gadget/pxa2xx_udc.c b/drivers/usb/gadget/pxa2xx_udc.c +index 4f7d4ef..2900556 100644 +--- a/drivers/usb/gadget/pxa2xx_udc.c ++++ b/drivers/usb/gadget/pxa2xx_udc.c +@@ -38,13 +38,14 @@ + #include <linux/timer.h> + #include <linux/list.h> + #include <linux/interrupt.h> +-#include <linux/proc_fs.h> + #include <linux/mm.h> + #include <linux/platform_device.h> + #include <linux/dma-mapping.h> + #include <linux/irq.h> + #include <linux/clk.h> + #include <linux/err.h> ++#include <linux/seq_file.h> ++#include <linux/debugfs.h> + + #include <asm/byteorder.h> + #include <asm/dma.h> +@@ -993,45 +994,36 @@ static const struct usb_gadget_ops pxa2xx_udc_ops = { + + /*-------------------------------------------------------------------------*/ + +-#ifdef CONFIG_USB_GADGET_DEBUG_FILES +- +-static const char proc_node_name [] = "driver/udc"; ++#ifdef CONFIG_USB_GADGET_DEBUG_FS + ++static struct pxa2xx_udc memory; + static int +-udc_proc_read(char *page, char **start, off_t off, int count, +- int *eof, void *_dev) ++udc_seq_show(struct seq_file *m, void *d) + { +- char *buf = page; +- struct pxa2xx_udc *dev = _dev; +- char *next = buf; +- unsigned size = count; ++ struct pxa2xx_udc *dev = m->private; + unsigned long flags; +- int i, t; ++ int i; + u32 tmp; + +- if (off != 0) +- return 0; ++ ++ BUG_ON(dev == NULL); + + local_irq_save(flags); + + /* basic device status */ +- t = scnprintf(next, size, DRIVER_DESC "\n" ++ seq_printf(m, DRIVER_DESC "\n" + "%s version: %s\nGadget driver: %s\nHost %s\n\n", + driver_name, DRIVER_VERSION SIZE_STR "(pio)", + dev->driver ? dev->driver->driver.name : "(none)", + is_vbus_present() ? "full speed" : "disconnected"); +- size -= t; +- next += t; + + /* registers for device and ep0 */ +- t = scnprintf(next, size, ++ seq_printf(m, + "uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n", + UICR1, UICR0, USIR1, USIR0, UFNRH, UFNRL); +- size -= t; +- next += t; + + tmp = UDCCR; +- t = scnprintf(next, size, ++ seq_printf(m, + "udccr %02X =%s%s%s%s%s%s%s%s\n", tmp, + (tmp & UDCCR_REM) ? " rem" : "", + (tmp & UDCCR_RSTIR) ? " rstir" : "", +@@ -1041,11 +1033,9 @@ udc_proc_read(char *page, char **start, off_t off, int count, + (tmp & UDCCR_RSM) ? " rsm" : "", + (tmp & UDCCR_UDA) ? " uda" : "", + (tmp & UDCCR_UDE) ? " ude" : ""); +- size -= t; +- next += t; + + tmp = UDCCS0; +- t = scnprintf(next, size, ++ seq_printf(m, + "udccs0 %02X =%s%s%s%s%s%s%s%s\n", tmp, + (tmp & UDCCS0_SA) ? " sa" : "", + (tmp & UDCCS0_RNE) ? " rne" : "", +@@ -1055,28 +1045,22 @@ udc_proc_read(char *page, char **start, off_t off, int count, + (tmp & UDCCS0_FTF) ? " ftf" : "", + (tmp & UDCCS0_IPR) ? " ipr" : "", + (tmp & UDCCS0_OPR) ? " opr" : ""); +- size -= t; +- next += t; + + if (dev->has_cfr) { + tmp = UDCCFR; +- t = scnprintf(next, size, ++ seq_printf(m, + "udccfr %02X =%s%s\n", tmp, + (tmp & UDCCFR_AREN) ? " aren" : "", + (tmp & UDCCFR_ACM) ? " acm" : ""); +- size -= t; +- next += t; + } + + if (!is_vbus_present() || !dev->driver) + goto done; + +- t = scnprintf(next, size, "ep0 IN %lu/%lu, OUT %lu/%lu\nirqs %lu\n\n", ++ seq_printf(m, "ep0 IN %lu/%lu, OUT %lu/%lu\nirqs %lu\n\n", + dev->stats.write.bytes, dev->stats.write.ops, + dev->stats.read.bytes, dev->stats.read.ops, + dev->stats.irqs); +- size -= t; +- next += t; + + /* dump endpoint queues */ + for (i = 0; i < PXA_UDC_NUM_ENDPOINTS; i++) { +@@ -1090,55 +1074,57 @@ udc_proc_read(char *page, char **start, off_t off, int count, + if (!d) + continue; + tmp = *dev->ep [i].reg_udccs; +- t = scnprintf(next, size, ++ seq_printf(m, + "%s max %d %s udccs %02x irqs %lu\n", + ep->ep.name, le16_to_cpu (d->wMaxPacketSize), + "pio", tmp, ep->pio_irqs); + /* TODO translate all five groups of udccs bits! */ + + } else /* ep0 should only have one transfer queued */ +- t = scnprintf(next, size, "ep0 max 16 pio irqs %lu\n", ++ seq_printf(m, "ep0 max 16 pio irqs %lu\n", + ep->pio_irqs); +- if (t <= 0 || t > size) +- goto done; +- size -= t; +- next += t; + + if (list_empty(&ep->queue)) { +- t = scnprintf(next, size, "\t(nothing queued)\n"); +- if (t <= 0 || t > size) +- goto done; +- size -= t; +- next += t; ++ seq_printf(m, "\t(nothing queued)\n"); + continue; + } + list_for_each_entry(req, &ep->queue, queue) { +- t = scnprintf(next, size, ++ seq_printf(m, + "\treq %p len %d/%d buf %p\n", + &req->req, req->req.actual, + req->req.length, req->req.buf); +- if (t <= 0 || t > size) +- goto done; +- size -= t; +- next += t; + } + } + + done: + local_irq_restore(flags); +- *eof = 1; +- return count - size; ++ return 0; + } + +-#define create_proc_files() \ +- create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev) +-#define remove_proc_files() \ +- remove_proc_entry(proc_node_name, NULL) ++static int ++udc_debugfs_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, udc_seq_show, inode->i_private); ++} ++ ++static const struct file_operations debug_fops = { ++ .open = udc_debugfs_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++ .owner = THIS_MODULE, ++}; ++ ++#define create_debug_files(dev) \ ++ dev->debugfs_udc = debugfs_create_file(dev->gadget.name, S_IRUGO, \ ++ NULL, dev, &debug_fops) ++#define remove_debug_files(dev) \ ++ if (dev->debugfs_udc) debugfs_remove(dev->debugfs_udc) + + #else /* !CONFIG_USB_GADGET_DEBUG_FILES */ + +-#define create_proc_files() do {} while (0) +-#define remove_proc_files() do {} while (0) ++#define create_debug_files(dev) do {} while (0) ++#define remove_debug_files(dev) do {} while (0) + + #endif /* CONFIG_USB_GADGET_DEBUG_FILES */ + +@@ -2245,7 +2231,7 @@ lubbock_fail0: + goto err_vbus_irq; + } + } +- create_proc_files(); ++ create_debug_files(dev); + + return 0; + +@@ -2282,7 +2268,7 @@ static int __exit pxa2xx_udc_remove(struct platform_device *pdev) + return -EBUSY; + + udc_disable(dev); +- remove_proc_files(); ++ remove_debug_files(dev); + + if (dev->got_irq) { + free_irq(platform_get_irq(pdev, 0), dev); +diff --git a/drivers/usb/gadget/pxa2xx_udc.h b/drivers/usb/gadget/pxa2xx_udc.h +index 1db46d7..c08b1a2 100644 +--- a/drivers/usb/gadget/pxa2xx_udc.h ++++ b/drivers/usb/gadget/pxa2xx_udc.h +@@ -129,6 +129,10 @@ struct pxa2xx_udc { + struct pxa2xx_udc_mach_info *mach; + u64 dma_mask; + struct pxa2xx_ep ep [PXA_UDC_NUM_ENDPOINTS]; ++ ++#ifdef CONFIG_USB_GADGET_DEBUG_FS ++ struct dentry *debugfs_udc; ++#endif + }; + + /*-------------------------------------------------------------------------*/ +@@ -153,6 +157,8 @@ static struct pxa2xx_udc *the_controller; + + #ifdef DEBUG + ++static int is_vbus_present(void); ++ + static const char *state_name[] = { + "EP0_IDLE", + "EP0_IN_DATA_PHASE", "EP0_OUT_DATA_PHASE", +@@ -207,8 +213,7 @@ dump_state(struct pxa2xx_udc *dev) + unsigned i; + + DMSG("%s %s, uicr %02X.%02X, usir %02X.%02x, ufnr %02X.%02X\n", +- //is_usb_connected() ? "host " : "disconnected", +- "host ", ++ is_vbus_present() ? "host " : "disconnected", + state_name[dev->ep0state], + UICR1, UICR0, USIR1, USIR0, UFNRH, UFNRL); + dump_udccr("udccr"); +@@ -224,7 +230,7 @@ dump_state(struct pxa2xx_udc *dev) + } else + DMSG("ep0 driver '%s'\n", dev->driver->driver.name); + +- //if (!is_usb_connected()) +- // return; ++ if (!is_vbus_present()) ++ return; + + dump_udccs0 ("udccs0"); +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0018-Fix-the-pxa2xx_udc-to-balance-calls-to-clk_enable-cl.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0018-Fix-the-pxa2xx_udc-to-balance-calls-to-clk_enable-cl.patch new file mode 100644 index 0000000000..7bf4ad02d6 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0018-Fix-the-pxa2xx_udc-to-balance-calls-to-clk_enable-cl.patch @@ -0,0 +1,225 @@ +From b9a0fdbf333b461682d5da8b9aaa42f4de91ffcf Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Sun, 10 Feb 2008 03:29:17 +0300 +Subject: [PATCH 18/64] Fix the pxa2xx_udc to balance calls to clk_enable/clk_disable + +Signed-off-by: Dmitry Baryshkov dbaryshkov@gmail.com +--- + drivers/usb/gadget/pxa2xx_udc.c | 84 +++++++++++++++++++++++---------------- + drivers/usb/gadget/pxa2xx_udc.h | 6 ++- + 2 files changed, 54 insertions(+), 36 deletions(-) + +diff --git a/drivers/usb/gadget/pxa2xx_udc.c b/drivers/usb/gadget/pxa2xx_udc.c +index 2900556..8e32d07 100644 +--- a/drivers/usb/gadget/pxa2xx_udc.c ++++ b/drivers/usb/gadget/pxa2xx_udc.c +@@ -680,7 +680,7 @@ pxa2xx_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) + + /* kickstart this i/o queue? */ + if (list_empty(&ep->queue) && !ep->stopped) { +- if (ep->desc == 0 /* ep0 */) { ++ if (ep->desc == NULL /* ep0 */) { + unsigned length = _req->length; + + switch (dev->ep0state) { +@@ -734,7 +734,7 @@ pxa2xx_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) + } + + /* pio or dma irq handler advances the queue. */ +- if (likely (req != 0)) ++ if (likely (req != NULL)) + list_add_tail(&req->queue, &ep->queue); + local_irq_restore(flags); + +@@ -934,20 +934,35 @@ static void udc_disable(struct pxa2xx_udc *); + /* We disable the UDC -- and its 48 MHz clock -- whenever it's not + * in active use. + */ +-static int pullup(struct pxa2xx_udc *udc, int is_active) ++static int pullup(struct pxa2xx_udc *udc) + { +- is_active = is_active && udc->vbus && udc->pullup; ++ int is_active = udc->vbus && udc->pullup && ! udc->suspended; + DMSG("%s\n", is_active ? "active" : "inactive"); +- if (is_active) +- udc_enable(udc); +- else { +- if (udc->gadget.speed != USB_SPEED_UNKNOWN) { +- DMSG("disconnect %s\n", udc->driver +- ? udc->driver->driver.name +- : "(no driver)"); +- stop_activity(udc, udc->driver); ++ if (is_active) { ++ if (!udc->active) { ++ udc->active = 1; ++#ifdef CONFIG_ARCH_PXA ++ /* Enable clock for USB device */ ++ clk_enable(udc->clk); ++#endif ++ udc_enable(udc); + } +- udc_disable(udc); ++ } else { ++ if (udc->active) { ++ if (udc->gadget.speed != USB_SPEED_UNKNOWN) { ++ DMSG("disconnect %s\n", udc->driver ++ ? udc->driver->driver.name ++ : "(no driver)"); ++ stop_activity(udc, udc->driver); ++ } ++ udc_disable(udc); ++#ifdef CONFIG_ARCH_PXA ++ /* Disable clock for USB device */ ++ clk_disable(udc->clk); ++#endif ++ udc->active = 0; ++ } ++ + } + return 0; + } +@@ -958,9 +973,9 @@ static int pxa2xx_udc_vbus_session(struct usb_gadget *_gadget, int is_active) + struct pxa2xx_udc *udc; + + udc = container_of(_gadget, struct pxa2xx_udc, gadget); +- udc->vbus = is_active = (is_active != 0); ++ udc->vbus = (is_active != 0); + DMSG("vbus %s\n", is_active ? "supplied" : "inactive"); +- pullup(udc, is_active); ++ pullup(udc); + return 0; + } + +@@ -975,9 +990,8 @@ static int pxa2xx_udc_pullup(struct usb_gadget *_gadget, int is_active) + if (!udc->mach->gpio_pullup && !udc->mach->udc_command) + return -EOPNOTSUPP; + +- is_active = (is_active != 0); +- udc->pullup = is_active; +- pullup(udc, is_active); ++ udc->pullup = (is_active != 0); ++ pullup(udc); + return 0; + } + +@@ -998,7 +1012,7 @@ static const struct usb_gadget_ops pxa2xx_udc_ops = { + + static struct pxa2xx_udc memory; + static int +-udc_seq_show(struct seq_file *m, void *d) ++udc_seq_show(struct seq_file *m, void *_d) + { + struct pxa2xx_udc *dev = m->private; + unsigned long flags; +@@ -1145,11 +1159,6 @@ static void udc_disable(struct pxa2xx_udc *dev) + + udc_clear_mask_UDCCR(UDCCR_UDE); + +-#ifdef CONFIG_ARCH_PXA +- /* Disable clock for USB device */ +- clk_disable(dev->clk); +-#endif +- + ep0_idle (dev); + dev->gadget.speed = USB_SPEED_UNKNOWN; + } +@@ -1190,11 +1199,6 @@ static void udc_enable (struct pxa2xx_udc *dev) + { + udc_clear_mask_UDCCR(UDCCR_UDE); + +-#ifdef CONFIG_ARCH_PXA +- /* Enable clock for USB device */ +- clk_enable(dev->clk); +-#endif +- + /* try to clear these bits before we enable the udc */ + udc_ack_int_UDCCR(UDCCR_SUSIR|/*UDCCR_RSTIR|*/UDCCR_RESIR); + +@@ -1285,7 +1289,7 @@ fail: + * for set_configuration as well as eventual disconnect. + */ + DMSG("registered gadget driver '%s'\n", driver->driver.name); +- pullup(dev, 1); ++ pullup(dev); + dump_state(dev); + return 0; + } +@@ -1328,7 +1332,8 @@ int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) + return -EINVAL; + + local_irq_disable(); +- pullup(dev, 0); ++ dev->pullup = 0; ++ pullup(dev); + stop_activity(dev, driver); + local_irq_enable(); + +@@ -2267,7 +2272,9 @@ static int __exit pxa2xx_udc_remove(struct platform_device *pdev) + if (dev->driver) + return -EBUSY; + +- udc_disable(dev); ++ dev->pullup = 0; ++ pullup(dev); ++ + remove_debug_files(dev); + + if (dev->got_irq) { +@@ -2315,10 +2322,15 @@ static int __exit pxa2xx_udc_remove(struct platform_device *pdev) + static int pxa2xx_udc_suspend(struct platform_device *dev, pm_message_t state) + { + struct pxa2xx_udc *udc = platform_get_drvdata(dev); ++ unsigned long flags; + + if (!udc->mach->gpio_pullup && !udc->mach->udc_command) + WARN("USB host won't detect disconnect!\n"); +- pullup(udc, 0); ++ udc->suspended = 1; ++ ++ local_irq_save(flags); ++ pullup(udc); ++ local_irq_restore(flags); + + return 0; + } +@@ -2326,8 +2338,12 @@ static int pxa2xx_udc_suspend(struct platform_device *dev, pm_message_t state) + static int pxa2xx_udc_resume(struct platform_device *dev) + { + struct pxa2xx_udc *udc = platform_get_drvdata(dev); ++ unsigned long flags; + +- pullup(udc, 1); ++ udc->suspended = 0; ++ local_irq_save(flags); ++ pullup(udc); ++ local_irq_restore(flags); + + return 0; + } +diff --git a/drivers/usb/gadget/pxa2xx_udc.h b/drivers/usb/gadget/pxa2xx_udc.h +index c08b1a2..93586b2 100644 +--- a/drivers/usb/gadget/pxa2xx_udc.h ++++ b/drivers/usb/gadget/pxa2xx_udc.h +@@ -119,7 +119,9 @@ struct pxa2xx_udc { + has_cfr : 1, + req_pending : 1, + req_std : 1, +- req_config : 1; ++ req_config : 1, ++ suspended : 1, ++ active : 1; + + #define start_watchdog(dev) mod_timer(&dev->timer, jiffies + (HZ/200)) + struct timer_list timer; +@@ -239,7 +241,7 @@ dump_state(struct pxa2xx_udc *dev) + dev->stats.read.bytes, dev->stats.read.ops); + + for (i = 1; i < PXA_UDC_NUM_ENDPOINTS; i++) { +- if (dev->ep [i].desc == 0) ++ if (dev->ep [i].desc == NULL) + continue; + DMSG ("udccs%d = %02x\n", i, *dev->ep->reg_udccs); + } +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0019-pxa-remove-periodic-mode-emulation-support.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0019-pxa-remove-periodic-mode-emulation-support.patch new file mode 100644 index 0000000000..4b4107d655 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0019-pxa-remove-periodic-mode-emulation-support.patch @@ -0,0 +1,128 @@ +From bda65817167cce5294e1d84670f36815262ed550 Mon Sep 17 00:00:00 2001 +From: Russell King <rmk@dyn-67.arm.linux.org.uk> +Date: Sun, 3 Feb 2008 21:58:12 +0300 +Subject: [PATCH 19/64] pxa: remove periodic mode emulation support + +Apparantly, the generic time subsystem can accurately emulate periodic +mode via the one-shot support code, so we don't need our own periodic +emulation code anymore. Just ensure that we build support for one shot +into the generic time subsystem. + +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + arch/arm/Kconfig | 1 + + arch/arm/mach-pxa/time.c | 61 ++++++---------------------------------------- + 2 files changed, 9 insertions(+), 53 deletions(-) + +diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig +index a04f507..1be7182 100644 +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -345,6 +345,7 @@ config ARCH_PXA + select GENERIC_GPIO + select GENERIC_TIME + select GENERIC_CLOCKEVENTS ++ select TICK_ONESHOT + help + Support for Intel/Marvell's PXA2xx/PXA3xx processor line. + +diff --git a/arch/arm/mach-pxa/time.c b/arch/arm/mach-pxa/time.c +index fbfa192..3c4abbf 100644 +--- a/arch/arm/mach-pxa/time.c ++++ b/arch/arm/mach-pxa/time.c +@@ -59,55 +59,17 @@ unsigned long long sched_clock(void) + } + + ++#define MIN_OSCR_DELTA 16 ++ + static irqreturn_t + pxa_ost0_interrupt(int irq, void *dev_id) + { +- int next_match; + struct clock_event_device *c = dev_id; + +- if (c->mode == CLOCK_EVT_MODE_ONESHOT) { +- /* Disarm the compare/match, signal the event. */ +- OIER &= ~OIER_E0; +- OSSR = OSSR_M0; +- c->event_handler(c); +- } else if (c->mode == CLOCK_EVT_MODE_PERIODIC) { +- /* Call the event handler as many times as necessary +- * to recover missed events, if any (if we update +- * OSMR0 and OSCR0 is still ahead of us, we've missed +- * the event). As we're dealing with that, re-arm the +- * compare/match for the next event. +- * +- * HACK ALERT: +- * +- * There's a latency between the instruction that +- * writes to OSMR0 and the actual commit to the +- * physical hardware, because the CPU doesn't (have +- * to) run at bus speed, there's a write buffer +- * between the CPU and the bus, etc. etc. So if the +- * target OSCR0 is "very close", to the OSMR0 load +- * value, the update to OSMR0 might not get to the +- * hardware in time and we'll miss that interrupt. +- * +- * To be safe, if the new OSMR0 is "very close" to the +- * target OSCR0 value, we call the event_handler as +- * though the event actually happened. According to +- * Nico's comment in the previous version of this +- * code, experience has shown that 6 OSCR ticks is +- * "very close" but he went with 8. We will use 16, +- * based on the results of testing on PXA270. +- * +- * To be doubly sure, we also tell clkevt via +- * clockevents_register_device() not to ask for +- * anything that might put us "very close". +- */ +-#define MIN_OSCR_DELTA 16 +- do { +- OSSR = OSSR_M0; +- next_match = (OSMR0 += LATCH); +- c->event_handler(c); +- } while (((signed long)(next_match - OSCR) <= MIN_OSCR_DELTA) +- && (c->mode == CLOCK_EVT_MODE_PERIODIC)); +- } ++ /* Disarm the compare/match, signal the event. */ ++ OIER &= ~OIER_E0; ++ OSSR = OSSR_M0; ++ c->event_handler(c); + + return IRQ_HANDLED; + } +@@ -133,14 +95,6 @@ pxa_osmr0_set_mode(enum clock_event_mode mode, struct clock_event_device *dev) + unsigned long irqflags; + + switch (mode) { +- case CLOCK_EVT_MODE_PERIODIC: +- raw_local_irq_save(irqflags); +- OSSR = OSSR_M0; +- OIER |= OIER_E0; +- OSMR0 = OSCR + LATCH; +- raw_local_irq_restore(irqflags); +- break; +- + case CLOCK_EVT_MODE_ONESHOT: + raw_local_irq_save(irqflags); + OIER &= ~OIER_E0; +@@ -158,13 +112,14 @@ pxa_osmr0_set_mode(enum clock_event_mode mode, struct clock_event_device *dev) + break; + + case CLOCK_EVT_MODE_RESUME: ++ case CLOCK_EVT_MODE_PERIODIC: + break; + } + } + + static struct clock_event_device ckevt_pxa_osmr0 = { + .name = "osmr0", +- .features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT, ++ .features = CLOCK_EVT_FEAT_ONESHOT, + .shift = 32, + .rating = 200, + .cpumask = CPU_MASK_CPU0, +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0020-Provide-dew-device-clock-backports-from-2.6.24-git.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0020-Provide-dew-device-clock-backports-from-2.6.24-git.patch new file mode 100644 index 0000000000..0a42bc5855 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0020-Provide-dew-device-clock-backports-from-2.6.24-git.patch @@ -0,0 +1,257 @@ +From ee8ca5742e0000dd2369ef4d328c2c1117276a3b Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Mon, 4 Feb 2008 02:56:28 +0300 +Subject: [PATCH 20/64] Provide dew device/clock backports from 2.6.24-git + +Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> +--- + arch/arm/Kconfig | 1 + + arch/arm/mach-pxa/devices.h | 12 ++++++++++++ + arch/arm/mach-pxa/pxa25x.c | 18 ++++++++++++------ + arch/arm/mach-pxa/pxa27x.c | 22 ++++++++++++++++------ + arch/arm/mach-pxa/pxa3xx.c | 30 ++++++++++++++++++++++++++++++ + kernel/Makefile | 1 + + 6 files changed, 72 insertions(+), 12 deletions(-) + +diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig +index 1be7182..10faf9c 100644 +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -367,6 +367,7 @@ config ARCH_SA1100 + select ARCH_DISCONTIGMEM_ENABLE + select ARCH_MTD_XIP + select GENERIC_GPIO ++ select GENERIC_TIME + help + Support for StrongARM 11x0 based boards. + +diff --git a/arch/arm/mach-pxa/devices.h b/arch/arm/mach-pxa/devices.h +index 94c8d5c..96c7c89 100644 +--- a/arch/arm/mach-pxa/devices.h ++++ b/arch/arm/mach-pxa/devices.h +@@ -1,4 +1,6 @@ + extern struct platform_device pxa_device_mci; ++extern struct platform_device pxa3xx_device_mci2; ++extern struct platform_device pxa3xx_device_mci3; + extern struct platform_device pxa_device_udc; + extern struct platform_device pxa_device_fb; + extern struct platform_device pxa_device_ffuart; +@@ -12,3 +14,13 @@ extern struct platform_device pxa_device_rtc; + + extern struct platform_device pxa27x_device_i2c_power; + extern struct platform_device pxa27x_device_ohci; ++ ++extern struct platform_device pxa25x_device_ssp; ++extern struct platform_device pxa25x_device_nssp; ++extern struct platform_device pxa25x_device_assp; ++extern struct platform_device pxa27x_device_ssp1; ++extern struct platform_device pxa27x_device_ssp2; ++extern struct platform_device pxa27x_device_ssp3; ++extern struct platform_device pxa3xx_device_ssp4; ++ ++void __init pxa_register_device(struct platform_device *dev, void *data); +diff --git a/arch/arm/mach-pxa/pxa25x.c b/arch/arm/mach-pxa/pxa25x.c +index 006a6e0..5988d99 100644 +--- a/arch/arm/mach-pxa/pxa25x.c ++++ b/arch/arm/mach-pxa/pxa25x.c +@@ -123,12 +123,15 @@ static struct clk pxa25x_clks[] = { + INIT_CKEN("UDCCLK", USB, 47923000, 5, &pxa_device_udc.dev), + INIT_CKEN("MMCCLK", MMC, 19169000, 0, &pxa_device_mci.dev), + INIT_CKEN("I2CCLK", I2C, 31949000, 0, &pxa_device_i2c.dev), ++ ++ INIT_CKEN("SSPCLK", SSP, 3686400, 0, &pxa25x_device_ssp.dev), ++ INIT_CKEN("SSPCLK", NSSP, 3686400, 0, &pxa25x_device_nssp.dev), ++ INIT_CKEN("SSPCLK", ASSP, 3686400, 0, &pxa25x_device_assp.dev), ++ + /* + INIT_CKEN("PWMCLK", PWM0, 3686400, 0, NULL), + INIT_CKEN("PWMCLK", PWM0, 3686400, 0, NULL), +- INIT_CKEN("SSPCLK", SSP, 3686400, 0, NULL), + INIT_CKEN("I2SCLK", I2S, 14745600, 0, NULL), +- INIT_CKEN("NSSPCLK", NSSP, 3686400, 0, NULL), + */ + INIT_CKEN("FICPCLK", FICP, 47923000, 0, NULL), + }; +@@ -216,8 +219,6 @@ static void pxa25x_cpu_pm_restore(unsigned long *sleep_save) + + static void pxa25x_cpu_pm_enter(suspend_state_t state) + { +- CKEN = 0; +- + switch (state) { + case PM_SUSPEND_MEM: + /* set resume return address */ +@@ -239,6 +240,8 @@ static void __init pxa25x_init_pm(void) + { + pxa_cpu_pm_fns = &pxa25x_cpu_pm_fns; + } ++#else ++static inline void pxa25x_init_pm(void) {} + #endif + + /* PXA25x: supports wakeup from GPIO0..GPIO15 and RTC alarm +@@ -300,6 +303,9 @@ static struct platform_device *pxa25x_devices[] __initdata = { + &pxa_device_i2s, + &pxa_device_ficp, + &pxa_device_rtc, ++ &pxa25x_device_ssp, ++ &pxa25x_device_nssp, ++ &pxa25x_device_assp, + }; + + static int __init pxa25x_init(void) +@@ -315,9 +321,9 @@ static int __init pxa25x_init(void) + + if ((ret = pxa_init_dma(16))) + return ret; +-#ifdef CONFIG_PM ++ + pxa25x_init_pm(); +-#endif ++ + ret = platform_add_devices(pxa25x_devices, + ARRAY_SIZE(pxa25x_devices)); + } +diff --git a/arch/arm/mach-pxa/pxa27x.c b/arch/arm/mach-pxa/pxa27x.c +index 8e126e6..30ca4fd 100644 +--- a/arch/arm/mach-pxa/pxa27x.c ++++ b/arch/arm/mach-pxa/pxa27x.c +@@ -150,11 +150,12 @@ static struct clk pxa27x_clks[] = { + INIT_CKEN("I2CCLK", PWRI2C, 13000000, 0, &pxa27x_device_i2c_power.dev), + INIT_CKEN("KBDCLK", KEYPAD, 32768, 0, NULL), + ++ INIT_CKEN("SSPCLK", SSP1, 13000000, 0, &pxa27x_device_ssp1.dev), ++ INIT_CKEN("SSPCLK", SSP2, 13000000, 0, &pxa27x_device_ssp2.dev), ++ INIT_CKEN("SSPCLK", SSP3, 13000000, 0, &pxa27x_device_ssp3.dev), ++ + /* + INIT_CKEN("PWMCLK", PWM0, 13000000, 0, NULL), +- INIT_CKEN("SSPCLK", SSP1, 13000000, 0, NULL), +- INIT_CKEN("SSPCLK", SSP2, 13000000, 0, NULL), +- INIT_CKEN("SSPCLK", SSP3, 13000000, 0, NULL), + INIT_CKEN("MSLCLK", MSL, 48000000, 0, NULL), + INIT_CKEN("USIMCLK", USIM, 48000000, 0, NULL), + INIT_CKEN("MSTKCLK", MEMSTK, 19500000, 0, NULL), +@@ -304,6 +305,8 @@ static void __init pxa27x_init_pm(void) + { + pxa_cpu_pm_fns = &pxa27x_cpu_pm_fns; + } ++#else ++static inline void pxa27x_init_pm(void) {} + #endif + + /* PXA27x: Various gpios can issue wakeup events. This logic only +@@ -423,6 +426,11 @@ struct platform_device pxa27x_device_i2c_power = { + .num_resources = ARRAY_SIZE(i2c_power_resources), + }; + ++void __init pxa_set_i2c_power_info(struct i2c_pxa_platform_data *info) ++{ ++ pxa27x_device_i2c_power.dev.platform_data = info; ++} ++ + static struct platform_device *devices[] __initdata = { + &pxa_device_mci, + &pxa_device_udc, +@@ -435,7 +443,9 @@ static struct platform_device *devices[] __initdata = { + &pxa_device_ficp, + &pxa_device_rtc, + &pxa27x_device_i2c_power, +- &pxa27x_device_ohci, ++ &pxa27x_device_ssp1, ++ &pxa27x_device_ssp2, ++ &pxa27x_device_ssp3, + }; + + static int __init pxa27x_init(void) +@@ -446,9 +456,9 @@ static int __init pxa27x_init(void) + + if ((ret = pxa_init_dma(32))) + return ret; +-#ifdef CONFIG_PM ++ + pxa27x_init_pm(); +-#endif ++ + ret = platform_add_devices(devices, ARRAY_SIZE(devices)); + } + return ret; +diff --git a/arch/arm/mach-pxa/pxa3xx.c b/arch/arm/mach-pxa/pxa3xx.c +index 61d9c9d..ccab9da 100644 +--- a/arch/arm/mach-pxa/pxa3xx.c ++++ b/arch/arm/mach-pxa/pxa3xx.c +@@ -189,8 +189,31 @@ static struct clk pxa3xx_clks[] = { + + PXA3xx_CKEN("I2CCLK", I2C, 32842000, 0, &pxa_device_i2c.dev), + PXA3xx_CKEN("UDCCLK", UDC, 48000000, 5, &pxa_device_udc.dev), ++ PXA3xx_CKEN("USBCLK", USBH, 48000000, 0, &pxa27x_device_ohci.dev), ++ ++ PXA3xx_CKEN("SSPCLK", SSP1, 13000000, 0, &pxa27x_device_ssp1.dev), ++ PXA3xx_CKEN("SSPCLK", SSP2, 13000000, 0, &pxa27x_device_ssp2.dev), ++ PXA3xx_CKEN("SSPCLK", SSP3, 13000000, 0, &pxa27x_device_ssp3.dev), ++ PXA3xx_CKEN("SSPCLK", SSP4, 13000000, 0, &pxa3xx_device_ssp4.dev), ++ ++ PXA3xx_CKEN("MMCCLK", MMC1, 19500000, 0, &pxa_device_mci.dev), ++ PXA3xx_CKEN("MMCCLK", MMC2, 19500000, 0, &pxa3xx_device_mci2.dev), ++ PXA3xx_CKEN("MMCCLK", MMC3, 19500000, 0, &pxa3xx_device_mci3.dev), + }; + ++#ifdef CONFIG_PM ++#define SLEEP_SAVE_SIZE 4 ++ ++#define ISRAM_START 0x5c000000 ++#define ISRAM_SIZE SZ_256K ++ ++static inline void pxa3xx_init_pm(void) {} ++static inline void pxa3xx_init_irq_pm(void) {} ++#else ++static inline void pxa3xx_init_pm(void) {} ++static inline void pxa3xx_init_irq_pm(void) {} ++#endif ++ + void __init pxa3xx_init_irq(void) + { + /* enable CP6 access */ +@@ -202,6 +225,7 @@ void __init pxa3xx_init_irq(void) + pxa_init_irq_low(); + pxa_init_irq_high(); + pxa_init_irq_gpio(128); ++ pxa3xx_init_irq_pm(); + } + + /* +@@ -219,6 +243,10 @@ static struct platform_device *devices[] __initdata = { + &pxa_device_i2s, + &pxa_device_ficp, + &pxa_device_rtc, ++ &pxa27x_device_ssp1, ++ &pxa27x_device_ssp2, ++ &pxa27x_device_ssp3, ++ &pxa3xx_device_ssp4, + }; + + static int __init pxa3xx_init(void) +@@ -231,6 +259,8 @@ static int __init pxa3xx_init(void) + if ((ret = pxa_init_dma(32))) + return ret; + ++ pxa3xx_init_pm(); ++ + return platform_add_devices(devices, ARRAY_SIZE(devices)); + } + return 0; +diff --git a/kernel/Makefile b/kernel/Makefile +index dfa9695..6d9a87c 100644 +--- a/kernel/Makefile ++++ b/kernel/Makefile +@@ -57,6 +57,7 @@ obj-$(CONFIG_SYSCTL) += utsname_sysctl.o + obj-$(CONFIG_TASK_DELAY_ACCT) += delayacct.o + obj-$(CONFIG_TASKSTATS) += taskstats.o tsacct.o + obj-$(CONFIG_MARKERS) += marker.o ++obj-$(CONFIG_LATENCYTOP) += latencytop.o + + ifneq ($(CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER),y) + # According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0021-Add-an-empty-drivers-gpio-directory-for-gpiolib-infr.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0021-Add-an-empty-drivers-gpio-directory-for-gpiolib-infr.patch new file mode 100644 index 0000000000..3f8512128a --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0021-Add-an-empty-drivers-gpio-directory-for-gpiolib-infr.patch @@ -0,0 +1,121 @@ +From b77665c545bc260d2b93add129413e4a724d7e6e Mon Sep 17 00:00:00 2001 +From: David Brownell <dbrownell@users.sourceforge.net> +Date: Fri, 18 Jan 2008 00:35:00 +0300 +Subject: [PATCH 21/64] Add an empty drivers/gpio directory for gpiolib infrastructure and GPIO + expanders. It will be populated by later patches. + +This won't be the only place to hold such gpio_chip code. Many external chips +add a few GPIOs as secondary functionality (such as MFD drivers) and platform +code frequently needs to closely integrate GPIO and IRQ support. + +This is placed *early* in the build/link sequence since it's common for other +drivers to depend on GPIOs to do their work, so they must be initialized early +in the device_initcall() sequence. + +Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> +Acked-by: Jean Delvare <khali@linux-fr.org> +Cc: Eric Miao <eric.miao@marvell.com> +Cc: Sam Ravnborg <sam@ravnborg.org> +Cc: Haavard Skinnemoen <hskinnemoen@atmel.com> +Cc: Philipp Zabel <philipp.zabel@gmail.com> +Cc: Russell King <rmk@arm.linux.org.uk> +Cc: Ben Gardner <bgardner@wabtec.com> +Signed-off-by: Andrew Morton <akpm@linux-foundation.org> +--- + arch/arm/Kconfig | 2 ++ + drivers/Kconfig | 2 ++ + drivers/Makefile | 1 + + drivers/gpio/Kconfig | 32 ++++++++++++++++++++++++++++++++ + drivers/gpio/Makefile | 3 +++ + 5 files changed, 40 insertions(+), 0 deletions(-) + create mode 100644 drivers/gpio/Kconfig + create mode 100644 drivers/gpio/Makefile + +diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig +index 10faf9c..06ca241 100644 +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -1042,6 +1042,8 @@ source "drivers/i2c/Kconfig" + + source "drivers/spi/Kconfig" + ++source "drivers/gpio/Kconfig" ++ + source "drivers/w1/Kconfig" + + source "drivers/power/Kconfig" +diff --git a/drivers/Kconfig b/drivers/Kconfig +index f4076d9..90e295a 100644 +--- a/drivers/Kconfig ++++ b/drivers/Kconfig +@@ -52,6 +52,8 @@ source "drivers/i2c/Kconfig" + + source "drivers/spi/Kconfig" + ++source "drivers/gpio/Kconfig" ++ + source "drivers/w1/Kconfig" + + source "drivers/power/Kconfig" +diff --git a/drivers/Makefile b/drivers/Makefile +index 8cb37e3..8e5101f 100644 +--- a/drivers/Makefile ++++ b/drivers/Makefile +@@ -5,6 +5,7 @@ + # Rewritten to use lists instead of if-statements. + # + ++obj-$(CONFIG_HAVE_GPIO_LIB) += gpio/ + obj-$(CONFIG_PCI) += pci/ + obj-$(CONFIG_PARISC) += parisc/ + obj-$(CONFIG_RAPIDIO) += rapidio/ +diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig +new file mode 100644 +index 0000000..560687c +--- /dev/null ++++ b/drivers/gpio/Kconfig +@@ -0,0 +1,32 @@ ++# ++# GPIO infrastructure and expanders ++# ++ ++config HAVE_GPIO_LIB ++ bool ++ help ++ Platforms select gpiolib if they use this infrastructure ++ for all their GPIOs, usually starting with ones integrated ++ into SOC processors. ++ ++menu "GPIO Support" ++ depends on HAVE_GPIO_LIB ++ ++config DEBUG_GPIO ++ bool "Debug GPIO calls" ++ depends on DEBUG_KERNEL ++ help ++ Say Y here to add some extra checks and diagnostics to GPIO calls. ++ The checks help ensure that GPIOs have been properly initialized ++ before they are used and that sleeping calls aren not made from ++ nonsleeping contexts. They can make bitbanged serial protocols ++ slower. The diagnostics help catch the type of setup errors ++ that are most common when setting up new platforms or boards. ++ ++# put expanders in the right section, in alphabetical order ++ ++comment "I2C GPIO expanders:" ++ ++comment "SPI GPIO expanders:" ++ ++endmenu +diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile +new file mode 100644 +index 0000000..cdbba6b +--- /dev/null ++++ b/drivers/gpio/Makefile +@@ -0,0 +1,3 @@ ++# gpio support: dedicated expander chips, etc ++ ++ccflags-$(CONFIG_DEBUG_GPIO) += -DDEBUG +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0022-Provide-new-implementation-infrastructure-that-platf.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0022-Provide-new-implementation-infrastructure-that-platf.patch new file mode 100644 index 0000000000..f39fedbbaa --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0022-Provide-new-implementation-infrastructure-that-platf.patch @@ -0,0 +1,746 @@ +From 3a0251c01446f3a6763e4406ca5495102db63aa4 Mon Sep 17 00:00:00 2001 +From: David Brownell <dbrownell@users.sourceforge.net> +Date: Fri, 18 Jan 2008 00:35:20 +0300 +Subject: [PATCH 22/64] Provide new implementation infrastructure that platforms may choose to use + when implementing the GPIO programming interface. Platforms can update their + GPIO support to use this. In many cases the incremental cost to access a + non-inlined GPIO should be less than a dozen instructions, with the memory + cost being about a page (total) of extra data and code. The upside is: + + * Providing two features which were "want to have (but OK to defer)" when + GPIO interfaces were first discussed in November 2006: + + - A "struct gpio_chip" to plug in GPIOs that aren't directly supported + by SOC platforms, but come from FPGAs or other multifunction devices + using conventional device registers (like UCB-1x00 or SM501 GPIOs, + and southbridges in PCs with more open specs than usual). + + - Full support for message-based GPIO expanders, where registers are + accessed through sleeping I/O calls. Previous support for these + "cansleep" calls was just stubs. (One example: the widely used + pcf8574 I2C chips, with 8 GPIOs each.) + + * Including a non-stub implementation of the gpio_{request,free}() calls, + making those calls much more useful. The diagnostic labels are also + recorded given DEBUG_FS, so /sys/kernel/debug/gpio can show a snapshot + of all GPIOs known to this infrastructure. + +The driver programming interfaces introduced in 2.6.21 do not change at all; +this infrastructure is entirely below those covers. + +Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> +Cc: Sam Ravnborg <sam@ravnborg.org> +Cc: Jean Delvare <khali@linux-fr.org> +Cc: Eric Miao <eric.miao@marvell.com> +Cc: Haavard Skinnemoen <hskinnemoen@atmel.com> +Cc: Philipp Zabel <philipp.zabel@gmail.com> +Cc: Russell King <rmk@arm.linux.org.uk> +Cc: Ben Gardner <bgardner@wabtec.com> +Signed-off-by: Andrew Morton <akpm@linux-foundation.org> +--- + drivers/gpio/Makefile | 2 + + drivers/gpio/gpiolib.c | 567 ++++++++++++++++++++++++++++++++++++++++++++ + include/asm-generic/gpio.h | 98 ++++++++ + 3 files changed, 667 insertions(+), 0 deletions(-) + create mode 100644 drivers/gpio/gpiolib.c + +diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile +index cdbba6b..2db28ce 100644 +--- a/drivers/gpio/Makefile ++++ b/drivers/gpio/Makefile +@@ -1,3 +1,5 @@ + # gpio support: dedicated expander chips, etc + + ccflags-$(CONFIG_DEBUG_GPIO) += -DDEBUG ++ ++obj-$(CONFIG_HAVE_GPIO_LIB) += gpiolib.o +diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c +new file mode 100644 +index 0000000..d8db2f8 +--- /dev/null ++++ b/drivers/gpio/gpiolib.c +@@ -0,0 +1,567 @@ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/irq.h> ++#include <linux/spinlock.h> ++ ++#include <asm/gpio.h> ++ ++ ++/* Optional implementation infrastructure for GPIO interfaces. ++ * ++ * Platforms may want to use this if they tend to use very many GPIOs ++ * that aren't part of a System-On-Chip core; or across I2C/SPI/etc. ++ * ++ * When kernel footprint or instruction count is an issue, simpler ++ * implementations may be preferred. The GPIO programming interface ++ * allows for inlining speed-critical get/set operations for common ++ * cases, so that access to SOC-integrated GPIOs can sometimes cost ++ * only an instruction or two per bit. ++ */ ++ ++ ++/* When debugging, extend minimal trust to callers and platform code. ++ * Also emit diagnostic messages that may help initial bringup, when ++ * board setup or driver bugs are most common. ++ * ++ * Otherwise, minimize overhead in what may be bitbanging codepaths. ++ */ ++#ifdef DEBUG ++#define extra_checks 1 ++#else ++#define extra_checks 0 ++#endif ++ ++/* gpio_lock prevents conflicts during gpio_desc[] table updates. ++ * While any GPIO is requested, its gpio_chip is not removable; ++ * each GPIO's "requested" flag serves as a lock and refcount. ++ */ ++static DEFINE_SPINLOCK(gpio_lock); ++ ++struct gpio_desc { ++ struct gpio_chip *chip; ++ unsigned long flags; ++/* flag symbols are bit numbers */ ++#define FLAG_REQUESTED 0 ++#define FLAG_IS_OUT 1 ++ ++#ifdef CONFIG_DEBUG_FS ++ const char *label; ++#endif ++}; ++static struct gpio_desc gpio_desc[ARCH_NR_GPIOS]; ++ ++static inline void desc_set_label(struct gpio_desc *d, const char *label) ++{ ++#ifdef CONFIG_DEBUG_FS ++ d->label = label; ++#endif ++} ++ ++/* Warn when drivers omit gpio_request() calls -- legal but ill-advised ++ * when setting direction, and otherwise illegal. Until board setup code ++ * and drivers use explicit requests everywhere (which won't happen when ++ * those calls have no teeth) we can't avoid autorequesting. This nag ++ * message should motivate switching to explicit requests... ++ */ ++static void gpio_ensure_requested(struct gpio_desc *desc) ++{ ++ if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0) { ++ pr_warning("GPIO-%d autorequested\n", (int)(desc - gpio_desc)); ++ desc_set_label(desc, "[auto]"); ++ } ++} ++ ++/* caller holds gpio_lock *OR* gpio is marked as requested */ ++static inline struct gpio_chip *gpio_to_chip(unsigned gpio) ++{ ++ return gpio_desc[gpio].chip; ++} ++ ++/** ++ * gpiochip_add() - register a gpio_chip ++ * @chip: the chip to register, with chip->base initialized ++ * Context: potentially before irqs or kmalloc will work ++ * ++ * Returns a negative errno if the chip can't be registered, such as ++ * because the chip->base is invalid or already associated with a ++ * different chip. Otherwise it returns zero as a success code. ++ */ ++int gpiochip_add(struct gpio_chip *chip) ++{ ++ unsigned long flags; ++ int status = 0; ++ unsigned id; ++ ++ /* NOTE chip->base negative is reserved to mean a request for ++ * dynamic allocation. We don't currently support that. ++ */ ++ ++ if (chip->base < 0 || (chip->base + chip->ngpio) >= ARCH_NR_GPIOS) { ++ status = -EINVAL; ++ goto fail; ++ } ++ ++ spin_lock_irqsave(&gpio_lock, flags); ++ ++ /* these GPIO numbers must not be managed by another gpio_chip */ ++ for (id = chip->base; id < chip->base + chip->ngpio; id++) { ++ if (gpio_desc[id].chip != NULL) { ++ status = -EBUSY; ++ break; ++ } ++ } ++ if (status == 0) { ++ for (id = chip->base; id < chip->base + chip->ngpio; id++) { ++ gpio_desc[id].chip = chip; ++ gpio_desc[id].flags = 0; ++ } ++ } ++ ++ spin_unlock_irqrestore(&gpio_lock, flags); ++fail: ++ /* failures here can mean systems won't boot... */ ++ if (status) ++ pr_err("gpiochip_add: gpios %d..%d (%s) not registered\n", ++ chip->base, chip->base + chip->ngpio, ++ chip->label ? : "generic"); ++ return status; ++} ++EXPORT_SYMBOL_GPL(gpiochip_add); ++ ++/** ++ * gpiochip_remove() - unregister a gpio_chip ++ * @chip: the chip to unregister ++ * ++ * A gpio_chip with any GPIOs still requested may not be removed. ++ */ ++int gpiochip_remove(struct gpio_chip *chip) ++{ ++ unsigned long flags; ++ int status = 0; ++ unsigned id; ++ ++ spin_lock_irqsave(&gpio_lock, flags); ++ ++ for (id = chip->base; id < chip->base + chip->ngpio; id++) { ++ if (test_bit(FLAG_REQUESTED, &gpio_desc[id].flags)) { ++ status = -EBUSY; ++ break; ++ } ++ } ++ if (status == 0) { ++ for (id = chip->base; id < chip->base + chip->ngpio; id++) ++ gpio_desc[id].chip = NULL; ++ } ++ ++ spin_unlock_irqrestore(&gpio_lock, flags); ++ return status; ++} ++EXPORT_SYMBOL_GPL(gpiochip_remove); ++ ++ ++/* These "optional" allocation calls help prevent drivers from stomping ++ * on each other, and help provide better diagnostics in debugfs. ++ * They're called even less than the "set direction" calls. ++ */ ++int gpio_request(unsigned gpio, const char *label) ++{ ++ struct gpio_desc *desc; ++ int status = -EINVAL; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&gpio_lock, flags); ++ ++ if (gpio >= ARCH_NR_GPIOS) ++ goto done; ++ desc = &gpio_desc[gpio]; ++ if (desc->chip == NULL) ++ goto done; ++ ++ /* NOTE: gpio_request() can be called in early boot, ++ * before IRQs are enabled. ++ */ ++ ++ if (test_and_set_bit(FLAG_REQUESTED, &desc->flags) == 0) { ++ desc_set_label(desc, label ? : "?"); ++ status = 0; ++ } else ++ status = -EBUSY; ++ ++done: ++ if (status) ++ pr_debug("gpio_request: gpio-%d (%s) status %d\n", ++ gpio, label ? : "?", status); ++ spin_unlock_irqrestore(&gpio_lock, flags); ++ return status; ++} ++EXPORT_SYMBOL_GPL(gpio_request); ++ ++void gpio_free(unsigned gpio) ++{ ++ unsigned long flags; ++ struct gpio_desc *desc; ++ ++ if (gpio >= ARCH_NR_GPIOS) { ++ WARN_ON(extra_checks); ++ return; ++ } ++ ++ spin_lock_irqsave(&gpio_lock, flags); ++ ++ desc = &gpio_desc[gpio]; ++ if (desc->chip && test_and_clear_bit(FLAG_REQUESTED, &desc->flags)) ++ desc_set_label(desc, NULL); ++ else ++ WARN_ON(extra_checks); ++ ++ spin_unlock_irqrestore(&gpio_lock, flags); ++} ++EXPORT_SYMBOL_GPL(gpio_free); ++ ++ ++/** ++ * gpiochip_is_requested - return string iff signal was requested ++ * @chip: controller managing the signal ++ * @offset: of signal within controller's 0..(ngpio - 1) range ++ * ++ * Returns NULL if the GPIO is not currently requested, else a string. ++ * If debugfs support is enabled, the string returned is the label passed ++ * to gpio_request(); otherwise it is a meaningless constant. ++ * ++ * This function is for use by GPIO controller drivers. The label can ++ * help with diagnostics, and knowing that the signal is used as a GPIO ++ * can help avoid accidentally multiplexing it to another controller. ++ */ ++const char *gpiochip_is_requested(struct gpio_chip *chip, unsigned offset) ++{ ++ unsigned gpio = chip->base + offset; ++ ++ if (gpio >= ARCH_NR_GPIOS || gpio_desc[gpio].chip != chip) ++ return NULL; ++ if (test_bit(FLAG_REQUESTED, &gpio_desc[gpio].flags) == 0) ++ return NULL; ++#ifdef CONFIG_DEBUG_FS ++ return gpio_desc[gpio].label; ++#else ++ return "?"; ++#endif ++} ++EXPORT_SYMBOL_GPL(gpiochip_is_requested); ++ ++ ++/* Drivers MUST set GPIO direction before making get/set calls. In ++ * some cases this is done in early boot, before IRQs are enabled. ++ * ++ * As a rule these aren't called more than once (except for drivers ++ * using the open-drain emulation idiom) so these are natural places ++ * to accumulate extra debugging checks. Note that we can't (yet) ++ * rely on gpio_request() having been called beforehand. ++ */ ++ ++int gpio_direction_input(unsigned gpio) ++{ ++ unsigned long flags; ++ struct gpio_chip *chip; ++ struct gpio_desc *desc = &gpio_desc[gpio]; ++ int status = -EINVAL; ++ ++ spin_lock_irqsave(&gpio_lock, flags); ++ ++ if (gpio >= ARCH_NR_GPIOS) ++ goto fail; ++ chip = desc->chip; ++ if (!chip || !chip->get || !chip->direction_input) ++ goto fail; ++ gpio -= chip->base; ++ if (gpio >= chip->ngpio) ++ goto fail; ++ gpio_ensure_requested(desc); ++ ++ /* now we know the gpio is valid and chip won't vanish */ ++ ++ spin_unlock_irqrestore(&gpio_lock, flags); ++ ++ might_sleep_if(extra_checks && chip->can_sleep); ++ ++ status = chip->direction_input(chip, gpio); ++ if (status == 0) ++ clear_bit(FLAG_IS_OUT, &desc->flags); ++ return status; ++fail: ++ spin_unlock_irqrestore(&gpio_lock, flags); ++ if (status) ++ pr_debug("%s: gpio-%d status %d\n", ++ __FUNCTION__, gpio, status); ++ return status; ++} ++EXPORT_SYMBOL_GPL(gpio_direction_input); ++ ++int gpio_direction_output(unsigned gpio, int value) ++{ ++ unsigned long flags; ++ struct gpio_chip *chip; ++ struct gpio_desc *desc = &gpio_desc[gpio]; ++ int status = -EINVAL; ++ ++ spin_lock_irqsave(&gpio_lock, flags); ++ ++ if (gpio >= ARCH_NR_GPIOS) ++ goto fail; ++ chip = desc->chip; ++ if (!chip || !chip->set || !chip->direction_output) ++ goto fail; ++ gpio -= chip->base; ++ if (gpio >= chip->ngpio) ++ goto fail; ++ gpio_ensure_requested(desc); ++ ++ /* now we know the gpio is valid and chip won't vanish */ ++ ++ spin_unlock_irqrestore(&gpio_lock, flags); ++ ++ might_sleep_if(extra_checks && chip->can_sleep); ++ ++ status = chip->direction_output(chip, gpio, value); ++ if (status == 0) ++ set_bit(FLAG_IS_OUT, &desc->flags); ++ return status; ++fail: ++ spin_unlock_irqrestore(&gpio_lock, flags); ++ if (status) ++ pr_debug("%s: gpio-%d status %d\n", ++ __FUNCTION__, gpio, status); ++ return status; ++} ++EXPORT_SYMBOL_GPL(gpio_direction_output); ++ ++ ++/* I/O calls are only valid after configuration completed; the relevant ++ * "is this a valid GPIO" error checks should already have been done. ++ * ++ * "Get" operations are often inlinable as reading a pin value register, ++ * and masking the relevant bit in that register. ++ * ++ * When "set" operations are inlinable, they involve writing that mask to ++ * one register to set a low value, or a different register to set it high. ++ * Otherwise locking is needed, so there may be little value to inlining. ++ * ++ *------------------------------------------------------------------------ ++ * ++ * IMPORTANT!!! The hot paths -- get/set value -- assume that callers ++ * have requested the GPIO. That can include implicit requesting by ++ * a direction setting call. Marking a gpio as requested locks its chip ++ * in memory, guaranteeing that these table lookups need no more locking ++ * and that gpiochip_remove() will fail. ++ * ++ * REVISIT when debugging, consider adding some instrumentation to ensure ++ * that the GPIO was actually requested. ++ */ ++ ++/** ++ * __gpio_get_value() - return a gpio's value ++ * @gpio: gpio whose value will be returned ++ * Context: any ++ * ++ * This is used directly or indirectly to implement gpio_get_value(). ++ * It returns the zero or nonzero value provided by the associated ++ * gpio_chip.get() method; or zero if no such method is provided. ++ */ ++int __gpio_get_value(unsigned gpio) ++{ ++ struct gpio_chip *chip; ++ ++ chip = gpio_to_chip(gpio); ++ WARN_ON(extra_checks && chip->can_sleep); ++ return chip->get ? chip->get(chip, gpio - chip->base) : 0; ++} ++EXPORT_SYMBOL_GPL(__gpio_get_value); ++ ++/** ++ * __gpio_set_value() - assign a gpio's value ++ * @gpio: gpio whose value will be assigned ++ * @value: value to assign ++ * Context: any ++ * ++ * This is used directly or indirectly to implement gpio_set_value(). ++ * It invokes the associated gpio_chip.set() method. ++ */ ++void __gpio_set_value(unsigned gpio, int value) ++{ ++ struct gpio_chip *chip; ++ ++ chip = gpio_to_chip(gpio); ++ WARN_ON(extra_checks && chip->can_sleep); ++ chip->set(chip, gpio - chip->base, value); ++} ++EXPORT_SYMBOL_GPL(__gpio_set_value); ++ ++/** ++ * __gpio_cansleep() - report whether gpio value access will sleep ++ * @gpio: gpio in question ++ * Context: any ++ * ++ * This is used directly or indirectly to implement gpio_cansleep(). It ++ * returns nonzero if access reading or writing the GPIO value can sleep. ++ */ ++int __gpio_cansleep(unsigned gpio) ++{ ++ struct gpio_chip *chip; ++ ++ /* only call this on GPIOs that are valid! */ ++ chip = gpio_to_chip(gpio); ++ ++ return chip->can_sleep; ++} ++EXPORT_SYMBOL_GPL(__gpio_cansleep); ++ ++ ++ ++/* There's no value in making it easy to inline GPIO calls that may sleep. ++ * Common examples include ones connected to I2C or SPI chips. ++ */ ++ ++int gpio_get_value_cansleep(unsigned gpio) ++{ ++ struct gpio_chip *chip; ++ ++ might_sleep_if(extra_checks); ++ chip = gpio_to_chip(gpio); ++ return chip->get(chip, gpio - chip->base); ++} ++EXPORT_SYMBOL_GPL(gpio_get_value_cansleep); ++ ++void gpio_set_value_cansleep(unsigned gpio, int value) ++{ ++ struct gpio_chip *chip; ++ ++ might_sleep_if(extra_checks); ++ chip = gpio_to_chip(gpio); ++ chip->set(chip, gpio - chip->base, value); ++} ++EXPORT_SYMBOL_GPL(gpio_set_value_cansleep); ++ ++ ++#ifdef CONFIG_DEBUG_FS ++ ++#include <linux/debugfs.h> ++#include <linux/seq_file.h> ++ ++ ++static void gpiolib_dbg_show(struct seq_file *s, struct gpio_chip *chip) ++{ ++ unsigned i; ++ unsigned gpio = chip->base; ++ struct gpio_desc *gdesc = &gpio_desc[gpio]; ++ int is_out; ++ ++ for (i = 0; i < chip->ngpio; i++, gpio++, gdesc++) { ++ if (!test_bit(FLAG_REQUESTED, &gdesc->flags)) ++ continue; ++ ++ is_out = test_bit(FLAG_IS_OUT, &gdesc->flags); ++ seq_printf(s, " gpio-%-3d (%-12s) %s %s", ++ gpio, gdesc->label, ++ is_out ? "out" : "in ", ++ chip->get ++ ? (chip->get(chip, i) ? "hi" : "lo") ++ : "? "); ++ ++ if (!is_out) { ++ int irq = gpio_to_irq(gpio); ++ struct irq_desc *desc = irq_desc + irq; ++ ++ /* This races with request_irq(), set_irq_type(), ++ * and set_irq_wake() ... but those are "rare". ++ * ++ * More significantly, trigger type flags aren't ++ * currently maintained by genirq. ++ */ ++ if (irq >= 0 && desc->action) { ++ char *trigger; ++ ++ switch (desc->status & IRQ_TYPE_SENSE_MASK) { ++ case IRQ_TYPE_NONE: ++ trigger = "(default)"; ++ break; ++ case IRQ_TYPE_EDGE_FALLING: ++ trigger = "edge-falling"; ++ break; ++ case IRQ_TYPE_EDGE_RISING: ++ trigger = "edge-rising"; ++ break; ++ case IRQ_TYPE_EDGE_BOTH: ++ trigger = "edge-both"; ++ break; ++ case IRQ_TYPE_LEVEL_HIGH: ++ trigger = "level-high"; ++ break; ++ case IRQ_TYPE_LEVEL_LOW: ++ trigger = "level-low"; ++ break; ++ default: ++ trigger = "?trigger?"; ++ break; ++ } ++ ++ seq_printf(s, " irq-%d %s%s", ++ irq, trigger, ++ (desc->status & IRQ_WAKEUP) ++ ? " wakeup" : ""); ++ } ++ } ++ ++ seq_printf(s, "\n"); ++ } ++} ++ ++static int gpiolib_show(struct seq_file *s, void *unused) ++{ ++ struct gpio_chip *chip = NULL; ++ unsigned gpio; ++ int started = 0; ++ ++ /* REVISIT this isn't locked against gpio_chip removal ... */ ++ ++ for (gpio = 0; gpio < ARCH_NR_GPIOS; gpio++) { ++ if (chip == gpio_desc[gpio].chip) ++ continue; ++ chip = gpio_desc[gpio].chip; ++ if (!chip) ++ continue; ++ ++ seq_printf(s, "%sGPIOs %d-%d, %s%s:\n", ++ started ? "\n" : "", ++ chip->base, chip->base + chip->ngpio - 1, ++ chip->label ? : "generic", ++ chip->can_sleep ? ", can sleep" : ""); ++ started = 1; ++ if (chip->dbg_show) ++ chip->dbg_show(s, chip); ++ else ++ gpiolib_dbg_show(s, chip); ++ } ++ return 0; ++} ++ ++static int gpiolib_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, gpiolib_show, NULL); ++} ++ ++static struct file_operations gpiolib_operations = { ++ .open = gpiolib_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static int __init gpiolib_debugfs_init(void) ++{ ++ /* /sys/kernel/debug/gpio */ ++ (void) debugfs_create_file("gpio", S_IFREG | S_IRUGO, ++ NULL, NULL, &gpiolib_operations); ++ return 0; ++} ++subsys_initcall(gpiolib_debugfs_init); ++ ++#endif /* DEBUG_FS */ +diff --git a/include/asm-generic/gpio.h b/include/asm-generic/gpio.h +index 2d0aab1..f29a502 100644 +--- a/include/asm-generic/gpio.h ++++ b/include/asm-generic/gpio.h +@@ -1,6 +1,102 @@ + #ifndef _ASM_GENERIC_GPIO_H + #define _ASM_GENERIC_GPIO_H + ++#ifdef CONFIG_HAVE_GPIO_LIB ++ ++/* Platforms may implement their GPIO interface with library code, ++ * at a small performance cost for non-inlined operations and some ++ * extra memory (for code and for per-GPIO table entries). ++ * ++ * While the GPIO programming interface defines valid GPIO numbers ++ * to be in the range 0..MAX_INT, this library restricts them to the ++ * smaller range 0..ARCH_NR_GPIOS. ++ */ ++ ++#ifndef ARCH_NR_GPIOS ++#define ARCH_NR_GPIOS 256 ++#endif ++ ++struct seq_file; ++ ++/** ++ * struct gpio_chip - abstract a GPIO controller ++ * @label: for diagnostics ++ * @direction_input: configures signal "offset" as input, or returns error ++ * @get: returns value for signal "offset"; for output signals this ++ * returns either the value actually sensed, or zero ++ * @direction_output: configures signal "offset" as output, or returns error ++ * @set: assigns output value for signal "offset" ++ * @dbg_show: optional routine to show contents in debugfs; default code ++ * will be used when this is omitted, but custom code can show extra ++ * state (such as pullup/pulldown configuration). ++ * @base: identifies the first GPIO number handled by this chip; or, if ++ * negative during registration, requests dynamic ID allocation. ++ * @ngpio: the number of GPIOs handled by this controller; the last GPIO ++ * handled is (base + ngpio - 1). ++ * @can_sleep: flag must be set iff get()/set() methods sleep, as they ++ * must while accessing GPIO expander chips over I2C or SPI ++ * ++ * A gpio_chip can help platforms abstract various sources of GPIOs so ++ * they can all be accessed through a common programing interface. ++ * Example sources would be SOC controllers, FPGAs, multifunction ++ * chips, dedicated GPIO expanders, and so on. ++ * ++ * Each chip controls a number of signals, identified in method calls ++ * by "offset" values in the range 0..(@ngpio - 1). When those signals ++ * are referenced through calls like gpio_get_value(gpio), the offset ++ * is calculated by subtracting @base from the gpio number. ++ */ ++struct gpio_chip { ++ char *label; ++ ++ int (*direction_input)(struct gpio_chip *chip, ++ unsigned offset); ++ int (*get)(struct gpio_chip *chip, ++ unsigned offset); ++ int (*direction_output)(struct gpio_chip *chip, ++ unsigned offset, int value); ++ void (*set)(struct gpio_chip *chip, ++ unsigned offset, int value); ++ void (*dbg_show)(struct seq_file *s, ++ struct gpio_chip *chip); ++ int base; ++ u16 ngpio; ++ unsigned can_sleep:1; ++}; ++ ++extern const char *gpiochip_is_requested(struct gpio_chip *chip, ++ unsigned offset); ++ ++/* add/remove chips */ ++extern int gpiochip_add(struct gpio_chip *chip); ++extern int __must_check gpiochip_remove(struct gpio_chip *chip); ++ ++ ++/* Always use the library code for GPIO management calls, ++ * or when sleeping may be involved. ++ */ ++extern int gpio_request(unsigned gpio, const char *label); ++extern void gpio_free(unsigned gpio); ++ ++extern int gpio_direction_input(unsigned gpio); ++extern int gpio_direction_output(unsigned gpio, int value); ++ ++extern int gpio_get_value_cansleep(unsigned gpio); ++extern void gpio_set_value_cansleep(unsigned gpio, int value); ++ ++ ++/* A platform's <asm/gpio.h> code may want to inline the I/O calls when ++ * the GPIO is constant and refers to some always-present controller, ++ * giving direct access to chip registers and tight bitbanging loops. ++ */ ++extern int __gpio_get_value(unsigned gpio); ++extern void __gpio_set_value(unsigned gpio, int value); ++ ++extern int __gpio_cansleep(unsigned gpio); ++ ++ ++#else ++ + /* platforms that don't directly support access to GPIOs through I2C, SPI, + * or other blocking infrastructure can use these wrappers. + */ +@@ -22,4 +118,6 @@ static inline void gpio_set_value_cansleep(unsigned gpio, int value) + gpio_set_value(gpio, value); + } + ++#endif ++ + #endif /* _ASM_GENERIC_GPIO_H */ +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0023-This-adds-gpiolib-support-for-the-PXA-architecture.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0023-This-adds-gpiolib-support-for-the-PXA-architecture.patch new file mode 100644 index 0000000000..7a37be85cf --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0023-This-adds-gpiolib-support-for-the-PXA-architecture.patch @@ -0,0 +1,498 @@ +From 49da9bd487e54164a75503e0037a054cce697ed5 Mon Sep 17 00:00:00 2001 +From: Philipp Zabel <philipp.zabel@gmail.com> +Date: Tue, 12 Feb 2008 04:38:12 +0300 +Subject: [PATCH 23/64] This adds gpiolib support for the PXA architecture: + - move all GPIO API functions from generic.c into gpio.c + - convert the gpio_get/set_value macros into inline functions + +This makes it easier to hook up GPIOs provided by external chips like +ASICs and CPLDs. + +Signed-off-by: Philipp Zabel <philipp.zabel@gmail.com> +Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> +Acked-by: Russell King <rmk+kernel@arm.linux.org.uk> +Cc: Jean Delvare <khali@linux-fr.org> +Cc: Eric Miao <eric.miao@marvell.com> +Cc: Sam Ravnborg <sam@ravnborg.org> +Cc: Haavard Skinnemoen <hskinnemoen@atmel.com> +Cc: Ben Gardner <bgardner@wabtec.com> +Signed-off-by: Andrew Morton <akpm@linux-foundation.org> +--- + arch/arm/Kconfig | 1 + + arch/arm/mach-pxa/Makefile | 3 +- + arch/arm/mach-pxa/generic.c | 93 ---------------- + arch/arm/mach-pxa/generic.h | 1 + + arch/arm/mach-pxa/gpio.c | 197 +++++++++++++++++++++++++++++++++++ + arch/arm/mach-pxa/irq.c | 2 + + include/asm-arm/arch-pxa/gpio.h | 48 ++++----- + include/asm-arm/arch-pxa/pxa-regs.h | 13 +++ + 8 files changed, 236 insertions(+), 122 deletions(-) + create mode 100644 arch/arm/mach-pxa/gpio.c + +diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig +index 06ca241..423e953 100644 +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -346,6 +346,7 @@ config ARCH_PXA + select GENERIC_TIME + select GENERIC_CLOCKEVENTS + select TICK_ONESHOT ++ select HAVE_GPIO_LIB + help + Support for Intel/Marvell's PXA2xx/PXA3xx processor line. + +diff --git a/arch/arm/mach-pxa/Makefile b/arch/arm/mach-pxa/Makefile +index 4263527..5cb0216 100644 +--- a/arch/arm/mach-pxa/Makefile ++++ b/arch/arm/mach-pxa/Makefile +@@ -3,7 +3,8 @@ + # + + # Common support (must be linked before board specific support) +-obj-y += clock.o generic.o irq.o dma.o time.o ++obj-y += clock.o generic.o irq.o dma.o \ ++ time.o gpio.o + obj-$(CONFIG_PXA25x) += pxa25x.o + obj-$(CONFIG_PXA27x) += pxa27x.o + obj-$(CONFIG_PXA3xx) += pxa3xx.o mfp.o +diff --git a/arch/arm/mach-pxa/generic.c b/arch/arm/mach-pxa/generic.c +index 1c34946..6c07292 100644 +--- a/arch/arm/mach-pxa/generic.c ++++ b/arch/arm/mach-pxa/generic.c +@@ -32,7 +32,6 @@ + #include <asm/mach/map.h> + + #include <asm/arch/pxa-regs.h> +-#include <asm/arch/gpio.h> + #include <asm/arch/udc.h> + #include <asm/arch/pxafb.h> + #include <asm/arch/mmc.h> +@@ -73,97 +72,6 @@ unsigned int get_memclk_frequency_10khz(void) + EXPORT_SYMBOL(get_memclk_frequency_10khz); + + /* +- * Handy function to set GPIO alternate functions +- */ +-int pxa_last_gpio; +- +-int pxa_gpio_mode(int gpio_mode) +-{ +- unsigned long flags; +- int gpio = gpio_mode & GPIO_MD_MASK_NR; +- int fn = (gpio_mode & GPIO_MD_MASK_FN) >> 8; +- int gafr; +- +- if (gpio > pxa_last_gpio) +- return -EINVAL; +- +- local_irq_save(flags); +- if (gpio_mode & GPIO_DFLT_LOW) +- GPCR(gpio) = GPIO_bit(gpio); +- else if (gpio_mode & GPIO_DFLT_HIGH) +- GPSR(gpio) = GPIO_bit(gpio); +- if (gpio_mode & GPIO_MD_MASK_DIR) +- GPDR(gpio) |= GPIO_bit(gpio); +- else +- GPDR(gpio) &= ~GPIO_bit(gpio); +- gafr = GAFR(gpio) & ~(0x3 << (((gpio) & 0xf)*2)); +- GAFR(gpio) = gafr | (fn << (((gpio) & 0xf)*2)); +- local_irq_restore(flags); +- +- return 0; +-} +- +-EXPORT_SYMBOL(pxa_gpio_mode); +- +-int gpio_direction_input(unsigned gpio) +-{ +- unsigned long flags; +- u32 mask; +- +- if (gpio > pxa_last_gpio) +- return -EINVAL; +- +- mask = GPIO_bit(gpio); +- local_irq_save(flags); +- GPDR(gpio) &= ~mask; +- local_irq_restore(flags); +- +- return 0; +-} +-EXPORT_SYMBOL(gpio_direction_input); +- +-int gpio_direction_output(unsigned gpio, int value) +-{ +- unsigned long flags; +- u32 mask; +- +- if (gpio > pxa_last_gpio) +- return -EINVAL; +- +- mask = GPIO_bit(gpio); +- local_irq_save(flags); +- if (value) +- GPSR(gpio) = mask; +- else +- GPCR(gpio) = mask; +- GPDR(gpio) |= mask; +- local_irq_restore(flags); +- +- return 0; +-} +-EXPORT_SYMBOL(gpio_direction_output); +- +-/* +- * Return GPIO level +- */ +-int pxa_gpio_get_value(unsigned gpio) +-{ +- return __gpio_get_value(gpio); +-} +- +-EXPORT_SYMBOL(pxa_gpio_get_value); +- +-/* +- * Set output GPIO level +- */ +-void pxa_gpio_set_value(unsigned gpio, int value) +-{ +- __gpio_set_value(gpio, value); +-} +- +-EXPORT_SYMBOL(pxa_gpio_set_value); +- +-/* + * Routine to safely enable or disable a clock in the CKEN + */ + void __pxa_set_cken(int clock, int enable) +@@ -178,7 +86,6 @@ void __pxa_set_cken(int clock, int enable) + + local_irq_restore(flags); + } +- + EXPORT_SYMBOL(__pxa_set_cken); + + /* +diff --git a/arch/arm/mach-pxa/generic.h b/arch/arm/mach-pxa/generic.h +index b30f240..727a9f5 100644 +--- a/arch/arm/mach-pxa/generic.h ++++ b/arch/arm/mach-pxa/generic.h +@@ -16,6 +16,7 @@ extern void __init pxa_init_irq_low(void); + extern void __init pxa_init_irq_high(void); + extern void __init pxa_init_irq_gpio(int gpio_nr); + extern void __init pxa_init_irq_set_wake(int (*set_wake)(unsigned int, unsigned int)); ++extern void __init pxa_init_gpio(int gpio_nr); + extern void __init pxa25x_init_irq(void); + extern void __init pxa27x_init_irq(void); + extern void __init pxa3xx_init_irq(void); +diff --git a/arch/arm/mach-pxa/gpio.c b/arch/arm/mach-pxa/gpio.c +new file mode 100644 +index 0000000..8638dd7 +--- /dev/null ++++ b/arch/arm/mach-pxa/gpio.c +@@ -0,0 +1,197 @@ ++/* ++ * linux/arch/arm/mach-pxa/gpio.c ++ * ++ * Generic PXA GPIO handling ++ * ++ * 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 <linux/init.h> ++#include <linux/module.h> ++ ++#include <asm/gpio.h> ++#include <asm/hardware.h> ++#include <asm/io.h> ++#include <asm/arch/pxa-regs.h> ++ ++#include "generic.h" ++ ++ ++struct pxa_gpio_chip { ++ struct gpio_chip chip; ++ void __iomem *regbase; ++}; ++ ++int pxa_last_gpio; ++ ++/* ++ * Configure pins for GPIO or other functions ++ */ ++int pxa_gpio_mode(int gpio_mode) ++{ ++ unsigned long flags; ++ int gpio = gpio_mode & GPIO_MD_MASK_NR; ++ int fn = (gpio_mode & GPIO_MD_MASK_FN) >> 8; ++ int gafr; ++ ++ if (gpio > pxa_last_gpio) ++ return -EINVAL; ++ ++ local_irq_save(flags); ++ if (gpio_mode & GPIO_DFLT_LOW) ++ GPCR(gpio) = GPIO_bit(gpio); ++ else if (gpio_mode & GPIO_DFLT_HIGH) ++ GPSR(gpio) = GPIO_bit(gpio); ++ if (gpio_mode & GPIO_MD_MASK_DIR) ++ GPDR(gpio) |= GPIO_bit(gpio); ++ else ++ GPDR(gpio) &= ~GPIO_bit(gpio); ++ gafr = GAFR(gpio) & ~(0x3 << (((gpio) & 0xf)*2)); ++ GAFR(gpio) = gafr | (fn << (((gpio) & 0xf)*2)); ++ local_irq_restore(flags); ++ ++ return 0; ++} ++EXPORT_SYMBOL(pxa_gpio_mode); ++ ++static int pxa_gpio_direction_input(struct gpio_chip *chip, unsigned offset) ++{ ++ unsigned long flags; ++ u32 mask = 1 << offset; ++ u32 value; ++ struct pxa_gpio_chip *pxa; ++ void __iomem *gpdr; ++ ++ pxa = container_of(chip, struct pxa_gpio_chip, chip); ++ gpdr = pxa->regbase + GPDR_OFFSET; ++ local_irq_save(flags); ++ value = __raw_readl(gpdr); ++ value &= ~mask; ++ __raw_writel(value, gpdr); ++ local_irq_restore(flags); ++ ++ return 0; ++} ++ ++static int pxa_gpio_direction_output(struct gpio_chip *chip, ++ unsigned offset, int value) ++{ ++ unsigned long flags; ++ u32 mask = 1 << offset; ++ u32 tmp; ++ struct pxa_gpio_chip *pxa; ++ void __iomem *gpdr; ++ ++ pxa = container_of(chip, struct pxa_gpio_chip, chip); ++ __raw_writel(mask, ++ pxa->regbase + (value ? GPSR_OFFSET : GPCR_OFFSET)); ++ gpdr = pxa->regbase + GPDR_OFFSET; ++ local_irq_save(flags); ++ tmp = __raw_readl(gpdr); ++ tmp |= mask; ++ __raw_writel(tmp, gpdr); ++ local_irq_restore(flags); ++ ++ return 0; ++} ++ ++/* ++ * Return GPIO level ++ */ ++static int pxa_gpio_get(struct gpio_chip *chip, unsigned offset) ++{ ++ u32 mask = 1 << offset; ++ struct pxa_gpio_chip *pxa; ++ ++ pxa = container_of(chip, struct pxa_gpio_chip, chip); ++ return __raw_readl(pxa->regbase + GPLR_OFFSET) & mask; ++} ++ ++/* ++ * Set output GPIO level ++ */ ++static void pxa_gpio_set(struct gpio_chip *chip, unsigned offset, int value) ++{ ++ u32 mask = 1 << offset; ++ struct pxa_gpio_chip *pxa; ++ ++ pxa = container_of(chip, struct pxa_gpio_chip, chip); ++ ++ if (value) ++ __raw_writel(mask, pxa->regbase + GPSR_OFFSET); ++ else ++ __raw_writel(mask, pxa->regbase + GPCR_OFFSET); ++} ++ ++static struct pxa_gpio_chip pxa_gpio_chip[] = { ++ [0] = { ++ .regbase = GPIO0_BASE, ++ .chip = { ++ .label = "gpio-0", ++ .direction_input = pxa_gpio_direction_input, ++ .direction_output = pxa_gpio_direction_output, ++ .get = pxa_gpio_get, ++ .set = pxa_gpio_set, ++ .base = 0, ++ .ngpio = 32, ++ }, ++ }, ++ [1] = { ++ .regbase = GPIO1_BASE, ++ .chip = { ++ .label = "gpio-1", ++ .direction_input = pxa_gpio_direction_input, ++ .direction_output = pxa_gpio_direction_output, ++ .get = pxa_gpio_get, ++ .set = pxa_gpio_set, ++ .base = 32, ++ .ngpio = 32, ++ }, ++ }, ++ [2] = { ++ .regbase = GPIO2_BASE, ++ .chip = { ++ .label = "gpio-2", ++ .direction_input = pxa_gpio_direction_input, ++ .direction_output = pxa_gpio_direction_output, ++ .get = pxa_gpio_get, ++ .set = pxa_gpio_set, ++ .base = 64, ++ .ngpio = 32, /* 21 for PXA25x */ ++ }, ++ }, ++#if defined(CONFIG_PXA27x) || defined(CONFIG_PXA3xx) ++ [3] = { ++ .regbase = GPIO3_BASE, ++ .chip = { ++ .label = "gpio-3", ++ .direction_input = pxa_gpio_direction_input, ++ .direction_output = pxa_gpio_direction_output, ++ .get = pxa_gpio_get, ++ .set = pxa_gpio_set, ++ .base = 96, ++ .ngpio = 32, ++ }, ++ }, ++#endif ++}; ++ ++void __init pxa_init_gpio(int gpio_nr) ++{ ++ int i; ++ ++ /* add a GPIO chip for each register bank. ++ * the last PXA25x register only contains 21 GPIOs ++ */ ++ for (i = 0; i < gpio_nr; i += 32) { ++ if (i+32 > gpio_nr) ++ pxa_gpio_chip[i/32].chip.ngpio = gpio_nr - i; ++ gpiochip_add(&pxa_gpio_chip[i/32].chip); ++ } ++} +diff --git a/arch/arm/mach-pxa/irq.c b/arch/arm/mach-pxa/irq.c +index 07acb45..d0965ef 100644 +--- a/arch/arm/mach-pxa/irq.c ++++ b/arch/arm/mach-pxa/irq.c +@@ -310,6 +310,8 @@ void __init pxa_init_irq_gpio(int gpio_nr) + /* Install handler for GPIO>=2 edge detect interrupts */ + set_irq_chip(IRQ_GPIO_2_x, &pxa_internal_chip_low); + set_irq_chained_handler(IRQ_GPIO_2_x, pxa_gpio_demux_handler); ++ ++ pxa_init_gpio(gpio_nr); + } + + void __init pxa_init_irq_set_wake(int (*set_wake)(unsigned int, unsigned int)) +diff --git a/include/asm-arm/arch-pxa/gpio.h b/include/asm-arm/arch-pxa/gpio.h +index 9dbc2dc..bdbf5f9 100644 +--- a/include/asm-arm/arch-pxa/gpio.h ++++ b/include/asm-arm/arch-pxa/gpio.h +@@ -28,43 +28,35 @@ + #include <asm/irq.h> + #include <asm/hardware.h> + +-static inline int gpio_request(unsigned gpio, const char *label) +-{ +- return 0; +-} ++#include <asm-generic/gpio.h> + +-static inline void gpio_free(unsigned gpio) +-{ +- return; +-} + +-extern int gpio_direction_input(unsigned gpio); +-extern int gpio_direction_output(unsigned gpio, int value); ++/* NOTE: some PXAs have fewer on-chip GPIOs (like PXA255, with 85). ++ * Those cases currently cause holes in the GPIO number space. ++ */ ++#define NR_BUILTIN_GPIO 128 + +-static inline int __gpio_get_value(unsigned gpio) ++static inline int gpio_get_value(unsigned gpio) + { +- return GPLR(gpio) & GPIO_bit(gpio); ++ if (__builtin_constant_p(gpio) && (gpio < NR_BUILTIN_GPIO)) ++ return GPLR(gpio) & GPIO_bit(gpio); ++ else ++ return __gpio_get_value(gpio); + } + +-#define gpio_get_value(gpio) \ +- (__builtin_constant_p(gpio) ? \ +- __gpio_get_value(gpio) : \ +- pxa_gpio_get_value(gpio)) +- +-static inline void __gpio_set_value(unsigned gpio, int value) ++static inline void gpio_set_value(unsigned gpio, int value) + { +- if (value) +- GPSR(gpio) = GPIO_bit(gpio); +- else +- GPCR(gpio) = GPIO_bit(gpio); ++ if (__builtin_constant_p(gpio) && (gpio < NR_BUILTIN_GPIO)) { ++ if (value) ++ GPSR(gpio) = GPIO_bit(gpio); ++ else ++ GPCR(gpio) = GPIO_bit(gpio); ++ } else { ++ __gpio_set_value(gpio, value); ++ } + } + +-#define gpio_set_value(gpio,value) \ +- (__builtin_constant_p(gpio) ? \ +- __gpio_set_value(gpio, value) : \ +- pxa_gpio_set_value(gpio, value)) +- +-#include <asm-generic/gpio.h> /* cansleep wrappers */ ++#define gpio_cansleep __gpio_cansleep + + #define gpio_to_irq(gpio) IRQ_GPIO(gpio) + #define irq_to_gpio(irq) IRQ_TO_GPIO(irq) +diff --git a/include/asm-arm/arch-pxa/pxa-regs.h b/include/asm-arm/arch-pxa/pxa-regs.h +index 1bd398d..bd57417 100644 +--- a/include/asm-arm/arch-pxa/pxa-regs.h ++++ b/include/asm-arm/arch-pxa/pxa-regs.h +@@ -1131,6 +1131,19 @@ + * General Purpose I/O + */ + ++#define GPIO0_BASE ((void __iomem *)io_p2v(0x40E00000)) ++#define GPIO1_BASE ((void __iomem *)io_p2v(0x40E00004)) ++#define GPIO2_BASE ((void __iomem *)io_p2v(0x40E00008)) ++#define GPIO3_BASE ((void __iomem *)io_p2v(0x40E00100)) ++ ++#define GPLR_OFFSET 0x00 ++#define GPDR_OFFSET 0x0C ++#define GPSR_OFFSET 0x18 ++#define GPCR_OFFSET 0x24 ++#define GRER_OFFSET 0x30 ++#define GFER_OFFSET 0x3C ++#define GEDR_OFFSET 0x48 ++ + #define GPLR0 __REG(0x40E00000) /* GPIO Pin-Level Register GPIO<31:0> */ + #define GPLR1 __REG(0x40E00004) /* GPIO Pin-Level Register GPIO<63:32> */ + #define GPLR2 __REG(0x40E00008) /* GPIO Pin-Level Register GPIO<80:64> */ +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0024-Update-Documentation-gpio.txt-primarily-to-include.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0024-Update-Documentation-gpio.txt-primarily-to-include.patch new file mode 100644 index 0000000000..e460379de6 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0024-Update-Documentation-gpio.txt-primarily-to-include.patch @@ -0,0 +1,238 @@ +From 7ba82399f2d2df6114ad552999f2e1b9a19cb47a Mon Sep 17 00:00:00 2001 +From: David Brownell <dbrownell@users.sourceforge.net> +Date: Sat, 19 Jan 2008 19:41:18 +0300 +Subject: [PATCH 24/64] Update Documentation/gpio.txt, primarily to include the new "gpiolib" + infrastructure. + +Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> +Cc: Jean Delvare <khali@linux-fr.org> +Cc: Eric Miao <eric.miao@marvell.com> +Cc: Sam Ravnborg <sam@ravnborg.org> +Cc: Haavard Skinnemoen <hskinnemoen@atmel.com> +Cc: Philipp Zabel <philipp.zabel@gmail.com> +Cc: Russell King <rmk@arm.linux.org.uk> +Cc: Ben Gardner <bgardner@wabtec.com> +Signed-off-by: Andrew Morton <akpm@linux-foundation.org> +--- + Documentation/gpio.txt | 133 +++++++++++++++++++++++++++++++++++++++++++---- + 1 files changed, 121 insertions(+), 12 deletions(-) + +diff --git a/Documentation/gpio.txt b/Documentation/gpio.txt +index 6bc2ba2..8da724e 100644 +--- a/Documentation/gpio.txt ++++ b/Documentation/gpio.txt +@@ -32,7 +32,7 @@ The exact capabilities of GPIOs vary between systems. Common options: + - Input values are likewise readable (1, 0). Some chips support readback + of pins configured as "output", which is very useful in such "wire-OR" + cases (to support bidirectional signaling). GPIO controllers may have +- input de-glitch logic, sometimes with software controls. ++ input de-glitch/debounce logic, sometimes with software controls. + + - Inputs can often be used as IRQ signals, often edge triggered but + sometimes level triggered. Such IRQs may be configurable as system +@@ -60,10 +60,13 @@ used on a board that's wired differently. Only least-common-denominator + functionality can be very portable. Other features are platform-specific, + and that can be critical for glue logic. + +-Plus, this doesn't define an implementation framework, just an interface. ++Plus, this doesn't require any implementation framework, just an interface. + One platform might implement it as simple inline functions accessing chip + registers; another might implement it by delegating through abstractions +-used for several very different kinds of GPIO controller. ++used for several very different kinds of GPIO controller. (There is some ++optional code supporting such an implementation strategy, described later ++in this document, but drivers acting as clients to the GPIO interface must ++not care how it's implemented.) + + That said, if the convention is supported on their platform, drivers should + use it when possible. Platforms should declare GENERIC_GPIO support in +@@ -121,6 +124,11 @@ before tasking is enabled, as part of early board setup. + For output GPIOs, the value provided becomes the initial output value. + This helps avoid signal glitching during system startup. + ++For compatibility with legacy interfaces to GPIOs, setting the direction ++of a GPIO implicitly requests that GPIO (see below) if it has not been ++requested already. That compatibility may be removed in the future; ++explicitly requesting GPIOs is strongly preferred. ++ + Setting the direction can fail if the GPIO number is invalid, or when + that particular GPIO can't be used in that mode. It's generally a bad + idea to rely on boot firmware to have set the direction correctly, since +@@ -133,6 +141,7 @@ Spinlock-Safe GPIO access + ------------------------- + Most GPIO controllers can be accessed with memory read/write instructions. + That doesn't need to sleep, and can safely be done from inside IRQ handlers. ++(That includes hardirq contexts on RT kernels.) + + Use these calls to access such GPIOs: + +@@ -145,7 +154,7 @@ Use these calls to access such GPIOs: + The values are boolean, zero for low, nonzero for high. When reading the + value of an output pin, the value returned should be what's seen on the + pin ... that won't always match the specified output value, because of +-issues including wire-OR and output latencies. ++issues including open-drain signaling and output latencies. + + The get/set calls have no error returns because "invalid GPIO" should have + been reported earlier from gpio_direction_*(). However, note that not all +@@ -170,7 +179,8 @@ get to the head of a queue to transmit a command and get its response. + This requires sleeping, which can't be done from inside IRQ handlers. + + Platforms that support this type of GPIO distinguish them from other GPIOs +-by returning nonzero from this call: ++by returning nonzero from this call (which requires a valid GPIO number, ++either explicitly or implicitly requested): + + int gpio_cansleep(unsigned gpio); + +@@ -209,8 +219,11 @@ before tasking is enabled, as part of early board setup. + These calls serve two basic purposes. One is marking the signals which + are actually in use as GPIOs, for better diagnostics; systems may have + several hundred potential GPIOs, but often only a dozen are used on any +-given board. Another is to catch conflicts between drivers, reporting +-errors when drivers wrongly think they have exclusive use of that signal. ++given board. Another is to catch conflicts, identifying errors when ++(a) two or more drivers wrongly think they have exclusive use of that ++signal, or (b) something wrongly believes it's safe to remove drivers ++needed to manage a signal that's in active use. That is, requesting a ++GPIO can serve as a kind of lock. + + These two calls are optional because not not all current Linux platforms + offer such functionality in their GPIO support; a valid implementation +@@ -223,6 +236,9 @@ Note that requesting a GPIO does NOT cause it to be configured in any + way; it just marks that GPIO as in use. Separate code must handle any + pin setup (e.g. controlling which pin the GPIO uses, pullup/pulldown). + ++Also note that it's your responsibility to have stopped using a GPIO ++before you free it. ++ + + GPIOs mapped to IRQs + -------------------- +@@ -238,7 +254,7 @@ map between them using calls like: + + Those return either the corresponding number in the other namespace, or + else a negative errno code if the mapping can't be done. (For example, +-some GPIOs can't used as IRQs.) It is an unchecked error to use a GPIO ++some GPIOs can't be used as IRQs.) It is an unchecked error to use a GPIO + number that wasn't set up as an input using gpio_direction_input(), or + to use an IRQ number that didn't originally come from gpio_to_irq(). + +@@ -299,17 +315,110 @@ Related to multiplexing is configuration and enabling of the pullups or + pulldowns integrated on some platforms. Not all platforms support them, + or support them in the same way; and any given board might use external + pullups (or pulldowns) so that the on-chip ones should not be used. ++(When a circuit needs 5 kOhm, on-chip 100 kOhm resistors won't do.) + + There are other system-specific mechanisms that are not specified here, + like the aforementioned options for input de-glitching and wire-OR output. + Hardware may support reading or writing GPIOs in gangs, but that's usually + configuration dependent: for GPIOs sharing the same bank. (GPIOs are + commonly grouped in banks of 16 or 32, with a given SOC having several such +-banks.) Some systems can trigger IRQs from output GPIOs. Code relying on +-such mechanisms will necessarily be nonportable. ++banks.) Some systems can trigger IRQs from output GPIOs, or read values ++from pins not managed as GPIOs. Code relying on such mechanisms will ++necessarily be nonportable. + +-Dynamic definition of GPIOs is not currently supported; for example, as ++Dynamic definition of GPIOs is not currently standard; for example, as + a side effect of configuring an add-on board with some GPIO expanders. + + These calls are purely for kernel space, but a userspace API could be built +-on top of it. ++on top of them. ++ ++ ++GPIO implementor's framework (OPTIONAL) ++======================================= ++As noted earlier, there is an optional implementation framework making it ++easier for platforms to support different kinds of GPIO controller using ++the same programming interface. ++ ++As a debugging aid, if debugfs is available a /sys/kernel/debug/gpio file ++will be found there. That will list all the controllers registered through ++this framework, and the state of the GPIOs currently in use. ++ ++ ++Controller Drivers: gpio_chip ++----------------------------- ++In this framework each GPIO controller is packaged as a "struct gpio_chip" ++with information common to each controller of that type: ++ ++ - methods to establish GPIO direction ++ - methods used to access GPIO values ++ - flag saying whether calls to its methods may sleep ++ - optional debugfs dump method (showing extra state like pullup config) ++ - label for diagnostics ++ ++There is also per-instance data, which may come from device.platform_data: ++the number of its first GPIO, and how many GPIOs it exposes. ++ ++The code implementing a gpio_chip should support multiple instances of the ++controller, possibly using the driver model. That code will configure each ++gpio_chip and issue gpiochip_add(). Removing a GPIO controller should be ++rare; use gpiochip_remove() when it is unavoidable. ++ ++Most often a gpio_chip is part of an instance-specific structure with state ++not exposed by the GPIO interfaces, such as addressing, power management, ++and more. Chips such as codecs will have complex non-GPIO state, ++ ++Any debugfs dump method should normally ignore signals which haven't been ++requested as GPIOs. They can use gpiochip_is_requested(), which returns ++either NULL or the label associated with that GPIO when it was requested. ++ ++ ++Platform Support ++---------------- ++To support this framework, a platform's Kconfig will "select HAVE_GPIO_LIB" ++and arrange that its <asm/gpio.h> includes <asm-generic/gpio.h> and defines ++three functions: gpio_get_value(), gpio_set_value(), and gpio_cansleep(). ++They may also want to provide a custom value for ARCH_NR_GPIOS. ++ ++Trivial implementations of those functions can directly use framework ++code, which always dispatches through the gpio_chip: ++ ++ #define gpio_get_value __gpio_get_value ++ #define gpio_set_value __gpio_set_value ++ #define gpio_cansleep __gpio_cansleep ++ ++Fancier implementations could instead define those as inline functions with ++logic optimizing access to specific SOC-based GPIOs. For example, if the ++referenced GPIO is the constant "12", getting or setting its value could ++cost as little as two or three instructions, never sleeping. When such an ++optimization is not possible those calls must delegate to the framework ++code, costing at least a few dozen instructions. For bitbanged I/O, such ++instruction savings can be significant. ++ ++For SOCs, platform-specific code defines and registers gpio_chip instances ++for each bank of on-chip GPIOs. Those GPIOs should be numbered/labeled to ++match chip vendor documentation, and directly match board schematics. They ++may well start at zero and go up to a platform-specific limit. Such GPIOs ++are normally integrated into platform initialization to make them always be ++available, from arch_initcall() or earlier; they can often serve as IRQs. ++ ++ ++Board Support ++------------- ++For external GPIO controllers -- such as I2C or SPI expanders, ASICs, multi ++function devices, FPGAs or CPLDs -- most often board-specific code handles ++registering controller devices and ensures that their drivers know what GPIO ++numbers to use with gpiochip_add(). Their numbers often start right after ++platform-specific GPIOs. ++ ++For example, board setup code could create structures identifying the range ++of GPIOs that chip will expose, and passes them to each GPIO expander chip ++using platform_data. Then the chip driver's probe() routine could pass that ++data to gpiochip_add(). ++ ++Initialization order can be important. For example, when a device relies on ++an I2C-based GPIO, its probe() routine should only be called after that GPIO ++becomes available. That may mean the device should not be registered until ++calls for that GPIO can work. One way to address such dependencies is for ++such gpio_chip controllers to provide setup() and teardown() callbacks to ++board specific code; those board specific callbacks would register devices ++once all the necessary resources are available. +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0025-Signed-off-by-Dmitry-Baryshkov-dbaryshkov-gmail.co.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0025-Signed-off-by-Dmitry-Baryshkov-dbaryshkov-gmail.co.patch new file mode 100644 index 0000000000..84d0fd3e19 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0025-Signed-off-by-Dmitry-Baryshkov-dbaryshkov-gmail.co.patch @@ -0,0 +1,434 @@ +From 39717c1328f6aa13330eded0e0e268993cfd1eea Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Tue, 12 Feb 2008 10:39:53 +0300 +Subject: [PATCH 25/64] Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> + +--- + arch/arm/mach-pxa/Makefile | 2 +- + arch/arm/mach-pxa/devices.c | 401 +++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 402 insertions(+), 1 deletions(-) + create mode 100644 arch/arm/mach-pxa/devices.c + +diff --git a/arch/arm/mach-pxa/Makefile b/arch/arm/mach-pxa/Makefile +index 5cb0216..f276d24 100644 +--- a/arch/arm/mach-pxa/Makefile ++++ b/arch/arm/mach-pxa/Makefile +@@ -4,7 +4,7 @@ + + # Common support (must be linked before board specific support) + obj-y += clock.o generic.o irq.o dma.o \ +- time.o gpio.o ++ time.o gpio.o devices.o + obj-$(CONFIG_PXA25x) += pxa25x.o + obj-$(CONFIG_PXA27x) += pxa27x.o + obj-$(CONFIG_PXA3xx) += pxa3xx.o mfp.o +diff --git a/arch/arm/mach-pxa/devices.c b/arch/arm/mach-pxa/devices.c +new file mode 100644 +index 0000000..928131a +--- /dev/null ++++ b/arch/arm/mach-pxa/devices.c +@@ -0,0 +1,401 @@ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/platform_device.h> ++#include <linux/dma-mapping.h> ++ ++#include <asm/arch/gpio.h> ++#include <asm/arch/udc.h> ++#include <asm/arch/pxafb.h> ++#include <asm/arch/mmc.h> ++#include <asm/arch/irda.h> ++#include <asm/arch/i2c.h> ++#include <asm/arch/ohci.h> ++ ++#include "devices.h" ++ ++#ifdef CONFIG_PXA25x ++ ++static u64 pxa25x_ssp_dma_mask = DMA_BIT_MASK(32); ++ ++static struct resource pxa25x_resource_ssp[] = { ++ [0] = { ++ .start = 0x41000000, ++ .end = 0x4100001f, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_SSP, ++ .end = IRQ_SSP, ++ .flags = IORESOURCE_IRQ, ++ }, ++ [2] = { ++ /* DRCMR for RX */ ++ .start = 13, ++ .end = 13, ++ .flags = IORESOURCE_DMA, ++ }, ++ [3] = { ++ /* DRCMR for TX */ ++ .start = 14, ++ .end = 14, ++ .flags = IORESOURCE_DMA, ++ }, ++}; ++ ++struct platform_device pxa25x_device_ssp = { ++ .name = "pxa25x-ssp", ++ .id = 0, ++ .dev = { ++ .dma_mask = &pxa25x_ssp_dma_mask, ++ .coherent_dma_mask = DMA_BIT_MASK(32), ++ }, ++ .resource = pxa25x_resource_ssp, ++ .num_resources = ARRAY_SIZE(pxa25x_resource_ssp), ++}; ++ ++static u64 pxa25x_nssp_dma_mask = DMA_BIT_MASK(32); ++ ++static struct resource pxa25x_resource_nssp[] = { ++ [0] = { ++ .start = 0x41400000, ++ .end = 0x4140002f, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_NSSP, ++ .end = IRQ_NSSP, ++ .flags = IORESOURCE_IRQ, ++ }, ++ [2] = { ++ /* DRCMR for RX */ ++ .start = 15, ++ .end = 15, ++ .flags = IORESOURCE_DMA, ++ }, ++ [3] = { ++ /* DRCMR for TX */ ++ .start = 16, ++ .end = 16, ++ .flags = IORESOURCE_DMA, ++ }, ++}; ++ ++struct platform_device pxa25x_device_nssp = { ++ .name = "pxa25x-nssp", ++ .id = 1, ++ .dev = { ++ .dma_mask = &pxa25x_nssp_dma_mask, ++ .coherent_dma_mask = DMA_BIT_MASK(32), ++ }, ++ .resource = pxa25x_resource_nssp, ++ .num_resources = ARRAY_SIZE(pxa25x_resource_nssp), ++}; ++ ++static u64 pxa25x_assp_dma_mask = DMA_BIT_MASK(32); ++ ++static struct resource pxa25x_resource_assp[] = { ++ [0] = { ++ .start = 0x41500000, ++ .end = 0x4150002f, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_ASSP, ++ .end = IRQ_ASSP, ++ .flags = IORESOURCE_IRQ, ++ }, ++ [2] = { ++ /* DRCMR for RX */ ++ .start = 23, ++ .end = 23, ++ .flags = IORESOURCE_DMA, ++ }, ++ [3] = { ++ /* DRCMR for TX */ ++ .start = 24, ++ .end = 24, ++ .flags = IORESOURCE_DMA, ++ }, ++}; ++ ++struct platform_device pxa25x_device_assp = { ++ /* ASSP is basically equivalent to NSSP */ ++ .name = "pxa25x-nssp", ++ .id = 2, ++ .dev = { ++ .dma_mask = &pxa25x_assp_dma_mask, ++ .coherent_dma_mask = DMA_BIT_MASK(32), ++ }, ++ .resource = pxa25x_resource_assp, ++ .num_resources = ARRAY_SIZE(pxa25x_resource_assp), ++}; ++#endif /* CONFIG_PXA25x */ ++ ++#if defined(CONFIG_PXA27x) || defined(CONFIG_PXA3xx) ++ ++static u64 pxa27x_ohci_dma_mask = DMA_BIT_MASK(32); ++ ++static struct resource pxa27x_resource_ohci[] = { ++ [0] = { ++ .start = 0x4C000000, ++ .end = 0x4C00ff6f, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_USBH1, ++ .end = IRQ_USBH1, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++struct platform_device pxa27x_device_ohci = { ++ .name = "pxa27x-ohci", ++ .id = -1, ++ .dev = { ++ .dma_mask = &pxa27x_ohci_dma_mask, ++ .coherent_dma_mask = DMA_BIT_MASK(32), ++ }, ++ .num_resources = ARRAY_SIZE(pxa27x_resource_ohci), ++ .resource = pxa27x_resource_ohci, ++}; ++ ++void __init pxa_set_ohci_info(struct pxaohci_platform_data *info) ++{ ++ pxa_register_device(&pxa27x_device_ohci, info); ++} ++ ++static u64 pxa27x_ssp1_dma_mask = DMA_BIT_MASK(32); ++ ++static struct resource pxa27x_resource_ssp1[] = { ++ [0] = { ++ .start = 0x41000000, ++ .end = 0x4100003f, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_SSP, ++ .end = IRQ_SSP, ++ .flags = IORESOURCE_IRQ, ++ }, ++ [2] = { ++ /* DRCMR for RX */ ++ .start = 13, ++ .end = 13, ++ .flags = IORESOURCE_DMA, ++ }, ++ [3] = { ++ /* DRCMR for TX */ ++ .start = 14, ++ .end = 14, ++ .flags = IORESOURCE_DMA, ++ }, ++}; ++ ++struct platform_device pxa27x_device_ssp1 = { ++ .name = "pxa27x-ssp", ++ .id = 0, ++ .dev = { ++ .dma_mask = &pxa27x_ssp1_dma_mask, ++ .coherent_dma_mask = DMA_BIT_MASK(32), ++ }, ++ .resource = pxa27x_resource_ssp1, ++ .num_resources = ARRAY_SIZE(pxa27x_resource_ssp1), ++}; ++ ++static u64 pxa27x_ssp2_dma_mask = DMA_BIT_MASK(32); ++ ++static struct resource pxa27x_resource_ssp2[] = { ++ [0] = { ++ .start = 0x41700000, ++ .end = 0x4170003f, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_SSP2, ++ .end = IRQ_SSP2, ++ .flags = IORESOURCE_IRQ, ++ }, ++ [2] = { ++ /* DRCMR for RX */ ++ .start = 15, ++ .end = 15, ++ .flags = IORESOURCE_DMA, ++ }, ++ [3] = { ++ /* DRCMR for TX */ ++ .start = 16, ++ .end = 16, ++ .flags = IORESOURCE_DMA, ++ }, ++}; ++ ++struct platform_device pxa27x_device_ssp2 = { ++ .name = "pxa27x-ssp", ++ .id = 1, ++ .dev = { ++ .dma_mask = &pxa27x_ssp2_dma_mask, ++ .coherent_dma_mask = DMA_BIT_MASK(32), ++ }, ++ .resource = pxa27x_resource_ssp2, ++ .num_resources = ARRAY_SIZE(pxa27x_resource_ssp2), ++}; ++ ++static u64 pxa27x_ssp3_dma_mask = DMA_BIT_MASK(32); ++ ++static struct resource pxa27x_resource_ssp3[] = { ++ [0] = { ++ .start = 0x41900000, ++ .end = 0x4190003f, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_SSP3, ++ .end = IRQ_SSP3, ++ .flags = IORESOURCE_IRQ, ++ }, ++ [2] = { ++ /* DRCMR for RX */ ++ .start = 66, ++ .end = 66, ++ .flags = IORESOURCE_DMA, ++ }, ++ [3] = { ++ /* DRCMR for TX */ ++ .start = 67, ++ .end = 67, ++ .flags = IORESOURCE_DMA, ++ }, ++}; ++ ++struct platform_device pxa27x_device_ssp3 = { ++ .name = "pxa27x-ssp", ++ .id = 2, ++ .dev = { ++ .dma_mask = &pxa27x_ssp3_dma_mask, ++ .coherent_dma_mask = DMA_BIT_MASK(32), ++ }, ++ .resource = pxa27x_resource_ssp3, ++ .num_resources = ARRAY_SIZE(pxa27x_resource_ssp3), ++}; ++#endif /* CONFIG_PXA27x || CONFIG_PXA3xx */ ++ ++#ifdef CONFIG_PXA3xx ++static u64 pxa3xx_ssp4_dma_mask = DMA_BIT_MASK(32); ++ ++static struct resource pxa3xx_resource_ssp4[] = { ++ [0] = { ++ .start = 0x41a00000, ++ .end = 0x41a0003f, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_SSP4, ++ .end = IRQ_SSP4, ++ .flags = IORESOURCE_IRQ, ++ }, ++ [2] = { ++ /* DRCMR for RX */ ++ .start = 2, ++ .end = 2, ++ .flags = IORESOURCE_DMA, ++ }, ++ [3] = { ++ /* DRCMR for TX */ ++ .start = 3, ++ .end = 3, ++ .flags = IORESOURCE_DMA, ++ }, ++}; ++ ++struct platform_device pxa3xx_device_ssp4 = { ++ /* PXA3xx SSP is basically equivalent to PXA27x */ ++ .name = "pxa27x-ssp", ++ .id = 3, ++ .dev = { ++ .dma_mask = &pxa3xx_ssp4_dma_mask, ++ .coherent_dma_mask = DMA_BIT_MASK(32), ++ }, ++ .resource = pxa3xx_resource_ssp4, ++ .num_resources = ARRAY_SIZE(pxa3xx_resource_ssp4), ++}; ++ ++static struct resource pxa3xx_resources_mci2[] = { ++ [0] = { ++ .start = 0x42000000, ++ .end = 0x42000fff, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_MMC2, ++ .end = IRQ_MMC2, ++ .flags = IORESOURCE_IRQ, ++ }, ++ [2] = { ++ .start = 93, ++ .end = 93, ++ .flags = IORESOURCE_DMA, ++ }, ++ [3] = { ++ .start = 94, ++ .end = 94, ++ .flags = IORESOURCE_DMA, ++ }, ++}; ++ ++struct platform_device pxa3xx_device_mci2 = { ++ .name = "pxa2xx-mci", ++ .id = 1, ++ .dev = { ++ .dma_mask = &pxamci_dmamask, ++ .coherent_dma_mask = 0xffffffff, ++ }, ++ .num_resources = ARRAY_SIZE(pxa3xx_resources_mci2), ++ .resource = pxa3xx_resources_mci2, ++}; ++ ++void __init pxa3xx_set_mci2_info(struct pxamci_platform_data *info) ++{ ++ pxa_register_device(&pxa3xx_device_mci2, info); ++} ++ ++static struct resource pxa3xx_resources_mci3[] = { ++ [0] = { ++ .start = 0x42500000, ++ .end = 0x42500fff, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_MMC3, ++ .end = IRQ_MMC3, ++ .flags = IORESOURCE_IRQ, ++ }, ++ [2] = { ++ .start = 100, ++ .end = 100, ++ .flags = IORESOURCE_DMA, ++ }, ++ [3] = { ++ .start = 101, ++ .end = 101, ++ .flags = IORESOURCE_DMA, ++ }, ++}; ++ ++struct platform_device pxa3xx_device_mci3 = { ++ .name = "pxa2xx-mci", ++ .id = 2, ++ .dev = { ++ .dma_mask = &pxamci_dmamask, ++ .coherent_dma_mask = 0xffffffff, ++ }, ++ .num_resources = ARRAY_SIZE(pxa3xx_resources_mci3), ++ .resource = pxa3xx_resources_mci3, ++}; ++ ++void __init pxa3xx_set_mci3_info(struct pxamci_platform_data *info) ++{ ++ pxa_register_device(&pxa3xx_device_mci3, info); ++} ++ ++#endif /* CONFIG_PXA3xx */ +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0026-I-don-t-think-we-should-check-for-IRQs-when-determin.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0026-I-don-t-think-we-should-check-for-IRQs-when-determin.patch new file mode 100644 index 0000000000..e1323e4edc --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0026-I-don-t-think-we-should-check-for-IRQs-when-determin.patch @@ -0,0 +1,134 @@ +From cbe46408b666983284e8be290950d526dbc0f0a4 Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Wed, 9 Jan 2008 02:08:16 +0300 +Subject: [PATCH 26/64] I don't think we should check for IRQs when determining which one + of power supplies to register. Better use is_{ac,usb}_online + callbacks, this will not produce an obstacle to implement polling -- + when irqs aren't mandatory. I'll send my two pending patches to show + the idea. + +For this particular issue, I think something like that should work. +If it works for you, I'll commit that version, preserving your +authorship, of course. +--- + drivers/power/pda_power.c | 80 ++++++++++++++++++++++++-------------------- + 1 files changed, 44 insertions(+), 36 deletions(-) + +diff --git a/drivers/power/pda_power.c b/drivers/power/pda_power.c +index c058f28..d98622f 100644 +--- a/drivers/power/pda_power.c ++++ b/drivers/power/pda_power.c +@@ -168,66 +168,74 @@ static int pda_power_probe(struct platform_device *pdev) + pda_power_supplies[1].num_supplicants = pdata->num_supplicants; + } + +- ret = power_supply_register(&pdev->dev, &pda_power_supplies[0]); +- if (ret) { +- dev_err(dev, "failed to register %s power supply\n", +- pda_power_supplies[0].name); +- goto supply0_failed; +- } ++ if (pdata->is_ac_online) { ++ ret = power_supply_register(&pdev->dev, &pda_power_supplies[0]); ++ if (ret) { ++ dev_err(dev, "failed to register %s power supply\n", ++ pda_power_supplies[0].name); ++ goto ac_supply_failed; ++ } + +- ret = power_supply_register(&pdev->dev, &pda_power_supplies[1]); +- if (ret) { +- dev_err(dev, "failed to register %s power supply\n", +- pda_power_supplies[1].name); +- goto supply1_failed; ++ if (ac_irq) { ++ ret = request_irq(ac_irq->start, power_changed_isr, ++ get_irq_flags(ac_irq), ac_irq->name, ++ &pda_power_supplies[0]); ++ if (ret) { ++ dev_err(dev, "request ac irq failed\n"); ++ goto ac_irq_failed; ++ } ++ } + } + +- if (ac_irq) { +- ret = request_irq(ac_irq->start, power_changed_isr, +- get_irq_flags(ac_irq), ac_irq->name, +- &pda_power_supplies[0]); ++ if (pdata->is_usb_online) { ++ ret = power_supply_register(&pdev->dev, &pda_power_supplies[1]); + if (ret) { +- dev_err(dev, "request ac irq failed\n"); +- goto ac_irq_failed; ++ dev_err(dev, "failed to register %s power supply\n", ++ pda_power_supplies[1].name); ++ goto usb_supply_failed; + } +- } + +- if (usb_irq) { +- ret = request_irq(usb_irq->start, power_changed_isr, +- get_irq_flags(usb_irq), usb_irq->name, +- &pda_power_supplies[1]); +- if (ret) { +- dev_err(dev, "request usb irq failed\n"); +- goto usb_irq_failed; ++ if (usb_irq) { ++ ret = request_irq(usb_irq->start, power_changed_isr, ++ get_irq_flags(usb_irq), ++ usb_irq->name, ++ &pda_power_supplies[1]); ++ if (ret) { ++ dev_err(dev, "request usb irq failed\n"); ++ goto usb_irq_failed; ++ } + } + } + +- goto success; ++ return 0; + + usb_irq_failed: +- if (ac_irq) ++ if (pdata->is_usb_online) ++ power_supply_unregister(&pda_power_supplies[1]); ++usb_supply_failed: ++ if (pdata->is_ac_online && ac_irq) + free_irq(ac_irq->start, &pda_power_supplies[0]); + ac_irq_failed: +- power_supply_unregister(&pda_power_supplies[1]); +-supply1_failed: +- power_supply_unregister(&pda_power_supplies[0]); +-supply0_failed: ++ if (pdata->is_ac_online) ++ power_supply_unregister(&pda_power_supplies[0]); ++ac_supply_failed: + noirqs: + wrongid: +-success: + return ret; + } + + static int pda_power_remove(struct platform_device *pdev) + { +- if (usb_irq) ++ if (pdata->is_usb_online && usb_irq) + free_irq(usb_irq->start, &pda_power_supplies[1]); +- if (ac_irq) ++ if (pdata->is_ac_online && ac_irq) + free_irq(ac_irq->start, &pda_power_supplies[0]); + del_timer_sync(&charger_timer); + del_timer_sync(&supply_timer); +- power_supply_unregister(&pda_power_supplies[1]); +- power_supply_unregister(&pda_power_supplies[0]); ++ if (pdata->is_usb_online) ++ power_supply_unregister(&pda_power_supplies[1]); ++ if (pdata->is_ac_online) ++ power_supply_unregister(&pda_power_supplies[0]); + return 0; + } + +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0027-Add-LiMn-one-of-the-most-common-for-small-non-recha.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0027-Add-LiMn-one-of-the-most-common-for-small-non-recha.patch new file mode 100644 index 0000000000..240d2d0bd9 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0027-Add-LiMn-one-of-the-most-common-for-small-non-recha.patch @@ -0,0 +1,59 @@ +From e5e9808fd5ed9cb54dd9da9fb91b32c4f7e9da52 Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Wed, 9 Jan 2008 02:08:17 +0300 +Subject: [PATCH 27/64] Add LiMn (one of the most common for small non-rechargable batteries)i + battery technology and voltage_min/_max properties support. + +Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> +--- + drivers/power/power_supply_sysfs.c | 5 ++++- + include/linux/power_supply.h | 3 +++ + 2 files changed, 7 insertions(+), 1 deletions(-) + +diff --git a/drivers/power/power_supply_sysfs.c b/drivers/power/power_supply_sysfs.c +index 249f61b..45d2f95 100644 +--- a/drivers/power/power_supply_sysfs.c ++++ b/drivers/power/power_supply_sysfs.c +@@ -46,7 +46,8 @@ static ssize_t power_supply_show_property(struct device *dev, + "Unspecified failure" + }; + static char *technology_text[] = { +- "Unknown", "NiMH", "Li-ion", "Li-poly", "LiFe", "NiCd" ++ "Unknown", "NiMH", "Li-ion", "Li-poly", "LiFe", "NiCd", ++ "LiMn" + }; + static char *capacity_level_text[] = { + "Unknown", "Critical", "Low", "Normal", "High", "Full" +@@ -88,6 +89,8 @@ static struct device_attribute power_supply_attrs[] = { + POWER_SUPPLY_ATTR(present), + POWER_SUPPLY_ATTR(online), + POWER_SUPPLY_ATTR(technology), ++ POWER_SUPPLY_ATTR(voltage_max), ++ POWER_SUPPLY_ATTR(voltage_min), + POWER_SUPPLY_ATTR(voltage_max_design), + POWER_SUPPLY_ATTR(voltage_min_design), + POWER_SUPPLY_ATTR(voltage_now), +diff --git a/include/linux/power_supply.h b/include/linux/power_supply.h +index 606c095..cdbc5b8 100644 +--- a/include/linux/power_supply.h ++++ b/include/linux/power_supply.h +@@ -54,6 +54,7 @@ enum { + POWER_SUPPLY_TECHNOLOGY_LIPO, + POWER_SUPPLY_TECHNOLOGY_LiFe, + POWER_SUPPLY_TECHNOLOGY_NiCd, ++ POWER_SUPPLY_TECHNOLOGY_LiMn, + }; + + enum { +@@ -72,6 +73,8 @@ enum power_supply_property { + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_ONLINE, + POWER_SUPPLY_PROP_TECHNOLOGY, ++ POWER_SUPPLY_PROP_VOLTAGE_MAX, ++ POWER_SUPPLY_PROP_VOLTAGE_MIN, + POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, + POWER_SUPPLY_PROP_VOLTAGE_NOW, +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0028-Add-suspend-resume-wakeup-support-for-pda_power.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0028-Add-suspend-resume-wakeup-support-for-pda_power.patch new file mode 100644 index 0000000000..ac5df97dff --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0028-Add-suspend-resume-wakeup-support-for-pda_power.patch @@ -0,0 +1,72 @@ +From df0801d2cd6a7081700c79f437d1185cbe1960a7 Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Wed, 9 Jan 2008 02:08:18 +0300 +Subject: [PATCH 28/64] Add suspend/resume/wakeup support for pda_power. + Now with device_init_wakeup. + +Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> +--- + drivers/power/pda_power.c | 34 ++++++++++++++++++++++++++++++++++ + 1 files changed, 34 insertions(+), 0 deletions(-) + +diff --git a/drivers/power/pda_power.c b/drivers/power/pda_power.c +index d98622f..28360e8 100644 +--- a/drivers/power/pda_power.c ++++ b/drivers/power/pda_power.c +@@ -207,6 +207,8 @@ static int pda_power_probe(struct platform_device *pdev) + } + } + ++ device_init_wakeup(&pdev->dev, 1); ++ + return 0; + + usb_irq_failed: +@@ -239,12 +241,44 @@ static int pda_power_remove(struct platform_device *pdev) + return 0; + } + ++#ifdef CONFIG_PM ++static int pda_power_suspend(struct platform_device *pdev, pm_message_t state) ++{ ++ if (device_may_wakeup(&pdev->dev)) { ++ if (ac_irq) ++ enable_irq_wake(ac_irq->start); ++ if (usb_irq) ++ enable_irq_wake(usb_irq->start); ++ } ++ ++ return 0; ++} ++ ++static int pda_power_resume(struct platform_device *pdev) ++{ ++ if (device_may_wakeup(&pdev->dev)) { ++ if (usb_irq) ++ disable_irq_wake(usb_irq->start); ++ if (ac_irq) ++ disable_irq_wake(ac_irq->start); ++ } ++ ++ return 0; ++} ++#else ++#define pda_power_suspend NULL ++#define pda_power_resume NULL ++#endif ++ ++ + static struct platform_driver pda_power_pdrv = { + .driver = { + .name = "pda-power", + }, + .probe = pda_power_probe, + .remove = pda_power_remove, ++ .suspend = pda_power_suspend, ++ .resume = pda_power_resume, + }; + + static int __init pda_power_init(void) +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0029-Support-using-VOLTAGE_-properties-for-apm-calculati.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0029-Support-using-VOLTAGE_-properties-for-apm-calculati.patch new file mode 100644 index 0000000000..7347fd5a00 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0029-Support-using-VOLTAGE_-properties-for-apm-calculati.patch @@ -0,0 +1,163 @@ +From 57d1450b4e5f27fa78c75895dc30213bde7191bc Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Wed, 9 Jan 2008 02:08:18 +0300 +Subject: [PATCH 29/64] Support using VOLTAGE_* properties for apm calculations. It's pretty + dummy, but useful for batteries for which we can only get voltages. + +--- + drivers/power/apm_power.c | 63 ++++++++++++++++++++++++++++++++++++-------- + 1 files changed, 51 insertions(+), 12 deletions(-) + +diff --git a/drivers/power/apm_power.c b/drivers/power/apm_power.c +index bbf3ee1..526c96e 100644 +--- a/drivers/power/apm_power.c ++++ b/drivers/power/apm_power.c +@@ -13,6 +13,12 @@ + #include <linux/power_supply.h> + #include <linux/apm-emulation.h> + ++typedef enum { ++ SOURCE_ENERGY, ++ SOURCE_CHARGE, ++ SOURCE_VOLTAGE, ++} apm_source; ++ + #define PSY_PROP(psy, prop, val) psy->get_property(psy, \ + POWER_SUPPLY_PROP_##prop, val) + +@@ -87,7 +93,7 @@ static void find_main_battery(void) + } + } + +-static int calculate_time(int status, int using_charge) ++static int calculate_time(int status, apm_source source) + { + union power_supply_propval full; + union power_supply_propval empty; +@@ -106,20 +112,34 @@ static int calculate_time(int status, int using_charge) + return -1; + } + +- if (using_charge) { ++ switch (source) { ++ case SOURCE_CHARGE: + full_prop = POWER_SUPPLY_PROP_CHARGE_FULL; + full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN; + empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY; + empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY; + cur_avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG; + cur_now_prop = POWER_SUPPLY_PROP_CHARGE_NOW; +- } else { ++ break; ++ case SOURCE_ENERGY: + full_prop = POWER_SUPPLY_PROP_ENERGY_FULL; + full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN; + empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY; + empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY; + cur_avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG; + cur_now_prop = POWER_SUPPLY_PROP_ENERGY_NOW; ++ break; ++ case SOURCE_VOLTAGE: ++ full_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX; ++ full_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN; ++ empty_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN; ++ empty_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN; ++ cur_avg_prop = POWER_SUPPLY_PROP_VOLTAGE_AVG; ++ cur_now_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW; ++ break; ++ default: ++ printk(KERN_ERR "Unsupported source: %d\n", source); ++ return -1; + } + + if (_MPSY_PROP(full_prop, &full)) { +@@ -146,7 +166,7 @@ static int calculate_time(int status, int using_charge) + return -((cur.intval - empty.intval) * 60L) / I.intval; + } + +-static int calculate_capacity(int using_charge) ++static int calculate_capacity(apm_source source) + { + enum power_supply_property full_prop, empty_prop; + enum power_supply_property full_design_prop, empty_design_prop; +@@ -154,20 +174,33 @@ static int calculate_capacity(int using_charge) + union power_supply_propval empty, full, cur; + int ret; + +- if (using_charge) { ++ switch (source) { ++ case SOURCE_CHARGE: + full_prop = POWER_SUPPLY_PROP_CHARGE_FULL; + empty_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY; + full_design_prop = POWER_SUPPLY_PROP_CHARGE_FULL_DESIGN; + empty_design_prop = POWER_SUPPLY_PROP_CHARGE_EMPTY_DESIGN; + now_prop = POWER_SUPPLY_PROP_CHARGE_NOW; + avg_prop = POWER_SUPPLY_PROP_CHARGE_AVG; +- } else { ++ break; ++ case SOURCE_ENERGY: + full_prop = POWER_SUPPLY_PROP_ENERGY_FULL; + empty_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY; + full_design_prop = POWER_SUPPLY_PROP_ENERGY_FULL_DESIGN; + empty_design_prop = POWER_SUPPLY_PROP_ENERGY_EMPTY_DESIGN; + now_prop = POWER_SUPPLY_PROP_ENERGY_NOW; + avg_prop = POWER_SUPPLY_PROP_ENERGY_AVG; ++ case SOURCE_VOLTAGE: ++ full_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX; ++ empty_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN; ++ full_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN; ++ empty_design_prop = POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN; ++ now_prop = POWER_SUPPLY_PROP_VOLTAGE_NOW; ++ avg_prop = POWER_SUPPLY_PROP_VOLTAGE_AVG; ++ break; ++ default: ++ printk(KERN_ERR "Unsupported source: %d\n", source); ++ return -1; + } + + if (_MPSY_PROP(full_prop, &full)) { +@@ -234,10 +267,12 @@ static void apm_battery_apm_get_power_status(struct apm_power_info *info) + info->battery_life = capacity.intval; + } else { + /* try calculate using energy */ +- info->battery_life = calculate_capacity(0); ++ info->battery_life = calculate_capacity(SOURCE_ENERGY); + /* if failed try calculate using charge instead */ + if (info->battery_life == -1) +- info->battery_life = calculate_capacity(1); ++ info->battery_life = calculate_capacity(SOURCE_CHARGE); ++ if (info->battery_life == -1) ++ info->battery_life = calculate_capacity(SOURCE_VOLTAGE); + } + + /* charging status */ +@@ -263,18 +298,22 @@ static void apm_battery_apm_get_power_status(struct apm_power_info *info) + !MPSY_PROP(TIME_TO_FULL_NOW, &time_to_full)) { + info->time = time_to_full.intval / 60; + } else { +- info->time = calculate_time(status.intval, 0); ++ info->time = calculate_time(status.intval, SOURCE_ENERGY); + if (info->time == -1) +- info->time = calculate_time(status.intval, 1); ++ info->time = calculate_time(status.intval, SOURCE_CHARGE); ++ if (info->time == -1) ++ info->time = calculate_time(status.intval, SOURCE_VOLTAGE); + } + } else { + if (!MPSY_PROP(TIME_TO_EMPTY_AVG, &time_to_empty) || + !MPSY_PROP(TIME_TO_EMPTY_NOW, &time_to_empty)) { + info->time = time_to_empty.intval / 60; + } else { +- info->time = calculate_time(status.intval, 0); ++ info->time = calculate_time(status.intval, SOURCE_ENERGY); ++ if (info->time == -1) ++ info->time = calculate_time(status.intval, SOURCE_CHARGE); + if (info->time == -1) +- info->time = calculate_time(status.intval, 1); ++ info->time = calculate_time(status.intval, SOURCE_VOLTAGE); + } + } + +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0030-Core-driver-for-WM97xx-touchscreens.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0030-Core-driver-for-WM97xx-touchscreens.patch new file mode 100644 index 0000000000..1c86a39c74 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0030-Core-driver-for-WM97xx-touchscreens.patch @@ -0,0 +1,1083 @@ +From d3e044e0e10e6c6b75716cb927e92b4ec284132f Mon Sep 17 00:00:00 2001 +From: Mark Brown <broonie@opensource.wolfsonmicro.com> +Date: Sat, 26 Jan 2008 21:14:20 +0300 +Subject: [PATCH 30/64] Core driver for WM97xx touchscreens + +This patch series adds support for the touchscreen controllers provided +by Wolfson Microelectronics WM97xx series chips in both polled and +streaming modes. + +These drivers have been maintained out of tree since 2003. During that +time the driver the primary maintainer was Liam Girdwood and a number of +people have made contributions including Stanley Cai, Rodolfo Giometti, +Russell King, Marc Kleine-Budde, Ian Molton, Vincent Sanders, Andrew +Zabolotny, Graeme Gregory, Mike Arthur and myself. Apologies to anyone +I have omitted. + +Signed-off-by: Liam Girdwood <liam.girdwood@wolfsonmicro.com> +Signed-off-by: Graeme Gregory <gg@opensource.wolfsonmicro.com> +Signed-off-by: Mike Arthur <mike.arthur@wolfsonmicro.com> +Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> +Cc: Dmitry Baryshkov <dbaryshkov@gmail.com> +Cc: Stanley Cai <stanley.cai@intel.com> +Cc: Rodolfo Giometti <giometti@enneenne.com> +Cc: Russell King <rmk@arm.linux.org.uk> +Cc: Marc Kleine-Budde <mkl@pengutronix.de> +Cc: Ian Molton <spyro@f2s.com> +Cc: Vincent Sanders <vince@kyllikki.org> +Cc: Andrew Zabolotny <zap@homelink.ru> +--- + drivers/input/touchscreen/wm97xx-core.c | 724 +++++++++++++++++++++++++++++++ + include/linux/wm97xx.h | 309 +++++++++++++ + 2 files changed, 1033 insertions(+), 0 deletions(-) + create mode 100644 drivers/input/touchscreen/wm97xx-core.c + create mode 100644 include/linux/wm97xx.h + +diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c +new file mode 100644 +index 0000000..27a0a99 +--- /dev/null ++++ b/drivers/input/touchscreen/wm97xx-core.c +@@ -0,0 +1,724 @@ ++/* ++ * wm97xx-core.c -- Touch screen driver core for Wolfson WM9705, WM9712 ++ * and WM9713 AC97 Codecs. ++ * ++ * Copyright 2003, 2004, 2005, 2006, 2007, 2008 Wolfson Microelectronics PLC. ++ * Author: Liam Girdwood ++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com ++ * Parts Copyright : Ian Molton <spyro@f2s.com> ++ * Andrew Zabolotny <zap@homelink.ru> ++ * Russell King <rmk@arm.linux.org.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * Notes: ++ * ++ * Features: ++ * - supports WM9705, WM9712, WM9713 ++ * - polling mode ++ * - continuous mode (arch-dependent) ++ * - adjustable rpu/dpp settings ++ * - adjustable pressure current ++ * - adjustable sample settle delay ++ * - 4 and 5 wire touchscreens (5 wire is WM9712 only) ++ * - pen down detection ++ * - battery monitor ++ * - sample AUX adcs ++ * - power management ++ * - codec GPIO ++ * - codec event notification ++ * Todo ++ * - Support for async sampling control for noisy LCDs. ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/version.h> ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/delay.h> ++#include <linux/string.h> ++#include <linux/proc_fs.h> ++#include <linux/pm.h> ++#include <linux/interrupt.h> ++#include <linux/bitops.h> ++#include <linux/workqueue.h> ++#include <linux/wm97xx.h> ++#include <linux/uaccess.h> ++#include <linux/io.h> ++ ++#define TS_NAME "wm97xx" ++#define WM_CORE_VERSION "0.65" ++#define DEFAULT_PRESSURE 0xb0c0 ++ ++ ++/* ++ * Touchscreen absolute values ++ * ++ * These parameters are used to help the input layer discard out of ++ * range readings and reduce jitter etc. ++ * ++ * o min, max:- indicate the min and max values your touch screen returns ++ * o fuzz:- use a higher number to reduce jitter ++ * ++ * The default values correspond to Mainstone II in QVGA mode ++ * ++ * Please read ++ * Documentation/input/input-programming.txt for more details. ++ */ ++ ++static int abs_x[3] = {350, 3900, 5}; ++module_param_array(abs_x, int, NULL, 0); ++MODULE_PARM_DESC(abs_x, "Touchscreen absolute X min, max, fuzz"); ++ ++static int abs_y[3] = {320, 3750, 40}; ++module_param_array(abs_y, int, NULL, 0); ++MODULE_PARM_DESC(abs_y, "Touchscreen absolute Y min, max, fuzz"); ++ ++static int abs_p[3] = {0, 150, 4}; ++module_param_array(abs_p, int, NULL, 0); ++MODULE_PARM_DESC(abs_p, "Touchscreen absolute Pressure min, max, fuzz"); ++ ++/* ++ * wm97xx IO access, all IO locking done by AC97 layer ++ */ ++int wm97xx_reg_read(struct wm97xx *wm, u16 reg) ++{ ++ if (wm->ac97) ++ return wm->ac97->bus->ops->read(wm->ac97, reg); ++ else ++ return -1; ++} ++EXPORT_SYMBOL_GPL(wm97xx_reg_read); ++ ++void wm97xx_reg_write(struct wm97xx *wm, u16 reg, u16 val) ++{ ++ /* cache digitiser registers */ ++ if (reg >= AC97_WM9713_DIG1 && reg <= AC97_WM9713_DIG3) ++ wm->dig[(reg - AC97_WM9713_DIG1) >> 1] = val; ++ ++ /* cache gpio regs */ ++ if (reg >= AC97_GPIO_CFG && reg <= AC97_MISC_AFE) ++ wm->gpio[(reg - AC97_GPIO_CFG) >> 1] = val; ++ ++ /* wm9713 irq reg */ ++ if (reg == 0x5a) ++ wm->misc = val; ++ ++ if (wm->ac97) ++ wm->ac97->bus->ops->write(wm->ac97, reg, val); ++} ++EXPORT_SYMBOL_GPL(wm97xx_reg_write); ++ ++/** ++ * wm97xx_read_aux_adc - Read the aux adc. ++ * @wm: wm97xx device. ++ * @adcsel: codec ADC to be read ++ * ++ * Reads the selected AUX ADC. ++ */ ++ ++int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel) ++{ ++ int power_adc = 0, auxval; ++ u16 power = 0; ++ ++ /* get codec */ ++ mutex_lock(&wm->codec_mutex); ++ ++ /* When the touchscreen is not in use, we may have to power up ++ * the AUX ADC before we can use sample the AUX inputs-> ++ */ ++ if (wm->id == WM9713_ID2 && ++ (power = wm97xx_reg_read(wm, AC97_EXTENDED_MID)) & 0x8000) { ++ power_adc = 1; ++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, power & 0x7fff); ++ } ++ ++ /* Prepare the codec for AUX reading */ ++ wm->codec->aux_prepare(wm); ++ ++ /* Turn polling mode on to read AUX ADC */ ++ wm->pen_probably_down = 1; ++ wm->codec->poll_sample(wm, adcsel, &auxval); ++ ++ if (power_adc) ++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, power | 0x8000); ++ ++ wm->codec->dig_restore(wm); ++ ++ wm->pen_probably_down = 0; ++ ++ mutex_unlock(&wm->codec_mutex); ++ return auxval & 0xfff; ++} ++EXPORT_SYMBOL_GPL(wm97xx_read_aux_adc); ++ ++/** ++ * wm97xx_get_gpio - Get the status of a codec GPIO. ++ * @wm: wm97xx device. ++ * @gpio: gpio ++ * ++ * Get the status of a codec GPIO pin ++ */ ++ ++enum wm97xx_gpio_status wm97xx_get_gpio(struct wm97xx *wm, u32 gpio) ++{ ++ u16 status; ++ enum wm97xx_gpio_status ret; ++ ++ mutex_lock(&wm->codec_mutex); ++ status = wm97xx_reg_read(wm, AC97_GPIO_STATUS); ++ ++ if (status & gpio) ++ ret = WM97XX_GPIO_HIGH; ++ else ++ ret = WM97XX_GPIO_LOW; ++ ++ mutex_unlock(&wm->codec_mutex); ++ return ret; ++} ++EXPORT_SYMBOL_GPL(wm97xx_get_gpio); ++ ++/** ++ * wm97xx_set_gpio - Set the status of a codec GPIO. ++ * @wm: wm97xx device. ++ * @gpio: gpio ++ * ++ * ++ * Set the status of a codec GPIO pin ++ */ ++ ++void wm97xx_set_gpio(struct wm97xx *wm, u32 gpio, ++ enum wm97xx_gpio_status status) ++{ ++ u16 reg; ++ ++ mutex_lock(&wm->codec_mutex); ++ reg = wm97xx_reg_read(wm, AC97_GPIO_STATUS); ++ ++ if (status & WM97XX_GPIO_HIGH) ++ reg |= gpio; ++ else ++ reg &= ~gpio; ++ ++ if (wm->id == WM9712_ID2) ++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg << 1); ++ else ++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, reg); ++ mutex_unlock(&wm->codec_mutex); ++} ++EXPORT_SYMBOL_GPL(wm97xx_set_gpio); ++ ++/* ++ * Codec GPIO pin configuration, this sets pin direction, polarity, ++ * stickyness and wake up. ++ */ ++void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio, enum wm97xx_gpio_dir dir, ++ enum wm97xx_gpio_pol pol, enum wm97xx_gpio_sticky sticky, ++ enum wm97xx_gpio_wake wake) ++{ ++ u16 reg; ++ ++ mutex_lock(&wm->codec_mutex); ++ reg = wm97xx_reg_read(wm, AC97_GPIO_POLARITY); ++ ++ if (pol == WM97XX_GPIO_POL_HIGH) ++ reg |= gpio; ++ else ++ reg &= ~gpio; ++ ++ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, reg); ++ reg = wm97xx_reg_read(wm, AC97_GPIO_STICKY); ++ ++ if (sticky == WM97XX_GPIO_STICKY) ++ reg |= gpio; ++ else ++ reg &= ~gpio; ++ ++ wm97xx_reg_write(wm, AC97_GPIO_STICKY, reg); ++ reg = wm97xx_reg_read(wm, AC97_GPIO_WAKEUP); ++ ++ if (wake == WM97XX_GPIO_WAKE) ++ reg |= gpio; ++ else ++ reg &= ~gpio; ++ ++ wm97xx_reg_write(wm, AC97_GPIO_WAKEUP, reg); ++ reg = wm97xx_reg_read(wm, AC97_GPIO_CFG); ++ ++ if (dir == WM97XX_GPIO_IN) ++ reg |= gpio; ++ else ++ reg &= ~gpio; ++ ++ wm97xx_reg_write(wm, AC97_GPIO_CFG, reg); ++ mutex_unlock(&wm->codec_mutex); ++} ++EXPORT_SYMBOL_GPL(wm97xx_config_gpio); ++ ++/* ++ * Handle a pen down interrupt. ++ */ ++static void wm97xx_pen_irq_worker(struct work_struct *work) ++{ ++ struct wm97xx *wm = container_of(work, struct wm97xx, pen_event_work); ++ ++ /* do we need to enable the touch panel reader */ ++ if (wm->id == WM9705_ID2) { ++ if (wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD) & ++ WM97XX_PEN_DOWN) ++ wm->pen_is_down = 1; ++ else ++ wm->pen_is_down = 0; ++ } else { ++ u16 status, pol; ++ mutex_lock(&wm->codec_mutex); ++ status = wm97xx_reg_read(wm, AC97_GPIO_STATUS); ++ pol = wm97xx_reg_read(wm, AC97_GPIO_POLARITY); ++ ++ if (WM97XX_GPIO_13 & pol & status) { ++ wm->pen_is_down = 1; ++ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, pol & ++ ~WM97XX_GPIO_13); ++ } else { ++ wm->pen_is_down = 0; ++ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, pol | ++ WM97XX_GPIO_13); ++ } ++ ++ if (wm->id == WM9712_ID2) ++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, (status & ++ ~WM97XX_GPIO_13) << 1); ++ else ++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, status & ++ ~WM97XX_GPIO_13); ++ mutex_unlock(&wm->codec_mutex); ++ } ++ ++ queue_delayed_work(wm->ts_workq, &wm->ts_reader, 0); ++ ++ if (!wm->pen_is_down && wm->mach_ops && wm->mach_ops->acc_enabled) ++ wm->mach_ops->acc_pen_up(wm); ++ wm->mach_ops->irq_enable(wm, 1); ++} ++ ++/* ++ * Codec PENDOWN irq handler ++ * ++ * We have to disable the codec interrupt in the handler because it can ++ * take upto 1ms to clear the interrupt source. The interrupt is then enabled ++ * again in the slow handler when the source has been cleared. ++ */ ++static irqreturn_t wm97xx_pen_interrupt(int irq, void *dev_id) ++{ ++ struct wm97xx *wm = dev_id; ++ wm->mach_ops->irq_enable(wm, 0); ++ queue_work(wm->ts_workq, &wm->pen_event_work); ++ return IRQ_HANDLED; ++} ++ ++/* ++ * initialise pen IRQ handler and workqueue ++ */ ++static int wm97xx_init_pen_irq(struct wm97xx *wm) ++{ ++ u16 reg; ++ ++ /* If an interrupt is supplied an IRQ enable operation must also be ++ * provided. */ ++ BUG_ON(!wm->mach_ops->irq_enable); ++ ++ INIT_WORK(&wm->pen_event_work, wm97xx_pen_irq_worker); ++ ++ if (request_irq(wm->pen_irq, wm97xx_pen_interrupt, IRQF_SHARED, ++ "wm97xx-pen", wm)) { ++ dev_err(wm->dev, ++ "Failed to register pen down interrupt, polling"); ++ wm->pen_irq = 0; ++ return -EINVAL; ++ } ++ ++ /* enable PEN down on wm9712/13 */ ++ if (wm->id != WM9705_ID2) { ++ reg = wm97xx_reg_read(wm, AC97_MISC_AFE); ++ wm97xx_reg_write(wm, AC97_MISC_AFE, reg & 0xfffb); ++ reg = wm97xx_reg_read(wm, 0x5a); ++ wm97xx_reg_write(wm, 0x5a, reg & ~0x0001); ++ } ++ ++ return 0; ++} ++ ++static int wm97xx_read_samples(struct wm97xx *wm) ++{ ++ struct wm97xx_data data; ++ int rc; ++ ++ mutex_lock(&wm->codec_mutex); ++ ++ if (wm->mach_ops && wm->mach_ops->acc_enabled) ++ rc = wm->mach_ops->acc_pen_down(wm); ++ else ++ rc = wm->codec->poll_touch(wm, &data); ++ ++ if (rc & RC_PENUP) { ++ if (wm->pen_is_down) { ++ wm->pen_is_down = 0; ++ dev_dbg(wm->dev, "pen up\n"); ++ input_report_abs(wm->input_dev, ABS_PRESSURE, 0); ++ input_sync(wm->input_dev); ++ } else if (!(rc & RC_AGAIN)) { ++ /* We need high frequency updates only while ++ * pen is down, the user never will be able to ++ * touch screen faster than a few times per ++ * second... On the other hand, when the user ++ * is actively working with the touchscreen we ++ * don't want to lose the quick response. So we ++ * will slowly increase sleep time after the ++ * pen is up and quicky restore it to ~one task ++ * switch when pen is down again. ++ */ ++ if (wm->ts_reader_interval < HZ / 10) ++ wm->ts_reader_interval++; ++ } ++ ++ } else if (rc & RC_VALID) { ++ dev_dbg(wm->dev, ++ "pen down: x=%x:%d, y=%x:%d, pressure=%x:%d\n", ++ data.x >> 12, data.x & 0xfff, data.y >> 12, ++ data.y & 0xfff, data.p >> 12, data.p & 0xfff); ++ input_report_abs(wm->input_dev, ABS_X, data.x & 0xfff); ++ input_report_abs(wm->input_dev, ABS_Y, data.y & 0xfff); ++ input_report_abs(wm->input_dev, ABS_PRESSURE, data.p & 0xfff); ++ input_sync(wm->input_dev); ++ wm->pen_is_down = 1; ++ wm->ts_reader_interval = wm->ts_reader_min_interval; ++ } else if (rc & RC_PENDOWN) { ++ dev_dbg(wm->dev, "pen down"); ++ wm->pen_is_down = 1; ++ wm->ts_reader_interval = wm->ts_reader_min_interval; ++ } ++ ++ mutex_unlock(&wm->codec_mutex); ++ return rc; ++} ++ ++/* ++* The touchscreen sample reader. ++*/ ++static void wm97xx_ts_reader(struct work_struct *work) ++{ ++ int rc; ++ struct wm97xx *wm = container_of(work, struct wm97xx, ts_reader.work); ++ ++ BUG_ON(!wm->codec); ++ ++ do { ++ rc = wm97xx_read_samples(wm); ++ } while (rc & RC_AGAIN); ++ ++ if (wm->pen_is_down || !wm->pen_irq) ++ queue_delayed_work(wm->ts_workq, &wm->ts_reader, ++ wm->ts_reader_interval); ++} ++ ++/** ++ * wm97xx_ts_input_open - Open the touch screen input device. ++ * @idev: Input device to be opened. ++ * ++ * Called by the input sub system to open a wm97xx touchscreen device. ++ * Starts the touchscreen thread and touch digitiser. ++ */ ++static int wm97xx_ts_input_open(struct input_dev *idev) ++{ ++ struct wm97xx *wm = input_get_drvdata(idev); ++ ++ wm->ts_workq = create_singlethread_workqueue("kwm97xx"); ++ if (wm->ts_workq == NULL) { ++ dev_err(wm->dev, ++ "Failed to create workqueue\n"); ++ return -EINVAL; ++ } ++ ++ /* start digitiser */ ++ if (wm->mach_ops && wm->mach_ops->acc_enabled) ++ wm->codec->acc_enable(wm, 1); ++ wm->codec->dig_enable(wm, 1); ++ ++ INIT_DELAYED_WORK(&wm->ts_reader, wm97xx_ts_reader); ++ ++ wm->ts_reader_min_interval = HZ >= 100 ? HZ / 100 : 1; ++ if (wm->ts_reader_min_interval < 1) ++ wm->ts_reader_min_interval = 1; ++ wm->ts_reader_interval = wm->ts_reader_min_interval; ++ ++ wm->pen_is_down = 0; ++ if (wm->pen_irq) ++ wm97xx_init_pen_irq(wm); ++ else ++ dev_err(wm->dev, "No IRQ specified\n"); ++ ++ /* If we either don't have an interrupt for pen down events or ++ * failed to acquire it then we need to poll. ++ */ ++ if (wm->pen_irq == 0) ++ queue_delayed_work(wm->ts_workq, &wm->ts_reader, ++ wm->ts_reader_interval); ++ ++ return 0; ++} ++ ++/** ++ * wm97xx_ts_input_close - Close the touch screen input device. ++ * @idev: Input device to be closed. ++ * ++ * Called by the input sub system to close a wm97xx touchscreen device. ++ * Kills the touchscreen thread and stops the touch digitiser. ++ */ ++ ++static void wm97xx_ts_input_close(struct input_dev *idev) ++{ ++ struct wm97xx *wm = input_get_drvdata(idev); ++ ++ if (wm->pen_irq) ++ free_irq(wm->pen_irq, wm); ++ ++ wm->pen_is_down = 0; ++ ++ /* ts_reader rearms itself so we need to explicitly stop it ++ * before we destroy the workqueue. ++ */ ++ cancel_delayed_work_sync(&wm->ts_reader); ++ destroy_workqueue(wm->ts_workq); ++ ++ /* stop digitiser */ ++ wm->codec->dig_enable(wm, 0); ++ if (wm->mach_ops && wm->mach_ops->acc_enabled) ++ wm->codec->acc_enable(wm, 0); ++} ++ ++static int wm97xx_probe(struct device *dev) ++{ ++ struct wm97xx *wm; ++ int ret = 0, id = 0; ++ ++ wm = kzalloc(sizeof(struct wm97xx), GFP_KERNEL); ++ if (!wm) ++ return -ENOMEM; ++ mutex_init(&wm->codec_mutex); ++ ++ wm->dev = dev; ++ dev->driver_data = wm; ++ wm->ac97 = to_ac97_t(dev); ++ ++ /* check that we have a supported codec */ ++ id = wm97xx_reg_read(wm, AC97_VENDOR_ID1); ++ if (id != WM97XX_ID1) { ++ dev_err(dev, "Device with vendor %04x is not a wm97xx\n", id); ++ kfree(wm); ++ return -ENODEV; ++ } ++ ++ wm->id = wm97xx_reg_read(wm, AC97_VENDOR_ID2); ++ ++ dev_info(wm->dev, "detected a wm97%02x codec", wm->id & 0xff); ++ ++ switch (wm->id & 0xff) { ++#ifdef CONFIG_TOUCHSCREEN_WM9705 ++ case 0x05: ++ wm->codec = &wm9705_codec; ++ break; ++#endif ++#ifdef CONFIG_TOUCHSCREEN_WM9712 ++ case 0x12: ++ wm->codec = &wm9712_codec; ++ break; ++#endif ++#ifdef CONFIG_TOUCHSCREEN_WM9713 ++ case 0x13: ++ wm->codec = &wm9713_codec; ++ break; ++#endif ++ default: ++ dev_err(wm->dev, "Support for wm97%02x not compiled in.\n", ++ wm->id & 0xff); ++ kfree(wm); ++ return -ENODEV; ++ } ++ ++ wm->input_dev = input_allocate_device(); ++ if (wm->input_dev == NULL) { ++ kfree(wm); ++ return -ENOMEM; ++ } ++ ++ /* set up touch configuration */ ++ wm->input_dev->name = "wm97xx touchscreen"; ++ wm->input_dev->open = wm97xx_ts_input_open; ++ wm->input_dev->close = wm97xx_ts_input_close; ++ set_bit(EV_ABS, wm->input_dev->evbit); ++ set_bit(ABS_X, wm->input_dev->absbit); ++ set_bit(ABS_Y, wm->input_dev->absbit); ++ set_bit(ABS_PRESSURE, wm->input_dev->absbit); ++ input_set_abs_params(wm->input_dev, ABS_X, abs_x[0], abs_x[1], ++ abs_x[2], 0); ++ input_set_abs_params(wm->input_dev, ABS_Y, abs_y[0], abs_y[1], ++ abs_y[2], 0); ++ input_set_abs_params(wm->input_dev, ABS_PRESSURE, abs_p[0], abs_p[1], ++ abs_p[2], 0); ++ input_set_drvdata(wm->input_dev, wm); ++ wm->input_dev->dev.parent = dev; ++ ret = input_register_device(wm->input_dev); ++ if (ret < 0) { ++ input_free_device(wm->input_dev); ++ kfree(wm); ++ return -ENOMEM; ++ } ++ ++ /* set up physical characteristics */ ++ wm->codec->phy_init(wm); ++ ++ /* load gpio cache */ ++ wm->gpio[0] = wm97xx_reg_read(wm, AC97_GPIO_CFG); ++ wm->gpio[1] = wm97xx_reg_read(wm, AC97_GPIO_POLARITY); ++ wm->gpio[2] = wm97xx_reg_read(wm, AC97_GPIO_STICKY); ++ wm->gpio[3] = wm97xx_reg_read(wm, AC97_GPIO_WAKEUP); ++ wm->gpio[4] = wm97xx_reg_read(wm, AC97_GPIO_STATUS); ++ wm->gpio[5] = wm97xx_reg_read(wm, AC97_MISC_AFE); ++ ++ /* register our battery device */ ++ wm->battery_dev = platform_device_alloc("wm97xx-battery", 0); ++ if (!wm->battery_dev) ++ goto batt_err; ++ platform_set_drvdata(wm->battery_dev, wm); ++ wm->battery_dev->dev.parent = dev; ++ ret = platform_device_register(wm->battery_dev); ++ if (ret < 0) ++ goto batt_reg_err; ++ ++ /* register our extended touch device (for machine specific ++ * extensions) */ ++ wm->touch_dev = platform_device_alloc("wm97xx-touch", 0); ++ if (!wm->touch_dev) ++ goto touch_err; ++ platform_set_drvdata(wm->touch_dev, wm); ++ wm->touch_dev->dev.parent = dev; ++ ret = platform_device_register(wm->touch_dev); ++ if (ret < 0) ++ goto touch_reg_err; ++ ++ return ret; ++ ++ touch_reg_err: ++ platform_device_put(wm->touch_dev); ++ touch_err: ++ platform_device_unregister(wm->battery_dev); ++ batt_reg_err: ++ platform_device_put(wm->battery_dev); ++ batt_err: ++ input_unregister_device(wm->input_dev); ++ kfree(wm); ++ return ret; ++} ++ ++static int wm97xx_remove(struct device *dev) ++{ ++ struct wm97xx *wm = dev_get_drvdata(dev); ++ ++ platform_device_unregister(wm->battery_dev); ++ platform_device_unregister(wm->touch_dev); ++ input_unregister_device(wm->input_dev); ++ ++ kfree(wm); ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int wm97xx_resume(struct device *dev) ++{ ++ struct wm97xx *wm = dev_get_drvdata(dev); ++ ++ /* restore digitiser and gpios */ ++ if (wm->id == WM9713_ID2) { ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, wm->dig[0]); ++ wm97xx_reg_write(wm, 0x5a, wm->misc); ++ if (wm->input_dev->users) { ++ u16 reg; ++ reg = wm97xx_reg_read(wm, AC97_EXTENDED_MID) & 0x7fff; ++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, reg); ++ } ++ } ++ ++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, wm->dig[1]); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2]); ++ ++ wm97xx_reg_write(wm, AC97_GPIO_CFG, wm->gpio[0]); ++ wm97xx_reg_write(wm, AC97_GPIO_POLARITY, wm->gpio[1]); ++ wm97xx_reg_write(wm, AC97_GPIO_STICKY, wm->gpio[2]); ++ wm97xx_reg_write(wm, AC97_GPIO_WAKEUP, wm->gpio[3]); ++ wm97xx_reg_write(wm, AC97_GPIO_STATUS, wm->gpio[4]); ++ wm97xx_reg_write(wm, AC97_MISC_AFE, wm->gpio[5]); ++ ++ return 0; ++} ++ ++#else ++#define wm97xx_resume NULL ++#endif ++ ++/* ++ * Machine specific operations ++ */ ++int wm97xx_register_mach_ops(struct wm97xx *wm, ++ struct wm97xx_mach_ops *mach_ops) ++{ ++ mutex_lock(&wm->codec_mutex); ++ if (wm->mach_ops) { ++ mutex_unlock(&wm->codec_mutex); ++ return -EINVAL; ++ } ++ wm->mach_ops = mach_ops; ++ mutex_unlock(&wm->codec_mutex); ++ return 0; ++} ++EXPORT_SYMBOL_GPL(wm97xx_register_mach_ops); ++ ++void wm97xx_unregister_mach_ops(struct wm97xx *wm) ++{ ++ mutex_lock(&wm->codec_mutex); ++ wm->mach_ops = NULL; ++ mutex_unlock(&wm->codec_mutex); ++} ++EXPORT_SYMBOL_GPL(wm97xx_unregister_mach_ops); ++ ++static struct device_driver wm97xx_driver = { ++ .name = "ac97", ++ .bus = &ac97_bus_type, ++ .owner = THIS_MODULE, ++ .probe = wm97xx_probe, ++ .remove = wm97xx_remove, ++ .resume = wm97xx_resume, ++}; ++ ++static int __init wm97xx_init(void) ++{ ++ return driver_register(&wm97xx_driver); ++} ++ ++static void __exit wm97xx_exit(void) ++{ ++ driver_unregister(&wm97xx_driver); ++} ++ ++module_init(wm97xx_init); ++module_exit(wm97xx_exit); ++ ++/* Module information */ ++MODULE_AUTHOR("Liam Girdwood <liam.girdwood@wolfsonmicro.com>"); ++MODULE_DESCRIPTION("WM97xx Core - Touch Screen / AUX ADC / GPIO Driver"); ++MODULE_LICENSE("GPL"); +diff --git a/include/linux/wm97xx.h b/include/linux/wm97xx.h +new file mode 100644 +index 0000000..fc6e0b3 +--- /dev/null ++++ b/include/linux/wm97xx.h +@@ -0,0 +1,309 @@ ++ ++/* ++ * Register bits and API for Wolfson WM97xx series of codecs ++ */ ++ ++#ifndef _LINUX_WM97XX_H ++#define _LINUX_WM97XX_H ++ ++#include <sound/driver.h> ++#include <sound/core.h> ++#include <sound/pcm.h> ++#include <sound/ac97_codec.h> ++#include <sound/initval.h> ++#include <linux/types.h> ++#include <linux/list.h> ++#include <linux/input.h> /* Input device layer */ ++#include <linux/platform_device.h> ++ ++/* ++ * 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) /* panel slot (5-11) */ ++#define WM97XX_SLT_MASK 0x0007 ++#define WM97XX_PRP_DETW 0x4000 /* detect on, digitise off, wake */ ++#define WM97XX_PRP_DET 0x8000 /* detect on, digitise off, no wake */ ++#define WM97XX_PRP_DET_DIG 0xc000 /* setect on, digitise on */ ++#define WM97XX_RPR 0x2000 /* wake up on pen down */ ++#define WM97XX_PEN_DOWN 0x8000 /* pen is down */ ++#define WM97XX_ADCSRC_MASK 0x7000 /* ADC source mask */ ++ ++#define WM97XX_AUX_ID1 0x8001 ++#define WM97XX_AUX_ID2 0x8002 ++#define WM97XX_AUX_ID3 0x8003 ++#define WM97XX_AUX_ID4 0x8004 ++ ++ ++/* WM9712 Bits */ ++#define WM9712_45W 0x1000 /* set for 5-wire touchscreen */ ++#define WM9712_PDEN 0x0800 /* measure only when pen down */ ++#define WM9712_WAIT 0x0200 /* wait until adc is read before next sample */ ++#define WM9712_PIL 0x0100 /* current used for pressure measurement. set 400uA else 200uA */ ++#define WM9712_MASK_HI 0x0040 /* hi on mask pin (47) stops conversions */ ++#define WM9712_MASK_EDGE 0x0080 /* rising/falling edge on pin delays sample */ ++#define WM9712_MASK_SYNC 0x00c0 /* rising/falling edge on mask initiates sample */ ++#define WM9712_RPU(i) (i&0x3f) /* internal pull up on pen detect (64k / rpu) */ ++#define WM9712_PD(i) (0x1 << i) /* power management */ ++ ++/* WM9712 Registers */ ++#define AC97_WM9712_POWER 0x24 ++#define AC97_WM9712_REV 0x58 ++ ++/* WM9705 Bits */ ++#define WM9705_PDEN 0x1000 /* measure only when pen is down */ ++#define WM9705_PINV 0x0800 /* inverts sense of pen down output */ ++#define WM9705_BSEN 0x0400 /* BUSY flag enable, pin47 is 1 when busy */ ++#define WM9705_BINV 0x0200 /* invert BUSY (pin47) output */ ++#define WM9705_WAIT 0x0100 /* wait until adc is read before next sample */ ++#define WM9705_PIL 0x0080 /* current used for pressure measurement. set 400uA else 200uA */ ++#define WM9705_PHIZ 0x0040 /* set PHONE and PCBEEP inputs to high impedance */ ++#define WM9705_MASK_HI 0x0010 /* hi on mask stops conversions */ ++#define WM9705_MASK_EDGE 0x0020 /* rising/falling edge on pin delays sample */ ++#define WM9705_MASK_SYNC 0x0030 /* rising/falling edge on mask initiates sample */ ++#define WM9705_PDD(i) (i & 0x000f) /* pen detect comparator threshold */ ++ ++ ++/* WM9713 Bits */ ++#define WM9713_PDPOL 0x0400 /* Pen down polarity */ ++#define WM9713_POLL 0x0200 /* initiate a polling measurement */ ++#define WM9713_CTC 0x0100 /* enable continuous mode */ ++#define WM9713_ADCSEL_X 0x0002 /* X measurement */ ++#define WM9713_ADCSEL_Y 0x0004 /* Y measurement */ ++#define WM9713_ADCSEL_PRES 0x0008 /* Pressure measurement */ ++#define WM9713_COO 0x0001 /* enable coordinate mode */ ++#define WM9713_PDEN 0x0800 /* measure only when pen down */ ++#define WM9713_ADCSEL_MASK 0x00fe /* ADC selection mask */ ++#define WM9713_WAIT 0x0200 /* coordinate wait */ ++ ++/* AUX ADC ID's */ ++#define TS_COMP1 0x0 ++#define TS_COMP2 0x1 ++#define TS_BMON 0x2 ++#define TS_WIPER 0x3 ++ ++/* ID numbers */ ++#define WM97XX_ID1 0x574d ++#define WM9712_ID2 0x4c12 ++#define WM9705_ID2 0x4c05 ++#define WM9713_ID2 0x4c13 ++ ++/* Codec GPIO's */ ++#define WM97XX_MAX_GPIO 16 ++#define WM97XX_GPIO_1 (1 << 1) ++#define WM97XX_GPIO_2 (1 << 2) ++#define WM97XX_GPIO_3 (1 << 3) ++#define WM97XX_GPIO_4 (1 << 4) ++#define WM97XX_GPIO_5 (1 << 5) ++#define WM97XX_GPIO_6 (1 << 6) ++#define WM97XX_GPIO_7 (1 << 7) ++#define WM97XX_GPIO_8 (1 << 8) ++#define WM97XX_GPIO_9 (1 << 9) ++#define WM97XX_GPIO_10 (1 << 10) ++#define WM97XX_GPIO_11 (1 << 11) ++#define WM97XX_GPIO_12 (1 << 12) ++#define WM97XX_GPIO_13 (1 << 13) ++#define WM97XX_GPIO_14 (1 << 14) ++#define WM97XX_GPIO_15 (1 << 15) ++ ++ ++#define AC97_LINK_FRAME 21 /* time in uS for AC97 link frame */ ++ ++ ++/*---------------- Return codes from sample reading functions ---------------*/ ++ ++/* More data is available; call the sample gathering function again */ ++#define RC_AGAIN 0x00000001 ++/* The returned sample is valid */ ++#define RC_VALID 0x00000002 ++/* The pen is up (the first RC_VALID without RC_PENUP means pen is down) */ ++#define RC_PENUP 0x00000004 ++/* The pen is down (RC_VALID implies RC_PENDOWN, but sometimes it is helpful ++ to tell the handler that the pen is down but we don't know yet his coords, ++ so the handler should not sleep or wait for pendown irq) */ ++#define RC_PENDOWN 0x00000008 ++ ++/* ++ * The wm97xx driver provides a private API for writing platform-specific ++ * drivers. ++ */ ++ ++/* The structure used to return arch specific sampled data into */ ++struct wm97xx_data { ++ int x; ++ int y; ++ int p; ++}; ++ ++/* ++ * Codec GPIO status ++ */ ++enum wm97xx_gpio_status { ++ WM97XX_GPIO_HIGH, ++ WM97XX_GPIO_LOW ++}; ++ ++/* ++ * Codec GPIO direction ++ */ ++enum wm97xx_gpio_dir { ++ WM97XX_GPIO_IN, ++ WM97XX_GPIO_OUT ++}; ++ ++/* ++ * Codec GPIO polarity ++ */ ++enum wm97xx_gpio_pol { ++ WM97XX_GPIO_POL_HIGH, ++ WM97XX_GPIO_POL_LOW ++}; ++ ++/* ++ * Codec GPIO sticky ++ */ ++enum wm97xx_gpio_sticky { ++ WM97XX_GPIO_STICKY, ++ WM97XX_GPIO_NOTSTICKY ++}; ++ ++/* ++ * Codec GPIO wake ++ */ ++enum wm97xx_gpio_wake { ++ WM97XX_GPIO_WAKE, ++ WM97XX_GPIO_NOWAKE ++}; ++ ++/* ++ * Digitiser ioctl commands ++ */ ++#define WM97XX_DIG_START 0x1 ++#define WM97XX_DIG_STOP 0x2 ++#define WM97XX_PHY_INIT 0x3 ++#define WM97XX_AUX_PREPARE 0x4 ++#define WM97XX_DIG_RESTORE 0x5 ++ ++struct wm97xx; ++ ++extern struct wm97xx_codec_drv wm9705_codec; ++extern struct wm97xx_codec_drv wm9712_codec; ++extern struct wm97xx_codec_drv wm9713_codec; ++ ++/* ++ * Codec driver interface - allows mapping to WM9705/12/13 and newer codecs ++ */ ++struct wm97xx_codec_drv { ++ u16 id; ++ char *name; ++ ++ /* read 1 sample */ ++ int (*poll_sample) (struct wm97xx *, int adcsel, int *sample); ++ ++ /* read X,Y,[P] in poll */ ++ int (*poll_touch) (struct wm97xx *, struct wm97xx_data *); ++ ++ int (*acc_enable) (struct wm97xx *, int enable); ++ void (*phy_init) (struct wm97xx *); ++ void (*dig_enable) (struct wm97xx *, int enable); ++ void (*dig_restore) (struct wm97xx *); ++ void (*aux_prepare) (struct wm97xx *); ++}; ++ ++ ++/* Machine specific and accelerated touch operations */ ++struct wm97xx_mach_ops { ++ ++ /* accelerated touch readback - coords are transmited on AC97 link */ ++ int acc_enabled; ++ void (*acc_pen_up) (struct wm97xx *); ++ int (*acc_pen_down) (struct wm97xx *); ++ int (*acc_startup) (struct wm97xx *); ++ void (*acc_shutdown) (struct wm97xx *); ++ ++ /* interrupt mask control - required for accelerated operation */ ++ void (*irq_enable) (struct wm97xx *, int enable); ++ ++ /* pre and post sample - can be used to minimise any analog noise */ ++ void (*pre_sample) (int); /* function to run before sampling */ ++ void (*post_sample) (int); /* function to run after sampling */ ++}; ++ ++struct wm97xx { ++ u16 dig[3], id, gpio[6], misc; /* Cached codec registers */ ++ u16 dig_save[3]; /* saved during aux reading */ ++ struct wm97xx_codec_drv *codec; /* attached codec driver*/ ++ struct input_dev *input_dev; /* touchscreen input device */ ++ struct snd_ac97 *ac97; /* ALSA codec access */ ++ struct device *dev; /* ALSA device */ ++ struct platform_device *battery_dev; ++ struct platform_device *touch_dev; ++ struct wm97xx_mach_ops *mach_ops; ++ struct mutex codec_mutex; ++ struct delayed_work ts_reader; /* Used to poll touchscreen */ ++ unsigned long ts_reader_interval; /* Current interval for timer */ ++ unsigned long ts_reader_min_interval; /* Minimum interval */ ++ unsigned int pen_irq; /* Pen IRQ number in use */ ++ struct workqueue_struct *ts_workq; ++ struct work_struct pen_event_work; ++ u16 acc_slot; /* AC97 slot used for acc touch data */ ++ u16 acc_rate; /* acc touch data rate */ ++ unsigned pen_is_down:1; /* Pen is down */ ++ unsigned aux_waiting:1; /* aux measurement waiting */ ++ unsigned pen_probably_down:1; /* used in polling mode */ ++}; ++ ++/* ++ * Codec GPIO access (not supported on WM9705) ++ * This can be used to set/get codec GPIO and Virtual GPIO status. ++ */ ++enum wm97xx_gpio_status wm97xx_get_gpio(struct wm97xx *wm, u32 gpio); ++void wm97xx_set_gpio(struct wm97xx *wm, u32 gpio, ++ enum wm97xx_gpio_status status); ++void wm97xx_config_gpio(struct wm97xx *wm, u32 gpio, ++ enum wm97xx_gpio_dir dir, ++ enum wm97xx_gpio_pol pol, ++ enum wm97xx_gpio_sticky sticky, ++ enum wm97xx_gpio_wake wake); ++ ++/* codec AC97 IO access */ ++int wm97xx_reg_read(struct wm97xx *wm, u16 reg); ++void wm97xx_reg_write(struct wm97xx *wm, u16 reg, u16 val); ++ ++/* aux adc readback */ ++int wm97xx_read_aux_adc(struct wm97xx *wm, u16 adcsel); ++ ++/* machine ops */ ++int wm97xx_register_mach_ops(struct wm97xx *, struct wm97xx_mach_ops *); ++void wm97xx_unregister_mach_ops(struct wm97xx *); ++ ++#endif +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0031-Add-chip-driver-for-WM9705-touchscreen.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0031-Add-chip-driver-for-WM9705-touchscreen.patch new file mode 100644 index 0000000000..3890795f61 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0031-Add-chip-driver-for-WM9705-touchscreen.patch @@ -0,0 +1,383 @@ +From 7b366ca784d0540613a43908de803e4dedc100d3 Mon Sep 17 00:00:00 2001 +From: Mark Brown <broonie@opensource.wolfsonmicro.com> +Date: Sat, 26 Jan 2008 21:14:20 +0300 +Subject: [PATCH 31/64] Add chip driver for WM9705 touchscreen + +Signed-off-by: Liam Girdwood <liam.girdwood@wolfsonmicro.com> +Signed-off-by: Graeme Gregory <gg@opensource.wolfsonmicro.com> +Signed-off-by: Mike Arthur <mike.arthur@wolfsonmicro.com> +Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> +Cc: Dmitry Baryshkov <dbaryshkov@gmail.com> +Cc: Stanley Cai <stanley.cai@intel.com> +Cc: Rodolfo Giometti <giometti@enneenne.com> +Cc: Russell King <rmk@arm.linux.org.uk> +Cc: Marc Kleine-Budde <mkl@pengutronix.de> +Cc: Ian Molton <spyro@f2s.com> +Cc: Vince Sanders <vince@kyllikki.org> +Cc: Andrew Zabolotny <zap@homelink.ru> +--- + drivers/input/touchscreen/wm9705.c | 352 ++++++++++++++++++++++++++++++++++++ + 1 files changed, 352 insertions(+), 0 deletions(-) + create mode 100644 drivers/input/touchscreen/wm9705.c + +diff --git a/drivers/input/touchscreen/wm9705.c b/drivers/input/touchscreen/wm9705.c +new file mode 100644 +index 0000000..f185104 +--- /dev/null ++++ b/drivers/input/touchscreen/wm9705.c +@@ -0,0 +1,352 @@ ++/* ++ * wm9705.c -- Codec driver for Wolfson WM9705 AC97 Codec. ++ * ++ * Copyright 2003, 2004, 2005, 2006, 2007 Wolfson Microelectronics PLC. ++ * Author: Liam Girdwood ++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com ++ * Parts Copyright : Ian Molton <spyro@f2s.com> ++ * Andrew Zabolotny <zap@homelink.ru> ++ * Russell King <rmk@arm.linux.org.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/version.h> ++#include <linux/kernel.h> ++#include <linux/input.h> ++#include <linux/delay.h> ++#include <linux/bitops.h> ++#include <linux/wm97xx.h> ++ ++#define TS_NAME "wm97xx" ++#define WM9705_VERSION "0.62" ++#define DEFAULT_PRESSURE 0xb0c0 ++ ++/* ++ * Module parameters ++ */ ++ ++/* ++ * Set current used for pressure measurement. ++ * ++ * Set pil = 2 to use 400uA ++ * pil = 1 to use 200uA and ++ * pil = 0 to disable pressure measurement. ++ * ++ * This is used to increase the range of values returned by the adc ++ * when measureing touchpanel pressure. ++ */ ++static int pil; ++module_param(pil, int, 0); ++MODULE_PARM_DESC(pil, "Set current used for pressure measurement."); ++ ++/* ++ * Set threshold for pressure measurement. ++ * ++ * Pen down pressure below threshold is ignored. ++ */ ++static int pressure = DEFAULT_PRESSURE & 0xfff; ++module_param(pressure, int, 0); ++MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement."); ++ ++/* ++ * Set adc sample delay. ++ * ++ * For accurate touchpanel measurements, some settling time may be ++ * required between the switch matrix applying a voltage across the ++ * touchpanel plate and the ADC sampling the signal. ++ * ++ * This delay can be set by setting delay = n, where n is the array ++ * position of the delay in the array delay_table below. ++ * Long delays > 1ms are supported for completeness, but are not ++ * recommended. ++ */ ++static int delay = 4; ++module_param(delay, int, 0); ++MODULE_PARM_DESC(delay, "Set adc sample delay."); ++ ++/* ++ * Pen detect comparator threshold. ++ * ++ * 0 to Vmid in 15 steps, 0 = use zero power comparator with Vmid threshold ++ * i.e. 1 = Vmid/15 threshold ++ * 15 = Vmid/1 threshold ++ * ++ * Adjust this value if you are having problems with pen detect not ++ * detecting any down events. ++ */ ++static int pdd = 8; ++module_param(pdd, int, 0); ++MODULE_PARM_DESC(pdd, "Set pen detect comparator threshold"); ++ ++/* ++ * Set adc mask function. ++ * ++ * Sources of glitch noise, such as signals driving an LCD display, may feed ++ * through to the touch screen plates and affect measurement accuracy. In ++ * order to minimise this, a signal may be applied to the MASK pin to delay or ++ * synchronise the sampling. ++ * ++ * 0 = No delay or sync ++ * 1 = High on pin stops conversions ++ * 2 = Edge triggered, edge on pin delays conversion by delay param (above) ++ * 3 = Edge triggered, edge on pin starts conversion after delay param ++ */ ++static int mask; ++module_param(mask, int, 0); ++MODULE_PARM_DESC(mask, "Set adc mask function."); ++ ++/* ++ * ADC sample delay times in uS ++ */ ++static const int delay_table[] = { ++ 21, /* 1 AC97 Link frames */ ++ 42, /* 2 */ ++ 84, /* 4 */ ++ 167, /* 8 */ ++ 333, /* 16 */ ++ 667, /* 32 */ ++ 1000, /* 48 */ ++ 1333, /* 64 */ ++ 2000, /* 96 */ ++ 2667, /* 128 */ ++ 3333, /* 160 */ ++ 4000, /* 192 */ ++ 4667, /* 224 */ ++ 5333, /* 256 */ ++ 6000, /* 288 */ ++ 0 /* No delay, switch matrix always on */ ++}; ++ ++/* ++ * Delay after issuing a POLL command. ++ * ++ * The delay is 3 AC97 link frames + the touchpanel settling delay ++ */ ++static inline void poll_delay(int d) ++{ ++ udelay(3 * AC97_LINK_FRAME + delay_table[d]); ++} ++ ++/* ++ * set up the physical settings of the WM9705 ++ */ ++static void wm9705_phy_init(struct wm97xx *wm) ++{ ++ u16 dig1 = 0, dig2 = WM97XX_RPR; ++ ++ /* ++ * mute VIDEO and AUX as they share X and Y touchscreen ++ * inputs on the WM9705 ++ */ ++ wm97xx_reg_write(wm, AC97_AUX, 0x8000); ++ wm97xx_reg_write(wm, AC97_VIDEO, 0x8000); ++ ++ /* touchpanel pressure current*/ ++ if (pil == 2) { ++ dig2 |= WM9705_PIL; ++ dev_dbg(wm->dev, ++ "setting pressure measurement current to 400uA."); ++ } else if (pil) ++ dev_dbg(wm->dev, ++ "setting pressure measurement current to 200uA."); ++ if (!pil) ++ pressure = 0; ++ ++ /* polling mode sample settling delay */ ++ if (delay != 4) { ++ if (delay < 0 || delay > 15) { ++ dev_dbg(wm->dev, "supplied delay out of range."); ++ delay = 4; ++ } ++ } ++ dig1 &= 0xff0f; ++ dig1 |= WM97XX_DELAY(delay); ++ dev_dbg(wm->dev, "setting adc sample delay to %d u Secs.", ++ delay_table[delay]); ++ ++ /* WM9705 pdd */ ++ dig2 |= (pdd & 0x000f); ++ dev_dbg(wm->dev, "setting pdd to Vmid/%d", 1 - (pdd & 0x000f)); ++ ++ /* mask */ ++ dig2 |= ((mask & 0x3) << 4); ++ ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2); ++} ++ ++static void wm9705_dig_enable(struct wm97xx *wm, int enable) ++{ ++ if (enable) { ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, ++ wm->dig[2] | WM97XX_PRP_DET_DIG); ++ wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */ ++ } else ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, ++ wm->dig[2] & ~WM97XX_PRP_DET_DIG); ++} ++ ++static void wm9705_aux_prepare(struct wm97xx *wm) ++{ ++ memcpy(wm->dig_save, wm->dig, sizeof(wm->dig)); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, 0); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG); ++} ++ ++static void wm9705_dig_restore(struct wm97xx *wm) ++{ ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, wm->dig_save[1]); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig_save[2]); ++} ++ ++static inline int is_pden(struct wm97xx *wm) ++{ ++ return wm->dig[2] & WM9705_PDEN; ++} ++ ++/* ++ * Read a sample from the WM9705 adc in polling mode. ++ */ ++static int wm9705_poll_sample(struct wm97xx *wm, int adcsel, int *sample) ++{ ++ int timeout = 5 * delay; ++ ++ if (!wm->pen_probably_down) { ++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (!(data & WM97XX_PEN_DOWN)) ++ return RC_PENUP; ++ wm->pen_probably_down = 1; ++ } ++ ++ /* set up digitiser */ ++ if (adcsel & 0x8000) ++ adcsel = ((adcsel & 0x7fff) + 3) << 12; ++ ++ if (wm->mach_ops && wm->mach_ops->pre_sample) ++ wm->mach_ops->pre_sample(adcsel); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, ++ adcsel | WM97XX_POLL | WM97XX_DELAY(delay)); ++ ++ /* wait 3 AC97 time slots + delay for conversion */ ++ poll_delay(delay); ++ ++ /* wait for POLL to go low */ ++ while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) ++ && timeout) { ++ udelay(AC97_LINK_FRAME); ++ timeout--; ++ } ++ ++ if (timeout <= 0) { ++ /* If PDEN is set, we can get a timeout when pen goes up */ ++ if (is_pden(wm)) ++ wm->pen_probably_down = 0; ++ else ++ dev_dbg(wm->dev, "adc sample timeout"); ++ return RC_PENUP; ++ } ++ ++ *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (wm->mach_ops && wm->mach_ops->post_sample) ++ wm->mach_ops->post_sample(adcsel); ++ ++ /* check we have correct sample */ ++ if ((*sample & WM97XX_ADCSEL_MASK) != adcsel) { ++ dev_dbg(wm->dev, "adc wrong sample, read %x got %x", adcsel, ++ *sample & WM97XX_ADCSEL_MASK); ++ return RC_PENUP; ++ } ++ ++ if (!(*sample & WM97XX_PEN_DOWN)) { ++ wm->pen_probably_down = 0; ++ return RC_PENUP; ++ } ++ ++ return RC_VALID; ++} ++ ++/* ++ * Sample the WM9705 touchscreen in polling mode ++ */ ++static int wm9705_poll_touch(struct wm97xx *wm, struct wm97xx_data *data) ++{ ++ int rc; ++ ++ rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_X, &data->x); ++ if (rc != RC_VALID) ++ return rc; ++ rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_Y, &data->y); ++ if (rc != RC_VALID) ++ return rc; ++ if (pil) { ++ rc = wm9705_poll_sample(wm, WM97XX_ADCSEL_PRES, &data->p); ++ if (rc != RC_VALID) ++ return rc; ++ } else ++ data->p = DEFAULT_PRESSURE; ++ ++ return RC_VALID; ++} ++ ++/* ++ * Enable WM9705 continuous mode, i.e. touch data is streamed across ++ * an AC97 slot ++ */ ++static int wm9705_acc_enable(struct wm97xx *wm, int enable) ++{ ++ u16 dig1, dig2; ++ int ret = 0; ++ ++ dig1 = wm->dig[1]; ++ dig2 = wm->dig[2]; ++ ++ if (enable) { ++ /* continous mode */ ++ if (wm->mach_ops->acc_startup && ++ (ret = wm->mach_ops->acc_startup(wm)) < 0) ++ return ret; ++ dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK | ++ WM97XX_DELAY_MASK | WM97XX_SLT_MASK); ++ dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN | ++ WM97XX_DELAY(delay) | ++ WM97XX_SLT(wm->acc_slot) | ++ WM97XX_RATE(wm->acc_rate); ++ if (pil) ++ dig1 |= WM97XX_ADCSEL_PRES; ++ dig2 |= WM9705_PDEN; ++ } else { ++ dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN); ++ dig2 &= ~WM9705_PDEN; ++ if (wm->mach_ops->acc_shutdown) ++ wm->mach_ops->acc_shutdown(wm); ++ } ++ ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2); ++ return ret; ++} ++ ++struct wm97xx_codec_drv wm9705_codec = { ++ .id = WM9705_ID2, ++ .name = "wm9705", ++ .poll_sample = wm9705_poll_sample, ++ .poll_touch = wm9705_poll_touch, ++ .acc_enable = wm9705_acc_enable, ++ .phy_init = wm9705_phy_init, ++ .dig_enable = wm9705_dig_enable, ++ .dig_restore = wm9705_dig_restore, ++ .aux_prepare = wm9705_aux_prepare, ++}; ++EXPORT_SYMBOL_GPL(wm9705_codec); ++ ++/* Module information */ ++MODULE_AUTHOR("Liam Girdwood <liam.girdwood@wolfsonmicro.com>"); ++MODULE_DESCRIPTION("WM9705 Touch Screen Driver"); ++MODULE_LICENSE("GPL"); +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0032-Add-chip-driver-for-WM9712-touchscreen.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0032-Add-chip-driver-for-WM9712-touchscreen.patch new file mode 100644 index 0000000000..6265910a1e --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0032-Add-chip-driver-for-WM9712-touchscreen.patch @@ -0,0 +1,492 @@ +From b2640063b8321bdfb324c00d5f0c3366ac31696b Mon Sep 17 00:00:00 2001 +From: Mark Brown <broonie@opensource.wolfsonmicro.com> +Date: Sat, 26 Jan 2008 21:14:19 +0300 +Subject: [PATCH 32/64] Add chip driver for WM9712 touchscreen + +Signed-off-by: Liam Girdwood <liam.girdwood@wolfsonmicro.com> +Signed-off-by: Graeme Gregory <gg@opensource.wolfsonmicro.com> +Signed-off-by: Mike Arthur <mike.arthur@wolfsonmicro.com> +Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> +Cc: Dmitry Baryshkov <dbaryshkov@gmail.com> +Cc: Stanley Cai <stanley.cai@intel.com> +Cc: Rodolfo Giometti <giometti@enneenne.com> +Cc: Russell King <rmk@arm.linux.org.uk> +Cc: Marc Kleine-Budde <mkl@pengutronix.de> +Cc: Ian Molton <spyro@f2s.com> +Cc: Vince Sanders <vince@kyllikki.org> +Cc: Andrew Zabolotny <zap@homelink.ru> +--- + drivers/input/touchscreen/wm9712.c | 461 ++++++++++++++++++++++++++++++++++++ + 1 files changed, 461 insertions(+), 0 deletions(-) + create mode 100644 drivers/input/touchscreen/wm9712.c + +diff --git a/drivers/input/touchscreen/wm9712.c b/drivers/input/touchscreen/wm9712.c +new file mode 100644 +index 0000000..eaab326 +--- /dev/null ++++ b/drivers/input/touchscreen/wm9712.c +@@ -0,0 +1,461 @@ ++/* ++ * wm9712.c -- Codec driver for Wolfson WM9712 AC97 Codecs. ++ * ++ * Copyright 2003, 2004, 2005, 2006, 2007 Wolfson Microelectronics PLC. ++ * Author: Liam Girdwood ++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com ++ * Parts Copyright : Ian Molton <spyro@f2s.com> ++ * Andrew Zabolotny <zap@homelink.ru> ++ * Russell King <rmk@arm.linux.org.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/version.h> ++#include <linux/kernel.h> ++#include <linux/input.h> ++#include <linux/delay.h> ++#include <linux/bitops.h> ++#include <linux/wm97xx.h> ++ ++#define TS_NAME "wm97xx" ++#define WM9712_VERSION "0.61" ++#define DEFAULT_PRESSURE 0xb0c0 ++ ++/* ++ * Module parameters ++ */ ++ ++/* ++ * Set internal pull up for pen detect. ++ * ++ * Pull up is in the range 1.02k (least sensitive) to 64k (most sensitive) ++ * i.e. pull up resistance = 64k Ohms / rpu. ++ * ++ * Adjust this value if you are having problems with pen detect not ++ * detecting any down event. ++ */ ++static int rpu = 8; ++module_param(rpu, int, 0); ++MODULE_PARM_DESC(rpu, "Set internal pull up resitor for pen detect."); ++ ++/* ++ * Set current used for pressure measurement. ++ * ++ * Set pil = 2 to use 400uA ++ * pil = 1 to use 200uA and ++ * pil = 0 to disable pressure measurement. ++ * ++ * This is used to increase the range of values returned by the adc ++ * when measureing touchpanel pressure. ++ */ ++static int pil; ++module_param(pil, int, 0); ++MODULE_PARM_DESC(pil, "Set current used for pressure measurement."); ++ ++/* ++ * Set threshold for pressure measurement. ++ * ++ * Pen down pressure below threshold is ignored. ++ */ ++static int pressure = DEFAULT_PRESSURE & 0xfff; ++module_param(pressure, int, 0); ++MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement."); ++ ++/* ++ * Set adc sample delay. ++ * ++ * For accurate touchpanel measurements, some settling time may be ++ * required between the switch matrix applying a voltage across the ++ * touchpanel plate and the ADC sampling the signal. ++ * ++ * This delay can be set by setting delay = n, where n is the array ++ * position of the delay in the array delay_table below. ++ * Long delays > 1ms are supported for completeness, but are not ++ * recommended. ++ */ ++static int delay = 3; ++module_param(delay, int, 0); ++MODULE_PARM_DESC(delay, "Set adc sample delay."); ++ ++/* ++ * Set five_wire = 1 to use a 5 wire touchscreen. ++ * ++ * NOTE: Five wire mode does not allow for readback of pressure. ++ */ ++static int five_wire; ++module_param(five_wire, int, 0); ++MODULE_PARM_DESC(five_wire, "Set to '1' to use 5-wire touchscreen."); ++ ++/* ++ * Set adc mask function. ++ * ++ * Sources of glitch noise, such as signals driving an LCD display, may feed ++ * through to the touch screen plates and affect measurement accuracy. In ++ * order to minimise this, a signal may be applied to the MASK pin to delay or ++ * synchronise the sampling. ++ * ++ * 0 = No delay or sync ++ * 1 = High on pin stops conversions ++ * 2 = Edge triggered, edge on pin delays conversion by delay param (above) ++ * 3 = Edge triggered, edge on pin starts conversion after delay param ++ */ ++static int mask; ++module_param(mask, int, 0); ++MODULE_PARM_DESC(mask, "Set adc mask function."); ++ ++/* ++ * Coordinate Polling Enable. ++ * ++ * Set to 1 to enable coordinate polling. e.g. x,y[,p] is sampled together ++ * for every poll. ++ */ ++static int coord; ++module_param(coord, int, 0); ++MODULE_PARM_DESC(coord, "Polling coordinate mode"); ++ ++/* ++ * ADC sample delay times in uS ++ */ ++static const int delay_table[] = { ++ 21, /* 1 AC97 Link frames */ ++ 42, /* 2 */ ++ 84, /* 4 */ ++ 167, /* 8 */ ++ 333, /* 16 */ ++ 667, /* 32 */ ++ 1000, /* 48 */ ++ 1333, /* 64 */ ++ 2000, /* 96 */ ++ 2667, /* 128 */ ++ 3333, /* 160 */ ++ 4000, /* 192 */ ++ 4667, /* 224 */ ++ 5333, /* 256 */ ++ 6000, /* 288 */ ++ 0 /* No delay, switch matrix always on */ ++}; ++ ++/* ++ * Delay after issuing a POLL command. ++ * ++ * The delay is 3 AC97 link frames + the touchpanel settling delay ++ */ ++static inline void poll_delay(int d) ++{ ++ udelay(3 * AC97_LINK_FRAME + delay_table[d]); ++} ++ ++/* ++ * set up the physical settings of the WM9712 ++ */ ++static void wm9712_phy_init(struct wm97xx *wm) ++{ ++ u16 dig1 = 0; ++ u16 dig2 = WM97XX_RPR | WM9712_RPU(1); ++ ++ /* WM9712 rpu */ ++ if (rpu) { ++ dig2 &= 0xffc0; ++ dig2 |= WM9712_RPU(rpu); ++ dev_dbg(wm->dev, "setting pen detect pull-up to %d Ohms", ++ 64000 / rpu); ++ } ++ ++ /* touchpanel pressure current*/ ++ if (pil == 2) { ++ dig2 |= WM9712_PIL; ++ dev_dbg(wm->dev, ++ "setting pressure measurement current to 400uA."); ++ } else if (pil) ++ dev_dbg(wm->dev, ++ "setting pressure measurement current to 200uA."); ++ if (!pil) ++ pressure = 0; ++ ++ /* WM9712 five wire */ ++ if (five_wire) { ++ dig2 |= WM9712_45W; ++ dev_dbg(wm->dev, "setting 5-wire touchscreen mode."); ++ } ++ ++ /* polling mode sample settling delay */ ++ if (delay < 0 || delay > 15) { ++ dev_dbg(wm->dev, "supplied delay out of range."); ++ delay = 4; ++ } ++ dig1 &= 0xff0f; ++ dig1 |= WM97XX_DELAY(delay); ++ dev_dbg(wm->dev, "setting adc sample delay to %d u Secs.", ++ delay_table[delay]); ++ ++ /* mask */ ++ dig2 |= ((mask & 0x3) << 6); ++ if (mask) { ++ u16 reg; ++ /* Set GPIO4 as Mask Pin*/ ++ reg = wm97xx_reg_read(wm, AC97_MISC_AFE); ++ wm97xx_reg_write(wm, AC97_MISC_AFE, reg | WM97XX_GPIO_4); ++ reg = wm97xx_reg_read(wm, AC97_GPIO_CFG); ++ wm97xx_reg_write(wm, AC97_GPIO_CFG, reg | WM97XX_GPIO_4); ++ } ++ ++ /* wait - coord mode */ ++ if (coord) ++ dig2 |= WM9712_WAIT; ++ ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2); ++} ++ ++static void wm9712_dig_enable(struct wm97xx *wm, int enable) ++{ ++ u16 dig2 = wm->dig[2]; ++ ++ if (enable) { ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, ++ dig2 | WM97XX_PRP_DET_DIG); ++ wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */ ++ } else ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, ++ dig2 & ~WM97XX_PRP_DET_DIG); ++} ++ ++static void wm9712_aux_prepare(struct wm97xx *wm) ++{ ++ memcpy(wm->dig_save, wm->dig, sizeof(wm->dig)); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, 0); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, WM97XX_PRP_DET_DIG); ++} ++ ++static void wm9712_dig_restore(struct wm97xx *wm) ++{ ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, wm->dig_save[1]); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, wm->dig_save[2]); ++} ++ ++static inline int is_pden(struct wm97xx *wm) ++{ ++ return wm->dig[2] & WM9712_PDEN; ++} ++ ++/* ++ * Read a sample from the WM9712 adc in polling mode. ++ */ ++static int wm9712_poll_sample(struct wm97xx *wm, int adcsel, int *sample) ++{ ++ int timeout = 5 * delay; ++ ++ if (!wm->pen_probably_down) { ++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (!(data & WM97XX_PEN_DOWN)) ++ return RC_PENUP; ++ wm->pen_probably_down = 1; ++ } ++ ++ /* set up digitiser */ ++ if (adcsel & 0x8000) ++ adcsel = ((adcsel & 0x7fff) + 3) << 12; ++ ++ if (wm->mach_ops && wm->mach_ops->pre_sample) ++ wm->mach_ops->pre_sample(adcsel); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, ++ adcsel | WM97XX_POLL | WM97XX_DELAY(delay)); ++ ++ /* wait 3 AC97 time slots + delay for conversion */ ++ poll_delay(delay); ++ ++ /* wait for POLL to go low */ ++ while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) ++ && timeout) { ++ udelay(AC97_LINK_FRAME); ++ timeout--; ++ } ++ ++ if (timeout <= 0) { ++ /* If PDEN is set, we can get a timeout when pen goes up */ ++ if (is_pden(wm)) ++ wm->pen_probably_down = 0; ++ else ++ dev_dbg(wm->dev, "adc sample timeout"); ++ return RC_PENUP; ++ } ++ ++ *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (wm->mach_ops && wm->mach_ops->post_sample) ++ wm->mach_ops->post_sample(adcsel); ++ ++ /* check we have correct sample */ ++ if ((*sample & WM97XX_ADCSEL_MASK) != adcsel) { ++ dev_dbg(wm->dev, "adc wrong sample, read %x got %x", adcsel, ++ *sample & WM97XX_ADCSEL_MASK); ++ return RC_PENUP; ++ } ++ ++ if (!(*sample & WM97XX_PEN_DOWN)) { ++ wm->pen_probably_down = 0; ++ return RC_PENUP; ++ } ++ ++ return RC_VALID; ++} ++ ++/* ++ * Read a coord from the WM9712 adc in polling mode. ++ */ ++static int wm9712_poll_coord(struct wm97xx *wm, struct wm97xx_data *data) ++{ ++ int timeout = 5 * delay; ++ ++ if (!wm->pen_probably_down) { ++ u16 data_rd = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (!(data_rd & WM97XX_PEN_DOWN)) ++ return RC_PENUP; ++ wm->pen_probably_down = 1; ++ } ++ ++ /* set up digitiser */ ++ if (wm->mach_ops && wm->mach_ops->pre_sample) ++ wm->mach_ops->pre_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y); ++ ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, ++ WM97XX_COO | WM97XX_POLL | WM97XX_DELAY(delay)); ++ ++ /* wait 3 AC97 time slots + delay for conversion and read x */ ++ poll_delay(delay); ++ data->x = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ /* wait for POLL to go low */ ++ while ((wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER1) & WM97XX_POLL) ++ && timeout) { ++ udelay(AC97_LINK_FRAME); ++ timeout--; ++ } ++ ++ if (timeout <= 0) { ++ /* If PDEN is set, we can get a timeout when pen goes up */ ++ if (is_pden(wm)) ++ wm->pen_probably_down = 0; ++ else ++ dev_dbg(wm->dev, "adc sample timeout"); ++ return RC_PENUP; ++ } ++ ++ /* read back y data */ ++ data->y = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (pil) ++ data->p = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ else ++ data->p = DEFAULT_PRESSURE; ++ ++ if (wm->mach_ops && wm->mach_ops->post_sample) ++ wm->mach_ops->post_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y); ++ ++ /* check we have correct sample */ ++ if (!(data->x & WM97XX_ADCSEL_X) || !(data->y & WM97XX_ADCSEL_Y)) ++ goto err; ++ if (pil && !(data->p & WM97XX_ADCSEL_PRES)) ++ goto err; ++ ++ if (!(data->x & WM97XX_PEN_DOWN)) { ++ wm->pen_probably_down = 0; ++ return RC_PENUP; ++ } ++ return RC_VALID; ++err: ++ return RC_PENUP; ++} ++ ++/* ++ * Sample the WM9712 touchscreen in polling mode ++ */ ++static int wm9712_poll_touch(struct wm97xx *wm, struct wm97xx_data *data) ++{ ++ int rc; ++ ++ if (coord) { ++ rc = wm9712_poll_coord(wm, data); ++ if (rc != RC_VALID) ++ return rc; ++ } else { ++ rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_X, &data->x); ++ if (rc != RC_VALID) ++ return rc; ++ ++ rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_Y, &data->y); ++ if (rc != RC_VALID) ++ return rc; ++ ++ if (pil && !five_wire) { ++ rc = wm9712_poll_sample(wm, WM97XX_ADCSEL_PRES, ++ &data->p); ++ if (rc != RC_VALID) ++ return rc; ++ } else ++ data->p = DEFAULT_PRESSURE; ++ } ++ return RC_VALID; ++} ++ ++/* ++ * Enable WM9712 continuous mode, i.e. touch data is streamed across ++ * an AC97 slot ++ */ ++static int wm9712_acc_enable(struct wm97xx *wm, int enable) ++{ ++ u16 dig1, dig2; ++ int ret = 0; ++ ++ dig1 = wm->dig[1]; ++ dig2 = wm->dig[2]; ++ ++ if (enable) { ++ /* continous mode */ ++ if (wm->mach_ops->acc_startup) { ++ ret = wm->mach_ops->acc_startup(wm); ++ if (ret < 0) ++ return ret; ++ } ++ dig1 &= ~(WM97XX_CM_RATE_MASK | WM97XX_ADCSEL_MASK | ++ WM97XX_DELAY_MASK | WM97XX_SLT_MASK); ++ dig1 |= WM97XX_CTC | WM97XX_COO | WM97XX_SLEN | ++ WM97XX_DELAY(delay) | ++ WM97XX_SLT(wm->acc_slot) | ++ WM97XX_RATE(wm->acc_rate); ++ if (pil) ++ dig1 |= WM97XX_ADCSEL_PRES; ++ dig2 |= WM9712_PDEN; ++ } else { ++ dig1 &= ~(WM97XX_CTC | WM97XX_COO | WM97XX_SLEN); ++ dig2 &= ~WM9712_PDEN; ++ if (wm->mach_ops->acc_shutdown) ++ wm->mach_ops->acc_shutdown(wm); ++ } ++ ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER1, dig1); ++ wm97xx_reg_write(wm, AC97_WM97XX_DIGITISER2, dig2); ++ return 0; ++} ++ ++struct wm97xx_codec_drv wm9712_codec = { ++ .id = WM9712_ID2, ++ .name = "wm9712", ++ .poll_sample = wm9712_poll_sample, ++ .poll_touch = wm9712_poll_touch, ++ .acc_enable = wm9712_acc_enable, ++ .phy_init = wm9712_phy_init, ++ .dig_enable = wm9712_dig_enable, ++ .dig_restore = wm9712_dig_restore, ++ .aux_prepare = wm9712_aux_prepare, ++}; ++EXPORT_SYMBOL_GPL(wm9712_codec); ++ ++/* Module information */ ++MODULE_AUTHOR("Liam Girdwood <liam.girdwood@wolfsonmicro.com>"); ++MODULE_DESCRIPTION("WM9712 Touch Screen Driver"); ++MODULE_LICENSE("GPL"); +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0033-Add-chip-driver-for-WM9713-touchscreen.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0033-Add-chip-driver-for-WM9713-touchscreen.patch new file mode 100644 index 0000000000..a9dfa18557 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0033-Add-chip-driver-for-WM9713-touchscreen.patch @@ -0,0 +1,490 @@ +From 05b2a361eedb5461e902c73ebc6e30f9916b3a8a Mon Sep 17 00:00:00 2001 +From: Mark Brown <broonie@opensource.wolfsonmicro.com> +Date: Sat, 26 Jan 2008 21:14:19 +0300 +Subject: [PATCH 33/64] Add chip driver for WM9713 touchscreen + +Signed-off-by: Liam Girdwood <liam.girdwood@wolfsonmicro.com> +Signed-off-by: Graeme Gregory <gg@opensource.wolfsonmicro.com> +Signed-off-by: Mike Arthur <mike.arthur@wolfsonmicro.com> +Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> +Cc: Dmitry Baryshkov <dbaryshkov@gmail.com> +Cc: Stanley Cai <stanley.cai@intel.com> +Cc: Rodolfo Giometti <giometti@enneenne.com> +Cc: Russell King <rmk@arm.linux.org.uk> +Cc: Marc Kleine-Budde <mkl@pengutronix.de> +Cc: Ian Molton <spyro@f2s.com> +Cc: Vince Sanders <vince@kyllikki.org> +Cc: Andrew Zabolotny <zap@homelink.ru> +--- + drivers/input/touchscreen/wm9713.c | 459 ++++++++++++++++++++++++++++++++++++ + 1 files changed, 459 insertions(+), 0 deletions(-) + create mode 100644 drivers/input/touchscreen/wm9713.c + +diff --git a/drivers/input/touchscreen/wm9713.c b/drivers/input/touchscreen/wm9713.c +new file mode 100644 +index 0000000..5067e59 +--- /dev/null ++++ b/drivers/input/touchscreen/wm9713.c +@@ -0,0 +1,459 @@ ++/* ++ * wm9713.c -- Codec touch driver for Wolfson WM9713 AC97 Codec. ++ * ++ * Copyright 2003, 2004, 2005, 2006, 2007, 2008 Wolfson Microelectronics PLC. ++ * Author: Liam Girdwood ++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com ++ * Parts Copyright : Ian Molton <spyro@f2s.com> ++ * Andrew Zabolotny <zap@homelink.ru> ++ * Russell King <rmk@arm.linux.org.uk> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/version.h> ++#include <linux/kernel.h> ++#include <linux/input.h> ++#include <linux/delay.h> ++#include <linux/bitops.h> ++#include <linux/wm97xx.h> ++ ++#define TS_NAME "wm97xx" ++#define WM9713_VERSION "0.53" ++#define DEFAULT_PRESSURE 0xb0c0 ++ ++/* ++ * Module parameters ++ */ ++ ++/* ++ * Set internal pull up for pen detect. ++ * ++ * Pull up is in the range 1.02k (least sensitive) to 64k (most sensitive) ++ * i.e. pull up resistance = 64k Ohms / rpu. ++ * ++ * Adjust this value if you are having problems with pen detect not ++ * detecting any down event. ++ */ ++static int rpu = 8; ++module_param(rpu, int, 0); ++MODULE_PARM_DESC(rpu, "Set internal pull up resitor for pen detect."); ++ ++/* ++ * Set current used for pressure measurement. ++ * ++ * Set pil = 2 to use 400uA ++ * pil = 1 to use 200uA and ++ * pil = 0 to disable pressure measurement. ++ * ++ * This is used to increase the range of values returned by the adc ++ * when measureing touchpanel pressure. ++ */ ++static int pil; ++module_param(pil, int, 0); ++MODULE_PARM_DESC(pil, "Set current used for pressure measurement."); ++ ++/* ++ * Set threshold for pressure measurement. ++ * ++ * Pen down pressure below threshold is ignored. ++ */ ++static int pressure = DEFAULT_PRESSURE & 0xfff; ++module_param(pressure, int, 0); ++MODULE_PARM_DESC(pressure, "Set threshold for pressure measurement."); ++ ++/* ++ * Set adc sample delay. ++ * ++ * For accurate touchpanel measurements, some settling time may be ++ * required between the switch matrix applying a voltage across the ++ * touchpanel plate and the ADC sampling the signal. ++ * ++ * This delay can be set by setting delay = n, where n is the array ++ * position of the delay in the array delay_table below. ++ * Long delays > 1ms are supported for completeness, but are not ++ * recommended. ++ */ ++static int delay = 4; ++module_param(delay, int, 0); ++MODULE_PARM_DESC(delay, "Set adc sample delay."); ++ ++/* ++ * Set adc mask function. ++ * ++ * Sources of glitch noise, such as signals driving an LCD display, may feed ++ * through to the touch screen plates and affect measurement accuracy. In ++ * order to minimise this, a signal may be applied to the MASK pin to delay or ++ * synchronise the sampling. ++ * ++ * 0 = No delay or sync ++ * 1 = High on pin stops conversions ++ * 2 = Edge triggered, edge on pin delays conversion by delay param (above) ++ * 3 = Edge triggered, edge on pin starts conversion after delay param ++ */ ++static int mask; ++module_param(mask, int, 0); ++MODULE_PARM_DESC(mask, "Set adc mask function."); ++ ++/* ++ * Coordinate Polling Enable. ++ * ++ * Set to 1 to enable coordinate polling. e.g. x,y[,p] is sampled together ++ * for every poll. ++ */ ++static int coord; ++module_param(coord, int, 0); ++MODULE_PARM_DESC(coord, "Polling coordinate mode"); ++ ++/* ++ * ADC sample delay times in uS ++ */ ++static const int delay_table[] = { ++ 21, /* 1 AC97 Link frames */ ++ 42, /* 2 */ ++ 84, /* 4 */ ++ 167, /* 8 */ ++ 333, /* 16 */ ++ 667, /* 32 */ ++ 1000, /* 48 */ ++ 1333, /* 64 */ ++ 2000, /* 96 */ ++ 2667, /* 128 */ ++ 3333, /* 160 */ ++ 4000, /* 192 */ ++ 4667, /* 224 */ ++ 5333, /* 256 */ ++ 6000, /* 288 */ ++ 0 /* No delay, switch matrix always on */ ++}; ++ ++/* ++ * Delay after issuing a POLL command. ++ * ++ * The delay is 3 AC97 link frames + the touchpanel settling delay ++ */ ++static inline void poll_delay(int d) ++{ ++ udelay(3 * AC97_LINK_FRAME + delay_table[d]); ++} ++ ++/* ++ * set up the physical settings of the WM9713 ++ */ ++static void wm9713_phy_init(struct wm97xx *wm) ++{ ++ u16 dig1 = 0, dig2, dig3; ++ ++ /* default values */ ++ dig2 = WM97XX_DELAY(4) | WM97XX_SLT(5); ++ dig3 = WM9712_RPU(1); ++ ++ /* rpu */ ++ if (rpu) { ++ dig3 &= 0xffc0; ++ dig3 |= WM9712_RPU(rpu); ++ dev_info(wm->dev, "setting pen detect pull-up to %d Ohms\n", ++ 64000 / rpu); ++ } ++ ++ /* touchpanel pressure */ ++ if (pil == 2) { ++ dig3 |= WM9712_PIL; ++ dev_info(wm->dev, ++ "setting pressure measurement current to 400uA."); ++ } else if (pil) ++ dev_info(wm->dev, ++ "setting pressure measurement current to 200uA."); ++ if (!pil) ++ pressure = 0; ++ ++ /* sample settling delay */ ++ if (delay < 0 || delay > 15) { ++ dev_info(wm->dev, "supplied delay out of range."); ++ delay = 4; ++ dev_info(wm->dev, "setting adc sample delay to %d u Secs.", ++ delay_table[delay]); ++ } ++ dig2 &= 0xff0f; ++ dig2 |= WM97XX_DELAY(delay); ++ ++ /* mask */ ++ dig3 |= ((mask & 0x3) << 4); ++ if (coord) ++ dig3 |= WM9713_WAIT; ++ ++ wm->misc = wm97xx_reg_read(wm, 0x5a); ++ ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, dig2); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, dig3); ++ wm97xx_reg_write(wm, AC97_GPIO_STICKY, 0x0); ++} ++ ++static void wm9713_dig_enable(struct wm97xx *wm, int enable) ++{ ++ u16 val; ++ ++ if (enable) { ++ val = wm97xx_reg_read(wm, AC97_EXTENDED_MID); ++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, val & 0x7fff); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2] | ++ WM97XX_PRP_DET_DIG); ++ wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); /* dummy read */ ++ } else { ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig[2] & ++ ~WM97XX_PRP_DET_DIG); ++ val = wm97xx_reg_read(wm, AC97_EXTENDED_MID); ++ wm97xx_reg_write(wm, AC97_EXTENDED_MID, val | 0x8000); ++ } ++} ++ ++static void wm9713_dig_restore(struct wm97xx *wm) ++{ ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, wm->dig_save[0]); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, wm->dig_save[1]); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, wm->dig_save[2]); ++} ++ ++static void wm9713_aux_prepare(struct wm97xx *wm) ++{ ++ memcpy(wm->dig_save, wm->dig, sizeof(wm->dig)); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, 0); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, 0); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, WM97XX_PRP_DET_DIG); ++} ++ ++static inline int is_pden(struct wm97xx *wm) ++{ ++ return wm->dig[2] & WM9713_PDEN; ++} ++ ++/* ++ * Read a sample from the WM9713 adc in polling mode. ++ */ ++static int wm9713_poll_sample(struct wm97xx *wm, int adcsel, int *sample) ++{ ++ u16 dig1; ++ int timeout = 5 * delay; ++ ++ if (!wm->pen_probably_down) { ++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (!(data & WM97XX_PEN_DOWN)) ++ return RC_PENUP; ++ wm->pen_probably_down = 1; ++ } ++ ++ /* set up digitiser */ ++ if (adcsel & 0x8000) ++ adcsel = 1 << ((adcsel & 0x7fff) + 3); ++ ++ dig1 = wm97xx_reg_read(wm, AC97_WM9713_DIG1); ++ dig1 &= ~WM9713_ADCSEL_MASK; ++ ++ if (wm->mach_ops && wm->mach_ops->pre_sample) ++ wm->mach_ops->pre_sample(adcsel); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1 | adcsel | WM9713_POLL); ++ ++ /* wait 3 AC97 time slots + delay for conversion */ ++ poll_delay(delay); ++ ++ /* wait for POLL to go low */ ++ while ((wm97xx_reg_read(wm, AC97_WM9713_DIG1) & WM9713_POLL) && ++ timeout) { ++ udelay(AC97_LINK_FRAME); ++ timeout--; ++ } ++ ++ if (timeout <= 0) { ++ /* If PDEN is set, we can get a timeout when pen goes up */ ++ if (is_pden(wm)) ++ wm->pen_probably_down = 0; ++ else ++ dev_dbg(wm->dev, "adc sample timeout"); ++ return RC_PENUP; ++ } ++ ++ *sample = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (wm->mach_ops && wm->mach_ops->post_sample) ++ wm->mach_ops->post_sample(adcsel); ++ ++ /* check we have correct sample */ ++ if ((*sample & WM97XX_ADCSRC_MASK) != ffs(adcsel >> 1) << 12) { ++ dev_dbg(wm->dev, "adc wrong sample, read %x got %x", adcsel, ++ *sample & WM97XX_ADCSRC_MASK); ++ return RC_PENUP; ++ } ++ ++ if (!(*sample & WM97XX_PEN_DOWN)) { ++ wm->pen_probably_down = 0; ++ return RC_PENUP; ++ } ++ ++ return RC_VALID; ++} ++ ++/* ++ * Read a coordinate from the WM9713 adc in polling mode. ++ */ ++static int wm9713_poll_coord(struct wm97xx *wm, struct wm97xx_data *data) ++{ ++ u16 dig1; ++ int timeout = 5 * delay; ++ ++ if (!wm->pen_probably_down) { ++ u16 data = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (!(data & WM97XX_PEN_DOWN)) ++ return RC_PENUP; ++ wm->pen_probably_down = 1; ++ } ++ ++ /* set up digitiser */ ++ dig1 = wm97xx_reg_read(wm, AC97_WM9713_DIG1); ++ dig1 &= ~WM9713_ADCSEL_MASK; ++ if (pil) ++ dig1 |= WM97XX_ADCSEL_PRES; ++ ++ if (wm->mach_ops && wm->mach_ops->pre_sample) ++ wm->mach_ops->pre_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, ++ dig1 | WM9713_POLL | WM9713_COO); ++ ++ /* wait 3 AC97 time slots + delay for conversion */ ++ poll_delay(delay); ++ data->x = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ /* wait for POLL to go low */ ++ while ((wm97xx_reg_read(wm, AC97_WM9713_DIG1) & WM9713_POLL) ++ && timeout) { ++ udelay(AC97_LINK_FRAME); ++ timeout--; ++ } ++ ++ if (timeout <= 0) { ++ /* If PDEN is set, we can get a timeout when pen goes up */ ++ if (is_pden(wm)) ++ wm->pen_probably_down = 0; ++ else ++ dev_dbg(wm->dev, "adc sample timeout"); ++ return RC_PENUP; ++ } ++ ++ /* read back data */ ++ data->y = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ if (pil) ++ data->p = wm97xx_reg_read(wm, AC97_WM97XX_DIGITISER_RD); ++ else ++ data->p = DEFAULT_PRESSURE; ++ ++ if (wm->mach_ops && wm->mach_ops->post_sample) ++ wm->mach_ops->post_sample(WM97XX_ADCSEL_X | WM97XX_ADCSEL_Y); ++ ++ /* check we have correct sample */ ++ if (!(data->x & WM97XX_ADCSEL_X) || !(data->y & WM97XX_ADCSEL_Y)) ++ goto err; ++ if (pil && !(data->p & WM97XX_ADCSEL_PRES)) ++ goto err; ++ ++ if (!(data->x & WM97XX_PEN_DOWN)) { ++ wm->pen_probably_down = 0; ++ return RC_PENUP; ++ } ++ return RC_VALID; ++err: ++ return RC_PENUP; ++} ++ ++/* ++ * Sample the WM9713 touchscreen in polling mode ++ */ ++static int wm9713_poll_touch(struct wm97xx *wm, struct wm97xx_data *data) ++{ ++ int rc; ++ ++ if (coord) { ++ rc = wm9713_poll_coord(wm, data); ++ if (rc != RC_VALID) ++ return rc; ++ } else { ++ rc = wm9713_poll_sample(wm, WM9713_ADCSEL_X, &data->x); ++ if (rc != RC_VALID) ++ return rc; ++ rc = wm9713_poll_sample(wm, WM9713_ADCSEL_Y, &data->y); ++ if (rc != RC_VALID) ++ return rc; ++ if (pil) { ++ rc = wm9713_poll_sample(wm, WM9713_ADCSEL_PRES, ++ &data->p); ++ if (rc != RC_VALID) ++ return rc; ++ } else ++ data->p = DEFAULT_PRESSURE; ++ } ++ return RC_VALID; ++} ++ ++/* ++ * Enable WM9713 continuous mode, i.e. touch data is streamed across ++ * an AC97 slot ++ */ ++static int wm9713_acc_enable(struct wm97xx *wm, int enable) ++{ ++ u16 dig1, dig2, dig3; ++ int ret = 0; ++ ++ dig1 = wm->dig[0]; ++ dig2 = wm->dig[1]; ++ dig3 = wm->dig[2]; ++ ++ if (enable) { ++ /* continous mode */ ++ if (wm->mach_ops->acc_startup && ++ (ret = wm->mach_ops->acc_startup(wm)) < 0) ++ return ret; ++ ++ dig1 &= ~WM9713_ADCSEL_MASK; ++ dig1 |= WM9713_CTC | WM9713_COO | WM9713_ADCSEL_X | ++ WM9713_ADCSEL_Y; ++ if (pil) ++ dig1 |= WM9713_ADCSEL_PRES; ++ dig2 &= ~(WM97XX_DELAY_MASK | WM97XX_SLT_MASK | ++ WM97XX_CM_RATE_MASK); ++ dig2 |= WM97XX_SLEN | WM97XX_DELAY(delay) | ++ WM97XX_SLT(wm->acc_slot) | WM97XX_RATE(wm->acc_rate); ++ dig3 |= WM9713_PDEN; ++ } else { ++ dig1 &= ~(WM9713_CTC | WM9713_COO); ++ dig2 &= ~WM97XX_SLEN; ++ dig3 &= ~WM9713_PDEN; ++ if (wm->mach_ops->acc_shutdown) ++ wm->mach_ops->acc_shutdown(wm); ++ } ++ ++ wm97xx_reg_write(wm, AC97_WM9713_DIG1, dig1); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG2, dig2); ++ wm97xx_reg_write(wm, AC97_WM9713_DIG3, dig3); ++ return ret; ++} ++ ++struct wm97xx_codec_drv wm9713_codec = { ++ .id = WM9713_ID2, ++ .name = "wm9713", ++ .poll_sample = wm9713_poll_sample, ++ .poll_touch = wm9713_poll_touch, ++ .acc_enable = wm9713_acc_enable, ++ .phy_init = wm9713_phy_init, ++ .dig_enable = wm9713_dig_enable, ++ .dig_restore = wm9713_dig_restore, ++ .aux_prepare = wm9713_aux_prepare, ++}; ++EXPORT_SYMBOL_GPL(wm9713_codec); ++ ++/* Module information */ ++MODULE_AUTHOR("Liam Girdwood <liam.girdwood@wolfsonmicro.com>"); ++MODULE_DESCRIPTION("WM9713 Touch Screen Driver"); ++MODULE_LICENSE("GPL"); +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0034-Driver-for-WM97xx-touchscreens-in-streaming-mode-on.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0034-Driver-for-WM97xx-touchscreens-in-streaming-mode-on.patch new file mode 100644 index 0000000000..0391cfcd83 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0034-Driver-for-WM97xx-touchscreens-in-streaming-mode-on.patch @@ -0,0 +1,329 @@ +From 821604bad5ce1ef942eeb420afd9ea2c5c92875e Mon Sep 17 00:00:00 2001 +From: Mark Brown <broonie@opensource.wolfsonmicro.com> +Date: Sat, 26 Jan 2008 21:14:19 +0300 +Subject: [PATCH 34/64] Driver for WM97xx touchscreens in streaming mode on Mainstone + +Signed-off-by: Liam Girdwood <liam.girdwood@wolfsonmicro.com> +Signed-off-by: Graeme Gregory <gg@opensource.wolfsonmicro.com> +Signed-off-by: Mike Arthur <mike.arthur@wolfsonmicro.com> +Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> +Cc: Dmitry Baryshkov <dbaryshkov@gmail.com> +Cc: Stanley Cai <stanley.cai@intel.com> +Cc: Rodolfo Giometti <giometti@enneenne.com> +Cc: Russell King <rmk@arm.linux.org.uk> +Cc: Marc Kleine-Budde <mkl@pengutronix.de> +Cc: Ian Molton <spyro@f2s.com> +Cc: Vince Sanders <vince@kyllikki.org> +Cc: Andrew Zabolotny <zap@homelink.ru> +--- + drivers/input/touchscreen/mainstone-wm97xx.c | 298 ++++++++++++++++++++++++++ + 1 files changed, 298 insertions(+), 0 deletions(-) + create mode 100644 drivers/input/touchscreen/mainstone-wm97xx.c + +diff --git a/drivers/input/touchscreen/mainstone-wm97xx.c b/drivers/input/touchscreen/mainstone-wm97xx.c +new file mode 100644 +index 0000000..8e1c35d +--- /dev/null ++++ b/drivers/input/touchscreen/mainstone-wm97xx.c +@@ -0,0 +1,298 @@ ++/* ++ * mainstone-wm97xx.c -- Mainstone Continuous Touch screen driver for ++ * Wolfson WM97xx AC97 Codecs. ++ * ++ * Copyright 2004, 2007 Wolfson Microelectronics PLC. ++ * Author: Liam Girdwood ++ * liam.girdwood@wolfsonmicro.com or linux@wolfsonmicro.com ++ * Parts Copyright : Ian Molton <spyro@f2s.com> ++ * Andrew Zabolotny <zap@homelink.ru> ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * Notes: ++ * This is a wm97xx extended touch driver 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 ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/version.h> ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/delay.h> ++#include <linux/irq.h> ++#include <linux/interrupt.h> ++#include <linux/wm97xx.h> ++#include <linux/io.h> ++#include <asm/arch/pxa-regs.h> ++ ++#define VERSION "0.13" ++ ++struct continuous { ++ u16 id; /* codec id */ ++ u8 code; /* continuous code */ ++ u8 reads; /* number of coord reads per read cycle */ ++ u32 speed; /* number of coords per second */ ++}; ++ ++#define WM_READS(sp) ((sp / HZ) + 1) ++ ++static const struct continuous cinfo[] = { ++ {WM9705_ID2, 0, WM_READS(94), 94}, ++ {WM9705_ID2, 1, WM_READS(188), 188}, ++ {WM9705_ID2, 2, WM_READS(375), 375}, ++ {WM9705_ID2, 3, WM_READS(750), 750}, ++ {WM9712_ID2, 0, WM_READS(94), 94}, ++ {WM9712_ID2, 1, WM_READS(188), 188}, ++ {WM9712_ID2, 2, WM_READS(375), 375}, ++ {WM9712_ID2, 3, WM_READS(750), 750}, ++ {WM9713_ID2, 0, WM_READS(94), 94}, ++ {WM9713_ID2, 1, WM_READS(120), 120}, ++ {WM9713_ID2, 2, WM_READS(154), 154}, ++ {WM9713_ID2, 3, WM_READS(188), 188}, ++}; ++ ++/* continuous speed index */ ++static int sp_idx; ++static u16 last, tries; ++ ++/* ++ * Pen sampling frequency (Hz) in continuous mode. ++ */ ++static int cont_rate = 200; ++module_param(cont_rate, int, 0); ++MODULE_PARM_DESC(cont_rate, "Sampling rate in continuous mode (Hz)"); ++ ++/* ++ * 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; ++module_param(pen_int, int, 0); ++MODULE_PARM_DESC(pen_int, "Pen down detection (1 = interrupt, 0 = polling)"); ++ ++/* ++ * Pressure readback. ++ * ++ * Set to 1 to read back pen down pressure ++ */ ++static int pressure; ++module_param(pressure, int, 0); ++MODULE_PARM_DESC(pressure, "Pressure readback (1 = pressure, 0 = no pressure)"); ++ ++/* ++ * AC97 touch data slot. ++ * ++ * Touch screen readback data ac97 slot ++ */ ++static int ac97_touch_slot = 5; ++module_param(ac97_touch_slot, int, 0); ++MODULE_PARM_DESC(ac97_touch_slot, "Touch screen data slot AC97 number"); ++ ++ ++/* flush AC97 slot 5 FIFO on pxa machines */ ++#ifdef CONFIG_PXA27x ++static void wm97xx_acc_pen_up(struct wm97xx *wm) ++{ ++ set_current_state(TASK_INTERRUPTIBLE); ++ schedule_timeout(1); ++ ++ while (MISR & (1 << 2)) ++ MODR; ++} ++#else ++static void wm97xx_acc_pen_up(struct wm97xx *wm) ++{ ++ int count = 16; ++ set_current_state(TASK_INTERRUPTIBLE); ++ schedule_timeout(1); ++ ++ while (count < 16) { ++ MODR; ++ count--; ++ } ++} ++#endif ++ ++static int wm97xx_acc_pen_down(struct wm97xx *wm) ++{ ++ u16 x, y, p = 0x100 | WM97XX_ADCSEL_PRES; ++ int reads = 0; ++ ++ /* data is never immediately available after pen down irq */ ++ set_current_state(TASK_INTERRUPTIBLE); ++ schedule_timeout(1); ++ ++ if (tries > 5) { ++ tries = 0; ++ return RC_PENUP; ++ } ++ ++ x = MODR; ++ if (x == last) { ++ tries++; ++ return RC_AGAIN; ++ } ++ last = x; ++ do { ++ if (reads) ++ x = MODR; ++ y = MODR; ++ if (pressure) ++ p = MODR; ++ ++ /* are samples valid */ ++ if ((x & 0x7000) != WM97XX_ADCSEL_X || ++ (y & 0x7000) != WM97XX_ADCSEL_Y || ++ (p & 0x7000) != WM97XX_ADCSEL_PRES) ++ goto up; ++ ++ /* coordinate is good */ ++ tries = 0; ++ input_report_abs(wm->input_dev, ABS_X, x & 0xfff); ++ input_report_abs(wm->input_dev, ABS_Y, y & 0xfff); ++ input_report_abs(wm->input_dev, ABS_PRESSURE, p & 0xfff); ++ input_sync(wm->input_dev); ++ reads++; ++ } while (reads < cinfo[sp_idx].reads); ++up: ++ return RC_PENDOWN | RC_AGAIN; ++} ++ ++static int wm97xx_acc_startup(struct wm97xx *wm) ++{ ++ int idx = 0; ++ ++ /* check we have a codec */ ++ if (wm->ac97 == NULL) ++ return -ENODEV; ++ ++ /* Go you big red fire engine */ ++ for (idx = 0; idx < ARRAY_SIZE(cinfo); idx++) { ++ if (wm->id != cinfo[idx].id) ++ continue; ++ sp_idx = idx; ++ if (cont_rate <= cinfo[idx].speed) ++ break; ++ } ++ wm->acc_rate = cinfo[sp_idx].code; ++ wm->acc_slot = ac97_touch_slot; ++ dev_info(wm->dev, ++ "mainstone accelerated touchscreen driver, %d samples/sec\n", ++ cinfo[sp_idx].speed); ++ ++ /* codec specific irq config */ ++ if (pen_int) { ++ switch (wm->id) { ++ case WM9705_ID2: ++ wm->pen_irq = IRQ_GPIO(4); ++ set_irq_type(IRQ_GPIO(4), IRQT_BOTHEDGE); ++ break; ++ case WM9712_ID2: ++ case WM9713_ID2: ++ /* enable pen down interrupt */ ++ /* use PEN_DOWN GPIO 13 to assert IRQ on GPIO line 2 */ ++ wm->pen_irq = MAINSTONE_AC97_IRQ; ++ wm97xx_config_gpio(wm, WM97XX_GPIO_13, WM97XX_GPIO_IN, ++ WM97XX_GPIO_POL_HIGH, ++ WM97XX_GPIO_STICKY, ++ WM97XX_GPIO_WAKE); ++ wm97xx_config_gpio(wm, WM97XX_GPIO_2, WM97XX_GPIO_OUT, ++ WM97XX_GPIO_POL_HIGH, ++ WM97XX_GPIO_NOTSTICKY, ++ WM97XX_GPIO_NOWAKE); ++ break; ++ default: ++ dev_err(wm->dev, ++ "pen down irq not supported on this device\n"); ++ pen_int = 0; ++ break; ++ } ++ } ++ ++ return 0; ++} ++ ++static void wm97xx_acc_shutdown(struct wm97xx *wm) ++{ ++ /* codec specific deconfig */ ++ if (pen_int) { ++ switch (wm->id & 0xffff) { ++ case WM9705_ID2: ++ wm->pen_irq = 0; ++ break; ++ case WM9712_ID2: ++ case WM9713_ID2: ++ /* disable interrupt */ ++ wm->pen_irq = 0; ++ break; ++ } ++ } ++} ++ ++static void wm97xx_irq_enable(struct wm97xx *wm, int enable) ++{ ++ if (enable) ++ enable_irq(wm->pen_irq); ++ else ++ disable_irq(wm->pen_irq); ++} ++ ++static struct wm97xx_mach_ops mainstone_mach_ops = { ++ .acc_enabled = 1, ++ .acc_pen_up = wm97xx_acc_pen_up, ++ .acc_pen_down = wm97xx_acc_pen_down, ++ .acc_startup = wm97xx_acc_startup, ++ .acc_shutdown = wm97xx_acc_shutdown, ++ .irq_enable = wm97xx_irq_enable, ++}; ++ ++static int mainstone_wm97xx_probe(struct platform_device *pdev) ++{ ++ struct wm97xx *wm = platform_get_drvdata(pdev); ++ return wm97xx_register_mach_ops(wm, &mainstone_mach_ops); ++} ++ ++static int mainstone_wm97xx_remove(struct platform_device *pdev) ++{ ++ struct wm97xx *wm = platform_get_drvdata(pdev); ++ wm97xx_unregister_mach_ops(wm); ++ return 0; ++} ++ ++static struct platform_driver mainstone_wm97xx_driver = { ++ .probe = mainstone_wm97xx_probe, ++ .remove = mainstone_wm97xx_remove, ++ .driver = { ++ .name = "wm97xx-touch", ++ }, ++}; ++ ++static int __init mainstone_wm97xx_init(void) ++{ ++ return platform_driver_register(&mainstone_wm97xx_driver); ++} ++ ++static void __exit mainstone_wm97xx_exit(void) ++{ ++ platform_driver_unregister(&mainstone_wm97xx_driver); ++} ++ ++module_init(mainstone_wm97xx_init); ++module_exit(mainstone_wm97xx_exit); ++ ++/* Module information */ ++MODULE_AUTHOR("Liam Girdwood <liam.girdwood@wolfsonmicro.com>"); ++MODULE_DESCRIPTION("wm97xx continuous touch driver for mainstone"); ++MODULE_LICENSE("GPL"); +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0035-Build-system-and-MAINTAINERS-entry-for-WM97xx-touchs.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0035-Build-system-and-MAINTAINERS-entry-for-WM97xx-touchs.patch new file mode 100644 index 0000000000..aa0918f43e --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0035-Build-system-and-MAINTAINERS-entry-for-WM97xx-touchs.patch @@ -0,0 +1,122 @@ +From eba6a504393932764a33aae64021827dd2c5c70c Mon Sep 17 00:00:00 2001 +From: Mark Brown <broonie@opensource.wolfsonmicro.com> +Date: Sat, 26 Jan 2008 21:14:18 +0300 +Subject: [PATCH 35/64] Build system and MAINTAINERS entry for WM97xx touchscreen drivers + +Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> +Signed-off-by: Liam Girdwood <liam.girdwood@wolfsonmicro.com> +--- + MAINTAINERS | 10 +++++++ + drivers/input/touchscreen/Kconfig | 52 ++++++++++++++++++++++++++++++++++++ + drivers/input/touchscreen/Makefile | 7 +++++ + 3 files changed, 69 insertions(+), 0 deletions(-) + +diff --git a/MAINTAINERS b/MAINTAINERS +index 2340cfb..f02851c 100644 +--- a/MAINTAINERS ++++ b/MAINTAINERS +@@ -4204,6 +4204,16 @@ L: linux-wireless@vger.kernel.org + W: http://oops.ghostprotocols.net:81/blog + S: Maintained + ++WM97XX TOUCHSCREEN DRIVERS ++P: Mark Brown ++M: broonie@opensource.wolfsonmicro.com ++P: Liam Girdwood ++M: liam.girdwood@wolfsonmicro.com ++L: linux-input@vger.kernel.org ++T: git git://opensource.wolfsonmicro.com/linux-2.6-touch ++W: http://opensource.wolfsonmicro.com/node/7 ++S: Supported ++ + X.25 NETWORK LAYER + P: Henner Eisen + M: eis@baty.hanse.de +diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig +index 90e8e92..0be05a2 100644 +--- a/drivers/input/touchscreen/Kconfig ++++ b/drivers/input/touchscreen/Kconfig +@@ -158,6 +158,58 @@ config TOUCHSCREEN_TOUCHRIGHT + To compile this driver as a module, choose M here: the + module will be called touchright. + ++config TOUCHSCREEN_WM97XX ++ tristate "Support for WM97xx AC97 touchscreen controllers" ++ depends on AC97_BUS ++ ++config TOUCHSCREEN_WM9705 ++ bool "WM9705 Touchscreen interface support" ++ depends on TOUCHSCREEN_WM97XX ++ help ++ Say Y here if you have a Wolfson Microelectronics WM9705 touchscreen ++ controller connected to your system. ++ ++ If unsure, say N. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called wm9705. ++ ++config TOUCHSCREEN_WM9712 ++ bool "WM9712 Touchscreen interface support" ++ depends on TOUCHSCREEN_WM97XX ++ help ++ Say Y here if you have a Wolfson Microelectronics WM9712 touchscreen ++ controller connected to your system. ++ ++ If unsure, say N. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called wm9712. ++ ++config TOUCHSCREEN_WM9713 ++ bool "WM9713 Touchscreen interface support" ++ depends on TOUCHSCREEN_WM97XX ++ help ++ Say Y here if you have a Wolfson Microelectronics WM9713 touchscreen ++ controller connected to your system. ++ ++ If unsure, say N. ++ ++ To compile this driver as a module, choose M here: the ++ module will be called wm9713. ++ ++config TOUCHSCREEN_WM97XX_MAINSTONE ++ tristate "WM97xx Mainstone accelerated touch" ++ depends on TOUCHSCREEN_WM97XX && ARCH_PXA ++ help ++ Say Y here for support for streaming mode with WM97xx touchscreens ++ on Mainstone systems. ++ ++ If unsure, say N ++ ++ To compile this driver as a module, choose M here: the ++ module will be called mainstone-wm97xx ++ + config TOUCHSCREEN_TOUCHWIN + tristate "Touchwin serial touchscreen" + select SERIO +diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile +index 35d4097..d38156e 100644 +--- a/drivers/input/touchscreen/Makefile ++++ b/drivers/input/touchscreen/Makefile +@@ -4,6 +4,8 @@ + + # Each configuration option enables a list of files. + ++wm97xx-ts-y := wm97xx-core.o ++ + obj-$(CONFIG_TOUCHSCREEN_ADS7846) += ads7846.o + obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3600_ts_input.o + obj-$(CONFIG_TOUCHSCREEN_CORGI) += corgi_ts.o +@@ -19,3 +21,8 @@ obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o + obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o + obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o + obj-$(CONFIG_TOUCHSCREEN_TSC2101) += tsc2101_ts.o ++obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o ++obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o ++wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o ++wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712) += wm9712.o ++wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9713) += wm9713.o +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0036-Set-id-to-1-for-wm97xx-subdevices.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0036-Set-id-to-1-for-wm97xx-subdevices.patch new file mode 100644 index 0000000000..dd10b34586 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0036-Set-id-to-1-for-wm97xx-subdevices.patch @@ -0,0 +1,35 @@ +From 9ea478cbd5473f52ca036cccc00dddad717d7861 Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Wed, 30 Jan 2008 19:27:13 +0300 +Subject: [PATCH 36/64] Set id to -1 for wm97xx subdevices + +Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> +--- + drivers/input/touchscreen/wm97xx-core.c | 4 ++-- + 1 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c +index 27a0a99..e066acc 100644 +--- a/drivers/input/touchscreen/wm97xx-core.c ++++ b/drivers/input/touchscreen/wm97xx-core.c +@@ -592,7 +592,7 @@ static int wm97xx_probe(struct device *dev) + wm->gpio[5] = wm97xx_reg_read(wm, AC97_MISC_AFE); + + /* register our battery device */ +- wm->battery_dev = platform_device_alloc("wm97xx-battery", 0); ++ wm->battery_dev = platform_device_alloc("wm97xx-battery", -1); + if (!wm->battery_dev) + goto batt_err; + platform_set_drvdata(wm->battery_dev, wm); +@@ -603,7 +603,7 @@ static int wm97xx_probe(struct device *dev) + + /* register our extended touch device (for machine specific + * extensions) */ +- wm->touch_dev = platform_device_alloc("wm97xx-touch", 0); ++ wm->touch_dev = platform_device_alloc("wm97xx-touch", -1); + if (!wm->touch_dev) + goto touch_err; + platform_set_drvdata(wm->touch_dev, wm); +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0037-Don-t-lock-the-codec-list-in-snd_soc_dapm_new_widget.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0037-Don-t-lock-the-codec-list-in-snd_soc_dapm_new_widget.patch new file mode 100644 index 0000000000..010194dd96 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0037-Don-t-lock-the-codec-list-in-snd_soc_dapm_new_widget.patch @@ -0,0 +1,41 @@ +From d2888c7643b07687b14a839239cbe7fc5bf565e6 Mon Sep 17 00:00:00 2001 +From: Mark Brown <broonie@opensource.wolfsonmicro.com> +Date: Mon, 14 Jan 2008 23:24:26 +0300 +Subject: [PATCH 37/64] Don't lock the codec list in snd_soc_dapm_new_widgets() + +snd_soc_dapm_new_widgets() takes the codec lock when adding new widgets, +causing lockdep warnings when applications later call down through ALSA +to adjust controls. Since widgets are only added during probe this lock +should be unneeded so don't take it. + +Thanks to Dmitry Baryshkov <dbaryshkov@gmail.com> for reporting this issue. + +Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com> +Cc: Dmitry Baryshkov <dbaryshkov@gmail.com> +--- + sound/soc/soc-dapm.c | 2 -- + 1 files changed, 0 insertions(+), 2 deletions(-) + +diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c +index 29a546f..e46cdc5 100644 +--- a/sound/soc/soc-dapm.c ++++ b/sound/soc/soc-dapm.c +@@ -963,7 +963,6 @@ int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec) + { + struct snd_soc_dapm_widget *w; + +- mutex_lock(&codec->mutex); + list_for_each_entry(w, &codec->dapm_widgets, list) + { + if (w->new) +@@ -998,7 +997,6 @@ int snd_soc_dapm_new_widgets(struct snd_soc_codec *codec) + } + + dapm_power_widgets(codec, SND_SOC_DAPM_STREAM_NOP); +- mutex_unlock(&codec->mutex); + return 0; + } + EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets); +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0038-Don-t-lock-the-codec-list-in-snd_soc_dapm_new_widget.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0038-Don-t-lock-the-codec-list-in-snd_soc_dapm_new_widget.patch new file mode 100644 index 0000000000..7a3eb61a27 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0038-Don-t-lock-the-codec-list-in-snd_soc_dapm_new_widget.patch @@ -0,0 +1,57 @@ +From 5bae1fab16c7b14a458aa90e5654fe3a1d8d960f Mon Sep 17 00:00:00 2001 +From: Mark Brown <broonie@opensource.wolfsonmicro.com> +Date: Sun, 20 Jan 2008 00:06:06 +0300 +Subject: [PATCH 38/64] Don't lock the codec list in snd_soc_dapm_new_widgets() + +On Wed, Jan 16, 2008 at 02:40:55AM +0300, Dmitry wrote: + +> I'm sorry, but I tested this patch only now. And I just got another +> message from lockdep: + +Could you give this patch a try, please? +--- + sound/soc/soc-core.c | 7 +++++-- + 1 files changed, 5 insertions(+), 2 deletions(-) + +diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c +index e6a67b5..7f3ed9f 100644 +--- a/sound/soc/soc-core.c ++++ b/sound/soc/soc-core.c +@@ -1090,7 +1090,6 @@ int snd_soc_register_card(struct snd_soc_device *socdev) + struct snd_soc_machine *machine = socdev->machine; + int ret = 0, i, ac97 = 0, err = 0; + +- mutex_lock(&codec->mutex); + for(i = 0; i < machine->num_links; i++) { + if (socdev->machine->dai_link[i].init) { + err = socdev->machine->dai_link[i].init(codec); +@@ -1116,12 +1115,14 @@ int snd_soc_register_card(struct snd_soc_device *socdev) + goto out; + } + ++ mutex_lock(&codec->mutex); + #ifdef CONFIG_SND_SOC_AC97_BUS + if (ac97) { + ret = soc_ac97_dev_register(codec); + if (ret < 0) { + printk(KERN_ERR "asoc: AC97 device register failed\n"); + snd_card_free(codec->card); ++ mutex_unlock(&codec->mutex); + goto out; + } + } +@@ -1134,8 +1135,10 @@ int snd_soc_register_card(struct snd_soc_device *socdev) + err = device_create_file(socdev->dev, &dev_attr_codec_reg); + if (err < 0) + printk(KERN_WARNING "asoc: failed to add codec sysfs entries\n"); +-out: ++ + mutex_unlock(&codec->mutex); ++ ++out: + return ret; + } + EXPORT_SYMBOL_GPL(snd_soc_register_card); +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0039-Add-generic-framework-for-managing-clocks.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0039-Add-generic-framework-for-managing-clocks.patch new file mode 100644 index 0000000000..c09c208c6a --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0039-Add-generic-framework-for-managing-clocks.patch @@ -0,0 +1,446 @@ +From 62c9a23cfa7181369637d0b61a8e90c83c562f03 Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Mon, 4 Feb 2008 03:01:06 +0300 +Subject: [PATCH 39/64] Add generic framework for managing clocks. + +Provide a generic framework that platform may choose +to support clocks api. In particular this provides +platform-independant struct clk definition, a full +implementation of clocks api and a set of functions +for registering and unregistering clocks in a safe way. + +Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> +--- + include/linux/clklib.h | 85 ++++++++++++++ + init/Kconfig | 7 + + kernel/Makefile | 1 + + kernel/clklib.c | 295 ++++++++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 388 insertions(+), 0 deletions(-) + create mode 100644 include/linux/clklib.h + create mode 100644 kernel/clklib.c + +diff --git a/include/linux/clklib.h b/include/linux/clklib.h +new file mode 100644 +index 0000000..4bd9b4a +--- /dev/null ++++ b/include/linux/clklib.h +@@ -0,0 +1,85 @@ ++/* ++ * Copyright (C) 2008 Dmitry Baryshkov ++ * ++ * This file is released under the GPL v2. ++ */ ++ ++#ifndef CLKLIB_H ++#define CLKLIB_H ++ ++#include <linux/list.h> ++ ++struct clk { ++ struct list_head node; ++ struct clk *parent; ++ ++ const char *name; ++ struct module *owner; ++ ++ int users; ++ unsigned long rate; ++ int delay; ++ ++ int (*can_get) (struct clk *, struct device *); ++ int (*set_parent) (struct clk *, struct clk *); ++ int (*enable) (struct clk *); ++ void (*disable) (struct clk *); ++ unsigned long (*getrate) (struct clk*); ++ int (*setrate) (struct clk *, unsigned long); ++ long (*roundrate) (struct clk *, unsigned long); ++ ++ void *priv; ++}; ++ ++int clk_register(struct clk *clk); ++void clk_unregister(struct clk *clk); ++static void __maybe_unused clks_register(struct clk *clks, size_t num) ++{ ++ int i; ++ for (i = 0; i < num; i++) { ++ clk_register(&clks[i]); ++ } ++} ++ ++ ++int clk_alloc_function(const char *parent, struct clk *clk); ++ ++struct clk_function { ++ const char *parent; ++ struct clk *clk; ++}; ++ ++#define CLK_FUNC(_clock, _function, _can_get, _data, _format) \ ++ { \ ++ .parent = _clock, \ ++ .clk = &(struct clk) { \ ++ .name= _function, \ ++ .owner = THIS_MODULE, \ ++ .can_get = _can_get, \ ++ .priv = _data, \ ++ .format = _format, \ ++ }, \ ++ } ++ ++static int __maybe_unused clk_alloc_functions( ++ struct clk_function *funcs, ++ int num) ++{ ++ int i; ++ int rc; ++ ++ for (i = 0; i < num; i++) { ++ rc = clk_alloc_function(funcs[i].parent, funcs[i].clk); ++ ++ if (rc) { ++ printk(KERN_ERR "Error allocating %s.%s function.\n", ++ funcs[i].parent, ++ funcs[i].clk->name); ++ return rc; ++ } ++ } ++ ++ return 0; ++} ++ ++#endif +diff --git a/init/Kconfig b/init/Kconfig +index b9d11a8..05b62ba 100644 +--- a/init/Kconfig ++++ b/init/Kconfig +@@ -435,6 +435,13 @@ config CC_OPTIMIZE_FOR_SIZE + config SYSCTL + bool + ++config HAVE_CLOCK_LIB ++ bool ++ help ++ Platforms select clocklib if they use this infrastructure ++ for managing their clocks both built into SoC and provided ++ by external devices. ++ + menuconfig EMBEDDED + bool "Configure standard kernel features (for small systems)" + help +diff --git a/kernel/Makefile b/kernel/Makefile +index 6d9a87c..0b2ade7 100644 +--- a/kernel/Makefile ++++ b/kernel/Makefile +@@ -58,6 +58,7 @@ obj-$(CONFIG_TASK_DELAY_ACCT) += delayacct.o + obj-$(CONFIG_TASKSTATS) += taskstats.o tsacct.o + obj-$(CONFIG_MARKERS) += marker.o + obj-$(CONFIG_LATENCYTOP) += latencytop.o ++obj-$(CONFIG_HAVE_CLOCK_LIB) += clklib.o + + ifneq ($(CONFIG_SCHED_NO_NO_OMIT_FRAME_POINTER),y) + # According to Alan Modra <alan@linuxcare.com.au>, the -fno-omit-frame-pointer is +diff --git a/kernel/clklib.c b/kernel/clklib.c +new file mode 100644 +index 0000000..203af3d +--- /dev/null ++++ b/kernel/clklib.c +@@ -0,0 +1,295 @@ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/clk.h> ++#include <linux/clklib.h> ++#include <linux/spinlock.h> ++#include <linux/err.h> ++#include <linux/delay.h> ++ ++static LIST_HEAD(clocks); ++static DEFINE_SPINLOCK(clocks_lock); ++ ++static int __clk_register(struct clk *clk) ++{ ++ if (clk->parent && ++ !try_module_get(clk->parent->owner)) ++ return -EINVAL; ++ ++ list_add_tail(&clk->node, &clocks); ++ ++ return 0; ++} ++ ++int clk_register(struct clk *clk) ++{ ++ unsigned long flags; ++ int rc; ++ ++ spin_lock_irqsave(&clocks_lock, flags); ++ ++ rc = __clk_register(clk); ++ ++ spin_unlock_irqrestore(&clocks_lock, flags); ++ ++ return rc; ++} ++EXPORT_SYMBOL(clk_register); ++ ++void clk_unregister(struct clk *clk) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&clocks_lock, flags); ++ list_del(&clk->node); ++ if (clk->parent) ++ module_put(clk->parent->owner); ++ spin_unlock_irqrestore(&clocks_lock, flags); ++} ++EXPORT_SYMBOL(clk_unregister); ++ ++struct clk *clk_get(struct device *dev, const char *id) ++{ ++ struct clk *p, *clk = ERR_PTR(-ENOENT); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&clocks_lock, flags); ++ ++ list_for_each_entry(p, &clocks, node) { ++ if (strcmp(id, p->name) == 0 && ++ (!p->can_get || p->can_get(p, dev)) && ++ try_module_get(p->owner)) { ++ clk = p; ++ break; ++ } ++ } ++ ++ spin_unlock_irqrestore(&clocks_lock, flags); ++ ++ return clk; ++} ++EXPORT_SYMBOL(clk_get); ++ ++void clk_put(struct clk *clk) ++{ ++ unsigned long flags; ++ ++ if (!clk || IS_ERR(clk)) ++ return; ++ ++ spin_lock_irqsave(&clocks_lock, flags); ++ ++ module_put(clk->owner); ++ ++ spin_unlock_irqrestore(&clocks_lock, flags); ++} ++EXPORT_SYMBOL(clk_put); ++ ++int clk_set_parent(struct clk *clk, struct clk *parent) ++{ ++ int rc; ++ unsigned long flags; ++ ++ if (!clk || IS_ERR(clk)) ++ return -EINVAL; ++ ++ if (!clk->set_parent) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&clocks_lock, flags); ++ ++ rc = clk->set_parent(clk, parent); ++ if (!rc) ++ clk->parent = parent; ++ ++ spin_unlock_irqrestore(&clocks_lock, flags); ++ ++ return rc; ++} ++EXPORT_SYMBOL(clk_set_parent); ++ ++static int __clk_enable(struct clk *clk) ++{ ++ int rc = 0; ++ ++ if (clk->parent) { ++ rc = __clk_enable(clk->parent); ++ ++ if (rc) ++ return rc; ++ } ++ ++ if (clk->users++ == 0) ++ if (clk->enable) ++ rc = clk->enable(clk); ++ ++ if (clk->delay) ++ udelay(clk->delay); ++ ++ return rc; ++} ++ ++int clk_enable(struct clk *clk) ++{ ++ unsigned long flags; ++ int rc; ++ ++ if (!clk || IS_ERR(clk)) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&clocks_lock, flags); ++ ++ rc = __clk_enable(clk); ++ ++ spin_unlock_irqrestore(&clocks_lock, flags); ++ ++ return rc; ++} ++EXPORT_SYMBOL(clk_enable); ++ ++static void __clk_disable(struct clk *clk) ++{ ++ if (clk->users <= 0) { ++ WARN_ON(1); ++ return; ++ } ++ ++ if (--clk->users == 0) ++ if (clk->disable) ++ clk->disable(clk); ++ ++ if (clk->parent) ++ __clk_disable(clk->parent); ++} ++ ++void clk_disable(struct clk *clk) ++{ ++ unsigned long flags; ++ ++ if (!clk || IS_ERR(clk)) ++ return; ++ ++ spin_lock_irqsave(&clocks_lock, flags); ++ ++ __clk_disable(clk); ++ ++ spin_unlock_irqrestore(&clocks_lock, flags); ++} ++EXPORT_SYMBOL(clk_disable); ++ ++static unsigned long __clk_get_rate(struct clk *clk) ++{ ++ unsigned long rate = 0; ++ ++ for (;;) { ++ if (rate || !clk) ++ return rate; ++ ++ if (clk->getrate) ++ rate = clk->getrate(clk); ++ else if (clk->rate) ++ rate = clk->rate; ++ else ++ clk = clk->parent; ++ } ++} ++ ++unsigned long clk_get_rate(struct clk *clk) ++{ ++ unsigned long rate = 0; ++ unsigned long flags; ++ ++ if (!clk || IS_ERR(clk)) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&clocks_lock, flags); ++ ++ rate = __clk_get_rate(clk); ++ ++ spin_unlock_irqrestore(&clocks_lock, flags); ++ ++ return rate; ++} ++EXPORT_SYMBOL(clk_get_rate); ++ ++long clk_round_rate(struct clk *clk, unsigned long rate) ++{ ++ long res; ++ unsigned long flags; ++ ++ if (!clk || IS_ERR(clk)) ++ return -EINVAL; ++ ++ if (!clk->roundrate) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&clocks_lock, flags); ++ ++ res = clk->roundrate(clk, rate); ++ ++ spin_unlock_irqrestore(&clocks_lock, flags); ++ ++ return res; ++} ++EXPORT_SYMBOL(clk_round_rate); ++ ++int clk_set_rate(struct clk *clk, unsigned long rate) ++{ ++ int rc; ++ unsigned long flags; ++ ++ if (!clk || IS_ERR(clk)) ++ return -EINVAL; ++ ++ if (!clk->setrate) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&clocks_lock, flags); ++ ++ rc = clk->setrate(clk, rate); ++ ++ spin_unlock_irqrestore(&clocks_lock, flags); ++ ++ return rc; ++} ++EXPORT_SYMBOL(clk_set_rate); ++ ++int clk_alloc_function(const char *parent, struct clk *clk) ++{ ++ int rc = 0; ++ unsigned long flags; ++ struct clk *pclk; ++ bool found = false; ++ ++ spin_lock_irqsave(&clocks_lock, flags); ++ ++ list_for_each_entry(pclk, &clocks, node) { ++ if (strcmp(parent, pclk->name) == 0 && ++ try_module_get(pclk->owner)) { ++ found = true; ++ break; ++ } ++ } ++ ++ if (!found) { ++ rc = -ENODEV; ++ goto out; ++ } ++ ++ clk->parent = pclk; ++ ++ __clk_register(clk); ++ /* ++ * We locked parent owner during search ++ * and also in __clk_register. Free one reference ++ */ ++ module_put(pclk->owner); ++ ++out: ++ if (rc) { ++ kfree(clk); ++ } ++ spin_unlock_irqrestore(&clocks_lock, flags); ++ ++ return rc; ++} ++EXPORT_SYMBOL(clk_alloc_function); +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0040-Clocklib-debugfs-support.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0040-Clocklib-debugfs-support.patch new file mode 100644 index 0000000000..160b274f4f --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0040-Clocklib-debugfs-support.patch @@ -0,0 +1,108 @@ +From cae12d96586dac77d223559d686487ea2d457a41 Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Mon, 4 Feb 2008 03:01:05 +0300 +Subject: [PATCH 40/64] Clocklib debugfs support + +Provide /sys/kernel/debug/clock to ease debugging. + +Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> +--- + include/linux/clklib.h | 5 +++ + kernel/clklib.c | 68 ++++++++++++++++++++++++++++++++++++++++++++++++ + 2 files changed, 73 insertions(+), 0 deletions(-) + +diff --git a/include/linux/clklib.h b/include/linux/clklib.h +index 4bd9b4a..f916693 100644 +--- a/include/linux/clklib.h ++++ b/include/linux/clklib.h +@@ -28,6 +28,11 @@ struct clk { + int (*setrate) (struct clk *, unsigned long); + long (*roundrate) (struct clk *, unsigned long); + ++ /* ++ * format any additional info ++ */ ++ int (*format) (struct clk *, struct seq_file *); ++ + void *priv; + }; + +diff --git a/kernel/clklib.c b/kernel/clklib.c +index 203af3d..b782220 100644 +--- a/kernel/clklib.c ++++ b/kernel/clklib.c +@@ -293,3 +293,71 @@ out: + return rc; + } + EXPORT_SYMBOL(clk_alloc_function); ++ ++#ifdef CONFIG_DEBUG_FS ++ ++#include <linux/debugfs.h> ++#include <linux/seq_file.h> ++static void dump_clocks(struct seq_file *s, struct clk *parent, int nest) ++{ ++ struct clk *clk; ++ int i; ++ ++ list_for_each_entry(clk, &clocks, node) { ++ if (clk->parent == parent) { ++ for (i = 0; i < nest; i++) ++ seq_putc(s, ' '); ++ seq_puts(s, clk->name); ++ ++ i = nest + strlen(clk->name); ++ if (i >= 16) ++ i = 15; ++ for (; i < 16; i++) ++ seq_putc(s, ' '); ++ seq_printf(s, "%c use=%d rate=%lu KHz", ++ clk->set_parent ? '*' : ' ', ++ clk->users, ++ __clk_get_rate(clk)); ++ if (clk->format) ++ clk->format(clk, s); ++ seq_putc(s, '\n'); ++ ++ dump_clocks(s, clk, nest + 1); ++ } ++ } ++} ++ ++static int clocklib_show(struct seq_file *s, void *unused) ++{ ++ unsigned long flags; ++ ++ spin_lock_irqsave(&clocks_lock, flags); ++ ++ dump_clocks(s, NULL, 0); ++ ++ spin_unlock_irqrestore(&clocks_lock, flags); ++ ++ return 0; ++} ++ ++static int clocklib_open(struct inode *inode, struct file *file) ++{ ++ return single_open(file, clocklib_show, NULL); ++} ++ ++static struct file_operations clocklib_operations = { ++ .open = clocklib_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++}; ++ ++static int __init clocklib_debugfs_init(void) ++{ ++ debugfs_create_file("clock", S_IFREG | S_IRUGO, ++ NULL, NULL, &clocklib_operations); ++ return 0; ++} ++subsys_initcall(clocklib_debugfs_init); ++ ++#endif /* DEBUG_FS */ +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0041-From-80a359e60c2aec59ccf4fca0a7fd20495f82b1d2-Mon-Se.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0041-From-80a359e60c2aec59ccf4fca0a7fd20495f82b1d2-Mon-Se.patch new file mode 100644 index 0000000000..9c95c67e78 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0041-From-80a359e60c2aec59ccf4fca0a7fd20495f82b1d2-Mon-Se.patch @@ -0,0 +1,593 @@ +From 2a143b9546b01fd6c58ebaac7eb46568a17d6a41 Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Tue, 12 Feb 2008 04:58:59 +0300 +Subject: [PATCH 41/64] From 80a359e60c2aec59ccf4fca0a7fd20495f82b1d2 Mon Sep 17 00:00:00 2001 + In-Reply-To: <20080207005839.GA28509@doriath.ww600.siemens.net> + References: <20080207005839.GA28509@doriath.ww600.siemens.net> + Date: Thu, 7 Feb 2008 03:35:08 +0300 + Subject: [PATCH 3/5] Use clocklib for ARM pxa sub-arch. + Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> + +--- + arch/arm/Kconfig | 1 + + arch/arm/mach-pxa/clock.c | 108 ++++++-------------------------------------- + arch/arm/mach-pxa/clock.h | 58 +++++++++++++----------- + arch/arm/mach-pxa/pxa25x.c | 64 +++++++++++++++----------- + arch/arm/mach-pxa/pxa27x.c | 61 +++++++++++++----------- + arch/arm/mach-pxa/pxa3xx.c | 91 +++++++++++++++++++++---------------- + 6 files changed, 169 insertions(+), 214 deletions(-) + +diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig +index 423e953..47f3c73 100644 +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -347,6 +347,7 @@ config ARCH_PXA + select GENERIC_CLOCKEVENTS + select TICK_ONESHOT + select HAVE_GPIO_LIB ++ select HAVE_CLOCK_LIB + help + Support for Intel/Marvell's PXA2xx/PXA3xx processor line. + +diff --git a/arch/arm/mach-pxa/clock.c b/arch/arm/mach-pxa/clock.c +index 83ef5ec..3296b02 100644 +--- a/arch/arm/mach-pxa/clock.c ++++ b/arch/arm/mach-pxa/clock.c +@@ -8,6 +8,7 @@ + #include <linux/err.h> + #include <linux/string.h> + #include <linux/clk.h> ++#include <linux/clklib.h> + #include <linux/spinlock.h> + #include <linux/platform_device.h> + #include <linux/delay.h> +@@ -19,123 +20,42 @@ + #include "generic.h" + #include "clock.h" + +-static LIST_HEAD(clocks); +-static DEFINE_MUTEX(clocks_mutex); +-static DEFINE_SPINLOCK(clocks_lock); +- +-struct clk *clk_get(struct device *dev, const char *id) +-{ +- struct clk *p, *clk = ERR_PTR(-ENOENT); +- +- mutex_lock(&clocks_mutex); +- list_for_each_entry(p, &clocks, node) { +- if (strcmp(id, p->name) == 0 && +- (p->dev == NULL || p->dev == dev)) { +- clk = p; +- break; +- } +- } +- mutex_unlock(&clocks_mutex); +- +- return clk; +-} +-EXPORT_SYMBOL(clk_get); +- +-void clk_put(struct clk *clk) ++static int clk_gpio27_enable(struct clk *clk) + { +-} +-EXPORT_SYMBOL(clk_put); +- +-int clk_enable(struct clk *clk) +-{ +- unsigned long flags; +- +- spin_lock_irqsave(&clocks_lock, flags); +- if (clk->enabled++ == 0) +- clk->ops->enable(clk); +- spin_unlock_irqrestore(&clocks_lock, flags); +- +- if (clk->delay) +- udelay(clk->delay); ++ pxa_gpio_mode(GPIO11_3_6MHz_MD); + + return 0; + } +-EXPORT_SYMBOL(clk_enable); +- +-void clk_disable(struct clk *clk) +-{ +- unsigned long flags; +- +- WARN_ON(clk->enabled == 0); +- +- spin_lock_irqsave(&clocks_lock, flags); +- if (--clk->enabled == 0) +- clk->ops->disable(clk); +- spin_unlock_irqrestore(&clocks_lock, flags); +-} +-EXPORT_SYMBOL(clk_disable); +- +-unsigned long clk_get_rate(struct clk *clk) +-{ +- unsigned long rate; +- +- rate = clk->rate; +- if (clk->ops->getrate) +- rate = clk->ops->getrate(clk); +- +- return rate; +-} +-EXPORT_SYMBOL(clk_get_rate); +- +- +-static void clk_gpio27_enable(struct clk *clk) +-{ +- pxa_gpio_mode(GPIO11_3_6MHz_MD); +-} + + static void clk_gpio27_disable(struct clk *clk) + { ++ /* FIXME: disable clock */ + } + +-static const struct clkops clk_gpio27_ops = { +- .enable = clk_gpio27_enable, +- .disable = clk_gpio27_disable, +-}; +- +- +-void clk_cken_enable(struct clk *clk) ++int clk_cken_enable(struct clk *clk) + { +- CKEN |= 1 << clk->cken; ++ int cken = ((struct clk_cken_priv *)clk->priv)->cken; ++ CKEN |= 1 << cken; ++ ++ return 0; + } + + void clk_cken_disable(struct clk *clk) + { +- CKEN &= ~(1 << clk->cken); ++ int cken = ((struct clk_cken_priv *)clk->priv)->cken; ++ CKEN &= ~(1 << cken); + } + +-const struct clkops clk_cken_ops = { +- .enable = clk_cken_enable, +- .disable = clk_cken_disable, +-}; +- + static struct clk common_clks[] = { + { + .name = "GPIO27_CLK", +- .ops = &clk_gpio27_ops, + .rate = 3686400, ++ .owner = THIS_MODULE, ++ .enable = clk_gpio27_enable, ++ .disable = clk_gpio27_disable, + }, + }; + +-void clks_register(struct clk *clks, size_t num) +-{ +- int i; +- +- mutex_lock(&clocks_mutex); +- for (i = 0; i < num; i++) +- list_add(&clks[i].node, &clocks); +- mutex_unlock(&clocks_mutex); +-} +- + static int __init clk_init(void) + { + clks_register(common_clks, ARRAY_SIZE(common_clks)); +diff --git a/arch/arm/mach-pxa/clock.h b/arch/arm/mach-pxa/clock.h +index bc6b77e..5d0d067 100644 +--- a/arch/arm/mach-pxa/clock.h ++++ b/arch/arm/mach-pxa/clock.h +@@ -1,43 +1,47 @@ +-struct clk; ++#include <linux/clklib.h> ++#include <linux/seq_file.h> + +-struct clkops { +- void (*enable)(struct clk *); +- void (*disable)(struct clk *); +- unsigned long (*getrate)(struct clk *); ++struct clk_cken_priv { ++ unsigned int cken; + }; + +-struct clk { +- struct list_head node; +- const char *name; +- struct device *dev; +- const struct clkops *ops; +- unsigned long rate; +- unsigned int cken; +- unsigned int delay; +- unsigned int enabled; +-}; +- +-#define INIT_CKEN(_name, _cken, _rate, _delay, _dev) \ ++#define INIT_CKEN(_name, _cken, _rate, _delay) \ + { \ + .name = _name, \ +- .dev = _dev, \ +- .ops = &clk_cken_ops, \ ++ .enable = clk_cken_enable, \ ++ .disable = clk_cken_disable, \ + .rate = _rate, \ +- .cken = CKEN_##_cken, \ + .delay = _delay, \ ++ .priv = &(struct clk_cken_priv) { \ ++ .cken = CKEN_##_cken, \ ++ }, \ + } + +-#define INIT_CK(_name, _cken, _ops, _dev) \ ++#define INIT_CK(_name, _cken, _getrate) \ + { \ + .name = _name, \ +- .dev = _dev, \ +- .ops = _ops, \ +- .cken = CKEN_##_cken, \ ++ .enable = clk_cken_enable, \ ++ .disable = clk_cken_disable, \ ++ .getrate = _getrate, \ ++ .priv = &(struct clk_cken_priv) { \ ++ .cken = CKEN_##_cken, \ ++ }, \ + } + +-extern const struct clkops clk_cken_ops; +- +-void clk_cken_enable(struct clk *clk); ++int clk_cken_enable(struct clk *clk); + void clk_cken_disable(struct clk *clk); + + void clks_register(struct clk *clks, size_t num); ++ ++static int __maybe_unused clk_dev_can_get(struct clk *clk, struct device *dev) ++{ ++ return (dev == clk->priv); ++} ++ ++static int __maybe_unused clk_dev_format(struct clk *clk, struct seq_file *s) ++{ ++ BUG_ON(!clk->priv); ++ seq_puts(s, "for device "); ++ seq_puts(s, ((struct device *)clk->priv)->bus_id); ++ return 0; ++} +diff --git a/arch/arm/mach-pxa/pxa25x.c b/arch/arm/mach-pxa/pxa25x.c +index 5988d99..ed3719b 100644 +--- a/arch/arm/mach-pxa/pxa25x.c ++++ b/arch/arm/mach-pxa/pxa25x.c +@@ -100,40 +100,50 @@ static unsigned long clk_pxa25x_lcd_getrate(struct clk *clk) + return pxa25x_get_memclk_frequency_10khz() * 10000; + } + +-static const struct clkops clk_pxa25x_lcd_ops = { +- .enable = clk_cken_enable, +- .disable = clk_cken_disable, +- .getrate = clk_pxa25x_lcd_getrate, +-}; +- + /* + * 3.6864MHz -> OST, GPIO, SSP, PWM, PLLs (95.842MHz, 147.456MHz) + * 95.842MHz -> MMC 19.169MHz, I2C 31.949MHz, FICP 47.923MHz, USB 47.923MHz + * 147.456MHz -> UART 14.7456MHz, AC97 12.288MHz, I2S 5.672MHz (allegedly) + */ +-static struct clk pxa25x_hwuart_clk = +- INIT_CKEN("UARTCLK", HWUART, 14745600, 1, &pxa_device_hwuart.dev) +-; ++static struct clk pxa25x_hwuart_clk[] = { ++ INIT_CKEN("HWUARTCLK", HWUART, 14745600, 1), ++ { ++ .parent = &pxa25x_hwuart_clk[0], ++ .name = "UARTCLK", ++ .can_get = clk_dev_can_get, ++ .priv = &pxa_device_hwuart.dev, ++ }, ++}; + + static struct clk pxa25x_clks[] = { +- INIT_CK("LCDCLK", LCD, &clk_pxa25x_lcd_ops, &pxa_device_fb.dev), +- INIT_CKEN("UARTCLK", FFUART, 14745600, 1, &pxa_device_ffuart.dev), +- INIT_CKEN("UARTCLK", BTUART, 14745600, 1, &pxa_device_btuart.dev), +- INIT_CKEN("UARTCLK", STUART, 14745600, 1, NULL), +- INIT_CKEN("UDCCLK", USB, 47923000, 5, &pxa_device_udc.dev), +- INIT_CKEN("MMCCLK", MMC, 19169000, 0, &pxa_device_mci.dev), +- INIT_CKEN("I2CCLK", I2C, 31949000, 0, &pxa_device_i2c.dev), +- +- INIT_CKEN("SSPCLK", SSP, 3686400, 0, &pxa25x_device_ssp.dev), +- INIT_CKEN("SSPCLK", NSSP, 3686400, 0, &pxa25x_device_nssp.dev), +- INIT_CKEN("SSPCLK", ASSP, 3686400, 0, &pxa25x_device_assp.dev), ++ INIT_CK("LCDCLK", LCD, &clk_pxa25x_lcd_getrate), ++ INIT_CKEN("FFUARTCLK", FFUART, 14745600, 1), ++ INIT_CKEN("BTUARTCLK", BTUART, 14745600, 1), ++ INIT_CKEN("STUARTCLK", STUART, 14745600, 1), ++ INIT_CKEN("UDCCLK", USB, 47923000, 5), ++ INIT_CKEN("MMCCLK", MMC, 19169000, 0), ++ INIT_CKEN("I2CCLK", I2C, 31949000, 0), ++ ++ INIT_CKEN("SSP_CLK", SSP, 3686400, 0), ++ INIT_CKEN("NSSPCLK", NSSP, 3686400, 0), ++ INIT_CKEN("ASSPCLK", ASSP, 3686400, 0), + + /* +- INIT_CKEN("PWMCLK", PWM0, 3686400, 0, NULL), +- INIT_CKEN("PWMCLK", PWM0, 3686400, 0, NULL), +- INIT_CKEN("I2SCLK", I2S, 14745600, 0, NULL), ++ INIT_CKEN("PWMCLK", PWM0, 3686400, 0), ++ INIT_CKEN("PWMCLK", PWM0, 3686400, 0), ++ INIT_CKEN("I2SCLK", I2S, 14745600, 0), + */ +- INIT_CKEN("FICPCLK", FICP, 47923000, 0, NULL), ++ INIT_CKEN("FICPCLK", FICP, 47923000, 0), ++}; ++ ++static struct clk_function __initdata pxa25x_clk_funcs[] = { ++ CLK_FUNC("FFUARTCLK", "UARTCLK", clk_dev_can_get, &pxa_device_ffuart.dev, clk_dev_format), ++ CLK_FUNC("BTUARTCLK", "UARTCLK", clk_dev_can_get, &pxa_device_btuart.dev, clk_dev_format), ++ CLK_FUNC("STUARTCLK", "UARTCLK", clk_dev_can_get, &pxa_device_stuart.dev, clk_dev_format), ++ CLK_FUNC("STUARTCLK", "SIRCLK", NULL, NULL, NULL), ++ CLK_FUNC("SSP_CLK", "SSPCLK", clk_dev_can_get, &pxa25x_device_ssp.dev, clk_dev_format), ++ CLK_FUNC("NSSPCLK", "SSPCLK", clk_dev_can_get, &pxa25x_device_nssp.dev, clk_dev_format), ++ CLK_FUNC("ASSPCLK", "SSPCLK", clk_dev_can_get, &pxa25x_device_assp.dev, clk_dev_format), + }; + + #ifdef CONFIG_PM +@@ -313,11 +323,13 @@ static int __init pxa25x_init(void) + int ret = 0; + + /* Only add HWUART for PXA255/26x; PXA210/250/27x do not have it. */ +- if (cpu_is_pxa25x()) +- clks_register(&pxa25x_hwuart_clk, 1); ++ if (cpu_is_pxa25x()) { ++ clks_register(pxa25x_hwuart_clk, ARRAY_SIZE(pxa25x_hwuart_clk)); ++ } + + if (cpu_is_pxa21x() || cpu_is_pxa25x()) { + clks_register(pxa25x_clks, ARRAY_SIZE(pxa25x_clks)); ++ clk_alloc_functions(pxa25x_clk_funcs, ARRAY_SIZE(pxa25x_clk_funcs)); + + if ((ret = pxa_init_dma(16))) + return ret; +diff --git a/arch/arm/mach-pxa/pxa27x.c b/arch/arm/mach-pxa/pxa27x.c +index 30ca4fd..c51e7b2 100644 +--- a/arch/arm/mach-pxa/pxa27x.c ++++ b/arch/arm/mach-pxa/pxa27x.c +@@ -126,44 +126,48 @@ static unsigned long clk_pxa27x_lcd_getrate(struct clk *clk) + return pxa27x_get_lcdclk_frequency_10khz() * 10000; + } + +-static const struct clkops clk_pxa27x_lcd_ops = { +- .enable = clk_cken_enable, +- .disable = clk_cken_disable, +- .getrate = clk_pxa27x_lcd_getrate, +-}; +- + static struct clk pxa27x_clks[] = { +- INIT_CK("LCDCLK", LCD, &clk_pxa27x_lcd_ops, &pxa_device_fb.dev), +- INIT_CK("CAMCLK", CAMERA, &clk_pxa27x_lcd_ops, NULL), ++ INIT_CK("LCDCLK", LCD, &clk_pxa27x_lcd_getrate), ++ INIT_CK("CAMCLK", CAMERA, &clk_pxa27x_lcd_getrate), + +- INIT_CKEN("UARTCLK", FFUART, 14857000, 1, &pxa_device_ffuart.dev), +- INIT_CKEN("UARTCLK", BTUART, 14857000, 1, &pxa_device_btuart.dev), +- INIT_CKEN("UARTCLK", STUART, 14857000, 1, NULL), ++ INIT_CKEN("FFUARTCLK", FFUART, 14857000, 1), ++ INIT_CKEN("BTUARTCLK", BTUART, 14857000, 1), ++ INIT_CKEN("STUARTCLK", STUART, 14857000, 1), + +- INIT_CKEN("I2SCLK", I2S, 14682000, 0, &pxa_device_i2s.dev), +- INIT_CKEN("I2CCLK", I2C, 32842000, 0, &pxa_device_i2c.dev), +- INIT_CKEN("UDCCLK", USB, 48000000, 5, &pxa_device_udc.dev), +- INIT_CKEN("MMCCLK", MMC, 19500000, 0, &pxa_device_mci.dev), +- INIT_CKEN("FICPCLK", FICP, 48000000, 0, &pxa_device_ficp.dev), ++ INIT_CKEN("I2SCLK", I2S, 14682000, 0), ++ INIT_CKEN("I2CCLK", I2C, 32842000, 0), ++ INIT_CKEN("UDCCLK", USB, 48000000, 5), ++ INIT_CKEN("MMCCLK", MMC, 19500000, 0), ++ INIT_CKEN("FICPCLK", FICP, 48000000, 0), + +- INIT_CKEN("USBCLK", USBHOST, 48000000, 0, &pxa27x_device_ohci.dev), +- INIT_CKEN("I2CCLK", PWRI2C, 13000000, 0, &pxa27x_device_i2c_power.dev), +- INIT_CKEN("KBDCLK", KEYPAD, 32768, 0, NULL), ++ INIT_CKEN("USBCLK", USBHOST, 48000000, 0), ++ INIT_CKEN("I2CCLK", PWRI2C, 13000000, 0), ++ INIT_CKEN("KBDCLK", KEYPAD, 32768, 0), + +- INIT_CKEN("SSPCLK", SSP1, 13000000, 0, &pxa27x_device_ssp1.dev), +- INIT_CKEN("SSPCLK", SSP2, 13000000, 0, &pxa27x_device_ssp2.dev), +- INIT_CKEN("SSPCLK", SSP3, 13000000, 0, &pxa27x_device_ssp3.dev), ++ INIT_CKEN("SSP1CLK", SSP1, 13000000, 0), ++ INIT_CKEN("SSP2CLK", SSP2, 13000000, 0), ++ INIT_CKEN("SSP3CLK", SSP3, 13000000, 0), + + /* +- INIT_CKEN("PWMCLK", PWM0, 13000000, 0, NULL), +- INIT_CKEN("MSLCLK", MSL, 48000000, 0, NULL), +- INIT_CKEN("USIMCLK", USIM, 48000000, 0, NULL), +- INIT_CKEN("MSTKCLK", MEMSTK, 19500000, 0, NULL), +- INIT_CKEN("IMCLK", IM, 0, 0, NULL), +- INIT_CKEN("MEMCLK", MEMC, 0, 0, NULL), ++ INIT_CKEN("PWMCLK", PWM0, 13000000, 0), ++ INIT_CKEN("MSLCLK", MSL, 48000000, 0), ++ INIT_CKEN("USIMCLK", USIM, 48000000, 0), ++ INIT_CKEN("MSTKCLK", MEMSTK, 19500000, 0), ++ INIT_CKEN("IMCLK", IM, 0, 0), ++ INIT_CKEN("MEMCLK", MEMC, 0, 0), + */ + }; + ++static struct clk_function __initdata pxa27x_clk_funcs[] = { ++ CLK_FUNC("FFUARTCLK", "UARTCLK", clk_dev_can_get, &pxa_device_ffuart.dev, clk_dev_format), ++ CLK_FUNC("BTUARTCLK", "UARTCLK", clk_dev_can_get, &pxa_device_btuart.dev, clk_dev_format), ++ CLK_FUNC("STUARTCLK", "UARTCLK", clk_dev_can_get, &pxa_device_stuart.dev, clk_dev_format), ++ CLK_FUNC("STUARTCLK", "SIRCLK", NULL, NULL, NULL), ++ CLK_FUNC("SSP1CLK", "SSPCLK", clk_dev_can_get, &pxa27x_device_ssp1.dev, clk_dev_format), ++ CLK_FUNC("SSP2CLK", "SSPCLK", clk_dev_can_get, &pxa27x_device_ssp2.dev, clk_dev_format), ++ CLK_FUNC("SSP3CLK", "SSPCLK", clk_dev_can_get, &pxa27x_device_ssp3.dev, clk_dev_format), ++}; ++ + #ifdef CONFIG_PM + + #define SAVE(x) sleep_save[SLEEP_SAVE_##x] = x +@@ -453,6 +457,7 @@ static int __init pxa27x_init(void) + int ret = 0; + if (cpu_is_pxa27x()) { + clks_register(pxa27x_clks, ARRAY_SIZE(pxa27x_clks)); ++ clk_alloc_functions(pxa27x_clk_funcs, ARRAY_SIZE(pxa27x_clk_funcs)); + + if ((ret = pxa_init_dma(32))) + return ret; +diff --git a/arch/arm/mach-pxa/pxa3xx.c b/arch/arm/mach-pxa/pxa3xx.c +index ccab9da..0f8bbf3 100644 +--- a/arch/arm/mach-pxa/pxa3xx.c ++++ b/arch/arm/mach-pxa/pxa3xx.c +@@ -122,27 +122,31 @@ static unsigned long clk_pxa3xx_hsio_getrate(struct clk *clk) + return hsio_clk; + } + +-static void clk_pxa3xx_cken_enable(struct clk *clk) ++static int clk_pxa3xx_cken_enable(struct clk *clk) + { +- unsigned long mask = 1ul << (clk->cken & 0x1f); ++ int cken = ((struct clk_cken_priv *)clk->priv)->cken; ++ unsigned long mask = 1ul << (cken & 0x1f); + + local_irq_disable(); + +- if (clk->cken < 32) ++ if (cken < 32) + CKENA |= mask; + else + CKENB |= mask; + + local_irq_enable(); ++ ++ return 0; + } + + static void clk_pxa3xx_cken_disable(struct clk *clk) + { +- unsigned long mask = 1ul << (clk->cken & 0x1f); ++ int cken = ((struct clk_cken_priv *)clk->priv)->cken; ++ unsigned long mask = 1ul << (cken & 0x1f); + + local_irq_disable(); + +- if (clk->cken < 32) ++ if (cken < 32) + CKENA &= ~mask; + else + CKENB &= ~mask; +@@ -150,55 +154,63 @@ static void clk_pxa3xx_cken_disable(struct clk *clk) + local_irq_enable(); + } + +-static const struct clkops clk_pxa3xx_cken_ops = { +- .enable = clk_pxa3xx_cken_enable, +- .disable = clk_pxa3xx_cken_disable, +-}; +- +-static const struct clkops clk_pxa3xx_hsio_ops = { +- .enable = clk_pxa3xx_cken_enable, +- .disable = clk_pxa3xx_cken_disable, +- .getrate = clk_pxa3xx_hsio_getrate, +-}; +- +-#define PXA3xx_CKEN(_name, _cken, _rate, _delay, _dev) \ ++#define PXA3xx_CKEN(_name, _cken, _rate, _delay) \ + { \ + .name = _name, \ +- .dev = _dev, \ +- .ops = &clk_pxa3xx_cken_ops, \ ++ .enable = clk_pxa3xx_cken_enable, \ ++ .disable = clk_pxa3xx_cken_disable, \ + .rate = _rate, \ +- .cken = CKEN_##_cken, \ + .delay = _delay, \ ++ .priv = &(struct clk_cken_priv) { \ ++ .cken = CKEN_##_cken, \ ++ }, \ + } + +-#define PXA3xx_CK(_name, _cken, _ops, _dev) \ ++#define PXA3xx_CK(_name, _cken, _getrate) \ + { \ + .name = _name, \ +- .dev = _dev, \ +- .ops = _ops, \ +- .cken = CKEN_##_cken, \ ++ .enable = clk_pxa3xx_cken_enable, \ ++ .disable = clk_pxa3xx_cken_disable, \ ++ .getrate = _getrate, \ ++ .priv = &(struct clk_cken_priv) { \ ++ .cken = CKEN_##_cken, \ ++ }, \ + } + + static struct clk pxa3xx_clks[] = { +- PXA3xx_CK("LCDCLK", LCD, &clk_pxa3xx_hsio_ops, &pxa_device_fb.dev), +- PXA3xx_CK("CAMCLK", CAMERA, &clk_pxa3xx_hsio_ops, NULL), ++ PXA3xx_CK("LCDCLK", LCD, &clk_pxa3xx_hsio_getrate), ++ PXA3xx_CK("CAMCLK", CAMERA, &clk_pxa3xx_hsio_getrate), + +- PXA3xx_CKEN("UARTCLK", FFUART, 14857000, 1, &pxa_device_ffuart.dev), +- PXA3xx_CKEN("UARTCLK", BTUART, 14857000, 1, &pxa_device_btuart.dev), +- PXA3xx_CKEN("UARTCLK", STUART, 14857000, 1, NULL), ++ PXA3xx_CKEN("FFUARTCLK", FFUART, 14857000, 1), ++ PXA3xx_CKEN("BTUARTCLK", BTUART, 14857000, 1), ++ PXA3xx_CKEN("STUARTCLK", STUART, 14857000, 1), + +- PXA3xx_CKEN("I2CCLK", I2C, 32842000, 0, &pxa_device_i2c.dev), +- PXA3xx_CKEN("UDCCLK", UDC, 48000000, 5, &pxa_device_udc.dev), +- PXA3xx_CKEN("USBCLK", USBH, 48000000, 0, &pxa27x_device_ohci.dev), ++ PXA3xx_CKEN("I2CCLK", I2C, 32842000, 0), ++ PXA3xx_CKEN("UDCCLK", UDC, 48000000, 5), ++ PXA3xx_CKEN("USBCLK", USBH, 48000000, 0), + +- PXA3xx_CKEN("SSPCLK", SSP1, 13000000, 0, &pxa27x_device_ssp1.dev), +- PXA3xx_CKEN("SSPCLK", SSP2, 13000000, 0, &pxa27x_device_ssp2.dev), +- PXA3xx_CKEN("SSPCLK", SSP3, 13000000, 0, &pxa27x_device_ssp3.dev), +- PXA3xx_CKEN("SSPCLK", SSP4, 13000000, 0, &pxa3xx_device_ssp4.dev), ++ PXA3xx_CKEN("SSP1CLK", SSP1, 13000000, 0), ++ PXA3xx_CKEN("SSP2CLK", SSP2, 13000000, 0), ++ PXA3xx_CKEN("SSP3CLK", SSP3, 13000000, 0), ++ PXA3xx_CKEN("SSP4CLK", SSP4, 13000000, 0), ++ ++ PXA3xx_CKEN("MMC1CLK", MMC1, 19500000, 0), ++ PXA3xx_CKEN("MMC2CLK", MMC2, 19500000, 0), ++ PXA3xx_CKEN("MMC3CLK", MMC3, 19500000, 0), ++}; + +- PXA3xx_CKEN("MMCCLK", MMC1, 19500000, 0, &pxa_device_mci.dev), +- PXA3xx_CKEN("MMCCLK", MMC2, 19500000, 0, &pxa3xx_device_mci2.dev), +- PXA3xx_CKEN("MMCCLK", MMC3, 19500000, 0, &pxa3xx_device_mci3.dev), ++static struct clk_function __initdata pxa3xx_clk_funcs[] = { ++ CLK_FUNC("FFUARTCLK", "UARTCLK", clk_dev_can_get, &pxa_device_ffuart.dev, clk_dev_format), ++ CLK_FUNC("BTUARTCLK", "UARTCLK", clk_dev_can_get, &pxa_device_btuart.dev, clk_dev_format), ++ CLK_FUNC("STUARTCLK", "UARTCLK", clk_dev_can_get, &pxa_device_stuart.dev, clk_dev_format), ++ CLK_FUNC("STUARTCLK", "SIRCLK", NULL, NULL, NULL), ++ CLK_FUNC("SSP1CLK", "SSPCLK", clk_dev_can_get, &pxa27x_device_ssp1.dev, clk_dev_format), ++ CLK_FUNC("SSP2CLK", "SSPCLK", clk_dev_can_get, &pxa27x_device_ssp2.dev, clk_dev_format), ++ CLK_FUNC("SSP3CLK", "SSPCLK", clk_dev_can_get, &pxa27x_device_ssp3.dev, clk_dev_format), ++ CLK_FUNC("SSP4CLK", "SSPCLK", clk_dev_can_get, &pxa3xx_device_ssp4.dev, clk_dev_format), ++ CLK_FUNC("MMC1CLK", "MMCCLK", clk_dev_can_get, &pxa_device_mci.dev, clk_dev_format), ++ CLK_FUNC("MMC2CLK", "MMCCLK", clk_dev_can_get, &pxa3xx_device_mci2.dev, clk_dev_format), ++ CLK_FUNC("MMC3CLK", "MMCCLK", clk_dev_can_get, &pxa3xx_device_mci3.dev, clk_dev_format), + }; + + #ifdef CONFIG_PM +@@ -255,6 +267,7 @@ static int __init pxa3xx_init(void) + + if (cpu_is_pxa3xx()) { + clks_register(pxa3xx_clks, ARRAY_SIZE(pxa3xx_clks)); ++ clk_alloc_functions(pxa3xx_clk_funcs, ARRAY_SIZE(pxa3xx_clk_funcs)); + + if ((ret = pxa_init_dma(32))) + return ret; +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0042-Use-correct-clock-for-IrDA-on-pxa.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0042-Use-correct-clock-for-IrDA-on-pxa.patch new file mode 100644 index 0000000000..a605735df0 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0042-Use-correct-clock-for-IrDA-on-pxa.patch @@ -0,0 +1,26 @@ +From 70dfe7e736467af6242c61092cb64f44d2fd50e3 Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Mon, 4 Feb 2008 03:01:05 +0300 +Subject: [PATCH 42/64] Use correct clock for IrDA on pxa + +Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> +--- + drivers/net/irda/pxaficp_ir.c | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +diff --git a/drivers/net/irda/pxaficp_ir.c b/drivers/net/irda/pxaficp_ir.c +index 8c09344..36d2ec0 100644 +--- a/drivers/net/irda/pxaficp_ir.c ++++ b/drivers/net/irda/pxaficp_ir.c +@@ -814,7 +814,7 @@ static int pxa_irda_probe(struct platform_device *pdev) + si->dev = &pdev->dev; + si->pdata = pdev->dev.platform_data; + +- si->sir_clk = clk_get(&pdev->dev, "UARTCLK"); ++ si->sir_clk = clk_get(&pdev->dev, "SIRCLK"); + si->fir_clk = clk_get(&pdev->dev, "FICPCLK"); + if (IS_ERR(si->sir_clk) || IS_ERR(si->fir_clk)) { + err = PTR_ERR(IS_ERR(si->sir_clk) ? si->sir_clk : si->fir_clk); +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0043-Use-clocklib-for-sa1100-sub-arch.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0043-Use-clocklib-for-sa1100-sub-arch.patch new file mode 100644 index 0000000000..22b8414b2d --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0043-Use-clocklib-for-sa1100-sub-arch.patch @@ -0,0 +1,153 @@ +From 3932e0f5c4c05200c030b60606ed2eb83550f4bb Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Mon, 4 Feb 2008 03:01:04 +0300 +Subject: [PATCH 43/64] Use clocklib for sa1100 sub-arch. + +Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> +--- + arch/arm/Kconfig | 1 + + arch/arm/mach-sa1100/clock.c | 95 ++--------------------------------------- + 2 files changed, 6 insertions(+), 90 deletions(-) + +diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig +index 47f3c73..fa47201 100644 +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -370,6 +370,7 @@ config ARCH_SA1100 + select ARCH_MTD_XIP + select GENERIC_GPIO + select GENERIC_TIME ++ select HAVE_CLOCK_LIB + help + Support for StrongARM 11x0 based boards. + +diff --git a/arch/arm/mach-sa1100/clock.c b/arch/arm/mach-sa1100/clock.c +index fc97fe5..6b3cc51 100644 +--- a/arch/arm/mach-sa1100/clock.c ++++ b/arch/arm/mach-sa1100/clock.c +@@ -8,83 +8,13 @@ + #include <linux/err.h> + #include <linux/string.h> + #include <linux/clk.h> ++#include <linux/clklib.h> + #include <linux/spinlock.h> + #include <linux/mutex.h> + + #include <asm/hardware.h> + +-/* +- * Very simple clock implementation - we only have one clock to +- * deal with at the moment, so we only match using the "name". +- */ +-struct clk { +- struct list_head node; +- unsigned long rate; +- const char *name; +- unsigned int enabled; +- void (*enable)(void); +- void (*disable)(void); +-}; +- +-static LIST_HEAD(clocks); +-static DEFINE_MUTEX(clocks_mutex); +-static DEFINE_SPINLOCK(clocks_lock); +- +-struct clk *clk_get(struct device *dev, const char *id) +-{ +- struct clk *p, *clk = ERR_PTR(-ENOENT); +- +- mutex_lock(&clocks_mutex); +- list_for_each_entry(p, &clocks, node) { +- if (strcmp(id, p->name) == 0) { +- clk = p; +- break; +- } +- } +- mutex_unlock(&clocks_mutex); +- +- return clk; +-} +-EXPORT_SYMBOL(clk_get); +- +-void clk_put(struct clk *clk) +-{ +-} +-EXPORT_SYMBOL(clk_put); +- +-int clk_enable(struct clk *clk) +-{ +- unsigned long flags; +- +- spin_lock_irqsave(&clocks_lock, flags); +- if (clk->enabled++ == 0) +- clk->enable(); +- spin_unlock_irqrestore(&clocks_lock, flags); +- return 0; +-} +-EXPORT_SYMBOL(clk_enable); +- +-void clk_disable(struct clk *clk) +-{ +- unsigned long flags; +- +- WARN_ON(clk->enabled == 0); +- +- spin_lock_irqsave(&clocks_lock, flags); +- if (--clk->enabled == 0) +- clk->disable(); +- spin_unlock_irqrestore(&clocks_lock, flags); +-} +-EXPORT_SYMBOL(clk_disable); +- +-unsigned long clk_get_rate(struct clk *clk) +-{ +- return clk->rate; +-} +-EXPORT_SYMBOL(clk_get_rate); +- +- +-static void clk_gpio27_enable(void) ++static int clk_gpio27_enable(struct clk *clk) + { + /* + * First, set up the 3.6864MHz clock on GPIO 27 for the SA-1111: +@@ -93,9 +23,11 @@ static void clk_gpio27_enable(void) + GAFR |= GPIO_32_768kHz; + GPDR |= GPIO_32_768kHz; + TUCR = TUCR_3_6864MHz; ++ ++ return 0; + } + +-static void clk_gpio27_disable(void) ++static void clk_gpio27_disable(struct clk *clk) + { + TUCR = 0; + GPDR &= ~GPIO_32_768kHz; +@@ -109,23 +41,6 @@ static struct clk clk_gpio27 = { + .disable = clk_gpio27_disable, + }; + +-int clk_register(struct clk *clk) +-{ +- mutex_lock(&clocks_mutex); +- list_add(&clk->node, &clocks); +- mutex_unlock(&clocks_mutex); +- return 0; +-} +-EXPORT_SYMBOL(clk_register); +- +-void clk_unregister(struct clk *clk) +-{ +- mutex_lock(&clocks_mutex); +- list_del(&clk->node); +- mutex_unlock(&clocks_mutex); +-} +-EXPORT_SYMBOL(clk_unregister); +- + static int __init clk_init(void) + { + clk_register(&clk_gpio27); +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0044-fix-tmio_mmc-debug-compilation.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0044-fix-tmio_mmc-debug-compilation.patch new file mode 100644 index 0000000000..5ca8228604 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0044-fix-tmio_mmc-debug-compilation.patch @@ -0,0 +1,26 @@ +From 03fdebde257197c13c0d10882e16a2a888ab4e0a Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Sat, 2 Feb 2008 20:23:01 +0300 +Subject: [PATCH 44/64] fix tmio_mmc debug compilation + +Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> +--- + drivers/mmc/host/tmio_mmc.c | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +diff --git a/drivers/mmc/host/tmio_mmc.c b/drivers/mmc/host/tmio_mmc.c +index 735c386..b0d38e2 100644 +--- a/drivers/mmc/host/tmio_mmc.c ++++ b/drivers/mmc/host/tmio_mmc.c +@@ -329,7 +329,7 @@ static irqreturn_t tmio_mmc_irq(int irq, void *devid) + if (!ireg) { + disable_mmc_irqs(ctl, status & ~irq_mask); + #ifdef CONFIG_MMC_DEBUG +- WARN("tmio_mmc: Spurious MMC irq, disabling! 0x%08x 0x%08x 0x%08x\n", status, irq_mask, ireg); ++ printk(KERN_WARNING "tmio_mmc: Spurious MMC irq, disabling! 0x%08x 0x%08x 0x%08x\n", status, irq_mask, ireg); + debug_status(status); + #endif + goto out; +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0045-Update-tmio_ohci.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0045-Update-tmio_ohci.patch new file mode 100644 index 0000000000..10f483b89d --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0045-Update-tmio_ohci.patch @@ -0,0 +1,416 @@ +From fe3c05491370965eb821aedc95f771b86ebab3ab Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Wed, 9 Jan 2008 02:01:44 +0300 +Subject: [PATCH 45/64] Update tmio_ohci: + Ports management. + Basic support for ohci suspend/resume. + +Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> +--- + drivers/mfd/tc6393xb.c | 40 ++++++++ + drivers/usb/host/ohci-tmio.c | 206 +++++++++++++++++++++++++++++++++++++++--- + 2 files changed, 235 insertions(+), 11 deletions(-) + +diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c +index 9439f39..5d17687 100644 +--- a/drivers/mfd/tc6393xb.c ++++ b/drivers/mfd/tc6393xb.c +@@ -224,6 +224,44 @@ static int tc6393xb_ohci_enable(struct platform_device *ohci) + return 0; + } + ++static int tc6393xb_ohci_suspend(struct platform_device *ohci) ++{ ++ struct platform_device *dev = to_platform_device(ohci->dev.parent); ++ struct tc6393xb *tc6393xb = platform_get_drvdata(dev); ++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr; ++ union tc6393xb_scr_ccr ccr; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&tc6393xb->lock, flags); ++ ++ ccr.raw = ioread16(&scr->ccr); ++ ccr.bits.usbcken = 0; ++ iowrite16(ccr.raw, &scr->ccr); ++ ++ spin_unlock_irqrestore(&tc6393xb->lock, flags); ++ ++ return 0; ++} ++ ++static int tc6393xb_ohci_resume(struct platform_device *ohci) ++{ ++ struct platform_device *dev = to_platform_device(ohci->dev.parent); ++ struct tc6393xb *tc6393xb = platform_get_drvdata(dev); ++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr; ++ union tc6393xb_scr_ccr ccr; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&tc6393xb->lock, flags); ++ ++ ccr.raw = ioread16(&scr->ccr); ++ ccr.bits.usbcken = 1; ++ iowrite16(ccr.raw, &scr->ccr); ++ ++ spin_unlock_irqrestore(&tc6393xb->lock, flags); ++ ++ return 0; ++} ++ + static int tc6393xb_fb_disable(struct platform_device *fb) + { + struct platform_device *dev = to_platform_device(fb->dev.parent); +@@ -423,6 +461,8 @@ static struct mfd_cell tc6393xb_cells[] = { + .name = "tmio-ohci", + .enable = tc6393xb_ohci_enable, + .disable = tc6393xb_ohci_disable, ++ .suspend = tc6393xb_ohci_suspend, ++ .resume = tc6393xb_ohci_resume, + .num_resources = ARRAY_SIZE(tc6393xb_ohci_resources), + .resources = tc6393xb_ohci_resources, + }, +diff --git a/drivers/usb/host/ohci-tmio.c b/drivers/usb/host/ohci-tmio.c +index be609f3..65e3cd3 100644 +--- a/drivers/usb/host/ohci-tmio.c ++++ b/drivers/usb/host/ohci-tmio.c +@@ -75,10 +75,13 @@ struct tmio_uhccr { + u8 x07[3]; + } __attribute__((packed)); + ++#define MAX_TMIO_OHCI_PORTS 3 ++ + #define UHCCR_PM_GKEN 0x0001 + #define UHCCR_PM_CKRNEN 0x0002 + #define UHCCR_PM_USBPW1 0x0004 + #define UHCCR_PM_USBPW2 0x0008 ++#define UHCCR_PM_USBPW3 0x0008 + #define UHCCR_PM_PMEE 0x0100 + #define UHCCR_PM_PMES 0x8000 + +@@ -86,44 +89,96 @@ struct tmio_uhccr { + + struct tmio_hcd { + struct tmio_uhccr __iomem *ccr; ++ spinlock_t lock; /* protects RMW cycles and disabled_ports data */ ++ bool disabled_ports[MAX_TMIO_OHCI_PORTS]; + }; + + #define hcd_to_tmio(hcd) ((struct tmio_hcd *)(hcd_to_ohci(hcd) + 1)) + #define ohci_to_tmio(ohci) ((struct tmio_hcd *)(ohci + 1)) + ++struct indexed_device_attribute{ ++ struct device_attribute dev_attr; ++ int index; ++}; ++#define to_indexed_dev_attr(_dev_attr) \ ++ container_of(_dev_attr, struct indexed_device_attribute, dev_attr) ++ ++#define INDEXED_ATTR(_name, _mode, _show, _store, _index) \ ++ { .dev_attr = __ATTR(_name ## _index, _mode, _show, _store), \ ++ .index = _index } ++ ++#define INDEXED_DEVICE_ATTR(_name, _mode, _show, _store, _index) \ ++struct indexed_device_attribute dev_attr_##_name ## _index \ ++ = INDEXED_ATTR(_name, _mode, _show, _store, _index) ++ ++static bool disabled_tmio_ports[MAX_TMIO_OHCI_PORTS]; ++module_param_array(disabled_tmio_ports, bool, NULL, 0644); ++MODULE_PARM_DESC(disabled_tmio_ports, ++ "disable specified TC6393 usb ports (default: all enabled)"); ++ + /*-------------------------------------------------------------------------*/ + ++static void tmio_write_pm(struct platform_device *dev) ++{ ++ struct usb_hcd *hcd = platform_get_drvdata(dev); ++ struct tmio_hcd *tmio = hcd_to_tmio(hcd); ++ struct tmio_uhccr __iomem *ccr = tmio->ccr; ++ u16 pm; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&tmio->lock, flags); ++ ++ pm = UHCCR_PM_GKEN | UHCCR_PM_CKRNEN | ++ UHCCR_PM_PMEE | UHCCR_PM_PMES; ++ ++ if (tmio->disabled_ports[0]) ++ pm |= UHCCR_PM_USBPW1; ++ if (tmio->disabled_ports[1]) ++ pm |= UHCCR_PM_USBPW2; ++ if (tmio->disabled_ports[2]) ++ pm |= UHCCR_PM_USBPW3; ++ ++ iowrite16(pm, &ccr->pm); ++ spin_unlock_irqrestore(&tmio->lock, flags); ++} ++ + static void tmio_stop_hc(struct platform_device *dev) + { + struct mfd_cell *cell = mfd_get_cell(dev); + struct usb_hcd *hcd = platform_get_drvdata(dev); ++ struct ohci_hcd *ohci = hcd_to_ohci(hcd); + struct tmio_hcd *tmio = hcd_to_tmio(hcd); + struct tmio_uhccr __iomem *ccr = tmio->ccr; + u16 pm; + +- pm = UHCCR_PM_GKEN | UHCCR_PM_CKRNEN | UHCCR_PM_USBPW1 | UHCCR_PM_USBPW2; ++ pm = UHCCR_PM_GKEN | UHCCR_PM_CKRNEN; ++ switch (ohci->num_ports) { ++ default: ++ dev_err(&dev->dev, "Unsupported amount of ports: %d\n", ohci->num_ports); ++ case 3: ++ pm |= UHCCR_PM_USBPW3; ++ case 2: ++ pm |= UHCCR_PM_USBPW2; ++ case 1: ++ pm |= UHCCR_PM_USBPW1; ++ } + iowrite8(0, &ccr->intc); + iowrite8(0, &ccr->ilme); + iowrite16(0, &ccr->basel); + iowrite16(0, &ccr->baseh); +- iowrite16(pm, &ccr->pm); ++ iowrite16(pm, &ccr->pm); + + cell->disable(dev); + } + + static void tmio_start_hc(struct platform_device *dev) + { +- struct mfd_cell *cell = mfd_get_cell(dev); + struct usb_hcd *hcd = platform_get_drvdata(dev); + struct tmio_hcd *tmio = hcd_to_tmio(hcd); + struct tmio_uhccr __iomem *ccr = tmio->ccr; +- u16 pm; + unsigned long base = hcd->rsrc_start; + +- pm = UHCCR_PM_CKRNEN | UHCCR_PM_GKEN | UHCCR_PM_PMEE | UHCCR_PM_PMES; +- cell->enable(dev); +- +- iowrite16(pm, &ccr->pm); ++ tmio_write_pm(dev); + iowrite16(base, &ccr->basel); + iowrite16(base >> 16, &ccr->baseh); + iowrite8(1, &ccr->ilme); +@@ -133,9 +188,56 @@ static void tmio_start_hc(struct platform_device *dev) + ioread8(&ccr->revid), hcd->rsrc_start, hcd->irq); + } + ++static ssize_t tmio_disabled_port_show(struct device *dev, ++ struct device_attribute *attr, ++ char *buf) ++{ ++ struct usb_hcd *hcd = dev_get_drvdata(dev); ++ struct tmio_hcd *tmio = hcd_to_tmio(hcd); ++ int index = to_indexed_dev_attr(attr)->index; ++ return snprintf(buf, PAGE_SIZE, "%c", ++ tmio->disabled_ports[index-1]? 'Y': 'N'); ++} ++ ++static ssize_t tmio_disabled_port_store(struct device *dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct usb_hcd *hcd = dev_get_drvdata(dev); ++ struct tmio_hcd *tmio = hcd_to_tmio(hcd); ++ int index = to_indexed_dev_attr(attr)->index; ++ ++ if (!count) ++ return -EINVAL; ++ ++ switch (buf[0]) { ++ case 'y': case 'Y': case '1': ++ tmio->disabled_ports[index-1] = true; ++ break; ++ case 'n': case 'N': case '0': ++ tmio->disabled_ports[index-1] = false; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ tmio_write_pm(to_platform_device(dev)); ++ ++ return 1; ++} ++ ++ ++static INDEXED_DEVICE_ATTR(disabled_usb_port, S_IRUGO | S_IWUSR, ++ tmio_disabled_port_show, tmio_disabled_port_store, 1); ++static INDEXED_DEVICE_ATTR(disabled_usb_port, S_IRUGO | S_IWUSR, ++ tmio_disabled_port_show, tmio_disabled_port_store, 2); ++static INDEXED_DEVICE_ATTR(disabled_usb_port, S_IRUGO | S_IWUSR, ++ tmio_disabled_port_show, tmio_disabled_port_store, 3); ++ + static int usb_hcd_tmio_probe(const struct hc_driver *driver, + struct platform_device *dev) + { ++ struct mfd_cell *cell = mfd_get_cell(dev); + struct resource *config = platform_get_resource_byname(dev, IORESOURCE_MEM, TMIO_OHCI_CONFIG); + struct resource *regs = platform_get_resource_byname(dev, IORESOURCE_MEM, TMIO_OHCI_CONTROL); + struct resource *sram = platform_get_resource_byname(dev, IORESOURCE_MEM, TMIO_OHCI_SRAM); +@@ -159,6 +261,12 @@ static int usb_hcd_tmio_probe(const struct hc_driver *driver, + + tmio = hcd_to_tmio(hcd); + ++ spin_lock_init(&tmio->lock); ++ ++ memcpy(tmio->disabled_ports, ++ disabled_tmio_ports, ++ sizeof(disabled_tmio_ports)); ++ + tmio->ccr = ioremap(config->start, config->end - config->start + 1); + if (!tmio->ccr) { + retval = -ENOMEM; +@@ -183,17 +291,46 @@ static int usb_hcd_tmio_probe(const struct hc_driver *driver, + if (retval) + goto err_dmabounce_register_dev; + ++ retval = cell->enable(dev); ++ if (retval) ++ goto err_enable; ++ + tmio_start_hc(dev); + ohci = hcd_to_ohci(hcd); + ohci_hcd_init(ohci); + + retval = usb_add_hcd(hcd, irq, IRQF_DISABLED); ++ if (retval) ++ goto err_add_hcd; ++ ++ switch (ohci->num_ports) { ++ default: ++ dev_err(&dev->dev, "Unsupported amount of ports: %d\n", ++ ohci->num_ports); ++ case 3: ++ retval |= device_create_file(&dev->dev, ++ &dev_attr_disabled_usb_port3.dev_attr); ++ case 2: ++ retval |= device_create_file(&dev->dev, ++ &dev_attr_disabled_usb_port2.dev_attr); ++ case 1: ++ retval |= device_create_file(&dev->dev, ++ &dev_attr_disabled_usb_port1.dev_attr); ++ } + + if (retval == 0) + return retval; + +- tmio_stop_hc(dev); ++ device_remove_file(&dev->dev, &dev_attr_disabled_usb_port3.dev_attr); ++ device_remove_file(&dev->dev, &dev_attr_disabled_usb_port2.dev_attr); ++ device_remove_file(&dev->dev, &dev_attr_disabled_usb_port1.dev_attr); ++ ++ usb_remove_hcd(hcd); + ++err_add_hcd: ++ tmio_stop_hc(dev); ++ cell->disable(dev); ++err_enable: + dmabounce_unregister_dev(&dev->dev); + err_dmabounce_register_dev: + dma_release_declared_memory(&dev->dev); +@@ -212,6 +349,9 @@ static void usb_hcd_tmio_remove(struct usb_hcd *hcd, struct platform_device *dev + { + struct tmio_hcd *tmio = hcd_to_tmio(hcd); + ++ device_remove_file(&dev->dev, &dev_attr_disabled_usb_port3.dev_attr); ++ device_remove_file(&dev->dev, &dev_attr_disabled_usb_port2.dev_attr); ++ device_remove_file(&dev->dev, &dev_attr_disabled_usb_port1.dev_attr); + usb_remove_hcd(hcd); + tmio_stop_hc(dev); + dmabounce_unregister_dev(&dev->dev); +@@ -297,13 +437,22 @@ static u64 dma_mask = DMA_32BIT_MASK; + static int ohci_hcd_tmio_drv_probe(struct platform_device *dev) + { + struct resource *sram = platform_get_resource_byname(dev, IORESOURCE_MEM, TMIO_OHCI_SRAM); ++ int retval; + + dev->dev.dma_mask = &dma_mask; + dev->dev.coherent_dma_mask = DMA_32BIT_MASK; + ++ /* FIXME: move dmabounce checkers to tc6393xb core? */ + dmabounce_register_checker(tmio_dmabounce_check, sram); + +- return usb_hcd_tmio_probe(&ohci_tmio_hc_driver, dev); ++ retval = usb_hcd_tmio_probe(&ohci_tmio_hc_driver, dev); ++ ++ if (retval == 0) ++ return retval; ++ ++ dmabounce_remove_checker(tmio_dmabounce_check, sram); ++ ++ return retval; + } + + static int ohci_hcd_tmio_drv_remove(struct platform_device *dev) +@@ -323,14 +472,31 @@ static int ohci_hcd_tmio_drv_remove(struct platform_device *dev) + #ifdef CONFIG_PM + static int ohci_hcd_tmio_drv_suspend(struct platform_device *dev, pm_message_t state) + { ++ struct mfd_cell *cell = mfd_get_cell(dev); + struct usb_hcd *hcd = platform_get_drvdata(dev); + struct ohci_hcd *ohci = hcd_to_ohci(hcd); ++ struct tmio_hcd *tmio = hcd_to_tmio(hcd); ++ struct tmio_uhccr __iomem *ccr = tmio->ccr; ++ unsigned long flags; ++ u8 misc; ++ int ret; + + if (time_before(jiffies, ohci->next_statechange)) + msleep(5); + ohci->next_statechange = jiffies; + +- tmio_stop_hc(dev); ++ spin_lock_irqsave(&tmio->lock, flags); ++ ++ misc = ioread8(&ccr->misc); ++ misc |= 1 << 3; /* USSUSP */ ++ iowrite8(misc, &ccr->misc); ++ ++ spin_unlock_irqrestore(&tmio->lock, flags); ++ ++ ret = cell->suspend(dev); ++ if (ret) ++ return ret; ++ + hcd->state = HC_STATE_SUSPENDED; + dev->dev.power.power_state = PMSG_SUSPEND; + +@@ -339,15 +505,33 @@ static int ohci_hcd_tmio_drv_suspend(struct platform_device *dev, pm_message_t s + + static int ohci_hcd_tmio_drv_resume(struct platform_device *dev) + { ++ struct mfd_cell *cell = mfd_get_cell(dev); + struct usb_hcd *hcd = platform_get_drvdata(dev); + struct ohci_hcd *ohci = hcd_to_ohci(hcd); ++ struct tmio_hcd *tmio = hcd_to_tmio(hcd); ++ struct tmio_uhccr __iomem *ccr = tmio->ccr; ++ unsigned long flags; ++ u8 misc; ++ int ret; + + if (time_before(jiffies, ohci->next_statechange)) + msleep(5); + ohci->next_statechange = jiffies; + ++ ret = cell->resume(dev); ++ if (ret) ++ return ret; ++ + tmio_start_hc(dev); + ++ spin_lock_irqsave(&tmio->lock, flags); ++ ++ misc = ioread8(&ccr->misc); ++ misc &= ~(1 << 3); /* USSUSP */ ++ iowrite8(misc, &ccr->misc); ++ ++ spin_unlock_irqrestore(&tmio->lock, flags); ++ + dev->dev.power.power_state = PMSG_ON; + usb_hcd_resume_root_hub(hcd); + +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0046-patch-tc6393xb-cleanup.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0046-patch-tc6393xb-cleanup.patch new file mode 100644 index 0000000000..c4b57cb2d1 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0046-patch-tc6393xb-cleanup.patch @@ -0,0 +1,66 @@ +From edaab7ec86235871d8ad219a1d225ce12f67f8af Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Wed, 9 Jan 2008 02:13:29 +0300 +Subject: [PATCH 46/64] patch tc6393xb-cleanup + +--- + drivers/mfd/tc6393xb.c | 20 ++++++++++++-------- + 1 files changed, 12 insertions(+), 8 deletions(-) + +diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c +index 5d17687..dfae61d 100644 +--- a/drivers/mfd/tc6393xb.c ++++ b/drivers/mfd/tc6393xb.c +@@ -590,16 +590,8 @@ static int tc6393xb_hw_init(struct platform_device *dev, int resume) + struct tc6393xb_platform_data *tcpd = dev->dev.platform_data; + struct tc6393xb *tc6393xb = platform_get_drvdata(dev); + struct tc6393xb_scr __iomem *scr = tc6393xb->scr; +- int ret; + int i; + +- if (resume) +- ret = tcpd->resume(dev); +- else +- ret = tcpd->enable(dev); +- if (ret) +- return ret; +- + iowrite8(resume ? + tc6393xb->suspend_state.fer.raw : + 0, &scr->fer); +@@ -664,6 +656,10 @@ static int __devinit tc6393xb_probe(struct platform_device *dev) + goto err_ioremap; + } + ++ retval = tcpd->enable(dev); ++ if (retval) ++ goto err_enable; ++ + retval = tc6393xb_hw_init(dev, 0); + if (retval) + goto err_hw_init; +@@ -690,6 +686,8 @@ static int __devinit tc6393xb_probe(struct platform_device *dev) + tc6393xb_detach_irq(dev); + + err_hw_init: ++ tcpd->disable(dev); ++err_enable: + iounmap(tc6393xb->scr); + err_ioremap: + release_resource(rscr); +@@ -743,6 +741,12 @@ static int tc6393xb_suspend(struct platform_device *dev, pm_message_t state) + + static int tc6393xb_resume(struct platform_device *dev) + { ++ struct tc6393xb_platform_data *tcpd = dev->dev.platform_data; ++ int ret = tcpd->resume(dev); ++ ++ if (ret) ++ return ret; ++ + return tc6393xb_hw_init(dev, 1); + } + #else +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0047-tc6393xb-use-bitmasks-instead-of-bit-field-structs.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0047-tc6393xb-use-bitmasks-instead-of-bit-field-structs.patch new file mode 100644 index 0000000000..54e88253d1 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0047-tc6393xb-use-bitmasks-instead-of-bit-field-structs.patch @@ -0,0 +1,412 @@ +From c18b8e34c39ec0d395988318e6651076a748d6bd Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Tue, 12 Feb 2008 04:40:54 +0300 +Subject: [PATCH 47/64] tc6393xb: use bitmasks instead of bit-field structs + +Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> +--- + drivers/mfd/tc6393xb.c | 162 ++++++++++++++++++++++++----------------- + include/linux/mfd/tc6393xb.h | 63 +++------------- + 2 files changed, 107 insertions(+), 118 deletions(-) + +diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c +index dfae61d..1a394e4 100644 +--- a/drivers/mfd/tc6393xb.c ++++ b/drivers/mfd/tc6393xb.c +@@ -24,6 +24,31 @@ + #include <linux/mfd/tmio.h> + #include <linux/mfd/tc6393xb.h> + ++#define TC6393XB_FER_USBEN BIT(0) /* USB host enable */ ++#define TC6393XB_FER_LCDCVEN BIT(1) /* polysilicon TFT enable */ ++#define TC6393XB_FER_SLCDEN BIT(2) /* SLCD enable */ ++ ++enum pincontrol { ++ opendrain = 0, ++ tristate = 1, ++ pushpull = 2, ++ /* reserved = 3, */ ++}; ++ ++#define TC6393XB_MCR_RDY_MASK (3 << 0) ++#define TC6393XB_MCR_RDY_OPENDRAIN (0 << 0) ++#define TC6393XB_MCR_RDY_TRISTATE (1 << 0) ++#define TC6393XB_MCR_RDY_PUSHPULL (2 << 0) ++#define TC6393XB_MCR_RDY_UNK BIT(2) ++#define TC6393XB_MCR_RDY_EN BIT(3) ++#define TC6393XB_MCR_INT_MASK (3 << 4) ++#define TC6393XB_MCR_INT_OPENDRAIN (0 << 4) ++#define TC6393XB_MCR_INT_TRISTATE (1 << 4) ++#define TC6393XB_MCR_INT_PUSHPULL (2 << 4) ++#define TC6393XB_MCR_INT_UNK BIT(6) ++#define TC6393XB_MCR_INT_EN BIT(7) ++/* bits 8 - 16 are unknown */ ++ + struct tc6393xb_scr { + u8 x00[8]; + u8 revid; /* 0x08 Revision ID */ +@@ -74,8 +99,8 @@ struct tc6393xb { + spinlock_t lock; /* protects RMW cycles */ + + struct { +- union tc6393xb_scr_fer fer; +- union tc6393xb_scr_ccr ccr; ++ u8 fer; ++ u16 ccr; + u8 gpi_bcr[4]; + } suspend_state; + +@@ -90,13 +115,13 @@ static int tc6393xb_mmc_enable(struct platform_device *mmc) { + struct platform_device *dev = to_platform_device(mmc->dev.parent); + struct tc6393xb *tc6393xb = platform_get_drvdata(dev); + struct tc6393xb_scr __iomem *scr = tc6393xb->scr; +- union tc6393xb_scr_ccr ccr; ++ u16 ccr; + unsigned long flags; + + spin_lock_irqsave(&tc6393xb->lock, flags); +- ccr.raw = ioread16(&scr->ccr); +- ccr.bits.ck32ken = 1; +- iowrite16(ccr.raw, &scr->ccr); ++ ccr = ioread16(&scr->ccr); ++ ccr |= TC6393XB_CCR_CK32K; ++ iowrite16(ccr, &scr->ccr); + spin_unlock_irqrestore(&tc6393xb->lock, flags); + + return 0; +@@ -106,13 +131,13 @@ static int tc6393xb_mmc_disable(struct platform_device *mmc) { + struct platform_device *dev = to_platform_device(mmc->dev.parent); + struct tc6393xb *tc6393xb = platform_get_drvdata(dev); + struct tc6393xb_scr __iomem *scr = tc6393xb->scr; +- union tc6393xb_scr_ccr ccr; ++ u16 ccr; + unsigned long flags; + + spin_lock_irqsave(&tc6393xb->lock, flags); +- ccr.raw = ioread16(&scr->ccr); +- ccr.bits.ck32ken = 0; +- iowrite16(ccr.raw, &scr->ccr); ++ ccr = ioread16(&scr->ccr); ++ ccr &= ~TC6393XB_CCR_CK32K; ++ iowrite16(ccr, &scr->ccr); + spin_unlock_irqrestore(&tc6393xb->lock, flags); + + return 0; +@@ -148,14 +173,17 @@ int tc6393xb_lcd_set_power(struct platform_device *fb, bool on) + struct platform_device *dev = to_platform_device(fb->dev.parent); + struct tc6393xb *tc6393xb = platform_get_drvdata(dev); + struct tc6393xb_scr __iomem *scr = tc6393xb->scr; +- union tc6393xb_scr_fer fer; ++ u8 fer; + unsigned long flags; + + spin_lock_irqsave(&tc6393xb->lock, flags); + +- fer.raw = ioread8(&scr->fer); +- fer.bits.slcden = on ? 1 : 0; +- iowrite8(fer.raw, &scr->fer); ++ fer = ioread8(&scr->fer); ++ if (on) ++ fer |= TC6393XB_FER_SLCDEN; ++ else ++ fer &= ~TC6393XB_FER_SLCDEN; ++ iowrite8(fer, &scr->fer); + + spin_unlock_irqrestore(&tc6393xb->lock, flags); + +@@ -181,19 +209,19 @@ static int tc6393xb_ohci_disable(struct platform_device *ohci) + struct platform_device *dev = to_platform_device(ohci->dev.parent); + struct tc6393xb *tc6393xb = platform_get_drvdata(dev); + struct tc6393xb_scr __iomem *scr = tc6393xb->scr; +- union tc6393xb_scr_ccr ccr; +- union tc6393xb_scr_fer fer; ++ u16 ccr; ++ u8 fer; + unsigned long flags; + + spin_lock_irqsave(&tc6393xb->lock, flags); + +- fer.raw = ioread8(&scr->fer); +- fer.bits.usben = 0; +- iowrite8(fer.raw, &scr->fer); ++ fer = ioread8(&scr->fer); ++ fer &= ~TC6393XB_FER_USBEN; ++ iowrite8(fer, &scr->fer); + +- ccr.raw = ioread16(&scr->ccr); +- ccr.bits.usbcken = 0; +- iowrite16(ccr.raw, &scr->ccr); ++ ccr = ioread16(&scr->ccr); ++ ccr &= ~TC6393XB_CCR_USBCK; ++ iowrite16(ccr, &scr->ccr); + + spin_unlock_irqrestore(&tc6393xb->lock, flags); + +@@ -205,19 +233,19 @@ static int tc6393xb_ohci_enable(struct platform_device *ohci) + struct platform_device *dev = to_platform_device(ohci->dev.parent); + struct tc6393xb *tc6393xb = platform_get_drvdata(dev); + struct tc6393xb_scr __iomem *scr = tc6393xb->scr; +- union tc6393xb_scr_ccr ccr; +- union tc6393xb_scr_fer fer; ++ u16 ccr; ++ u8 fer; + unsigned long flags; + + spin_lock_irqsave(&tc6393xb->lock, flags); + +- ccr.raw = ioread16(&scr->ccr); +- ccr.bits.usbcken = 1; +- iowrite16(ccr.raw, &scr->ccr); ++ ccr = ioread16(&scr->ccr); ++ ccr |= TC6393XB_CCR_USBCK; ++ iowrite16(ccr, &scr->ccr); + +- fer.raw = ioread8(&scr->fer); +- fer.bits.usben = 1; +- iowrite8(fer.raw, &scr->fer); ++ fer = ioread8(&scr->fer); ++ fer |= TC6393XB_FER_USBEN; ++ iowrite8(fer, &scr->fer); + + spin_unlock_irqrestore(&tc6393xb->lock, flags); + +@@ -229,14 +257,14 @@ static int tc6393xb_ohci_suspend(struct platform_device *ohci) + struct platform_device *dev = to_platform_device(ohci->dev.parent); + struct tc6393xb *tc6393xb = platform_get_drvdata(dev); + struct tc6393xb_scr __iomem *scr = tc6393xb->scr; +- union tc6393xb_scr_ccr ccr; ++ u16 ccr; + unsigned long flags; + + spin_lock_irqsave(&tc6393xb->lock, flags); + +- ccr.raw = ioread16(&scr->ccr); +- ccr.bits.usbcken = 0; +- iowrite16(ccr.raw, &scr->ccr); ++ ccr = ioread16(&scr->ccr); ++ ccr &= ~TC6393XB_CCR_USBCK; ++ iowrite16(ccr, &scr->ccr); + + spin_unlock_irqrestore(&tc6393xb->lock, flags); + +@@ -248,14 +276,14 @@ static int tc6393xb_ohci_resume(struct platform_device *ohci) + struct platform_device *dev = to_platform_device(ohci->dev.parent); + struct tc6393xb *tc6393xb = platform_get_drvdata(dev); + struct tc6393xb_scr __iomem *scr = tc6393xb->scr; +- union tc6393xb_scr_ccr ccr; ++ u16 ccr; + unsigned long flags; + + spin_lock_irqsave(&tc6393xb->lock, flags); + +- ccr.raw = ioread16(&scr->ccr); +- ccr.bits.usbcken = 1; +- iowrite16(ccr.raw, &scr->ccr); ++ ccr = ioread16(&scr->ccr); ++ ccr |= TC6393XB_CCR_USBCK; ++ iowrite16(ccr, &scr->ccr); + + spin_unlock_irqrestore(&tc6393xb->lock, flags); + +@@ -267,8 +295,8 @@ static int tc6393xb_fb_disable(struct platform_device *fb) + struct platform_device *dev = to_platform_device(fb->dev.parent); + struct tc6393xb *tc6393xb = platform_get_drvdata(dev); + struct tc6393xb_scr __iomem *scr = tc6393xb->scr; +- union tc6393xb_scr_ccr ccr; +- union tc6393xb_scr_fer fer; ++ u16 ccr; ++ u8 fer; + unsigned long flags; + + spin_lock_irqsave(&tc6393xb->lock, flags); +@@ -276,14 +304,13 @@ static int tc6393xb_fb_disable(struct platform_device *fb) + /* + * FIXME: is this correct or it should be moved to other _disable? + */ +- fer.raw = ioread8(&scr->fer); +- fer.bits.slcden = 0; +-/* fer.bits.lcdcven = 0; */ +- iowrite8(fer.raw, &scr->fer); ++ fer = ioread8(&scr->fer); ++ fer &= ~TC6393XB_FER_SLCDEN; ++ iowrite8(fer, &scr->fer); + +- ccr.raw = ioread16(&scr->ccr); +- ccr.bits.mclksel = disable; +- iowrite16(ccr.raw, &scr->ccr); ++ ccr = ioread16(&scr->ccr); ++ ccr = (ccr & ~TC6393XB_CCR_MCLK_MASK) | TC6393XB_CCR_MCLK_OFF; ++ iowrite16(ccr, &scr->ccr); + + spin_unlock_irqrestore(&tc6393xb->lock, flags); + +@@ -295,14 +322,14 @@ static int tc6393xb_fb_enable(struct platform_device *fb) + struct platform_device *dev = to_platform_device(fb->dev.parent); + struct tc6393xb *tc6393xb = platform_get_drvdata(dev); + struct tc6393xb_scr __iomem *scr = tc6393xb->scr; +- union tc6393xb_scr_ccr ccr; ++ u16 ccr; + unsigned long flags; + + spin_lock_irqsave(&tc6393xb->lock, flags); + +- ccr.raw = ioread16(&scr->ccr); +- ccr.bits.mclksel = m48MHz; +- iowrite16(ccr.raw, &scr->ccr); ++ ccr = ioread16(&scr->ccr); ++ ccr = (ccr & ~TC6393XB_CCR_MCLK_MASK) | TC6393XB_CCR_MCLK_48; ++ iowrite16(ccr, &scr->ccr); + + spin_unlock_irqrestore(&tc6393xb->lock, flags); + +@@ -314,14 +341,14 @@ static int tc6393xb_fb_suspend(struct platform_device *fb) + struct platform_device *dev = to_platform_device(fb->dev.parent); + struct tc6393xb *tc6393xb = platform_get_drvdata(dev); + struct tc6393xb_scr __iomem *scr = tc6393xb->scr; +- union tc6393xb_scr_ccr ccr; ++ u16 ccr; + unsigned long flags; + + spin_lock_irqsave(&tc6393xb->lock, flags); + +- ccr.raw = ioread16(&scr->ccr); +- ccr.bits.mclksel = disable; +- iowrite16(ccr.raw, &scr->ccr); ++ ccr = ioread16(&scr->ccr); ++ ccr = (ccr & ~TC6393XB_CCR_MCLK_MASK) | TC6393XB_CCR_MCLK_OFF; ++ iowrite16(ccr, &scr->ccr); + + spin_unlock_irqrestore(&tc6393xb->lock, flags); + +@@ -333,14 +360,14 @@ static int tc6393xb_fb_resume(struct platform_device *fb) + struct platform_device *dev = to_platform_device(fb->dev.parent); + struct tc6393xb *tc6393xb = platform_get_drvdata(dev); + struct tc6393xb_scr __iomem *scr = tc6393xb->scr; +- union tc6393xb_scr_ccr ccr; ++ u16 ccr; + unsigned long flags; + + spin_lock_irqsave(&tc6393xb->lock, flags); + +- ccr.raw = ioread16(&scr->ccr); +- ccr.bits.mclksel = m48MHz; +- iowrite16(ccr.raw, &scr->ccr); ++ ccr = ioread16(&scr->ccr); ++ ccr = (ccr & ~TC6393XB_CCR_MCLK_MASK) | TC6393XB_CCR_MCLK_48; ++ iowrite16(ccr, &scr->ccr); + + spin_unlock_irqrestore(&tc6393xb->lock, flags); + +@@ -592,14 +619,15 @@ static int tc6393xb_hw_init(struct platform_device *dev, int resume) + struct tc6393xb_scr __iomem *scr = tc6393xb->scr; + int i; + +- iowrite8(resume ? +- tc6393xb->suspend_state.fer.raw : +- 0, &scr->fer); ++ iowrite8(resume ? tc6393xb->suspend_state.fer : 0, ++ &scr->fer); + iowrite16(tcpd->scr_pll2cr, &scr->pll2cr); + iowrite16(resume? +- tc6393xb->suspend_state.ccr.raw : +- tcpd->scr_ccr.raw, &scr->ccr); +- iowrite16(tcpd->scr_mcr.raw, &scr->mcr); ++ tc6393xb->suspend_state.ccr : ++ tcpd->scr_ccr, &scr->ccr); ++ iowrite16(TC6393XB_MCR_RDY_OPENDRAIN | TC6393XB_MCR_RDY_UNK | TC6393XB_MCR_RDY_EN | ++ TC6393XB_MCR_INT_OPENDRAIN | TC6393XB_MCR_INT_UNK | TC6393XB_MCR_INT_EN | ++ BIT(15), &scr->mcr); + iowrite16(tcpd->scr_gper, &scr->gper); + iowrite8(0, &scr->irr); + iowrite8(0xbf, &scr->imr); +@@ -730,8 +758,8 @@ static int tc6393xb_suspend(struct platform_device *dev, pm_message_t state) + int i; + + +- tc6393xb->suspend_state.ccr.raw = ioread16(&scr->ccr); +- tc6393xb->suspend_state.fer.raw = ioread8(&scr->fer); ++ tc6393xb->suspend_state.ccr = ioread16(&scr->ccr); ++ tc6393xb->suspend_state.fer = ioread8(&scr->fer); + for (i = 0; i < 4; i++) + tc6393xb->suspend_state.gpi_bcr[i] = + ioread8(scr->gpi_bcr + i); +diff --git a/include/linux/mfd/tc6393xb.h b/include/linux/mfd/tc6393xb.h +index e699294..2c69f63 100644 +--- a/include/linux/mfd/tc6393xb.h ++++ b/include/linux/mfd/tc6393xb.h +@@ -20,60 +20,21 @@ + #include <linux/mfd-core.h> + #include <linux/mfd/tmio.h> + +-union tc6393xb_scr_fer { +- u8 raw; +-struct { +- unsigned usben:1; /* D0 USB enable */ +- unsigned lcdcven:1; /* D1 polysylicon TFT enable */ +- unsigned slcden:1; /* D2 SLCD enable */ +-} __attribute__ ((packed)) bits; +-} __attribute__ ((packed)); +- +-union tc6393xb_scr_ccr { +- u16 raw; +-struct { +- unsigned ck32ken:1; /* D0 SD host clock enable */ +- unsigned usbcken:1; /* D1 USB host clock enable */ +- unsigned x00:2; +- unsigned sharp:1; /* D4 ??? set in Sharp's code */ +- unsigned x01:3; +- enum { disable = 0, +- m12MHz = 1, +- m24MHz = 2, +- m48MHz = 3, +- } mclksel:3; /* D10-D8 LCD controller clock */ +- unsigned x02:1; +- enum { h24MHz = 0, +- h48MHz = 1, +- } hclksel:2; /* D13-D12 host bus clock */ +- unsigned x03:2; +-} __attribute__ ((packed)) bits; +-} __attribute__ ((packed)); +- +-enum pincontrol { +- opendrain = 0, +- tristate = 1, +- pushpull = 2, +- /* reserved = 3, */ +-}; +- +-union tc6393xb_scr_mcr { +- u16 raw; +-struct { +- enum pincontrol rdyst:2; /* D1-D0 HRDY control */ +- unsigned x00:1; +- unsigned aren:1; /* D3 HRDY pull up resistance cut off */ +- enum pincontrol intst:2; /* D5-D4 #HINT control */ +- unsigned x01:1; +- unsigned aien:1; /* D7 #HINT pull up resitance cut off */ +- unsigned x02:8; +-} __attribute__ ((packed)) bits; +-} __attribute__ ((packed)); ++#define TC6393XB_CCR_CK32K BIT(0) ++#define TC6393XB_CCR_USBCK BIT(1) ++#define TC6393XB_CCR_UNK1 BIT(4) ++#define TC6393XB_CCR_MCLK_MASK (7 << 8) ++#define TC6393XB_CCR_MCLK_OFF (0 << 8) ++#define TC6393XB_CCR_MCLK_12 (1 << 8) ++#define TC6393XB_CCR_MCLK_24 (2 << 8) ++#define TC6393XB_CCR_MCLK_48 (3 << 8) ++#define TC6393XB_CCR_HCLK_MASK (3 << 12) ++#define TC6393XB_CCR_HCLK_24 (0 << 12) ++#define TC6393XB_CCR_HCLK_48 (1 << 12) + + struct tc6393xb_platform_data { + u16 scr_pll2cr; /* PLL2 Control */ +- union tc6393xb_scr_ccr scr_ccr; /* Clock Control */ +- union tc6393xb_scr_mcr scr_mcr; /* Mode Control */ ++ u16 scr_ccr; /* Clock Control */ + u16 scr_gper; /* GP Enable */ + u32 scr_gpo_doecr; /* GPO Data OE Control */ + u32 scr_gpo_dsr; /* GPO Data Set */ +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0048-tc6393xb-GPIO-support.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0048-tc6393xb-GPIO-support.patch new file mode 100644 index 0000000000..ef47d6cc21 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0048-tc6393xb-GPIO-support.patch @@ -0,0 +1,225 @@ +From 4fb4d83c7090ea21619bb652f2ea9b5c8c0c453e Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Wed, 9 Jan 2008 01:42:58 +0300 +Subject: [PATCH 48/64] tc6393xb GPIO support + +Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> +--- + drivers/mfd/tc6393xb.c | 124 ++++++++++++++++++++++++++++++++++++++++-- + include/linux/mfd/tc6393xb.h | 2 +- + 2 files changed, 119 insertions(+), 7 deletions(-) + +diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c +index 1a394e4..9001687 100644 +--- a/drivers/mfd/tc6393xb.c ++++ b/drivers/mfd/tc6393xb.c +@@ -49,6 +49,8 @@ enum pincontrol { + #define TC6393XB_MCR_INT_EN BIT(7) + /* bits 8 - 16 are unknown */ + ++#include <asm/gpio.h> ++ + struct tc6393xb_scr { + u8 x00[8]; + u8 revid; /* 0x08 Revision ID */ +@@ -96,6 +98,8 @@ struct tc6393xb_scr { + struct tc6393xb { + struct tc6393xb_scr __iomem *scr; + ++ struct gpio_chip gpio; ++ + spinlock_t lock; /* protects RMW cycles */ + + struct { +@@ -513,6 +517,96 @@ static struct mfd_cell tc6393xb_cells[] = { + + /*--------------------------------------------------------------------------*/ + ++static int tc6393xb_gpio_get(struct gpio_chip *chip, ++ unsigned offset) ++{ ++ struct tc6393xb *tc6393xb = container_of(chip, ++ struct tc6393xb, gpio); ++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr; ++ u32 mask = 1 << offset; ++ ++ return tmio_ioread32(scr->gpo_dsr) & mask; ++} ++ ++static void __tc6393xb_gpio_set(struct gpio_chip *chip, ++ unsigned offset, int value) ++{ ++ struct tc6393xb *tc6393xb = container_of(chip, ++ struct tc6393xb, gpio); ++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr; ++ u32 dsr; ++ ++ dsr = tmio_ioread32(scr->gpo_dsr); ++ if (value) ++ dsr |= (1L << offset); ++ else ++ dsr &= ~(1L << offset); ++ ++ tmio_iowrite32(dsr, scr->gpo_dsr); ++} ++ ++static void tc6393xb_gpio_set(struct gpio_chip *chip, ++ unsigned offset, int value) ++{ ++ struct tc6393xb *tc6393xb = container_of(chip, ++ struct tc6393xb, gpio); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&tc6393xb->lock, flags); ++ ++ __tc6393xb_gpio_set(chip, offset, value); ++ ++ spin_unlock_irqrestore(&tc6393xb->lock, flags); ++} ++ ++static int tc6393xb_gpio_direction_input(struct gpio_chip *chip, ++ unsigned offset) ++{ ++ struct tc6393xb *tc6393xb = container_of(chip, ++ struct tc6393xb, gpio); ++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr; ++ unsigned long flags; ++ u32 doecr; ++ ++ spin_lock_irqsave(&tc6393xb->lock, flags); ++ ++ doecr = tmio_ioread32(scr->gpo_doecr); ++ ++ doecr &= ~(1 << offset); ++ ++ tmio_iowrite32(doecr, scr->gpo_doecr); ++ ++ spin_unlock_irqrestore(&tc6393xb->lock, flags); ++ ++ return 0; ++} ++ ++static int tc6393xb_gpio_direction_output(struct gpio_chip *chip, ++ unsigned offset, int value) ++{ ++ struct tc6393xb *tc6393xb = container_of(chip, ++ struct tc6393xb, gpio); ++ struct tc6393xb_scr __iomem *scr = tc6393xb->scr; ++ unsigned long flags; ++ u32 doecr; ++ ++ spin_lock_irqsave(&tc6393xb->lock, flags); ++ ++ doecr = tmio_ioread32(scr->gpo_doecr); ++ ++ doecr |= (1 << offset); ++ ++ tmio_iowrite32(doecr, scr->gpo_doecr); ++ ++ __tc6393xb_gpio_set(chip, offset, value); ++ ++ spin_unlock_irqrestore(&tc6393xb->lock, flags); ++ ++ return 0; ++} ++ ++/*--------------------------------------------------------------------------*/ ++ + static void + tc6393xb_irq(unsigned int irq, struct irq_desc *desc) + { +@@ -631,10 +725,8 @@ static int tc6393xb_hw_init(struct platform_device *dev, int resume) + iowrite16(tcpd->scr_gper, &scr->gper); + iowrite8(0, &scr->irr); + iowrite8(0xbf, &scr->imr); +- iowrite16(tcpd->scr_gpo_dsr, scr->gpo_dsr + 0); +- iowrite16(tcpd->scr_gpo_dsr >> 16, scr->gpo_dsr + 1); +- iowrite16(tcpd->scr_gpo_doecr, scr->gpo_doecr + 0); +- iowrite16(tcpd->scr_gpo_doecr >> 16, scr->gpo_doecr + 1); ++ tmio_iowrite32(tcpd->scr_gpo_dsr, &scr->gpo_dsr); ++ tmio_iowrite32(tcpd->scr_gpo_doecr, &scr->gpo_doecr); + + if (resume) + for (i = 0; i < 4; i++) +@@ -650,7 +742,7 @@ static int __devinit tc6393xb_probe(struct platform_device *dev) + struct tc6393xb *tc6393xb; + struct resource *iomem; + struct resource *rscr; +- int retval; ++ int retval, temp; + + iomem = platform_get_resource(dev, IORESOURCE_MEM, 0); + if (!iomem) +@@ -696,6 +788,18 @@ static int __devinit tc6393xb_probe(struct platform_device *dev) + ioread8(&tc6393xb->scr->revid), + (unsigned long) iomem->start, tc6393xb->irq); + ++ tc6393xb->gpio.label = "tc6393xb"; ++ tc6393xb->gpio.base = tcpd->gpio_base; ++ tc6393xb->gpio.ngpio = 16; /* FIXME: actually 32, but I'm not sure */ ++ tc6393xb->gpio.set = tc6393xb_gpio_set; ++ tc6393xb->gpio.get = tc6393xb_gpio_get; ++ tc6393xb->gpio.direction_input = tc6393xb_gpio_direction_input; ++ tc6393xb->gpio.direction_output = tc6393xb_gpio_direction_output; ++ ++ retval = gpiochip_add(&tc6393xb->gpio); ++ if (retval) ++ goto err_gpio_add; ++ + if (tc6393xb->irq) + tc6393xb_attach_irq(dev); + +@@ -713,6 +817,8 @@ static int __devinit tc6393xb_probe(struct platform_device *dev) + if (tc6393xb->irq) + tc6393xb_detach_irq(dev); + ++err_gpio_add: ++ temp = gpiochip_remove(&tc6393xb->gpio); + err_hw_init: + tcpd->disable(dev); + err_enable: +@@ -734,6 +840,12 @@ static int __devexit tc6393xb_remove(struct platform_device *dev) { + if (tc6393xb->irq) + tc6393xb_detach_irq(dev); + ++ ret = gpiochip_remove(&tc6393xb->gpio); ++ if (ret) { ++ dev_err(&dev->dev, "Can't remove gpio chip: %d\n", ret); ++ return ret; ++ } ++ + ret = tcpd->disable(dev); + + iounmap(tc6393xb->scr); +@@ -804,7 +916,7 @@ static void __exit tc6393xb_exit(void) + platform_driver_unregister(&tc6393xb_driver); + } + +-module_init(tc6393xb_init); ++subsys_initcall(tc6393xb_init); + module_exit(tc6393xb_exit); + + MODULE_LICENSE("GPL"); +diff --git a/include/linux/mfd/tc6393xb.h b/include/linux/mfd/tc6393xb.h +index 2c69f63..97c4c7c 100644 +--- a/include/linux/mfd/tc6393xb.h ++++ b/include/linux/mfd/tc6393xb.h +@@ -45,6 +45,7 @@ struct tc6393xb_platform_data { + int (*resume)(struct platform_device *dev); + + int irq_base; /* a base for cascaded irq */ ++ int gpio_base; + + struct tmio_nand_data *nand_data; + struct tmio_fb_data *fb_data; +@@ -54,7 +55,6 @@ extern int tc6393xb_lcd_set_power(struct platform_device *fb_dev, bool on); + extern int tc6393xb_lcd_mode(struct platform_device *fb_dev, + struct fb_videomode *mode); + +- + /* + * Relative to irq_base + */ +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0049-platform-support-for-TMIO-on-tosa.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0049-platform-support-for-TMIO-on-tosa.patch new file mode 100644 index 0000000000..ff1186cb71 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0049-platform-support-for-TMIO-on-tosa.patch @@ -0,0 +1,373 @@ +From 30588bdd5c5cdd9fbe269643f582862a76f09efb Mon Sep 17 00:00:00 2001 +From: Ian Molton <spyro@f2s.com> +Date: Tue, 12 Feb 2008 04:52:48 +0300 +Subject: [PATCH 49/64] platform support for TMIO on tosa + +Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> +--- + arch/arm/mach-pxa/tosa.c | 179 ++++++++++++++++++++++++++++++++++++++- + include/asm-arm/arch-pxa/irqs.h | 1 + + include/asm-arm/arch-pxa/tosa.h | 45 ++++++++-- + sound/soc/pxa/tosa.c | 3 +- + 4 files changed, 216 insertions(+), 12 deletions(-) + +diff --git a/arch/arm/mach-pxa/tosa.c b/arch/arm/mach-pxa/tosa.c +index 5268e94..e2eec0f 100644 +--- a/arch/arm/mach-pxa/tosa.c ++++ b/arch/arm/mach-pxa/tosa.c +@@ -18,7 +18,13 @@ + #include <linux/major.h> + #include <linux/fs.h> + #include <linux/interrupt.h> ++#include <linux/delay.h> ++#include <linux/fb.h> + #include <linux/mmc/host.h> ++#include <linux/mfd/tc6393xb.h> ++#include <linux/mfd/tmio.h> ++#include <linux/mtd/nand.h> ++#include <linux/mtd/partitions.h> + #include <linux/pm.h> + #include <linux/delay.h> + #include <linux/gpio_keys.h> +@@ -298,12 +304,183 @@ static struct platform_device tosaled_device = { + .id = -1, + }; + ++/* ++ * Toshiba Mobile IO Controller ++ */ ++static struct resource tc6393xb_resources[] = { ++ [0] = { ++ .start = TOSA_LCDC_PHYS, ++ .end = TOSA_LCDC_PHYS + 0x3ffffff, ++ .flags = IORESOURCE_MEM, ++ }, ++ ++ [1] = { ++ .start = TOSA_IRQ_GPIO_TC6393XB_INT, ++ .end = TOSA_IRQ_GPIO_TC6393XB_INT, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++ ++static int tosa_tc6393xb_enable(struct platform_device *dev) ++{ ++ ++ reset_scoop_gpio(&tosascoop_jc_device.dev, TOSA_SCOOP_JC_TC6393XB_L3V_ON); ++ reset_scoop_gpio(&tosascoop_jc_device.dev, TOSA_SCOOP_JC_TC6393XB_SUSPEND); ++ reset_scoop_gpio(&tosascoop_device.dev, TOSA_SCOOP_TC6393XB_REST_IN); /* #PCLR */ ++ pxa_gpio_mode(GPIO11_3_6MHz_MD); ++ pxa_gpio_mode(GPIO18_RDY_MD); ++ mdelay(1); ++ set_scoop_gpio(&tosascoop_jc_device.dev, TOSA_SCOOP_JC_TC6393XB_SUSPEND); ++ mdelay(10); ++ set_scoop_gpio(&tosascoop_device.dev, TOSA_SCOOP_TC6393XB_REST_IN); /* #PCLR */ ++ set_scoop_gpio(&tosascoop_jc_device.dev, TOSA_SCOOP_JC_TC6393XB_L3V_ON); ++ ++ return 0; ++} ++ ++static int tosa_tc6393xb_disable(struct platform_device *dev) ++{ ++ ++ reset_scoop_gpio(&tosascoop_jc_device.dev, TOSA_SCOOP_JC_TC6393XB_L3V_ON); ++ reset_scoop_gpio(&tosascoop_jc_device.dev, TOSA_SCOOP_JC_TC6393XB_SUSPEND); ++ reset_scoop_gpio(&tosascoop_device.dev, TOSA_SCOOP_TC6393XB_REST_IN); /* #PCLR */ ++ pxa_gpio_mode(GPIO11_3_6MHz_MD|GPIO_OUT); ++ GPSR0 = GPIO_bit(GPIO11_3_6MHz); ++ ++ return 0; ++} ++ ++static int tosa_tc6393xb_resume(struct platform_device *dev) ++{ ++ ++ pxa_gpio_mode(GPIO11_3_6MHz_MD); ++ pxa_gpio_mode(GPIO18_RDY_MD); ++ mdelay(1); ++ set_scoop_gpio(&tosascoop_jc_device.dev, TOSA_SCOOP_JC_TC6393XB_SUSPEND); ++ mdelay(10); ++ set_scoop_gpio(&tosascoop_jc_device.dev, TOSA_SCOOP_JC_TC6393XB_L3V_ON); ++ mdelay(10); ++ ++ return 0; ++} ++ ++static int tosa_tc6393xb_suspend(struct platform_device *dev) ++{ ++ ++ reset_scoop_gpio(&tosascoop_jc_device.dev, TOSA_SCOOP_JC_TC6393XB_L3V_ON); ++ reset_scoop_gpio(&tosascoop_jc_device.dev, TOSA_SCOOP_JC_TC6393XB_SUSPEND); ++ pxa_gpio_mode(GPIO11_3_6MHz_MD|GPIO_OUT); ++ GPSR0 = GPIO_bit(GPIO11_3_6MHz); ++ ++ return 0; ++} ++ ++static struct mtd_partition tosa_nand_partition[] = { ++ { ++ .name = "smf", ++ .offset = 0, ++ .size = 7 * 1024 * 1024, ++ }, ++ { ++ .name = "root", ++ .offset = MTDPART_OFS_APPEND, ++ .size = 28 * 1024 * 1024, ++ }, ++ { ++ .name = "home", ++ .offset = MTDPART_OFS_APPEND, ++ .size = MTDPART_SIZ_FULL, ++ }, ++}; ++ ++static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; ++ ++static struct nand_bbt_descr tosa_tc6393xb_nand_bbt = { ++ .options = 0, ++ .offs = 4, ++ .len = 2, ++ .pattern = scan_ff_pattern ++}; ++ ++static struct tmio_nand_data tosa_tc6393xb_nand_config = { ++ .num_partitions = ARRAY_SIZE(tosa_nand_partition), ++ .partition = tosa_nand_partition, ++ .badblock_pattern = &tosa_tc6393xb_nand_bbt, ++}; ++ ++static struct fb_videomode tosa_tc6393xb_lcd_mode[] = { ++ { ++ .xres = 480, ++ .yres = 640, ++ .pixclock = 0x002cdf00,/* PLL divisor */ ++ .left_margin = 0x004c, ++ .right_margin = 0x005b, ++ .upper_margin = 0x0001, ++ .lower_margin = 0x000d, ++ .hsync_len = 0x0002, ++ .vsync_len = 0x0001, ++ .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, ++ .vmode = FB_VMODE_NONINTERLACED, ++ },{ ++ .xres = 240, ++ .yres = 320, ++ .pixclock = 0x00e7f203,/* PLL divisor */ ++ .left_margin = 0x0024, ++ .right_margin = 0x002f, ++ .upper_margin = 0x0001, ++ .lower_margin = 0x000d, ++ .hsync_len = 0x0002, ++ .vsync_len = 0x0001, ++ .sync = FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, ++ .vmode = FB_VMODE_NONINTERLACED, ++ } ++}; ++ ++static struct tmio_fb_data tosa_tc6393xb_fb_config = { ++ .lcd_set_power = tc6393xb_lcd_set_power, ++ .lcd_mode = tc6393xb_lcd_mode, ++ .num_modes = ARRAY_SIZE(tosa_tc6393xb_lcd_mode), ++ .modes = &tosa_tc6393xb_lcd_mode[0], ++}; ++ ++static struct tc6393xb_platform_data tosa_tc6393xb_setup = { ++ .scr_pll2cr = 0x0cc1, ++ .scr_ccr = TC6393XB_CCR_UNK1 | TC6393XB_CCR_HCLK_48, ++ .scr_gper = 0x3300, ++ .scr_gpo_dsr = TOSA_TC6393XB_CARD_VCC_ON | TOSA_TC6393XB_CHARGE_OFF_JC, ++ .scr_gpo_doecr = TOSA_TC6393XB_GPO_OE, ++ ++ .irq_base = IRQ_BOARD_START, ++ ++ .enable = tosa_tc6393xb_enable, ++ .disable = tosa_tc6393xb_disable, ++ .suspend = tosa_tc6393xb_suspend, ++ .resume = tosa_tc6393xb_resume, ++ ++ .nand_data = &tosa_tc6393xb_nand_config, ++ .fb_data = &tosa_tc6393xb_fb_config, ++}; ++ ++ ++struct platform_device tc6393xb_device = { ++ .name = "tc6393xb", ++ .id = -1, ++ .dev = { ++ .platform_data = &tosa_tc6393xb_setup, ++ }, ++ .num_resources = ARRAY_SIZE(tc6393xb_resources), ++ .resource = tc6393xb_resources, ++}; ++EXPORT_SYMBOL(tc6393xb_device); ++ + static struct platform_device *devices[] __initdata = { + &tosascoop_device, + &tosascoop_jc_device, + &tosakbd_device, + &tosa_gpio_keys_device, + &tosaled_device, ++ &tc6393xb_device, + }; + + static void tosa_poweroff(void) +@@ -332,7 +509,7 @@ static void __init tosa_init(void) + arm_pm_restart = tosa_restart; + + pxa_gpio_mode(TOSA_GPIO_ON_RESET | GPIO_IN); +- pxa_gpio_mode(TOSA_GPIO_TC6393_INT | GPIO_IN); ++ pxa_gpio_mode(TOSA_GPIO_TC6393XB_INT | GPIO_IN); + pxa_gpio_mode(TOSA_GPIO_USB_IN | GPIO_IN); + + /* setup sleep mode values */ +diff --git a/include/asm-arm/arch-pxa/irqs.h b/include/asm-arm/arch-pxa/irqs.h +index b76ee6d..bf622d8 100644 +--- a/include/asm-arm/arch-pxa/irqs.h ++++ b/include/asm-arm/arch-pxa/irqs.h +@@ -180,6 +180,7 @@ + #define NR_IRQS (IRQ_LOCOMO_SPI_TEND + 1) + #elif defined(CONFIG_ARCH_LUBBOCK) || \ + defined(CONFIG_MACH_LOGICPD_PXA270) || \ ++ defined(CONFIG_MACH_TOSA) || \ + defined(CONFIG_MACH_MAINSTONE) + #define NR_IRQS (IRQ_BOARD_END) + #else +diff --git a/include/asm-arm/arch-pxa/tosa.h b/include/asm-arm/arch-pxa/tosa.h +index c05e4fa..1b202b2 100644 +--- a/include/asm-arm/arch-pxa/tosa.h ++++ b/include/asm-arm/arch-pxa/tosa.h +@@ -20,11 +20,35 @@ + /* Jacket Scoop */ + #define TOSA_SCOOP_PHYS (PXA_CS5_PHYS + 0x00800000) + ++#define TC6393XB_GPIO(i) (1 << (i)) ++/* ++ * TC6393 GPIOs ++ */ ++#define TOSA_TC6393XB_TG_ON TC6393XB_GPIO(0) ++#define TOSA_TC6393XB_L_MUTE TC6393XB_GPIO(1) ++#define TOSA_TC6393XB_BL_C20MA TC6393XB_GPIO(3) ++#define TOSA_TC6393XB_CARD_VCC_ON TC6393XB_GPIO(4) ++#define TOSA_TC6393XB_CHARGE_OFF TC6393XB_GPIO(6) ++#define TOSA_TC6393XB_CHARGE_OFF_JC TC6393XB_GPIO(7) ++#define TOSA_TC6393XB_BAT0_V_ON TC6393XB_GPIO(9) ++#define TOSA_TC6393XB_BAT1_V_ON TC6393XB_GPIO(10) ++#define TOSA_TC6393XB_BU_CHRG_ON TC6393XB_GPIO(11) ++#define TOSA_TC6393XB_BAT_SW_ON TC6393XB_GPIO(12) ++#define TOSA_TC6393XB_BAT0_TH_ON TC6393XB_GPIO(14) ++#define TOSA_TC6393XB_BAT1_TH_ON TC6393XB_GPIO(15) ++ ++#define TOSA_TC6393XB_GPO_OE (TOSA_TC6393XB_TG_ON | TOSA_TC6393XB_L_MUTE | TOSA_TC6393XB_BL_C20MA | \ ++ TOSA_TC6393XB_CARD_VCC_ON | TOSA_TC6393XB_CHARGE_OFF | \ ++ TOSA_TC6393XB_CHARGE_OFF_JC | TOSA_TC6393XB_BAT0_V_ON | \ ++ TOSA_TC6393XB_BAT1_V_ON | TOSA_TC6393XB_BU_CHRG_ON | \ ++ TOSA_TC6393XB_BAT_SW_ON | TOSA_TC6393XB_BAT0_TH_ON | \ ++ TOSA_TC6393XB_BAT1_TH_ON) ++ + /* + * SCOOP2 internal GPIOs + */ + #define TOSA_SCOOP_PXA_VCORE1 SCOOP_GPCR_PA11 +-#define TOSA_SCOOP_TC6393_REST_IN SCOOP_GPCR_PA12 ++#define TOSA_SCOOP_TC6393XB_REST_IN SCOOP_GPCR_PA12 + #define TOSA_SCOOP_IR_POWERDWN SCOOP_GPCR_PA13 + #define TOSA_SCOOP_SD_WP SCOOP_GPCR_PA14 + #define TOSA_SCOOP_PWR_ON SCOOP_GPCR_PA15 +@@ -34,11 +58,11 @@ + #define TOSA_SCOOP_AC_IN_OL SCOOP_GPCR_PA19 + + /* GPIO Direction 1 : output mode / 0:input mode */ +-#define TOSA_SCOOP_IO_DIR ( TOSA_SCOOP_PXA_VCORE1 | TOSA_SCOOP_TC6393_REST_IN | \ ++#define TOSA_SCOOP_IO_DIR ( TOSA_SCOOP_PXA_VCORE1 | TOSA_SCOOP_TC6393XB_REST_IN | \ + TOSA_SCOOP_IR_POWERDWN | TOSA_SCOOP_PWR_ON | TOSA_SCOOP_AUD_PWR_ON |\ + TOSA_SCOOP_BT_RESET | TOSA_SCOOP_BT_PWR_EN ) + /* GPIO out put level when init 1: Hi */ +-#define TOSA_SCOOP_IO_OUT ( TOSA_SCOOP_TC6393_REST_IN ) ++#define TOSA_SCOOP_IO_OUT ( TOSA_SCOOP_TC6393XB_REST_IN ) + + /* + * SCOOP2 jacket GPIOs +@@ -47,8 +71,8 @@ + #define TOSA_SCOOP_JC_NOTE_LED SCOOP_GPCR_PA12 + #define TOSA_SCOOP_JC_CHRG_ERR_LED SCOOP_GPCR_PA13 + #define TOSA_SCOOP_JC_USB_PULLUP SCOOP_GPCR_PA14 +-#define TOSA_SCOOP_JC_TC6393_SUSPEND SCOOP_GPCR_PA15 +-#define TOSA_SCOOP_JC_TC3693_L3V_ON SCOOP_GPCR_PA16 ++#define TOSA_SCOOP_JC_TC6393XB_SUSPEND SCOOP_GPCR_PA15 ++#define TOSA_SCOOP_JC_TC6393XB_L3V_ON SCOOP_GPCR_PA16 + #define TOSA_SCOOP_JC_WLAN_DETECT SCOOP_GPCR_PA17 + #define TOSA_SCOOP_JC_WLAN_LED SCOOP_GPCR_PA18 + #define TOSA_SCOOP_JC_CARD_LIMIT_SEL SCOOP_GPCR_PA19 +@@ -56,7 +80,7 @@ + /* GPIO Direction 1 : output mode / 0:input mode */ + #define TOSA_SCOOP_JC_IO_DIR ( TOSA_SCOOP_JC_BT_LED | TOSA_SCOOP_JC_NOTE_LED | \ + TOSA_SCOOP_JC_CHRG_ERR_LED | TOSA_SCOOP_JC_USB_PULLUP | \ +- TOSA_SCOOP_JC_TC6393_SUSPEND | TOSA_SCOOP_JC_TC3693_L3V_ON | \ ++ TOSA_SCOOP_JC_TC6393XB_SUSPEND | TOSA_SCOOP_JC_TC6393XB_L3V_ON | \ + TOSA_SCOOP_JC_WLAN_LED | TOSA_SCOOP_JC_CARD_LIMIT_SEL ) + /* GPIO out put level when init 1: Hi */ + #define TOSA_SCOOP_JC_IO_OUT ( 0 ) +@@ -94,13 +118,13 @@ + #define TOSA_GPIO_JACKET_DETECT (7) + #define TOSA_GPIO_nSD_DETECT (9) + #define TOSA_GPIO_nSD_INT (10) +-#define TOSA_GPIO_TC6393_CLK (11) ++#define TOSA_GPIO_TC6393XB_CLK (11) + #define TOSA_GPIO_BAT1_CRG (12) + #define TOSA_GPIO_CF_CD (13) + #define TOSA_GPIO_BAT0_CRG (14) +-#define TOSA_GPIO_TC6393_INT (15) ++#define TOSA_GPIO_TC6393XB_INT (15) + #define TOSA_GPIO_BAT0_LOW (17) +-#define TOSA_GPIO_TC6393_RDY (18) ++#define TOSA_GPIO_TC6393XB_RDY (18) + #define TOSA_GPIO_ON_RESET (19) + #define TOSA_GPIO_EAR_IN (20) + #define TOSA_GPIO_CF_IRQ (21) /* CF slot0 Ready */ +@@ -147,7 +171,7 @@ + #define TOSA_IRQ_GPIO_BAT1_CRG IRQ_GPIO(TOSA_GPIO_BAT1_CRG) + #define TOSA_IRQ_GPIO_CF_CD IRQ_GPIO(TOSA_GPIO_CF_CD) + #define TOSA_IRQ_GPIO_BAT0_CRG IRQ_GPIO(TOSA_GPIO_BAT0_CRG) +-#define TOSA_IRQ_GPIO_TC6393_INT IRQ_GPIO(TOSA_GPIO_TC6393_INT) ++#define TOSA_IRQ_GPIO_TC6393XB_INT IRQ_GPIO(TOSA_GPIO_TC6393XB_INT) + #define TOSA_IRQ_GPIO_BAT0_LOW IRQ_GPIO(TOSA_GPIO_BAT0_LOW) + #define TOSA_IRQ_GPIO_EAR_IN IRQ_GPIO(TOSA_GPIO_EAR_IN) + #define TOSA_IRQ_GPIO_CF_IRQ IRQ_GPIO(TOSA_GPIO_CF_IRQ) +@@ -161,6 +185,7 @@ + + #define TOSA_IRQ_GPIO_MAIN_BAT_LOW IRQ_GPIO(TOSA_GPIO_MAIN_BAT_LOW) + ++extern struct platform_device tc6393xb_device; + extern struct platform_device tosascoop_jc_device; + extern struct platform_device tosascoop_device; + +diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c +index 5504e30..21c51b5 100644 +--- a/sound/soc/pxa/tosa.c ++++ b/sound/soc/pxa/tosa.c +@@ -32,7 +32,6 @@ + #include <sound/soc-dapm.h> + + #include <asm/mach-types.h> +-#include <asm/hardware/tmio.h> + #include <asm/arch/pxa-regs.h> + #include <asm/arch/hardware.h> + #include <asm/arch/audio.h> +@@ -138,10 +137,12 @@ static int tosa_set_spk(struct snd_kcontrol *kcontrol, + /* tosa dapm event handlers */ + static int tosa_hp_event(struct snd_soc_dapm_widget *w, int event) + { ++#if 0 + if (SND_SOC_DAPM_EVENT_ON(event)) + set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_L_MUTE); + else + reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_L_MUTE); ++#endif + return 0; + } + +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0050-tosa-update-for-tc6393xb-gpio.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0050-tosa-update-for-tc6393xb-gpio.patch new file mode 100644 index 0000000000..c9b5ac29d4 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0050-tosa-update-for-tc6393xb-gpio.patch @@ -0,0 +1,99 @@ +From f24c23ba56cdd072b332e8de3e0cff8a31e7e36a Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Wed, 9 Jan 2008 02:03:19 +0300 +Subject: [PATCH 50/64] tosa update for tc6393xb gpio + +Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> +--- + arch/arm/mach-pxa/tosa.c | 6 +++++- + include/asm-arm/arch-pxa/tosa.h | 36 ++++++++++++++++++++++++------------ + 2 files changed, 29 insertions(+), 13 deletions(-) + +diff --git a/arch/arm/mach-pxa/tosa.c b/arch/arm/mach-pxa/tosa.c +index e2eec0f..3e832dc 100644 +--- a/arch/arm/mach-pxa/tosa.c ++++ b/arch/arm/mach-pxa/tosa.c +@@ -35,6 +35,7 @@ + #include <asm/mach-types.h> + #include <asm/hardware.h> + #include <asm/irq.h> ++#include <asm/gpio.h> + #include <asm/system.h> + #include <asm/arch/pxa-regs.h> + #include <asm/arch/irda.h> +@@ -448,10 +449,13 @@ static struct tc6393xb_platform_data tosa_tc6393xb_setup = { + .scr_pll2cr = 0x0cc1, + .scr_ccr = TC6393XB_CCR_UNK1 | TC6393XB_CCR_HCLK_48, + .scr_gper = 0x3300, +- .scr_gpo_dsr = TOSA_TC6393XB_CARD_VCC_ON | TOSA_TC6393XB_CHARGE_OFF_JC, ++ .scr_gpo_dsr = ++ TC6393XB_GPIO_BIT(TOSA_TC6393XB_CARD_VCC_ON) | ++ TC6393XB_GPIO_BIT(TOSA_TC6393XB_CHARGE_OFF_JC), + .scr_gpo_doecr = TOSA_TC6393XB_GPO_OE, + + .irq_base = IRQ_BOARD_START, ++ .gpio_base = TOSA_TC6393XB_GPIO_BASE, + + .enable = tosa_tc6393xb_enable, + .disable = tosa_tc6393xb_disable, +diff --git a/include/asm-arm/arch-pxa/tosa.h b/include/asm-arm/arch-pxa/tosa.h +index 1b202b2..410fa9a 100644 +--- a/include/asm-arm/arch-pxa/tosa.h ++++ b/include/asm-arm/arch-pxa/tosa.h +@@ -20,16 +20,21 @@ + /* Jacket Scoop */ + #define TOSA_SCOOP_PHYS (PXA_CS5_PHYS + 0x00800000) + +-#define TC6393XB_GPIO(i) (1 << (i)) + /* + * TC6393 GPIOs + */ +-#define TOSA_TC6393XB_TG_ON TC6393XB_GPIO(0) +-#define TOSA_TC6393XB_L_MUTE TC6393XB_GPIO(1) +-#define TOSA_TC6393XB_BL_C20MA TC6393XB_GPIO(3) +-#define TOSA_TC6393XB_CARD_VCC_ON TC6393XB_GPIO(4) ++ ++#define TOSA_TC6393XB_GPIO_BASE NR_BUILTIN_GPIO ++ ++#define TC6393XB_GPIO(i) (TOSA_TC6393XB_GPIO_BASE + (i)) ++#define TC6393XB_GPIO_BIT(gpio) (1 << (gpio - TOSA_TC6393XB_GPIO_BASE)) ++ ++#define TOSA_TC6393XB_TG_ON TC6393XB_GPIO(0) ++#define TOSA_TC6393XB_L_MUTE TC6393XB_GPIO(1) ++#define TOSA_TC6393XB_BL_C20MA TC6393XB_GPIO(3) ++#define TOSA_TC6393XB_CARD_VCC_ON TC6393XB_GPIO(4) + #define TOSA_TC6393XB_CHARGE_OFF TC6393XB_GPIO(6) +-#define TOSA_TC6393XB_CHARGE_OFF_JC TC6393XB_GPIO(7) ++#define TOSA_TC6393XB_CHARGE_OFF_JC TC6393XB_GPIO(7) + #define TOSA_TC6393XB_BAT0_V_ON TC6393XB_GPIO(9) + #define TOSA_TC6393XB_BAT1_V_ON TC6393XB_GPIO(10) + #define TOSA_TC6393XB_BU_CHRG_ON TC6393XB_GPIO(11) +@@ -37,12 +42,19 @@ + #define TOSA_TC6393XB_BAT0_TH_ON TC6393XB_GPIO(14) + #define TOSA_TC6393XB_BAT1_TH_ON TC6393XB_GPIO(15) + +-#define TOSA_TC6393XB_GPO_OE (TOSA_TC6393XB_TG_ON | TOSA_TC6393XB_L_MUTE | TOSA_TC6393XB_BL_C20MA | \ +- TOSA_TC6393XB_CARD_VCC_ON | TOSA_TC6393XB_CHARGE_OFF | \ +- TOSA_TC6393XB_CHARGE_OFF_JC | TOSA_TC6393XB_BAT0_V_ON | \ +- TOSA_TC6393XB_BAT1_V_ON | TOSA_TC6393XB_BU_CHRG_ON | \ +- TOSA_TC6393XB_BAT_SW_ON | TOSA_TC6393XB_BAT0_TH_ON | \ +- TOSA_TC6393XB_BAT1_TH_ON) ++#define TOSA_TC6393XB_GPO_OE ( \ ++ TC6393XB_GPIO_BIT(TOSA_TC6393XB_TG_ON) | \ ++ TC6393XB_GPIO_BIT(TOSA_TC6393XB_L_MUTE) | \ ++ TC6393XB_GPIO_BIT(TOSA_TC6393XB_BL_C20MA) | \ ++ TC6393XB_GPIO_BIT(TOSA_TC6393XB_CARD_VCC_ON) | \ ++ TC6393XB_GPIO_BIT(TOSA_TC6393XB_CHARGE_OFF) | \ ++ TC6393XB_GPIO_BIT(TOSA_TC6393XB_CHARGE_OFF_JC) | \ ++ TC6393XB_GPIO_BIT(TOSA_TC6393XB_BAT0_V_ON) | \ ++ TC6393XB_GPIO_BIT(TOSA_TC6393XB_BAT1_V_ON) | \ ++ TC6393XB_GPIO_BIT(TOSA_TC6393XB_BU_CHRG_ON) | \ ++ TC6393XB_GPIO_BIT(TOSA_TC6393XB_BAT_SW_ON) | \ ++ TC6393XB_GPIO_BIT(TOSA_TC6393XB_BAT0_TH_ON) | \ ++ TC6393XB_GPIO_BIT(TOSA_TC6393XB_BAT1_TH_ON)) + + /* + * SCOOP2 internal GPIOs +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0051-fix-sound-soc-pxa-tosa.c-to-new-gpio-api.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0051-fix-sound-soc-pxa-tosa.c-to-new-gpio-api.patch new file mode 100644 index 0000000000..585f1af288 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0051-fix-sound-soc-pxa-tosa.c-to-new-gpio-api.patch @@ -0,0 +1,86 @@ +From 38ef1b452cc3138157b92d02b31cad439d12d0ca Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Wed, 9 Jan 2008 02:03:34 +0300 +Subject: [PATCH 51/64] fix sound/soc/pxa/tosa.c to new gpio api + +Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> +--- + sound/soc/pxa/tosa.c | 33 ++++++++++++++++++++++++++------- + 1 files changed, 26 insertions(+), 7 deletions(-) + +diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c +index 21c51b5..b758de8 100644 +--- a/sound/soc/pxa/tosa.c ++++ b/sound/soc/pxa/tosa.c +@@ -36,6 +36,7 @@ + #include <asm/arch/hardware.h> + #include <asm/arch/audio.h> + #include <asm/arch/tosa.h> ++#include <asm/gpio.h> + + #include "../codecs/wm9712.h" + #include "pxa2xx-pcm.h" +@@ -137,11 +138,11 @@ static int tosa_set_spk(struct snd_kcontrol *kcontrol, + /* tosa dapm event handlers */ + static int tosa_hp_event(struct snd_soc_dapm_widget *w, int event) + { +-#if 0 ++#ifdef CONFIG_MFD_TC6393XB + if (SND_SOC_DAPM_EVENT_ON(event)) +- set_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_L_MUTE); ++ gpio_set_value(TOSA_TC6393XB_L_MUTE, 1); + else +- reset_tc6393_gpio(&tc6393_device.dev,TOSA_TC6393_L_MUTE); ++ gpio_set_value(TOSA_TC6393XB_L_MUTE, 0); + #endif + return 0; + } +@@ -262,16 +263,31 @@ static int __init tosa_init(void) + if (!machine_is_tosa()) + return -ENODEV; + ++#ifdef CONFIG_MFD_TC6393XB ++ ret = gpio_request(TOSA_TC6393XB_L_MUTE, "Headphone Jack"); ++ if (ret) ++ return ret; ++ gpio_direction_output(TOSA_TC6393XB_L_MUTE, 0); ++#endif + tosa_snd_device = platform_device_alloc("soc-audio", -1); +- if (!tosa_snd_device) +- return -ENOMEM; ++ if (!tosa_snd_device) { ++ ret = -ENOMEM; ++ goto err_alloc; ++ } + + platform_set_drvdata(tosa_snd_device, &tosa_snd_devdata); + tosa_snd_devdata.dev = &tosa_snd_device->dev; + ret = platform_device_add(tosa_snd_device); + +- if (ret) +- platform_device_put(tosa_snd_device); ++ if (!ret) ++ return 0; ++ ++ platform_device_put(tosa_snd_device); ++ ++err_alloc: ++#ifdef CONFIG_MFD_TC6393XB ++ gpio_free(TOSA_TC6393XB_L_MUTE); ++#endif + + return ret; + } +@@ -279,6 +295,9 @@ static int __init tosa_init(void) + static void __exit tosa_exit(void) + { + platform_device_unregister(tosa_snd_device); ++#ifdef CONFIG_MFD_TC6393XB ++ gpio_free(TOSA_TC6393XB_L_MUTE); ++#endif + } + + module_init(tosa_init); +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0052-tosa-platform-backlight-support.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0052-tosa-platform-backlight-support.patch new file mode 100644 index 0000000000..ef5263c18e --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0052-tosa-platform-backlight-support.patch @@ -0,0 +1,400 @@ +From c7537657bc33d4ee1616accd0259e160d57c5c1b Mon Sep 17 00:00:00 2001 +From: Ian Molton <spyro@f2s.com> +Date: Wed, 9 Jan 2008 02:05:40 +0300 +Subject: [PATCH 52/64] tosa platform backlight support + +Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> +--- + drivers/video/backlight/Kconfig | 10 + + drivers/video/backlight/Makefile | 1 + + drivers/video/backlight/tosa_bl.c | 345 +++++++++++++++++++++++++++++++++++++ + 3 files changed, 356 insertions(+), 0 deletions(-) + create mode 100644 drivers/video/backlight/tosa_bl.c + +diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig +index 9609a6c..f47a601 100644 +--- a/drivers/video/backlight/Kconfig ++++ b/drivers/video/backlight/Kconfig +@@ -59,6 +59,16 @@ config BACKLIGHT_CORGI + known as the Corgi backlight driver. If you have a Sharp Zaurus + SL-C7xx, SL-Cxx00 or SL-6000x say y. Most users can say n. + ++config BACKLIGHT_TOSA ++ tristate "Sharp Tosa LCD/Backlight Driver (SL-6000)" ++ depends on BACKLIGHT_CLASS_DEVICE && MACH_TOSA ++ default y ++ select I2C ++ select I2C_PXA ++ select PXA_SSP ++ help ++ If you have a Sharp Zaurus SL-6000y enable this driver. ++ + config BACKLIGHT_LOCOMO + tristate "Sharp LOCOMO LCD/Backlight Driver" + depends on BACKLIGHT_CLASS_DEVICE && SHARP_LOCOMO +diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile +index 965a78b..e8a6a7c 100644 +--- a/drivers/video/backlight/Makefile ++++ b/drivers/video/backlight/Makefile +@@ -5,6 +5,7 @@ obj-$(CONFIG_LCD_LTV350QV) += ltv350qv.o + + obj-$(CONFIG_BACKLIGHT_CLASS_DEVICE) += backlight.o + obj-$(CONFIG_BACKLIGHT_CORGI) += corgi_bl.o ++obj-$(CONFIG_BACKLIGHT_TOSA) += tosa_bl.o + obj-$(CONFIG_BACKLIGHT_HP680) += hp680_bl.o + obj-$(CONFIG_BACKLIGHT_LOCOMO) += locomolcd.o + obj-$(CONFIG_BACKLIGHT_PROGEAR) += progear_bl.o +diff --git a/drivers/video/backlight/tosa_bl.c b/drivers/video/backlight/tosa_bl.c +new file mode 100644 +index 0000000..11a89c6 +--- /dev/null ++++ b/drivers/video/backlight/tosa_bl.c +@@ -0,0 +1,345 @@ ++/* ++ * LCD / Backlight control code for Sharp SL-6000x (tosa) ++ * ++ * Copyright (c) 2005 Dirk Opfer ++ * Copyright (c) 2007 Dmitry Baryshkov ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/i2c.h> ++#include <linux/backlight.h> ++#include <linux/platform_device.h> ++#include <linux/delay.h> ++#include <linux/fb.h> ++#include <linux/mfd/tc6393xb.h> ++ ++#include <asm/hardware/scoop.h> ++#include <asm/mach/sharpsl_param.h> ++#include <asm/arch/ssp.h> ++#include <asm/arch/pxa-regs.h> ++#include <asm/arch/tosa.h> ++#include <asm/gpio.h> ++ ++#define DAC_BASE 0x4e ++#define DAC_CH1 0 ++#define DAC_CH2 1 ++ ++#define TG_REG0_VQV 0x0001 ++#define TG_REG0_COLOR 0x0002 ++#define TG_REG0_UD 0x0004 ++#define TG_REG0_LR 0x0008 ++#define COMADJ_DEFAULT 97 ++ ++static unsigned short normal_i2c[] = { DAC_BASE, I2C_CLIENT_END }; ++I2C_CLIENT_INSMOD; ++ ++struct tosa_bl_data { ++ struct i2c_client client; ++ ++ int comadj; ++ spinlock_t nssp_lock; ++ struct ssp_dev nssp_dev; ++ struct ssp_state nssp_state; ++ ++ struct backlight_device *bl_dev; ++}; ++ ++static struct i2c_driver tosa_bl_driver; ++ ++static void pxa_nssp_output(struct tosa_bl_data *data, unsigned char reg, unsigned char value) ++{ ++ unsigned long flag; ++ u32 dummy; ++ u32 dat = ( ((reg << 5) & 0xe0) | (value & 0x1f) ); ++ spin_lock_irqsave(&data->nssp_lock, flag); ++ ++ ssp_config(&data->nssp_dev, (SSCR0_Motorola | (SSCR0_DSS & 0x07 )), 0, 0, SSCR0_SerClkDiv(128)); ++ ssp_enable(&data->nssp_dev); ++ ++ ssp_write_word(&data->nssp_dev,dat); ++ ++ /* Read null data back from device to prevent SSP overflow */ ++ ssp_read_word(&data->nssp_dev, &dummy); ++ ssp_disable(&data->nssp_dev); ++ spin_unlock_irqrestore(&data->nssp_lock, flag); ++ ++} ++ ++static void tosa_set_backlight(struct tosa_bl_data *data, int brightness) ++{ ++ /* SetBacklightDuty */ ++ i2c_smbus_write_byte_data(&data->client, DAC_CH2, (unsigned char)brightness); ++ ++ /* SetBacklightVR */ ++ if (brightness) ++ gpio_set_value(TOSA_TC6393XB_BL_C20MA, 1); ++ else ++ gpio_set_value(TOSA_TC6393XB_BL_C20MA, 0); ++ ++ /* bl_enable GP04=1 otherwise GP04=0*/ ++ pxa_nssp_output(data, TG_GPODR2, brightness ? 0x01 : 0x00); ++} ++ ++static void tosa_lcd_tg_init(struct tosa_bl_data *data) ++{ ++ dev_dbg(&data->bl_dev->dev, "tosa_lcd_init\n"); ++ ++ /* L3V On */ ++ set_scoop_gpio( &tosascoop_jc_device.dev,TOSA_SCOOP_JC_TC6393XB_L3V_ON); ++ mdelay(60); ++ ++ /* TG On */ ++ gpio_set_value(TOSA_TC6393XB_TG_ON, 0); ++ mdelay(60); ++ ++ pxa_nssp_output(data, TG_TPOSCTL,0x00); /* delayed 0clk TCTL signal for VGA */ ++ pxa_nssp_output(data, TG_GPOSR,0x02); /* GPOS0=powercontrol, GPOS1=GPIO, GPOS2=TCTL */ ++} ++ ++static void tosa_lcd_tg_on(struct tosa_bl_data *data/*, const struct fb_videomode *mode*/) ++{ ++ const int value = TG_REG0_COLOR | TG_REG0_UD | TG_REG0_LR; ++ ++ tosa_lcd_tg_init(data); ++ ++ dev_dbg(&data->bl_dev->dev, "tosa_lcd_on\n"); ++ pxa_nssp_output(data, TG_PNLCTL, value | (/*mode->yres == 320 ? 0 : */ TG_REG0_VQV)); ++ ++ /* TG LCD pannel power up */ ++ pxa_nssp_output(data, TG_PINICTL,0x4); ++ mdelay(50); ++ ++ /* TG LCD GVSS */ ++ pxa_nssp_output(data, TG_PINICTL,0x0); ++ mdelay(50); ++ ++ /* set common voltage */ ++ i2c_smbus_write_byte_data(&data->client, DAC_CH1, data->comadj); ++} ++ ++static void tosa_lcd_tg_off(struct tosa_bl_data *data) ++{ ++ tosa_set_backlight(data, 0); ++ dev_dbg(&data->bl_dev->dev, "tosa_lcd_off\n"); ++ /* TG LCD VHSA off */ ++ pxa_nssp_output(data, TG_PINICTL,0x4); ++ mdelay(50); ++ ++ /* TG LCD signal off */ ++ pxa_nssp_output(data, TG_PINICTL,0x6); ++ mdelay(50); ++ ++ /* TG Off */ ++ gpio_set_value(TOSA_TC6393XB_TG_ON, 1); ++ mdelay(100); ++ ++ /* L3V Off */ ++ reset_scoop_gpio( &tosascoop_jc_device.dev,TOSA_SCOOP_JC_TC6393XB_L3V_ON); ++} ++ ++ ++static int tosa_bl_update_status(struct backlight_device *dev) ++{ ++ struct backlight_properties *props = &dev->props; ++ struct tosa_bl_data *data = dev_get_drvdata(&dev->dev); ++ int new_power = max(props->power, props->fb_blank); ++ ++ tosa_set_backlight(data, props->brightness); ++ ++ if (new_power) ++ tosa_lcd_tg_off(data); ++ else ++ tosa_lcd_tg_on(data); ++ ++ return 0; ++} ++ ++static int tosa_bl_get_brightness(struct backlight_device *dev) ++{ ++ struct backlight_properties *props = &dev->props; ++ ++ return props->brightness; ++} ++ ++static struct backlight_ops tosa_bl_ops = { ++ .get_brightness = tosa_bl_get_brightness, ++ .update_status = tosa_bl_update_status, ++}; ++ ++static int tosa_bl_detect_client(struct i2c_adapter *adapter, int address, ++ int kind) ++{ ++ int err = 0; ++ struct i2c_client *client; ++ struct tosa_bl_data *data; ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA )) ++ goto out; ++ ++ if (!(data = kzalloc(sizeof(struct tosa_bl_data), GFP_KERNEL))) { ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ client = &data->client; ++ i2c_set_clientdata(client, data); ++ ++ client->addr = address; ++ client->adapter = adapter; ++ client->driver = &tosa_bl_driver; ++ ++ strlcpy(client->name, "tosa_bl", I2C_NAME_SIZE); ++ ++ spin_lock_init(&data->nssp_lock); ++ data->comadj = sharpsl_param.comadj == -1 ? COMADJ_DEFAULT : sharpsl_param.comadj; ++ ++ err = gpio_request(TOSA_TC6393XB_BL_C20MA, "backlight"); ++ if (err) { ++ dev_dbg(&data->bl_dev->dev, "Unable to request gpio!\n"); ++ goto err_gpio_bl; ++ } ++ ++ err = gpio_request(TOSA_TC6393XB_TG_ON, "tg"); ++ if (err) { ++ dev_dbg(&data->bl_dev->dev, "Unable to request gpio!\n"); ++ goto err_gpio_tg; ++ } ++ ++ err = ssp_init(&data->nssp_dev,2,0); ++ if (err) { ++ dev_err(&data->bl_dev->dev, "Unable to register NSSP handler!\n"); ++ goto err_ssp_init; ++ } ++ ++ /* Tell the i2c layer a new client has arrived */ ++ err = i2c_attach_client(client); ++ if (err) ++ goto err_i2c_attach; ++ ++ gpio_direction_output(TOSA_TC6393XB_BL_C20MA, 0); ++ gpio_direction_output(TOSA_TC6393XB_TG_ON, 1); ++ ++ tosa_lcd_tg_init(data); ++ ++ data->bl_dev = backlight_device_register("tosa_bl", ++ &client->dev, data, &tosa_bl_ops); ++ if (err) ++ goto err_bl_register; ++ ++ data->bl_dev->props.brightness = 69; ++ data->bl_dev->props.max_brightness = 255; ++ data->bl_dev->props.power = FB_BLANK_UNBLANK; ++ backlight_update_status(data->bl_dev); ++ ++ ++ return 0; ++ ++err_bl_register: ++ tosa_set_backlight(data, 0); ++ tosa_lcd_tg_off(data); ++ ++ err = i2c_detach_client(client); ++ if (err) ++ return err; ++err_i2c_attach: ++ ssp_exit(&data->nssp_dev); ++err_ssp_init: ++ gpio_free(TOSA_TC6393XB_TG_ON); ++err_gpio_tg: ++ gpio_free(TOSA_TC6393XB_BL_C20MA); ++err_gpio_bl: ++ kfree(data); ++out: ++ return err; ++} ++ ++static int tosa_bl_detach_client(struct i2c_client *client) ++{ ++ int err = 0; ++ struct tosa_bl_data *data = i2c_get_clientdata(client); ++ ++ backlight_device_unregister(data->bl_dev); ++ ++ tosa_set_backlight(data, 0); ++ tosa_lcd_tg_off(data); ++ ++ /* Try to detach the client from i2c space */ ++ if ((err = i2c_detach_client(client))) ++ return err; ++ ++ ssp_exit(&data->nssp_dev); ++ ++ gpio_free(TOSA_TC6393XB_TG_ON); ++ gpio_free(TOSA_TC6393XB_BL_C20MA); ++ ++ kfree(data); ++ ++ return err; ++} ++ ++#ifdef CONFIG_PM ++static int tosa_bl_suspend(struct i2c_client *client, pm_message_t mesg) ++{ ++ struct tosa_bl_data *data = i2c_get_clientdata(client); ++ ++ tosa_lcd_tg_off(data); ++ ssp_flush(&data->nssp_dev); ++ ssp_save_state(&data->nssp_dev,&data->nssp_state); ++ ++ return 0; ++} ++ ++static int tosa_bl_resume(struct i2c_client *client) ++{ ++ struct tosa_bl_data *data = i2c_get_clientdata(client); ++ ++ ssp_restore_state(&data->nssp_dev,&data->nssp_state); ++ ssp_enable(&data->nssp_dev); ++ tosa_bl_update_status(data->bl_dev); ++ ++ return 0; ++} ++#else ++#define tosa_bl_suspend NULL ++#define tosa_bl_resume NULL ++#endif ++ ++static int tosa_bl_attach_adapter(struct i2c_adapter *adapter) ++{ ++ return i2c_probe(adapter, &addr_data, &tosa_bl_detect_client); ++} ++ ++static struct i2c_driver tosa_bl_driver = { ++ .driver = { ++ .name = "tosa_bl", ++ }, ++ ++ .attach_adapter = tosa_bl_attach_adapter, ++ .detach_client = tosa_bl_detach_client, ++ ++ .suspend = tosa_bl_suspend, ++ .resume = tosa_bl_resume, ++}; ++ ++static int __init tosa_bl_init(void) ++{ ++ return i2c_add_driver(&tosa_bl_driver); ++} ++ ++static void __exit tosa_bl_cleanup (void) ++{ ++ i2c_del_driver(&tosa_bl_driver); ++} ++ ++module_init(tosa_bl_init); ++module_exit(tosa_bl_cleanup); ++ ++MODULE_DESCRIPTION("Tosa LCD device"); ++MODULE_AUTHOR("Dirk Opfer, Dmitry Baryshkov"); ++MODULE_LICENSE("GPL v2"); +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0053-sound-soc-codecs-wm9712.c-28.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0053-sound-soc-codecs-wm9712.c-28.patch new file mode 100644 index 0000000000..0675342508 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0053-sound-soc-codecs-wm9712.c-28.patch @@ -0,0 +1,56 @@ +From 47616d22f8f303dfd66cf3b9125af212194a0f3c Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Wed, 9 Jan 2008 02:08:17 +0300 +Subject: [PATCH 53/64] sound/soc/codecs/wm9712.c | 28 ++++++++++++++++++---------- + 1 file changed, 18 insertions(+), 10 deletions(-) + +Index: git/sound/soc/codecs/wm9712.c +=================================================================== +--- + sound/soc/codecs/wm9712.c | 28 ++++++++++++++++++---------- + 1 files changed, 18 insertions(+), 10 deletions(-) + +diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c +index 986b5d5..dfb31e1 100644 +--- a/sound/soc/codecs/wm9712.c ++++ b/sound/soc/codecs/wm9712.c +@@ -606,18 +606,26 @@ static int wm9712_dapm_event(struct snd_soc_codec *codec, int event) + + static int wm9712_reset(struct snd_soc_codec *codec, int try_warm) + { +- if (try_warm && soc_ac97_ops.warm_reset) { +- soc_ac97_ops.warm_reset(codec->ac97); +- if (!(ac97_read(codec, 0) & 0x8000)) +- return 1; +- } ++ int retry = 3; + +- soc_ac97_ops.reset(codec->ac97); +- if (ac97_read(codec, 0) & 0x8000) +- goto err; +- return 0; ++ while (retry--) ++ { ++ if(try_warm && soc_ac97_ops.warm_reset) { ++ soc_ac97_ops.warm_reset(codec->ac97); ++ if(ac97_read(codec, 0) & 0x8000) ++ continue; ++ else ++ return 1; ++ } ++ ++ soc_ac97_ops.reset(codec->ac97); ++ if(ac97_read(codec, 0) & 0x8000) ++ continue; ++ else ++ return 0; ++ ++ } + +-err: + printk(KERN_ERR "WM9712 AC97 reset failed\n"); + return -EIO; + } +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0054-sound-soc-codecs-wm9712.c-2.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0054-sound-soc-codecs-wm9712.c-2.patch new file mode 100644 index 0000000000..be7300ab24 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0054-sound-soc-codecs-wm9712.c-2.patch @@ -0,0 +1,28 @@ +From 08fbae2307163b3f0c3b704c4b00a9447752a45e Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Thu, 10 Jan 2008 17:56:58 +0300 +Subject: [PATCH 54/64] sound/soc/codecs/wm9712.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +Index: git/sound/soc/codecs/wm9712.c +=================================================================== +--- + sound/soc/codecs/wm9712.c | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c +index dfb31e1..a3d9f96 100644 +--- a/sound/soc/codecs/wm9712.c ++++ b/sound/soc/codecs/wm9712.c +@@ -647,7 +647,7 @@ static int wm9712_soc_resume(struct platform_device *pdev) + int i, ret; + u16 *cache = codec->reg_cache; + +- ret = wm9712_reset(codec, 1); ++ ret = wm9712_reset(codec, 0); + if (ret < 0){ + printk(KERN_ERR "could not reset AC97 codec\n"); + return ret; +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0055-Add-GPIO_POWERON-to-the-list-of-devices-that-we-supp.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0055-Add-GPIO_POWERON-to-the-list-of-devices-that-we-supp.patch new file mode 100644 index 0000000000..5bf691cbda --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0055-Add-GPIO_POWERON-to-the-list-of-devices-that-we-supp.patch @@ -0,0 +1,30 @@ +From bee8b808445a53a7dbb6c15a27064f14dec410c5 Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Sun, 20 Jan 2008 03:01:41 +0300 +Subject: [PATCH 55/64] Add GPIO_POWERON to the list of devices that we support resume on. + +Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> +--- + arch/arm/mach-pxa/tosa.c | 6 +++--- + 1 files changed, 3 insertions(+), 3 deletions(-) + +diff --git a/arch/arm/mach-pxa/tosa.c b/arch/arm/mach-pxa/tosa.c +index 3e832dc..d1cf3dc 100644 +--- a/arch/arm/mach-pxa/tosa.c ++++ b/arch/arm/mach-pxa/tosa.c +@@ -517,9 +517,9 @@ static void __init tosa_init(void) + pxa_gpio_mode(TOSA_GPIO_USB_IN | GPIO_IN); + + /* setup sleep mode values */ +- PWER = 0x00000002; +- PFER = 0x00000000; +- PRER = 0x00000002; ++ PWER = BIT(TOSA_GPIO_POWERON) | BIT(TOSA_GPIO_RESET); ++ PFER = 0; ++ PRER = BIT(TOSA_GPIO_POWERON) | BIT(TOSA_GPIO_RESET); + PGSR0 = 0x00000000; + PGSR1 = 0x00FF0002; + PGSR2 = 0x00014000; +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0056-Support-resetting-by-asserting-GPIO-pin.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0056-Support-resetting-by-asserting-GPIO-pin.patch new file mode 100644 index 0000000000..99220f9200 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0056-Support-resetting-by-asserting-GPIO-pin.patch @@ -0,0 +1,126 @@ +From e039614a0ce6df645f8fa4cbe32e4b21fe46a288 Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Sun, 20 Jan 2008 02:44:03 +0300 +Subject: [PATCH 56/64] Support resetting by asserting GPIO pin + +This adds support for resetting via assertion of GPIO pin. +This e.g. is used on Sharp Zaurus SL-6000. + +Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> +--- + arch/arm/mach-pxa/gpio.c | 43 +++++++++++++++++++++++++++++++++++++ + arch/arm/mach-pxa/pm.c | 4 +- + include/asm-arm/arch-pxa/system.h | 10 ++++++++ + 3 files changed, 55 insertions(+), 2 deletions(-) + +diff --git a/arch/arm/mach-pxa/gpio.c b/arch/arm/mach-pxa/gpio.c +index 8638dd7..589da3b 100644 +--- a/arch/arm/mach-pxa/gpio.c ++++ b/arch/arm/mach-pxa/gpio.c +@@ -19,6 +19,7 @@ + #include <asm/hardware.h> + #include <asm/io.h> + #include <asm/arch/pxa-regs.h> ++#include <asm/arch/system.h> + + #include "generic.h" + +@@ -194,4 +195,46 @@ void __init pxa_init_gpio(int gpio_nr) + pxa_gpio_chip[i/32].chip.ngpio = gpio_nr - i; + gpiochip_add(&pxa_gpio_chip[i/32].chip); + } ++ ++ if (reset_gpio < gpio_nr) ++ init_reset_gpio(); ++} ++ ++int reset_gpio = -1; ++static int __init reset_gpio_setup(char *str) ++{ ++ if (get_option(&str, &reset_gpio) != 1) { ++ printk(KERN_ERR "reset_gpio: bad value secified"); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++__setup("reset_gpio=", reset_gpio_setup); ++ ++int init_reset_gpio(void) ++{ ++ int rc = 0; ++ if (reset_gpio == -1) ++ goto out; ++ ++ rc = gpio_request(reset_gpio, "reset generator"); ++ if (rc) { ++ printk(KERN_ERR "Can't request reset_gpio\n"); ++ goto out; ++ } ++ ++ rc = gpio_direction_input(reset_gpio); ++ if (rc) { ++ printk(KERN_ERR "Can't configure reset_gpio for input\n"); ++ gpio_free(reset_gpio); ++ goto out; ++ } ++ ++out: ++ if (rc) ++ reset_gpio = -1; ++ ++ return rc; + } +diff --git a/arch/arm/mach-pxa/pm.c b/arch/arm/mach-pxa/pm.c +index a941c71..64f37e5 100644 +--- a/arch/arm/mach-pxa/pm.c ++++ b/arch/arm/mach-pxa/pm.c +@@ -40,8 +40,8 @@ int pxa_pm_enter(suspend_state_t state) + + pxa_cpu_pm_fns->save(sleep_save); + +- /* Clear sleep reset status */ +- RCSR = RCSR_SMR; ++ /* Clear reset status */ ++ RCSR = RCSR_HWR | RCSR_WDR | RCSR_SMR | RCSR_GPR; + + /* before sleeping, calculate and save a checksum */ + for (i = 0; i < pxa_cpu_pm_fns->save_size - 1; i++) +diff --git a/include/asm-arm/arch-pxa/system.h b/include/asm-arm/arch-pxa/system.h +index 1d56a3e..c075018 100644 +--- a/include/asm-arm/arch-pxa/system.h ++++ b/include/asm-arm/arch-pxa/system.h +@@ -11,6 +11,7 @@ + */ + + #include <asm/proc-fns.h> ++#include <asm/gpio.h> + #include "hardware.h" + #include "pxa-regs.h" + +@@ -19,12 +20,21 @@ static inline void arch_idle(void) + cpu_do_idle(); + } + ++extern int reset_gpio; ++ ++int init_reset_gpio(void); + + static inline void arch_reset(char mode) + { ++ RCSR = RCSR_HWR | RCSR_WDR | RCSR_SMR | RCSR_GPR; ++ + if (mode == 's') { + /* Jump into ROM at address 0 */ + cpu_reset(0); ++ } else if (mode == 'g' && reset_gpio != -1) { ++ /* Use GPIO reset */ ++ gpio_direction_output(reset_gpio, 0); ++ gpio_set_value(reset_gpio, 1); + } else { + /* Initialize the watchdog and let it fire */ + OWER = OWER_WME; +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0057-Clean-up-tosa-resetting.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0057-Clean-up-tosa-resetting.patch new file mode 100644 index 0000000000..441e1bba75 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0057-Clean-up-tosa-resetting.patch @@ -0,0 +1,70 @@ +From a6f03929fa4d20cef339dbed7ef5cd1e040d0548 Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Sun, 20 Jan 2008 02:48:07 +0300 +Subject: [PATCH 57/64] Clean up tosa resetting + +Use new gpio-assertion reset. + +Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> +--- + arch/arm/mach-pxa/tosa.c | 16 +++++++--------- + 1 files changed, 7 insertions(+), 9 deletions(-) + +diff --git a/arch/arm/mach-pxa/tosa.c b/arch/arm/mach-pxa/tosa.c +index d1cf3dc..2b4aef7 100644 +--- a/arch/arm/mach-pxa/tosa.c ++++ b/arch/arm/mach-pxa/tosa.c +@@ -41,6 +41,8 @@ + #include <asm/arch/irda.h> + #include <asm/arch/mmc.h> + #include <asm/arch/udc.h> ++#include <asm/arch/pm.h> ++#include <asm/arch/system.h> + + #include <asm/mach/arch.h> + #include <asm/mach/map.h> +@@ -489,13 +491,7 @@ static struct platform_device *devices[] __initdata = { + + static void tosa_poweroff(void) + { +- RCSR = RCSR_HWR | RCSR_WDR | RCSR_SMR | RCSR_GPR; +- +- pxa_gpio_mode(TOSA_GPIO_ON_RESET | GPIO_OUT); +- GPSR(TOSA_GPIO_ON_RESET) = GPIO_bit(TOSA_GPIO_ON_RESET); +- +- mdelay(1000); +- arm_machine_restart('h'); ++ arm_machine_restart('g'); + } + + static void tosa_restart(char mode) +@@ -504,7 +500,7 @@ static void tosa_restart(char mode) + if((MSC0 & 0xffff0000) == 0x7ff00000) + MSC0 = (MSC0 & 0xffff) | 0x7ee00000; + +- tosa_poweroff(); ++ arm_machine_restart('g'); + } + + static void __init tosa_init(void) +@@ -512,7 +508,6 @@ static void __init tosa_init(void) + pm_power_off = tosa_poweroff; + arm_pm_restart = tosa_restart; + +- pxa_gpio_mode(TOSA_GPIO_ON_RESET | GPIO_IN); + pxa_gpio_mode(TOSA_GPIO_TC6393XB_INT | GPIO_IN); + pxa_gpio_mode(TOSA_GPIO_USB_IN | GPIO_IN); + +@@ -544,6 +539,9 @@ static void __init fixup_tosa(struct machine_desc *desc, + mi->bank[0].start = 0xa0000000; + mi->bank[0].node = 0; + mi->bank[0].size = (64*1024*1024); ++ ++ if (reset_gpio == -1) ++ reset_gpio = TOSA_GPIO_ON_RESET; + } + + MACHINE_START(TOSA, "SHARP Tosa") +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0058-Fix-tosakbd-suspend.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0058-Fix-tosakbd-suspend.patch new file mode 100644 index 0000000000..e965857dff --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0058-Fix-tosakbd-suspend.patch @@ -0,0 +1,27 @@ +From 8b57c409802e5feef64c4bb7659570e06558c0f2 Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Sun, 20 Jan 2008 02:24:43 +0300 +Subject: [PATCH 58/64] Fix tosakbd suspend + +Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> +--- + drivers/input/keyboard/tosakbd.c | 3 +++ + 1 files changed, 3 insertions(+), 0 deletions(-) + +diff --git a/drivers/input/keyboard/tosakbd.c b/drivers/input/keyboard/tosakbd.c +index 3884d1e..306cbe8 100644 +--- a/drivers/input/keyboard/tosakbd.c ++++ b/drivers/input/keyboard/tosakbd.c +@@ -210,6 +210,9 @@ static int tosakbd_suspend(struct platform_device *dev, pm_message_t state) + + del_timer_sync(&tosakbd->timer); + ++ PGSR1 = (PGSR1 & ~TOSA_GPIO_LOW_STROBE_BIT); ++ PGSR2 = (PGSR2 & ~TOSA_GPIO_HIGH_STROBE_BIT); ++ + return 0; + } + +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0059-patch-tosa-wakeup-test.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0059-patch-tosa-wakeup-test.patch new file mode 100644 index 0000000000..812b5bad41 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0059-patch-tosa-wakeup-test.patch @@ -0,0 +1,46 @@ +From 00f6e9b946d1f653fc776d71c86a1f6a7534cd1d Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Fri, 25 Jan 2008 19:16:20 +0300 +Subject: [PATCH 59/64] patch tosa-wakeup-test + +--- + arch/arm/mach-pxa/tosa.c | 18 +++++++++++++++++- + 1 files changed, 17 insertions(+), 1 deletions(-) + +diff --git a/arch/arm/mach-pxa/tosa.c b/arch/arm/mach-pxa/tosa.c +index 2b4aef7..7008919 100644 +--- a/arch/arm/mach-pxa/tosa.c ++++ b/arch/arm/mach-pxa/tosa.c +@@ -260,12 +260,28 @@ static struct platform_device tosakbd_device = { + }; + + static struct gpio_keys_button tosa_gpio_keys[] = { ++ /* ++ * Two following keys are directly tied to "ON" button of tosa. Why? ++ * The first one can be used as a wakeup source, the second can't: ++ * it's outside of permitted area. ++ */ ++ { ++ .type = EV_PWR, ++ .code = KEY_RESERVED, ++ .gpio = TOSA_GPIO_POWERON, ++ .desc = "Poweron", ++ .wakeup = 1, ++ .active_low = 1, ++ }, + { + .type = EV_PWR, + .code = KEY_SUSPEND, + .gpio = TOSA_GPIO_ON_KEY, + .desc = "On key", +- .wakeup = 1, ++ /* ++ * can't be used as wakeup ++ * .wakeup = 1, ++ */ + .active_low = 1, + }, + { +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0060-Add-support-for-power_supply-on-tosa.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0060-Add-support-for-power_supply-on-tosa.patch new file mode 100644 index 0000000000..f7420de040 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0060-Add-support-for-power_supply-on-tosa.patch @@ -0,0 +1,623 @@ +From f6ec15733eb55e851c8ad19c2143d425558f6044 Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Mon, 4 Feb 2008 20:11:58 +0300 +Subject: [PATCH 60/64] Add support for power_supply on tosa + +Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> +--- + arch/arm/mach-pxa/Makefile | 2 +- + arch/arm/mach-pxa/tosa_power.c | 82 +++++++ + drivers/leds/leds-tosa.c | 2 +- + drivers/power/Kconfig | 7 + + drivers/power/Makefile | 1 + + drivers/power/tosa_battery.c | 458 ++++++++++++++++++++++++++++++++++++++++ + 6 files changed, 550 insertions(+), 2 deletions(-) + create mode 100644 arch/arm/mach-pxa/tosa_power.c + create mode 100644 drivers/power/tosa_battery.c + +diff --git a/arch/arm/mach-pxa/Makefile b/arch/arm/mach-pxa/Makefile +index f276d24..2b68254 100644 +--- a/arch/arm/mach-pxa/Makefile ++++ b/arch/arm/mach-pxa/Makefile +@@ -21,7 +21,7 @@ obj-$(CONFIG_PXA_SHARP_C7xx) += corgi.o corgi_ssp.o corgi_lcd.o sharpsl_pm.o cor + obj-$(CONFIG_PXA_SHARP_Cxx00) += spitz.o corgi_ssp.o corgi_lcd.o sharpsl_pm.o spitz_pm.o + obj-$(CONFIG_MACH_AKITA) += akita-ioexp.o + obj-$(CONFIG_MACH_POODLE) += poodle.o corgi_ssp.o sharpsl_pm.o poodle_pm.o +-obj-$(CONFIG_MACH_TOSA) += tosa.o ++obj-$(CONFIG_MACH_TOSA) += tosa.o tosa_power.o + obj-$(CONFIG_MACH_EM_X270) += em-x270.o + + ifeq ($(CONFIG_MACH_ZYLONITE),y) +diff --git a/arch/arm/mach-pxa/tosa_power.c b/arch/arm/mach-pxa/tosa_power.c +new file mode 100644 +index 0000000..61ca7dc +--- /dev/null ++++ b/arch/arm/mach-pxa/tosa_power.c +@@ -0,0 +1,82 @@ ++/* ++ * Battery and Power Management code for the Sharp SL-6000x ++ * ++ * Copyright (c) 2005 Dirk Opfer ++ * Copyright (c) 2008 Dmitry Baryshkov ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/power_supply.h> ++#include <linux/pda_power.h> ++#include <linux/platform_device.h> ++#include <linux/interrupt.h> ++ ++#include <asm/arch/tosa.h> ++#include <asm/gpio.h> ++ ++static int tosa_power_ac_online(void) ++{ ++ return gpio_get_value(TOSA_GPIO_AC_IN) == 0; ++} ++ ++static char *tosa_ac_supplied_to[] = { ++ "main-battery", ++ "backup-battery", ++ "jacket-battery", ++}; ++ ++static struct pda_power_pdata tosa_power_data = { ++ .is_ac_online = tosa_power_ac_online, ++ .supplied_to = tosa_ac_supplied_to, ++ .num_supplicants = ARRAY_SIZE(tosa_ac_supplied_to), ++}; ++ ++static struct resource tosa_power_resource[] = { ++ { ++ .name = "ac", ++ .start = gpio_to_irq(TOSA_GPIO_AC_IN), ++ .end = gpio_to_irq(TOSA_GPIO_AC_IN), ++ .flags = IORESOURCE_IRQ | ++ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, ++ }, ++}; ++ ++static struct platform_device tosa_power_device = { ++ .name = "pda-power", ++ .id = -1, ++ .dev.platform_data = &tosa_power_data, ++ .resource = tosa_power_resource, ++ .num_resources = ARRAY_SIZE(tosa_power_resource), ++}; ++ ++static int __init tosa_power_init(void) ++{ ++ int ret = gpio_request(TOSA_GPIO_AC_IN, "ac"); ++ if (ret) ++ goto err_gpio_req; ++ ++ ret = gpio_direction_input(TOSA_GPIO_AC_IN); ++ if (ret) ++ goto err_gpio_in; ++ ++ return platform_device_register(&tosa_power_device); ++ ++err_gpio_in: ++ gpio_free(TOSA_GPIO_AC_IN); ++err_gpio_req: ++ return ret; ++} ++ ++static void __exit tosa_power_exit(void) ++{ ++ platform_device_unregister(&tosa_power_device); ++ gpio_free(TOSA_GPIO_AC_IN); ++} ++ ++module_init(tosa_power_init); ++module_exit(tosa_power_exit); +diff --git a/drivers/leds/leds-tosa.c b/drivers/leds/leds-tosa.c +index fb2416a..b4498b5 100644 +--- a/drivers/leds/leds-tosa.c ++++ b/drivers/leds/leds-tosa.c +@@ -46,7 +46,7 @@ static void tosaled_green_set(struct led_classdev *led_cdev, + + static struct led_classdev tosa_amber_led = { + .name = "tosa:amber", +- .default_trigger = "sharpsl-charge", ++ .default_trigger = "main-battery-charging", + .brightness_set = tosaled_amber_set, + }; + +diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig +index 58c806e..e3a9c37 100644 +--- a/drivers/power/Kconfig ++++ b/drivers/power/Kconfig +@@ -49,4 +49,11 @@ config BATTERY_OLPC + help + Say Y to enable support for the battery on the OLPC laptop. + ++config BATTERY_TOSA ++ tristate "Sharp SL-6000 (tosa) battery" ++ depends on MACH_TOSA && MFD_TC6393XB ++ help ++ Say Y to enable support for the battery on the Sharp Zaurus ++ SL-6000 (tosa) models. ++ + endif # POWER_SUPPLY +diff --git a/drivers/power/Makefile b/drivers/power/Makefile +index 6413ded..1e408fa 100644 +--- a/drivers/power/Makefile ++++ b/drivers/power/Makefile +@@ -20,3 +20,4 @@ obj-$(CONFIG_APM_POWER) += apm_power.o + obj-$(CONFIG_BATTERY_DS2760) += ds2760_battery.o + obj-$(CONFIG_BATTERY_PMU) += pmu_battery.o + obj-$(CONFIG_BATTERY_OLPC) += olpc_battery.o ++obj-$(CONFIG_BATTERY_TOSA) += tosa_battery.o +diff --git a/drivers/power/tosa_battery.c b/drivers/power/tosa_battery.c +new file mode 100644 +index 0000000..b0fd2f2 +--- /dev/null ++++ b/drivers/power/tosa_battery.c +@@ -0,0 +1,458 @@ ++/* ++ * Battery and Power Management code for the Sharp SL-6000x ++ * ++ * Copyright (c) 2005 Dirk Opfer ++ * Copyright (c) 2008 Dmitry Baryshkov ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/power_supply.h> ++#include <linux/wm97xx.h> ++#include <linux/delay.h> ++#include <linux/spinlock.h> ++#include <linux/interrupt.h> ++ ++#include <asm/mach-types.h> ++#include <asm/gpio.h> ++#include <asm/arch/tosa.h> ++ ++#define BAT_TO_VOLTS(v) ((v) * 1000000 / 414) ++#define BU_TO_VOLTS(v) ((v) * 1000000 / 1266) ++/* ++ * It's pretty strange value, but that's roughly what I get from ++ * zaurus maintainer menu ++ */ ++//#define BAT_TO_TEMP(t) ((t) * 10000/2000) ++#define BAT_TO_TEMP(t) (t) ++ ++static DEFINE_MUTEX(bat_lock); /* protects gpio pins */ ++static struct work_struct bat_work; ++ ++struct tosa_bat { ++ int status; ++ struct power_supply psy; ++ int full_chrg; ++ ++ struct mutex work_lock; /* protects data */ ++ bool (*is_present)(struct tosa_bat *bat); ++ int gpio_full; ++ int gpio_charge_off; ++ int gpio_bat; ++ int adc_bat; ++ int gpio_temp; ++ int adc_temp; ++}; ++ ++static struct tosa_bat tosa_bat_main; ++static struct tosa_bat tosa_bat_jacket; ++ ++static enum power_supply_property tosa_bat_main_props[] = { ++ POWER_SUPPLY_PROP_STATUS, ++ POWER_SUPPLY_PROP_TECHNOLOGY, ++ POWER_SUPPLY_PROP_VOLTAGE_NOW, ++ POWER_SUPPLY_PROP_VOLTAGE_MAX, ++ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, ++ POWER_SUPPLY_PROP_TEMP, ++ POWER_SUPPLY_PROP_PRESENT, ++}; ++ ++static enum power_supply_property tosa_bat_bu_props[] = { ++ POWER_SUPPLY_PROP_STATUS, ++ POWER_SUPPLY_PROP_TECHNOLOGY, ++ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, ++ POWER_SUPPLY_PROP_VOLTAGE_NOW, ++ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, ++ POWER_SUPPLY_PROP_PRESENT, ++}; ++ ++static unsigned long tosa_read_bat(struct tosa_bat *bat) ++{ ++ unsigned long value = 0; ++ ++ if (bat->gpio_bat < 0 || bat->adc_bat < 0) ++ return 0; ++ ++ mutex_lock(&bat_lock); ++ gpio_set_value(bat->gpio_bat, 1); ++ mdelay(5); ++ value = wm97xx_read_aux_adc(bat->psy.dev->parent->driver_data, bat->adc_bat); ++ gpio_set_value(bat->gpio_bat, 0); ++ mutex_unlock(&bat_lock); ++ return value; ++} ++ ++static unsigned long tosa_read_temp(struct tosa_bat *bat) ++{ ++ unsigned long value = 0; ++ ++ if (bat->gpio_temp < 0 || bat->adc_temp < 0) ++ return 0; ++ ++ mutex_lock(&bat_lock); ++ gpio_set_value(bat->gpio_temp, 1); ++ mdelay(5); ++ value = wm97xx_read_aux_adc(bat->psy.dev->parent->driver_data, bat->adc_temp); ++ gpio_set_value(bat->gpio_temp, 0); ++ mutex_unlock(&bat_lock); ++ return value; ++} ++ ++static int tosa_bat_get_property(struct power_supply *psy, ++ enum power_supply_property psp, ++ union power_supply_propval *val) ++{ ++ int ret = 0; ++ struct tosa_bat *bat = container_of(psy, struct tosa_bat, psy); ++ ++ if (bat->is_present && !bat->is_present(bat) ++ && psp != POWER_SUPPLY_PROP_PRESENT) { ++ return -ENODEV; ++ } ++ ++ switch (psp) { ++ case POWER_SUPPLY_PROP_STATUS: ++ val->intval = bat->status; ++ break; ++ case POWER_SUPPLY_PROP_TECHNOLOGY: ++ val->intval = POWER_SUPPLY_TECHNOLOGY_LIPO; ++ break; ++ case POWER_SUPPLY_PROP_VOLTAGE_NOW: ++ val->intval = BAT_TO_VOLTS(tosa_read_bat(bat)); ++ break; ++ case POWER_SUPPLY_PROP_VOLTAGE_MAX: ++ if (bat->full_chrg == -1) ++ val->intval = -1; ++ else ++ val->intval = BAT_TO_VOLTS(bat->full_chrg); ++ break; ++ case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: ++ val->intval = BAT_TO_VOLTS(1551); ++ break; ++ case POWER_SUPPLY_PROP_TEMP: ++ val->intval = BAT_TO_TEMP(tosa_read_temp(bat)); ++ break; ++ case POWER_SUPPLY_PROP_PRESENT: ++ val->intval = bat->is_present ? bat->is_present(bat) : 1; ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ return ret; ++} ++ ++static int tosa_bu_get_property(struct power_supply *psy, ++ enum power_supply_property psp, ++ union power_supply_propval *val) ++{ ++ int ret = 0; ++ struct tosa_bat *bat = container_of(psy, struct tosa_bat, psy); ++ ++ switch (psp) { ++ case POWER_SUPPLY_PROP_STATUS: ++ val->intval = POWER_SUPPLY_STATUS_UNKNOWN; ++ break; ++ case POWER_SUPPLY_PROP_TECHNOLOGY: ++ val->intval = POWER_SUPPLY_TECHNOLOGY_LiMn; ++ break; ++ case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: ++ val->intval = 0; ++ break; ++ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: ++ val->intval = 3 * 1000000; /* 3 V */ ++ break; ++ case POWER_SUPPLY_PROP_VOLTAGE_NOW: ++ /* I think so */ ++ val->intval = BU_TO_VOLTS(tosa_read_bat(bat)); ++ break; ++ case POWER_SUPPLY_PROP_PRESENT: ++ val->intval = 1; ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ return ret; ++} ++ ++static bool tosa_jacket_bat_is_present(struct tosa_bat *bat) { ++ // FIXME ++ return 1; ++} ++ ++static void tosa_bat_external_power_changed(struct power_supply *psy) ++{ ++ schedule_work(&bat_work); ++} ++ ++static irqreturn_t tosa_bat_full_isr(int irq, void *data) ++{ ++ printk(KERN_ERR "bat_full irq: %d\n", gpio_get_value(irq_to_gpio(irq))); ++ schedule_work(&bat_work); ++ return IRQ_HANDLED; ++} ++ ++static void tosa_bat_update(struct tosa_bat *bat) ++{ ++ int old = bat->status; ++ struct power_supply *psy = &bat->psy; ++ ++ mutex_lock(&bat->work_lock); ++ ++ if (bat->is_present && !bat->is_present(bat)) { ++ printk(KERN_DEBUG "%s not present\n", psy->name); ++ bat->status = POWER_SUPPLY_STATUS_NOT_CHARGING; ++ bat->full_chrg = -1; ++ } else if (power_supply_am_i_supplied(psy)) { ++ if (gpio_get_value(bat->gpio_full)) { ++ printk(KERN_DEBUG "%s full\n", psy->name); ++ ++ if (old == POWER_SUPPLY_STATUS_CHARGING || bat->full_chrg == -1) ++ bat->full_chrg = tosa_read_bat(bat); ++ ++ gpio_set_value(bat->gpio_charge_off, 1); ++ bat->status = POWER_SUPPLY_STATUS_FULL; ++ } else { ++ printk(KERN_ERR "%s charge\n", psy->name); ++ gpio_set_value(bat->gpio_charge_off, 0); ++ bat->status = POWER_SUPPLY_STATUS_CHARGING; ++ } ++ } else { ++ printk(KERN_ERR "%s discharge\n", psy->name); ++ gpio_set_value(bat->gpio_charge_off, 1); ++ bat->status = POWER_SUPPLY_STATUS_DISCHARGING; ++ } ++ ++ if (old != bat->status) ++ power_supply_changed(psy); ++ ++ mutex_unlock(&bat->work_lock); ++} ++ ++static void tosa_bat_work(struct work_struct *work) ++{ ++ tosa_bat_update(&tosa_bat_main); ++ tosa_bat_update(&tosa_bat_jacket); ++} ++ ++ ++static struct tosa_bat tosa_bat_main = { ++ .status = POWER_SUPPLY_STATUS_UNKNOWN, ++ .full_chrg = -1, ++ .psy = { ++ .name = "main-battery", ++ .type = POWER_SUPPLY_TYPE_BATTERY, ++ .properties = tosa_bat_main_props, ++ .num_properties = ARRAY_SIZE(tosa_bat_main_props), ++ .get_property = tosa_bat_get_property, ++ .external_power_changed = tosa_bat_external_power_changed, ++ .use_for_apm = 1, ++ }, ++ ++ .gpio_full = TOSA_GPIO_BAT0_CRG, ++ .gpio_charge_off = TOSA_TC6393XB_CHARGE_OFF, ++ .gpio_bat = TOSA_TC6393XB_BAT0_V_ON, ++ .adc_bat = WM97XX_AUX_ID3, ++ .gpio_temp = TOSA_TC6393XB_BAT1_TH_ON, ++ .adc_temp = WM97XX_AUX_ID2, ++}; ++ ++static struct tosa_bat tosa_bat_jacket = { ++ .status = POWER_SUPPLY_STATUS_UNKNOWN, ++ .full_chrg = -1, ++ .psy = { ++ .name = "jacket-battery", ++ .type = POWER_SUPPLY_TYPE_BATTERY, ++ .properties = tosa_bat_main_props, ++ .num_properties = ARRAY_SIZE(tosa_bat_main_props), ++ .get_property = tosa_bat_get_property, ++ .external_power_changed = tosa_bat_external_power_changed, ++// .use_for_apm = 1, ++ }, ++ ++ .is_present = tosa_jacket_bat_is_present, ++ .gpio_full = TOSA_GPIO_BAT1_CRG, ++ .gpio_charge_off = TOSA_TC6393XB_CHARGE_OFF_JC, ++ .gpio_bat = TOSA_TC6393XB_BAT1_V_ON, ++ .adc_bat = WM97XX_AUX_ID3, ++ .gpio_temp = TOSA_TC6393XB_BAT0_TH_ON, ++ .adc_temp = WM97XX_AUX_ID2, ++}; ++ ++static struct tosa_bat tosa_bat_bu = { ++ .status = POWER_SUPPLY_STATUS_UNKNOWN, ++ .full_chrg = -1, ++ ++ .psy = { ++ .name = "backup-battery", ++ .type = POWER_SUPPLY_TYPE_BATTERY, ++ .properties = tosa_bat_bu_props, ++ .num_properties = ARRAY_SIZE(tosa_bat_bu_props), ++ .get_property = tosa_bu_get_property, ++ .external_power_changed = tosa_bat_external_power_changed, ++ }, ++ ++ .gpio_full = -1, ++ .gpio_charge_off = -1, ++ .gpio_bat = TOSA_TC6393XB_BU_CHRG_ON, ++ .adc_bat = WM97XX_AUX_ID4, ++ .gpio_temp = -1, ++ .adc_temp = -1, ++}; ++ ++static struct { ++ int gpio; ++ char *name; ++ bool output; ++ int value; ++} gpios[] = { ++ { TOSA_TC6393XB_CHARGE_OFF, "main charge off", 1, 1 }, ++ { TOSA_TC6393XB_CHARGE_OFF_JC, "jacket charge off", 1, 1 }, ++ { TOSA_TC6393XB_BAT_SW_ON, "battery switch", 1, 0 }, ++ { TOSA_TC6393XB_BAT0_V_ON, "main battery", 1, 0 }, ++ { TOSA_TC6393XB_BAT1_V_ON, "jacket battery", 1, 0 }, ++ { TOSA_TC6393XB_BAT1_TH_ON, "main battery temp", 1, 0 }, ++ { TOSA_TC6393XB_BAT0_TH_ON, "jacket battery temp", 1, 0 }, ++ { TOSA_TC6393XB_BU_CHRG_ON, "backup battery", 1, 0 }, ++ { TOSA_GPIO_BAT0_CRG, "main battery full", 0, 0 }, ++ { TOSA_GPIO_BAT1_CRG, "jacket battery full", 0, 0 }, ++ { TOSA_GPIO_BAT0_LOW, "main battery low", 0, 0 }, ++ { TOSA_GPIO_BAT1_LOW, "jacket battery low", 0, 0 }, ++}; ++ ++#ifdef CONFIG_PM ++static int tosa_bat_suspend(struct device *dev, pm_message_t state) ++{ ++ /* do nothing */ ++ return 0; ++} ++ ++static int tosa_bat_resume(struct device *dev) ++{ ++ schedule_work(&bat_work); ++ return 0; ++} ++#else ++#define tosa_bat_suspend NULL ++#define tosa_bat_resume NULL ++#endif ++ ++static int __devinit tosa_bat_probe(struct device *dev) ++{ ++ int ret; ++ int i; ++ ++ if (!machine_is_tosa()) ++ return -ENODEV; ++ ++ for (i = 0; i < ARRAY_SIZE(gpios); i++) { ++ ret = gpio_request(gpios[i].gpio, gpios[i].name); ++ if (ret) { ++ i --; ++ goto err_gpio; ++ } ++ ++ if (gpios[i].output) ++ ret = gpio_direction_output(gpios[i].gpio, ++ gpios[i].value); ++ else ++ ret = gpio_direction_input(gpios[i].gpio); ++ ++ if (ret) ++ goto err_gpio; ++ } ++ ++ mutex_init(&tosa_bat_main.work_lock); ++ mutex_init(&tosa_bat_jacket.work_lock); ++ ++ INIT_WORK(&bat_work, tosa_bat_work); ++ ++ ret = power_supply_register(dev, &tosa_bat_main.psy); ++ if (ret) ++ goto err_psy_reg_main; ++ ret = power_supply_register(dev, &tosa_bat_jacket.psy); ++ if (ret) ++ goto err_psy_reg_jacket; ++ ret = power_supply_register(dev, &tosa_bat_bu.psy); ++ if (ret) ++ goto err_psy_reg_bu; ++ ++ ret = request_irq(gpio_to_irq(TOSA_GPIO_BAT0_CRG), ++ tosa_bat_full_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, ++ "main full", &tosa_bat_main); ++ if (ret) ++ goto err_req_main; ++ ++ ret = request_irq(gpio_to_irq(TOSA_GPIO_BAT1_CRG), ++ tosa_bat_full_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, ++ "jacket full", &tosa_bat_jacket); ++ if (!ret) { ++ schedule_work(&bat_work); ++ return 0; ++ } ++ ++ free_irq(gpio_to_irq(TOSA_GPIO_BAT0_CRG), &tosa_bat_main); ++err_req_main: ++ power_supply_unregister(&tosa_bat_bu.psy); ++err_psy_reg_bu: ++ power_supply_unregister(&tosa_bat_jacket.psy); ++err_psy_reg_jacket: ++ power_supply_unregister(&tosa_bat_main.psy); ++err_psy_reg_main: ++ ++ i --; ++err_gpio: ++ for (; i >= 0; i --) ++ gpio_free(gpios[i].gpio); ++ ++ return ret; ++} ++ ++static int __devexit tosa_bat_remove(struct device *dev) ++{ ++ int i; ++ ++ free_irq(gpio_to_irq(TOSA_GPIO_BAT1_CRG), &tosa_bat_jacket); ++ free_irq(gpio_to_irq(TOSA_GPIO_BAT0_CRG), &tosa_bat_main); ++ ++ power_supply_unregister(&tosa_bat_bu.psy); ++ power_supply_unregister(&tosa_bat_jacket.psy); ++ power_supply_unregister(&tosa_bat_main.psy); ++ ++ for (i = ARRAY_SIZE(gpios) - 1; i >= 0; i --) ++ gpio_free(gpios[i].gpio); ++ ++ return 0; ++} ++ ++static struct device_driver tosa_bat_driver = { ++ .name = "wm97xx-battery", ++ .bus = &wm97xx_bus_type, ++ .owner = THIS_MODULE, ++ .probe = tosa_bat_probe, ++ .remove = __devexit_p(tosa_bat_remove), ++ .suspend = tosa_bat_suspend, ++ .resume = tosa_bat_resume, ++}; ++ ++static int __init tosa_bat_init(void) ++{ ++ return driver_register(&tosa_bat_driver); ++} ++ ++static void __exit tosa_bat_exit(void) ++{ ++ driver_unregister(&tosa_bat_driver); ++} ++ ++module_init(tosa_bat_init); ++module_exit(tosa_bat_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Dmitry Baryshkov"); ++MODULE_DESCRIPTION("Tosa battery driver"); +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0061-tosa-bat-unify.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0061-tosa-bat-unify.patch new file mode 100644 index 0000000000..2bcede36a9 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0061-tosa-bat-unify.patch @@ -0,0 +1,342 @@ +From f05aa38af5bd5962ae04c4b128644e7f55451527 Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Fri, 8 Feb 2008 01:14:48 +0300 +Subject: [PATCH 61/64] tosa-bat-unify + +Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> +--- + drivers/power/tosa_battery.c | 161 ++++++++++++++++++++--------------------- + 1 files changed, 79 insertions(+), 82 deletions(-) + +diff --git a/drivers/power/tosa_battery.c b/drivers/power/tosa_battery.c +index b0fd2f2..008e791 100644 +--- a/drivers/power/tosa_battery.c ++++ b/drivers/power/tosa_battery.c +@@ -21,15 +21,6 @@ + #include <asm/gpio.h> + #include <asm/arch/tosa.h> + +-#define BAT_TO_VOLTS(v) ((v) * 1000000 / 414) +-#define BU_TO_VOLTS(v) ((v) * 1000000 / 1266) +-/* +- * It's pretty strange value, but that's roughly what I get from +- * zaurus maintainer menu +- */ +-//#define BAT_TO_TEMP(t) ((t) * 10000/2000) +-#define BAT_TO_TEMP(t) (t) +- + static DEFINE_MUTEX(bat_lock); /* protects gpio pins */ + static struct work_struct bat_work; + +@@ -39,37 +30,27 @@ struct tosa_bat { + int full_chrg; + + struct mutex work_lock; /* protects data */ ++ + bool (*is_present)(struct tosa_bat *bat); + int gpio_full; + int gpio_charge_off; ++ ++ int technology; ++ + int gpio_bat; + int adc_bat; ++ int adc_bat_divider; ++ int bat_max; ++ int bat_min; ++ + int gpio_temp; + int adc_temp; ++ int adc_temp_divider; + }; + + static struct tosa_bat tosa_bat_main; + static struct tosa_bat tosa_bat_jacket; + +-static enum power_supply_property tosa_bat_main_props[] = { +- POWER_SUPPLY_PROP_STATUS, +- POWER_SUPPLY_PROP_TECHNOLOGY, +- POWER_SUPPLY_PROP_VOLTAGE_NOW, +- POWER_SUPPLY_PROP_VOLTAGE_MAX, +- POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, +- POWER_SUPPLY_PROP_TEMP, +- POWER_SUPPLY_PROP_PRESENT, +-}; +- +-static enum power_supply_property tosa_bat_bu_props[] = { +- POWER_SUPPLY_PROP_STATUS, +- POWER_SUPPLY_PROP_TECHNOLOGY, +- POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, +- POWER_SUPPLY_PROP_VOLTAGE_NOW, +- POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, +- POWER_SUPPLY_PROP_PRESENT, +-}; +- + static unsigned long tosa_read_bat(struct tosa_bat *bat) + { + unsigned long value = 0; +@@ -83,6 +64,9 @@ static unsigned long tosa_read_bat(struct tosa_bat *bat) + value = wm97xx_read_aux_adc(bat->psy.dev->parent->driver_data, bat->adc_bat); + gpio_set_value(bat->gpio_bat, 0); + mutex_unlock(&bat_lock); ++ ++ value = value * 1000000 / bat->adc_bat_divider; ++ + return value; + } + +@@ -99,6 +83,9 @@ static unsigned long tosa_read_temp(struct tosa_bat *bat) + value = wm97xx_read_aux_adc(bat->psy.dev->parent->driver_data, bat->adc_temp); + gpio_set_value(bat->gpio_temp, 0); + mutex_unlock(&bat_lock); ++ ++ value = value * 10000 / bat->adc_temp_divider; ++ + return value; + } + +@@ -119,22 +106,25 @@ static int tosa_bat_get_property(struct power_supply *psy, + val->intval = bat->status; + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: +- val->intval = POWER_SUPPLY_TECHNOLOGY_LIPO; ++ val->intval = bat->technology; + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: +- val->intval = BAT_TO_VOLTS(tosa_read_bat(bat)); ++ val->intval = tosa_read_bat(bat); + break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX: + if (bat->full_chrg == -1) +- val->intval = -1; ++ val->intval = bat->bat_max; + else +- val->intval = BAT_TO_VOLTS(bat->full_chrg); ++ val->intval = bat->full_chrg; ++ break; ++ case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: ++ val->intval = bat->bat_max; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: +- val->intval = BAT_TO_VOLTS(1551); ++ val->intval = bat->bat_min; + break; + case POWER_SUPPLY_PROP_TEMP: +- val->intval = BAT_TO_TEMP(tosa_read_temp(bat)); ++ val->intval = tosa_read_temp(bat); + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = bat->is_present ? bat->is_present(bat) : 1; +@@ -146,40 +136,6 @@ static int tosa_bat_get_property(struct power_supply *psy, + return ret; + } + +-static int tosa_bu_get_property(struct power_supply *psy, +- enum power_supply_property psp, +- union power_supply_propval *val) +-{ +- int ret = 0; +- struct tosa_bat *bat = container_of(psy, struct tosa_bat, psy); +- +- switch (psp) { +- case POWER_SUPPLY_PROP_STATUS: +- val->intval = POWER_SUPPLY_STATUS_UNKNOWN; +- break; +- case POWER_SUPPLY_PROP_TECHNOLOGY: +- val->intval = POWER_SUPPLY_TECHNOLOGY_LiMn; +- break; +- case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: +- val->intval = 0; +- break; +- case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: +- val->intval = 3 * 1000000; /* 3 V */ +- break; +- case POWER_SUPPLY_PROP_VOLTAGE_NOW: +- /* I think so */ +- val->intval = BU_TO_VOLTS(tosa_read_bat(bat)); +- break; +- case POWER_SUPPLY_PROP_PRESENT: +- val->intval = 1; +- break; +- default: +- ret = -EINVAL; +- break; +- } +- return ret; +-} +- + static bool tosa_jacket_bat_is_present(struct tosa_bat *bat) { + // FIXME + return 1; +@@ -241,6 +197,25 @@ static void tosa_bat_work(struct work_struct *work) + } + + ++static enum power_supply_property tosa_bat_main_props[] = { ++ POWER_SUPPLY_PROP_STATUS, ++ POWER_SUPPLY_PROP_TECHNOLOGY, ++ POWER_SUPPLY_PROP_VOLTAGE_NOW, ++ POWER_SUPPLY_PROP_VOLTAGE_MAX, ++ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, ++ POWER_SUPPLY_PROP_TEMP, ++ POWER_SUPPLY_PROP_PRESENT, ++}; ++ ++static enum power_supply_property tosa_bat_bu_props[] = { ++ POWER_SUPPLY_PROP_STATUS, ++ POWER_SUPPLY_PROP_TECHNOLOGY, ++ POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, ++ POWER_SUPPLY_PROP_VOLTAGE_NOW, ++ POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN, ++ POWER_SUPPLY_PROP_PRESENT, ++}; ++ + static struct tosa_bat tosa_bat_main = { + .status = POWER_SUPPLY_STATUS_UNKNOWN, + .full_chrg = -1, +@@ -256,10 +231,18 @@ static struct tosa_bat tosa_bat_main = { + + .gpio_full = TOSA_GPIO_BAT0_CRG, + .gpio_charge_off = TOSA_TC6393XB_CHARGE_OFF, ++ ++ .technology = POWER_SUPPLY_TECHNOLOGY_LIPO, ++ + .gpio_bat = TOSA_TC6393XB_BAT0_V_ON, + .adc_bat = WM97XX_AUX_ID3, ++ .adc_bat_divider = 414, ++ .bat_max = 4310000, ++ .bat_min = 1551 * 100000 / 414, ++ + .gpio_temp = TOSA_TC6393XB_BAT1_TH_ON, + .adc_temp = WM97XX_AUX_ID2, ++ .adc_temp_divider = 10000, + }; + + static struct tosa_bat tosa_bat_jacket = { +@@ -278,10 +261,18 @@ static struct tosa_bat tosa_bat_jacket = { + .is_present = tosa_jacket_bat_is_present, + .gpio_full = TOSA_GPIO_BAT1_CRG, + .gpio_charge_off = TOSA_TC6393XB_CHARGE_OFF_JC, ++ ++ .technology = POWER_SUPPLY_TECHNOLOGY_LIPO, ++ + .gpio_bat = TOSA_TC6393XB_BAT1_V_ON, + .adc_bat = WM97XX_AUX_ID3, ++ .adc_bat_divider = 414, ++ .bat_max = 4310000, ++ .bat_min = 1551 * 100000 / 414, ++ + .gpio_temp = TOSA_TC6393XB_BAT0_TH_ON, + .adc_temp = WM97XX_AUX_ID2, ++ .adc_temp_divider = 10000, + }; + + static struct tosa_bat tosa_bat_bu = { +@@ -293,16 +284,22 @@ static struct tosa_bat tosa_bat_bu = { + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = tosa_bat_bu_props, + .num_properties = ARRAY_SIZE(tosa_bat_bu_props), +- .get_property = tosa_bu_get_property, ++ .get_property = tosa_bat_get_property, + .external_power_changed = tosa_bat_external_power_changed, + }, + + .gpio_full = -1, + .gpio_charge_off = -1, ++ ++ .technology = POWER_SUPPLY_TECHNOLOGY_LiMn, ++ + .gpio_bat = TOSA_TC6393XB_BU_CHRG_ON, + .adc_bat = WM97XX_AUX_ID4, ++ .adc_bat_divider = 1266, ++ + .gpio_temp = -1, + .adc_temp = -1, ++ .adc_temp_divider = -1, + }; + + static struct { +@@ -326,13 +323,14 @@ static struct { + }; + + #ifdef CONFIG_PM +-static int tosa_bat_suspend(struct device *dev, pm_message_t state) ++static int tosa_bat_suspend(struct platform_device *dev, pm_message_t state) + { + /* do nothing */ ++ flush_scheduled_work(); + return 0; + } + +-static int tosa_bat_resume(struct device *dev) ++static int tosa_bat_resume(struct platform_device *dev) + { + schedule_work(&bat_work); + return 0; +@@ -342,7 +340,7 @@ static int tosa_bat_resume(struct device *dev) + #define tosa_bat_resume NULL + #endif + +-static int __devinit tosa_bat_probe(struct device *dev) ++static int __devinit tosa_bat_probe(struct platform_device *dev) + { + int ret; + int i; +@@ -372,13 +370,13 @@ static int __devinit tosa_bat_probe(struct device *dev) + + INIT_WORK(&bat_work, tosa_bat_work); + +- ret = power_supply_register(dev, &tosa_bat_main.psy); ++ ret = power_supply_register(&dev->dev, &tosa_bat_main.psy); + if (ret) + goto err_psy_reg_main; +- ret = power_supply_register(dev, &tosa_bat_jacket.psy); ++ ret = power_supply_register(&dev->dev, &tosa_bat_jacket.psy); + if (ret) + goto err_psy_reg_jacket; +- ret = power_supply_register(dev, &tosa_bat_bu.psy); ++ ret = power_supply_register(&dev->dev, &tosa_bat_bu.psy); + if (ret) + goto err_psy_reg_bu; + +@@ -413,7 +411,7 @@ err_gpio: + return ret; + } + +-static int __devexit tosa_bat_remove(struct device *dev) ++static int __devexit tosa_bat_remove(struct platform_device *dev) + { + int i; + +@@ -430,10 +428,9 @@ static int __devexit tosa_bat_remove(struct device *dev) + return 0; + } + +-static struct device_driver tosa_bat_driver = { +- .name = "wm97xx-battery", +- .bus = &wm97xx_bus_type, +- .owner = THIS_MODULE, ++static struct platform_driver tosa_bat_driver = { ++ .driver.name = "wm97xx-battery", ++ .driver.owner = THIS_MODULE, + .probe = tosa_bat_probe, + .remove = __devexit_p(tosa_bat_remove), + .suspend = tosa_bat_suspend, +@@ -442,12 +439,12 @@ static struct device_driver tosa_bat_driver = { + + static int __init tosa_bat_init(void) + { +- return driver_register(&tosa_bat_driver); ++ return platform_driver_register(&tosa_bat_driver); + } + + static void __exit tosa_bat_exit(void) + { +- driver_unregister(&tosa_bat_driver); ++ platform_driver_unregister(&tosa_bat_driver); + } + + module_init(tosa_bat_init); +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0062-tosa-bat-fix-charging.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0062-tosa-bat-fix-charging.patch new file mode 100644 index 0000000000..e3a6b74772 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0062-tosa-bat-fix-charging.patch @@ -0,0 +1,78 @@ +From 0b9f80ab540b2e51f6e86f6a1adec67761f9368d Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Fri, 8 Feb 2008 00:50:03 +0300 +Subject: [PATCH 62/64] tosa-bat-fix-charging + +Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> +--- + drivers/power/tosa_battery.c | 28 +++++++++++++++++++++------- + 1 files changed, 21 insertions(+), 7 deletions(-) + +diff --git a/drivers/power/tosa_battery.c b/drivers/power/tosa_battery.c +index 008e791..2db9116 100644 +--- a/drivers/power/tosa_battery.c ++++ b/drivers/power/tosa_battery.c +@@ -153,39 +153,53 @@ static irqreturn_t tosa_bat_full_isr(int irq, void *data) + return IRQ_HANDLED; + } + ++static char *status_text[] = { ++ [POWER_SUPPLY_STATUS_UNKNOWN] = "Unknown", ++ [POWER_SUPPLY_STATUS_CHARGING] = "Charging", ++ [POWER_SUPPLY_STATUS_DISCHARGING] = "Discharging", ++ [POWER_SUPPLY_STATUS_NOT_CHARGING] = "Not charging", ++ [POWER_SUPPLY_STATUS_FULL] = "Full", ++}; ++ + static void tosa_bat_update(struct tosa_bat *bat) + { +- int old = bat->status; ++ int old; + struct power_supply *psy = &bat->psy; + + mutex_lock(&bat->work_lock); + ++ old = bat->status; ++ + if (bat->is_present && !bat->is_present(bat)) { +- printk(KERN_DEBUG "%s not present\n", psy->name); ++ printk(KERN_NOTICE "%s not present\n", psy->name); + bat->status = POWER_SUPPLY_STATUS_NOT_CHARGING; + bat->full_chrg = -1; + } else if (power_supply_am_i_supplied(psy)) { ++ if (bat->status == POWER_SUPPLY_STATUS_DISCHARGING) { ++ gpio_set_value(bat->gpio_charge_off, 0); ++ mdelay(15); ++ } + if (gpio_get_value(bat->gpio_full)) { +- printk(KERN_DEBUG "%s full\n", psy->name); +- + if (old == POWER_SUPPLY_STATUS_CHARGING || bat->full_chrg == -1) + bat->full_chrg = tosa_read_bat(bat); + + gpio_set_value(bat->gpio_charge_off, 1); + bat->status = POWER_SUPPLY_STATUS_FULL; + } else { +- printk(KERN_ERR "%s charge\n", psy->name); + gpio_set_value(bat->gpio_charge_off, 0); + bat->status = POWER_SUPPLY_STATUS_CHARGING; + } + } else { +- printk(KERN_ERR "%s discharge\n", psy->name); + gpio_set_value(bat->gpio_charge_off, 1); + bat->status = POWER_SUPPLY_STATUS_DISCHARGING; + } + +- if (old != bat->status) ++ if (old != bat->status) { ++ printk(KERN_NOTICE "%s %s -> %s\n", psy->name, ++ status_text[old], ++ status_text[bat->status]); + power_supply_changed(psy); ++ } + + mutex_unlock(&bat->work_lock); + } +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0063-patch-tosa-bat-jacket-detect.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0063-patch-tosa-bat-jacket-detect.patch new file mode 100644 index 0000000000..416cae44ec --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0063-patch-tosa-bat-jacket-detect.patch @@ -0,0 +1,84 @@ +From 4ef7289137132959e3db5a1e77580ff9db185d90 Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Fri, 8 Feb 2008 01:13:54 +0300 +Subject: [PATCH 63/64] patch tosa-bat-jacket-detect + +--- + drivers/power/tosa_battery.c | 21 +++++++++++++++------ + 1 files changed, 15 insertions(+), 6 deletions(-) + +diff --git a/drivers/power/tosa_battery.c b/drivers/power/tosa_battery.c +index 2db9116..70beed2 100644 +--- a/drivers/power/tosa_battery.c ++++ b/drivers/power/tosa_battery.c +@@ -137,8 +137,7 @@ static int tosa_bat_get_property(struct power_supply *psy, + } + + static bool tosa_jacket_bat_is_present(struct tosa_bat *bat) { +- // FIXME +- return 1; ++ return gpio_get_value(TOSA_GPIO_JACKET_DETECT) == 0; + } + + static void tosa_bat_external_power_changed(struct power_supply *psy) +@@ -146,9 +145,9 @@ static void tosa_bat_external_power_changed(struct power_supply *psy) + schedule_work(&bat_work); + } + +-static irqreturn_t tosa_bat_full_isr(int irq, void *data) ++static irqreturn_t tosa_bat_gpio_isr(int irq, void *data) + { +- printk(KERN_ERR "bat_full irq: %d\n", gpio_get_value(irq_to_gpio(irq))); ++ printk(KERN_ERR "bat_gpio irq: %d\n", gpio_get_value(irq_to_gpio(irq))); + schedule_work(&bat_work); + return IRQ_HANDLED; + } +@@ -334,6 +333,7 @@ static struct { + { TOSA_GPIO_BAT1_CRG, "jacket battery full", 0, 0 }, + { TOSA_GPIO_BAT0_LOW, "main battery low", 0, 0 }, + { TOSA_GPIO_BAT1_LOW, "jacket battery low", 0, 0 }, ++ { TOSA_GPIO_JACKET_DETECT, "jacket detect", 0, 0 }, + }; + + #ifdef CONFIG_PM +@@ -395,19 +395,27 @@ static int __devinit tosa_bat_probe(struct platform_device *dev) + goto err_psy_reg_bu; + + ret = request_irq(gpio_to_irq(TOSA_GPIO_BAT0_CRG), +- tosa_bat_full_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, ++ tosa_bat_gpio_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "main full", &tosa_bat_main); + if (ret) + goto err_req_main; + + ret = request_irq(gpio_to_irq(TOSA_GPIO_BAT1_CRG), +- tosa_bat_full_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, ++ tosa_bat_gpio_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, + "jacket full", &tosa_bat_jacket); ++ if (ret) ++ goto err_req_jacket; ++ ++ ret = request_irq(gpio_to_irq(TOSA_GPIO_JACKET_DETECT), ++ tosa_bat_gpio_isr, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, ++ "jacket detect", &tosa_bat_jacket); + if (!ret) { + schedule_work(&bat_work); + return 0; + } + ++ free_irq(gpio_to_irq(TOSA_GPIO_BAT1_CRG), &tosa_bat_jacket); ++err_req_jacket: + free_irq(gpio_to_irq(TOSA_GPIO_BAT0_CRG), &tosa_bat_main); + err_req_main: + power_supply_unregister(&tosa_bat_bu.psy); +@@ -429,6 +437,7 @@ static int __devexit tosa_bat_remove(struct platform_device *dev) + { + int i; + ++ free_irq(gpio_to_irq(TOSA_GPIO_JACKET_DETECT), &tosa_bat_jacket); + free_irq(gpio_to_irq(TOSA_GPIO_BAT1_CRG), &tosa_bat_jacket); + free_irq(gpio_to_irq(TOSA_GPIO_BAT0_CRG), &tosa_bat_main); + +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0064-Export-modes-via-sysfs.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0064-Export-modes-via-sysfs.patch new file mode 100644 index 0000000000..eeb92cfdd5 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0064-Export-modes-via-sysfs.patch @@ -0,0 +1,27 @@ +From feeee5d22c00d9d1e2e06eb5610740be238749b9 Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Thu, 7 Feb 2008 22:27:38 +0300 +Subject: [PATCH 64/64] Export modes via sysfs + +Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> +--- + drivers/video/tmiofb.c | 3 +++ + 1 files changed, 3 insertions(+), 0 deletions(-) + +diff --git a/drivers/video/tmiofb.c b/drivers/video/tmiofb.c +index 6b963a1..9389a77 100644 +--- a/drivers/video/tmiofb.c ++++ b/drivers/video/tmiofb.c +@@ -800,6 +800,9 @@ static int tmiofb_probe(struct platform_device *dev) + if (retval) + goto err_set_par;*/ + ++ fb_videomode_to_modelist(data->modes, data->num_modes, ++ &info->modelist); ++ + retval = register_framebuffer(info); + if (retval < 0) + goto err_register_framebuffer; +-- +1.5.3.8 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0065-wm97xx-core-fixes.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0065-wm97xx-core-fixes.patch new file mode 100644 index 0000000000..5db0cc6ba0 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0065-wm97xx-core-fixes.patch @@ -0,0 +1,49 @@ +From 2544412fc47dc13f4f3935cb4c2fd500d217e905 Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Wed, 13 Feb 2008 02:00:15 +0300 +Subject: [PATCH] wm97xx-core fixes + +Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> +--- + drivers/input/touchscreen/wm97xx-core.c | 8 ++++---- + include/linux/wm97xx.h | 1 - + 2 files changed, 4 insertions(+), 5 deletions(-) + +diff --git a/drivers/input/touchscreen/wm97xx-core.c b/drivers/input/touchscreen/wm97xx-core.c +index 840d9ff..4cbb9e5 100644 +--- a/drivers/input/touchscreen/wm97xx-core.c ++++ b/drivers/input/touchscreen/wm97xx-core.c +@@ -596,7 +596,7 @@ static int wm97xx_probe(struct device *dev) + } + platform_set_drvdata(wm->battery_dev, wm); + wm->battery_dev->dev.parent = dev; +- ret = platform_device_register(wm->battery_dev); ++ ret = platform_device_add(wm->battery_dev); + if (ret < 0) + goto batt_reg_err; + +@@ -609,7 +609,7 @@ static int wm97xx_probe(struct device *dev) + } + platform_set_drvdata(wm->touch_dev, wm); + wm->touch_dev->dev.parent = dev; +- ret = platform_device_register(wm->touch_dev); ++ ret = platform_device_add(wm->touch_dev); + if (ret < 0) + goto touch_reg_err; + +@@ -619,10 +619,12 @@ static int wm97xx_probe(struct device *dev) + platform_device_put(wm->touch_dev); + touch_err: + platform_device_unregister(wm->battery_dev); ++ wm->battery_dev = NULL; + batt_reg_err: + platform_device_put(wm->battery_dev); + batt_err: + input_unregister_device(wm->input_dev); ++ wm->input_dev = NULL; + kfree(wm); + return ret; + } +-- +1.5.4.1 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0066-tmiofb_probe-should-be-__devinit.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0066-tmiofb_probe-should-be-__devinit.patch new file mode 100644 index 0000000000..42320be50b --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0066-tmiofb_probe-should-be-__devinit.patch @@ -0,0 +1,26 @@ +From b6a63ad546cc26519a342f3fdd7b1def49ca33ee Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Wed, 13 Feb 2008 02:00:14 +0300 +Subject: [PATCH] tmiofb_probe should be __devinit + +Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> +--- + drivers/video/tmiofb.c | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +diff --git a/drivers/video/tmiofb.c b/drivers/video/tmiofb.c +index 9389a77..958ee8a 100644 +--- a/drivers/video/tmiofb.c ++++ b/drivers/video/tmiofb.c +@@ -704,7 +704,7 @@ static irqreturn_t tmiofb_irq(int irq, void *__info) + return IRQ_HANDLED; + } + +-static int tmiofb_probe(struct platform_device *dev) ++static int __devinit tmiofb_probe(struct platform_device *dev) + { + struct mfd_cell *cell = mfd_get_cell(dev); + struct tmio_fb_data *data = cell->driver_data; +-- +1.5.4.1 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0067-modeswitching.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0067-modeswitching.patch new file mode 100644 index 0000000000..42b69d9377 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0067-modeswitching.patch @@ -0,0 +1,225 @@ +From aded2e51a7a2113dae6431c858df1d95fb312851 Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Wed, 13 Feb 2008 19:34:08 +0300 +Subject: [PATCH] modeswitching + +Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> +--- + arch/arm/mach-pxa/tosa.c | 33 +++++++++++++++++++++++++++++++- + drivers/mfd/tc6393xb.c | 2 +- + drivers/video/backlight/tosa_bl.c | 38 +++++++++++++++++++++++++++++++++--- + drivers/video/tmiofb.c | 8 +++--- + include/asm-arm/arch-pxa/tosa.h | 2 + + include/linux/mfd/tc6393xb.h | 2 +- + include/linux/mfd/tmio.h | 2 +- + 7 files changed, 75 insertions(+), 12 deletions(-) + +diff --git a/arch/arm/mach-pxa/tosa.c b/arch/arm/mach-pxa/tosa.c +index 94c9915..6631de2 100644 +--- a/arch/arm/mach-pxa/tosa.c ++++ b/arch/arm/mach-pxa/tosa.c +@@ -455,9 +455,40 @@ static struct fb_videomode tosa_tc6393xb_lcd_mode[] = { + } + }; + ++static DEFINE_SPINLOCK(tosa_lcd_mode_lock); ++ ++static const struct fb_videomode *tosa_lcd_mode = &tosa_tc6393xb_lcd_mode[0]; ++ ++static int tosa_lcd_set_mode(struct platform_device *fb_dev, ++ const struct fb_videomode *mode) ++{ ++ int rc; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&tosa_lcd_mode_lock, flags); ++ rc = tc6393xb_lcd_mode(fb_dev, mode); ++ if (!rc) ++ tosa_lcd_mode = mode; ++ spin_unlock_irqrestore(&tosa_lcd_mode_lock, flags); ++ ++ return rc; ++} ++ ++const struct fb_videomode *tosa_lcd_get_mode(void) ++{ ++ unsigned long flags; ++ const struct fb_videomode *mode; ++ ++ spin_lock_irqsave(&tosa_lcd_mode_lock, flags); ++ mode = tosa_lcd_mode; ++ spin_unlock_irqrestore(&tosa_lcd_mode_lock, flags); ++ ++ return mode; ++} ++ + static struct tmio_fb_data tosa_tc6393xb_fb_config = { + .lcd_set_power = tc6393xb_lcd_set_power, +- .lcd_mode = tc6393xb_lcd_mode, ++ .lcd_mode = tosa_lcd_set_mode, + .num_modes = ARRAY_SIZE(tosa_tc6393xb_lcd_mode), + .modes = &tosa_tc6393xb_lcd_mode[0], + }; +diff --git a/drivers/mfd/tc6393xb.c b/drivers/mfd/tc6393xb.c +index 9001687..21190f3 100644 +--- a/drivers/mfd/tc6393xb.c ++++ b/drivers/mfd/tc6393xb.c +@@ -196,7 +196,7 @@ int tc6393xb_lcd_set_power(struct platform_device *fb, bool on) + EXPORT_SYMBOL(tc6393xb_lcd_set_power); + + int tc6393xb_lcd_mode(struct platform_device *fb_dev, +- struct fb_videomode *mode) { ++ const struct fb_videomode *mode) { + struct tc6393xb *tc6393xb = + platform_get_drvdata(to_platform_device(fb_dev->dev.parent)); + struct tc6393xb_scr __iomem *scr = tc6393xb->scr; +diff --git a/drivers/video/backlight/tosa_bl.c b/drivers/video/backlight/tosa_bl.c +index 9ef0bfb..45fc6ae 100644 +--- a/drivers/video/backlight/tosa_bl.c ++++ b/drivers/video/backlight/tosa_bl.c +@@ -48,6 +48,9 @@ struct tosa_bl_data { + struct ssp_dev nssp_dev; + struct ssp_state nssp_state; + ++ /* listen for mode changes */ ++ struct notifier_block fb_notif; ++ + struct backlight_device *bl_dev; + }; + +@@ -103,14 +106,19 @@ static void tosa_lcd_tg_init(struct tosa_bl_data *data) + pxa_nssp_output(data, TG_GPOSR,0x02); /* GPOS0=powercontrol, GPOS1=GPIO, GPOS2=TCTL */ + } + +-static void tosa_lcd_tg_on(struct tosa_bl_data *data/*, const struct fb_videomode *mode*/) ++static void tosa_lcd_tg_on(struct tosa_bl_data *data) + { +- const int value = TG_REG0_COLOR | TG_REG0_UD | TG_REG0_LR; ++ const struct fb_videomode *mode = tosa_lcd_get_mode(); ++ ++ int value = TG_REG0_COLOR | TG_REG0_UD | TG_REG0_LR; ++ ++ if (mode->yres == 640) ++ value |= TG_REG0_VQV; + + tosa_lcd_tg_init(data); + +- dev_dbg(&data->bl_dev->dev, "tosa_lcd_on\n"); +- pxa_nssp_output(data, TG_PNLCTL, value | (/*mode->yres == 320 ? 0 : */ TG_REG0_VQV)); ++ dev_dbg(&data->bl_dev->dev, "tosa_lcd_on: %04x (%d)\n", value, mode->yres); ++ pxa_nssp_output(data, TG_PNLCTL, value); + + /* TG LCD pannel power up */ + pxa_nssp_output(data, TG_PINICTL,0x4); +@@ -173,6 +181,20 @@ static struct backlight_ops tosa_bl_ops = { + .update_status = tosa_bl_update_status, + }; + ++static int fb_notifier_callback(struct notifier_block *self, ++ unsigned long event, void *_data) ++{ ++ struct tosa_bl_data *bl_data = ++ container_of(self, struct tosa_bl_data, fb_notif); ++ ++ if (event != FB_EVENT_MODE_CHANGE && event != FB_EVENT_MODE_CHANGE_ALL) ++ return 0; ++ ++ tosa_bl_update_status(bl_data->bl_dev); ++ ++ return 0; ++} ++ + static int tosa_bl_detect_client(struct i2c_adapter *adapter, int address, + int kind) + { +@@ -238,9 +260,15 @@ static int tosa_bl_detect_client(struct i2c_adapter *adapter, int address, + data->bl_dev->props.power = FB_BLANK_UNBLANK; + backlight_update_status(data->bl_dev); + ++ data->fb_notif.notifier_call = fb_notifier_callback; ++ err = fb_register_client(&data->fb_notif); ++ if (err) ++ goto err_fb_register; + + return 0; + ++err_fb_register: ++ backlight_device_unregister(data->bl_dev); + err_bl_register: + tosa_set_backlight(data, 0); + tosa_lcd_tg_off(data); +@@ -265,6 +293,8 @@ static int tosa_bl_detach_client(struct i2c_client *client) + int err = 0; + struct tosa_bl_data *data = i2c_get_clientdata(client); + ++ fb_unregister_client(&data->fb_notif); ++ + backlight_device_unregister(data->bl_dev); + + tosa_set_backlight(data, 0); +diff --git a/drivers/video/tmiofb.c b/drivers/video/tmiofb.c +index 958ee8a..cc75df9 100644 +--- a/drivers/video/tmiofb.c ++++ b/drivers/video/tmiofb.c +@@ -618,17 +618,17 @@ static int tmiofb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) + + static int tmiofb_set_par(struct fb_info *info) + { +-/* struct fb_var_screeninfo *var = &info->var; ++ struct fb_var_screeninfo *var = &info->var; + struct fb_videomode *mode; + + mode = tmiofb_find_mode(info, var); + if (!mode) + return -EINVAL; + +- if (info->mode == mode) +- return 0; ++/* if (info->mode == mode) ++ return 0;*/ + +- info->mode = mode; */ ++ info->mode = mode; + info->fix.line_length = info->mode->xres * 2; + + tmiofb_hw_mode(to_platform_device(info->device)); +diff --git a/include/asm-arm/arch-pxa/tosa.h b/include/asm-arm/arch-pxa/tosa.h +index 410fa9a..624c636 100644 +--- a/include/asm-arm/arch-pxa/tosa.h ++++ b/include/asm-arm/arch-pxa/tosa.h +@@ -230,4 +230,6 @@ extern struct platform_device tosascoop_device; + #define TOSA_KEY_MAIL KEY_MAIL + #endif + ++const struct fb_videomode *tosa_lcd_get_mode(void); ++ + #endif /* _ASM_ARCH_TOSA_H_ */ +diff --git a/include/linux/mfd/tc6393xb.h b/include/linux/mfd/tc6393xb.h +index 97c4c7c..8ab9e91 100644 +--- a/include/linux/mfd/tc6393xb.h ++++ b/include/linux/mfd/tc6393xb.h +@@ -53,7 +53,7 @@ struct tc6393xb_platform_data { + + extern int tc6393xb_lcd_set_power(struct platform_device *fb_dev, bool on); + extern int tc6393xb_lcd_mode(struct platform_device *fb_dev, +- struct fb_videomode *mode); ++ const struct fb_videomode *mode); + + /* + * Relative to irq_base +diff --git a/include/linux/mfd/tmio.h b/include/linux/mfd/tmio.h +index b6d4aac..fe7ff2d 100644 +--- a/include/linux/mfd/tmio.h ++++ b/include/linux/mfd/tmio.h +@@ -19,7 +19,7 @@ struct tmio_fb_data { + int (*lcd_set_power)(struct platform_device *fb_dev, + bool on); + int (*lcd_mode)(struct platform_device *fb_dev, +- struct fb_videomode *mode); ++ const struct fb_videomode *mode); + int num_modes; + struct fb_videomode *modes; + }; +-- +1.5.4.1 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0068-Preliminary-tosa-denoiser.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0068-Preliminary-tosa-denoiser.patch new file mode 100644 index 0000000000..e90e3751c0 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/0068-Preliminary-tosa-denoiser.patch @@ -0,0 +1,239 @@ +From f7dad1cd9c1bd3fce5d228e0b644a51baea50cd9 Mon Sep 17 00:00:00 2001 +From: Dmitry Baryshkov <dbaryshkov@gmail.com> +Date: Fri, 15 Feb 2008 15:35:07 +0300 +Subject: [PATCH] Preliminary tosa denoiser + +Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com> +--- + drivers/input/touchscreen/Kconfig | 12 ++ + drivers/input/touchscreen/Makefile | 1 + + drivers/input/touchscreen/tosa-wm97xx.c | 182 +++++++++++++++++++++++++++++++ + 3 files changed, 195 insertions(+), 0 deletions(-) + create mode 100644 drivers/input/touchscreen/tosa-wm97xx.c + +diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig +index 0be05a2..938aed2 100644 +--- a/drivers/input/touchscreen/Kconfig ++++ b/drivers/input/touchscreen/Kconfig +@@ -210,6 +210,18 @@ config TOUCHSCREEN_WM97XX_MAINSTONE + To compile this driver as a module, choose M here: the + module will be called mainstone-wm97xx + ++config TOUCHSCREEN_WM97XX_TOSA ++ tristate "WM97xx Tosa denoiser" ++ depends on TOUCHSCREEN_WM97XX && MACH_TOSA ++ help ++ Say Y here for support for touchscreen denoising on ++ Sharl Zaurus SL-6000 (tosa) system. ++ ++ If unsure, say N ++ ++ To compile this driver as a module, choose M here: the ++ module will be called tosa-wm97xx ++ + config TOUCHSCREEN_TOUCHWIN + tristate "Touchwin serial touchscreen" + select SERIO +diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile +index d38156e..d86278b 100644 +--- a/drivers/input/touchscreen/Makefile ++++ b/drivers/input/touchscreen/Makefile +@@ -23,6 +23,7 @@ obj-$(CONFIG_TOUCHSCREEN_TOUCHWIN) += touchwin.o + obj-$(CONFIG_TOUCHSCREEN_UCB1400) += ucb1400_ts.o + obj-$(CONFIG_TOUCHSCREEN_WM97XX) += wm97xx-ts.o + obj-$(CONFIG_TOUCHSCREEN_WM97XX_MAINSTONE) += mainstone-wm97xx.o ++obj-$(CONFIG_TOUCHSCREEN_WM97XX_TOSA) += tosa-wm97xx.o + wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9705) += wm9705.o + wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9712) += wm9712.o + wm97xx-ts-$(CONFIG_TOUCHSCREEN_WM9713) += wm9713.o +diff --git a/drivers/input/touchscreen/tosa-wm97xx.c b/drivers/input/touchscreen/tosa-wm97xx.c +new file mode 100644 +index 0000000..8fd542b +--- /dev/null ++++ b/drivers/input/touchscreen/tosa-wm97xx.c +@@ -0,0 +1,182 @@ ++/* ++ * tosa_ts.c -- Touchscreen driver for Sharp SL-6000 (Tosa). ++ * ++ * Copyright 2008 Dmitry Baryshkov ++ * Copyright 2006 Wolfson Microelectronics PLC. ++ * Author: Mike Arthur ++ * linux@wolfsonmicro.com ++ * ++ * This program is free software; you can redistribute it and/or modify it ++ * under the terms of the GNU General Public License as published by the ++ * Free Software Foundation; either version 2 of the License, or (at your ++ * option) any later version. ++ * ++ * Revision history ++ * 1st Sep 2006 Initial version. ++ * ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/wm97xx.h> ++#include <linux/fb.h> ++ ++#include <asm/arch/tosa.h> ++#include <asm/gpio.h> ++ ++static unsigned long hsync_time = 0; ++ ++static void calc_hsync_time(const struct fb_videomode *mode) ++{ ++ /* The 25 and 44 'magic numbers' are from Sharp's 2.4 patches */ ++ if (mode->yres == 640) { ++ hsync_time = 25; ++ } else if (mode->yres == 320) { ++ hsync_time = 44; ++ } else { ++ printk(KERN_ERR "unknown video mode res specified: %dx%d!", mode->xres, mode->yres); ++ WARN_ON(1); ++ } ++ ++ printk(KERN_ERR "tosa-wm97xx: using %lu hsync time\n", hsync_time); ++} ++ ++static int fb_notifier_callback(struct notifier_block *self, ++ unsigned long event, void *_data) ++{ ++ if (event != FB_EVENT_MODE_CHANGE && event != FB_EVENT_MODE_CHANGE_ALL) ++ return 0; ++ ++ calc_hsync_time(tosa_lcd_get_mode()); ++ ++ return 0; ++} ++ ++static void tosa_lcd_wait_hsync(void) ++{ ++ /* Waits for a rising edge on the VGA line */ ++ while (gpio_get_value(TOSA_GPIO_VGA_LINE) == 0); ++ while (gpio_get_value(TOSA_GPIO_VGA_LINE) != 0); ++} ++ ++/* Taken from the Sharp 2.4 kernel code */ ++#define CCNT(a) asm volatile ("mrc p14, 0, %0, C1, C1, 0" : "=r"(a)) ++#define CCNT_ON() asm("mcr p14, 0, %0, C0, C0, 0" : : "r"(1)) ++#define CCNT_OFF() asm("mcr p14, 0, %0, C0, C0, 0" : : "r"(0)) ++ ++/* On the Sharp SL-6000 (Tosa), due to a noisy LCD, we need to perform a wait ++ * before sampling the Y axis of the touchscreen */ ++static void tosa_lcd_sync_on(int adcsel) ++{ ++ unsigned long timer1 = 0, timer2 = 0, wait_time = 0; ++ if (adcsel & WM97XX_ADCSEL_Y) { ++ CCNT_ON(); ++ wait_time = hsync_time; ++ ++ if (wait_time) { ++ ++ /* wait for LCD rising edge */ ++ tosa_lcd_wait_hsync(); ++ /* get clock */ ++ CCNT(timer1); ++ CCNT(timer2); ++ ++ while ((timer2 - timer1) < wait_time) { ++ CCNT(timer2); ++ } ++ } ++ } ++} ++ ++static void tosa_lcd_sync_off(int adcsel) ++{ ++ if (adcsel & WM97XX_ADCSEL_Y) ++ CCNT_OFF(); ++} ++ ++static struct wm97xx_mach_ops tosa_mach_ops = { ++ .pre_sample = tosa_lcd_sync_on, ++ .post_sample = tosa_lcd_sync_off, ++}; ++ ++static int __devinit tosa_ts_probe(struct platform_device *dev) { ++ struct wm97xx *wm = platform_get_drvdata(dev); ++ struct notifier_block *notif; ++ int err = -ENOMEM; ++ ++ notif = kzalloc(sizeof(struct notifier_block), GFP_KERNEL); ++ if (!notif) ++ goto err_alloc; ++ ++ notif->notifier_call = fb_notifier_callback; ++ ++ err = gpio_request(TOSA_GPIO_VGA_LINE, "hsync"); ++ if (err) ++ goto err_gpio; ++ ++ err = gpio_direction_input(TOSA_GPIO_VGA_LINE); ++ if (err) ++ goto err_gpio; ++ ++ platform_set_drvdata(dev, notif); ++ ++ err = fb_register_client(notif); ++ if (err) ++ goto err_register; ++ ++ err = wm97xx_register_mach_ops(wm, &tosa_mach_ops); ++ if (err) ++ goto err_wm97xx; ++ ++ calc_hsync_time(tosa_lcd_get_mode()); ++ ++ return 0; ++ ++err_wm97xx: ++ fb_unregister_client(notif); ++err_register: ++ gpio_free(TOSA_GPIO_VGA_LINE); ++err_gpio: ++ kfree(notif); ++err_alloc: ++ return err; ++} ++ ++ ++static int __devexit tosa_ts_remove(struct platform_device *dev) { ++ struct wm97xx *wm = platform_get_drvdata(dev); ++ ++ wm97xx_unregister_mach_ops(wm); ++ ++ fb_unregister_client(platform_get_drvdata(dev)); ++ gpio_free(TOSA_GPIO_VGA_LINE); ++ kfree(platform_get_drvdata(dev)); ++ ++ return 0; ++} ++ ++static struct platform_driver tosa_ts_driver = { ++ .driver.name = "wm97xx-touch", ++ .driver.owner = THIS_MODULE, ++ .probe = tosa_ts_probe, ++ .remove = __devexit_p(tosa_ts_remove), ++}; ++ ++static int __init tosa_ts_init(void) ++{ ++ return platform_driver_register(&tosa_ts_driver); ++} ++ ++static void __exit tosa_ts_exit(void) ++{ ++ platform_driver_unregister(&tosa_ts_driver); ++} ++ ++module_init(tosa_ts_init); ++module_exit(tosa_ts_exit); ++ ++/* Module information */ ++MODULE_AUTHOR("Dmitry Baryshkov, Mike Arthur, mike@mikearthur.co.uk, www.wolfsonmicro.com"); ++MODULE_DESCRIPTION("Sharp SL6000 Tosa Touch Screen Denoiser"); ++MODULE_LICENSE("GPL"); +-- +1.5.4.1 + diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/defconfig b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/defconfig new file mode 100644 index 0000000000..9513c418f9 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/defconfig @@ -0,0 +1,1137 @@ +# +# Automatically generated make config: don't edit +# Linux kernel version: 2.6.24 +# Mon Dec 8 00:58:55 2008 +# +CONFIG_ARM=y +CONFIG_SYS_SUPPORTS_APM_EMULATION=y +CONFIG_GENERIC_GPIO=y +CONFIG_GENERIC_TIME=y +CONFIG_GENERIC_CLOCKEVENTS=y +CONFIG_MMU=y +# CONFIG_NO_IOPORT is not set +CONFIG_GENERIC_HARDIRQS=y +CONFIG_STACKTRACE_SUPPORT=y +CONFIG_LOCKDEP_SUPPORT=y +CONFIG_TRACE_IRQFLAGS_SUPPORT=y +CONFIG_HARDIRQS_SW_RESEND=y +CONFIG_GENERIC_IRQ_PROBE=y +CONFIG_RWSEM_GENERIC_SPINLOCK=y +# CONFIG_ARCH_HAS_ILOG2_U32 is not set +# CONFIG_ARCH_HAS_ILOG2_U64 is not set +CONFIG_GENERIC_HWEIGHT=y +CONFIG_GENERIC_CALIBRATE_DELAY=y +CONFIG_ZONE_DMA=y +CONFIG_ARCH_MTD_XIP=y +CONFIG_VECTORS_BASE=0xffff0000 +CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" + +# +# General setup +# +CONFIG_EXPERIMENTAL=y +CONFIG_BROKEN_ON_SMP=y +CONFIG_LOCK_KERNEL=y +CONFIG_INIT_ENV_ARG_LIMIT=32 +CONFIG_LOCALVERSION="" +CONFIG_LOCALVERSION_AUTO=y +# CONFIG_SWAP is not set +# CONFIG_SYSVIPC is not set +# CONFIG_POSIX_MQUEUE is not set +# CONFIG_BSD_PROCESS_ACCT is not set +# CONFIG_TASKSTATS is not set +# CONFIG_USER_NS is not set +# CONFIG_PID_NS is not set +# CONFIG_AUDIT is not set +# CONFIG_IKCONFIG is not set +CONFIG_LOG_BUF_SHIFT=14 +# CONFIG_CGROUPS is not set +CONFIG_FAIR_GROUP_SCHED=y +CONFIG_FAIR_USER_SCHED=y +# CONFIG_FAIR_CGROUP_SCHED is not set +# CONFIG_SYSFS_DEPRECATED is not set +# CONFIG_RELAY is not set +CONFIG_BLK_DEV_INITRD=y +CONFIG_INITRAMFS_SOURCE="initramfs.cpio.gz" +CONFIG_INITRAMFS_ROOT_UID=0 +CONFIG_INITRAMFS_ROOT_GID=0 +CONFIG_CC_OPTIMIZE_FOR_SIZE=y +CONFIG_SYSCTL=y +CONFIG_HAVE_CLOCK_LIB=y +CONFIG_EMBEDDED=y +# CONFIG_UID16 is not set +# CONFIG_SYSCTL_SYSCALL is not set +# CONFIG_KALLSYMS is not set +CONFIG_HOTPLUG=y +CONFIG_PRINTK=y +CONFIG_BUG=y +# CONFIG_ELF_CORE is not set +CONFIG_BASE_FULL=y +CONFIG_FUTEX=y +CONFIG_ANON_INODES=y +CONFIG_EPOLL=y +CONFIG_SIGNALFD=y +CONFIG_EVENTFD=y +CONFIG_SHMEM=y +# CONFIG_VM_EVENT_COUNTERS is not set +CONFIG_SLAB=y +# CONFIG_SLUB is not set +# CONFIG_SLOB is not set +CONFIG_SLABINFO=y +CONFIG_RT_MUTEXES=y +# CONFIG_TINY_SHMEM is not set +CONFIG_BASE_SMALL=0 +# CONFIG_MODULES is not set +CONFIG_BLOCK=y +# CONFIG_LBD is not set +# CONFIG_BLK_DEV_IO_TRACE is not set +# CONFIG_LSF is not set +# CONFIG_BLK_DEV_BSG is not set + +# +# IO Schedulers +# +CONFIG_IOSCHED_NOOP=y +# CONFIG_IOSCHED_AS is not set +# CONFIG_IOSCHED_DEADLINE is not set +# CONFIG_IOSCHED_CFQ is not set +# CONFIG_DEFAULT_AS is not set +# CONFIG_DEFAULT_DEADLINE is not set +# CONFIG_DEFAULT_CFQ is not set +CONFIG_DEFAULT_NOOP=y +CONFIG_DEFAULT_IOSCHED="noop" + +# +# System Type +# +# CONFIG_ARCH_AAEC2000 is not set +# CONFIG_ARCH_INTEGRATOR is not set +# CONFIG_ARCH_REALVIEW is not set +# CONFIG_ARCH_VERSATILE is not set +# CONFIG_ARCH_AT91 is not set +# CONFIG_ARCH_CLPS7500 is not set +# CONFIG_ARCH_CLPS711X is not set +# CONFIG_ARCH_CO285 is not set +# CONFIG_ARCH_EBSA110 is not set +# CONFIG_ARCH_EP93XX is not set +# CONFIG_ARCH_FOOTBRIDGE is not set +# CONFIG_ARCH_NETX is not set +# CONFIG_ARCH_H720X is not set +# CONFIG_ARCH_IMX is not set +# CONFIG_ARCH_IOP13XX is not set +# CONFIG_ARCH_IOP32X is not set +# CONFIG_ARCH_IOP33X is not set +# CONFIG_ARCH_IXP23XX is not set +# CONFIG_ARCH_IXP2000 is not set +# CONFIG_ARCH_IXP4XX is not set +# CONFIG_ARCH_L7200 is not set +# CONFIG_ARCH_KS8695 is not set +# CONFIG_ARCH_NS9XXX is not set +# CONFIG_ARCH_MXC is not set +# CONFIG_ARCH_PNX4008 is not set +CONFIG_ARCH_PXA=y +# CONFIG_ARCH_RPC is not set +# CONFIG_ARCH_SA1100 is not set +# CONFIG_ARCH_S3C2410 is not set +# CONFIG_ARCH_SHARK is not set +# CONFIG_ARCH_LH7A40X is not set +# CONFIG_ARCH_DAVINCI is not set +# CONFIG_ARCH_OMAP is not set + +# +# Intel PXA2xx/PXA3xx Implementations +# +# CONFIG_ARCH_LUBBOCK is not set +# CONFIG_MACH_LOGICPD_PXA270 is not set +# CONFIG_MACH_MAINSTONE is not set +# CONFIG_ARCH_PXA_IDP is not set +CONFIG_PXA_SHARPSL=y +# CONFIG_MACH_TRIZEPS4 is not set +# CONFIG_MACH_HX2750 is not set +# CONFIG_MACH_EM_X270 is not set +# CONFIG_MACH_ZYLONITE is not set +# CONFIG_MACH_ARMCORE is not set +CONFIG_PXA_SHARPSL_25x=y +# CONFIG_PXA_SHARPSL_27x is not set +# CONFIG_MACH_HTCUNIVERSAL is not set +# CONFIG_MACH_POODLE is not set +# CONFIG_MACH_CORGI is not set +# CONFIG_MACH_SHEPHERD is not set +# CONFIG_MACH_HUSKY is not set +CONFIG_MACH_TOSA=y +CONFIG_PXA25x=y +CONFIG_PXA_SSP=y +# CONFIG_PXA_KEYS is not set + +# +# Boot options +# + +# +# Power management +# + +# +# Processor Type +# +CONFIG_CPU_32=y +CONFIG_CPU_XSCALE=y +CONFIG_CPU_32v5=y +CONFIG_CPU_ABRT_EV5T=y +CONFIG_CPU_CACHE_VIVT=y +CONFIG_CPU_TLB_V4WBI=y +CONFIG_CPU_CP15=y +CONFIG_CPU_CP15_MMU=y + +# +# Processor Features +# +CONFIG_ARM_THUMB=y +# CONFIG_CPU_DCACHE_DISABLE is not set +# CONFIG_OUTER_CACHE is not set +# CONFIG_IWMMXT is not set +CONFIG_XSCALE_PMU=y +CONFIG_SHARP_PARAM=y +CONFIG_SHARP_SCOOP=y + +# +# Bus support +# +# CONFIG_PCI_SYSCALL is not set +# CONFIG_ARCH_SUPPORTS_MSI is not set +CONFIG_PCCARD=y +# CONFIG_PCMCIA_DEBUG is not set +CONFIG_PCMCIA=y +CONFIG_PCMCIA_LOAD_CIS=y +# CONFIG_PCMCIA_IOCTL is not set + +# +# PC-card bridges +# +CONFIG_PCMCIA_PXA2XX=y + +# +# Kernel Features +# +CONFIG_TICK_ONESHOT=y +# CONFIG_NO_HZ is not set +# CONFIG_HIGH_RES_TIMERS is not set +CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +CONFIG_PREEMPT=y +CONFIG_HZ=100 +CONFIG_AEABI=y +# CONFIG_OABI_COMPAT is not set +# CONFIG_ARCH_DISCONTIGMEM_ENABLE is not set +CONFIG_SELECT_MEMORY_MODEL=y +CONFIG_FLATMEM_MANUAL=y +# CONFIG_DISCONTIGMEM_MANUAL is not set +# CONFIG_SPARSEMEM_MANUAL is not set +CONFIG_FLATMEM=y +CONFIG_FLAT_NODE_MEM_MAP=y +# CONFIG_SPARSEMEM_STATIC is not set +# CONFIG_SPARSEMEM_VMEMMAP_ENABLE is not set +CONFIG_SPLIT_PTLOCK_CPUS=4096 +# CONFIG_RESOURCES_64BIT is not set +CONFIG_ZONE_DMA_FLAG=1 +CONFIG_BOUNCE=y +CONFIG_VIRT_TO_BUS=y +CONFIG_ALIGNMENT_TRAP=y + +# +# Boot options +# +CONFIG_ZBOOT_ROM_TEXT=0x0 +CONFIG_ZBOOT_ROM_BSS=0x0 +CONFIG_CMDLINE="console=ttyS0,115200n8 console=tty1 debug " +# CONFIG_XIP_KERNEL is not set +CONFIG_KEXEC=y +CONFIG_ATAGS_PROC=y + +# +# CPU Frequency scaling +# +# CONFIG_CPU_FREQ is not set + +# +# Floating point emulation +# + +# +# At least one emulation must be selected +# + +# +# Userspace binary formats +# +CONFIG_BINFMT_ELF=y +# CONFIG_BINFMT_AOUT is not set +# CONFIG_BINFMT_MISC is not set + +# +# Power management options +# +CONFIG_PM=y +# CONFIG_PM_LEGACY is not set +# CONFIG_PM_DEBUG is not set +CONFIG_PM_SLEEP=y +CONFIG_SUSPEND_UP_POSSIBLE=y +CONFIG_SUSPEND=y +CONFIG_APM_EMULATION=y + +# +# Networking +# +CONFIG_NET=y + +# +# Networking options +# +# CONFIG_PACKET is not set +CONFIG_UNIX=y +# CONFIG_NET_KEY is not set +CONFIG_INET=y +# CONFIG_IP_MULTICAST is not set +# CONFIG_IP_ADVANCED_ROUTER is not set +CONFIG_IP_FIB_HASH=y +# CONFIG_IP_PNP is not set +# CONFIG_NET_IPIP is not set +# CONFIG_NET_IPGRE is not set +# CONFIG_ARPD is not set +CONFIG_SYN_COOKIES=y +# CONFIG_INET_AH is not set +# CONFIG_INET_ESP is not set +# CONFIG_INET_IPCOMP is not set +# CONFIG_INET_XFRM_TUNNEL is not set +# CONFIG_INET_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set +# CONFIG_INET_XFRM_MODE_TUNNEL is not set +# CONFIG_INET_XFRM_MODE_BEET is not set +# CONFIG_INET_LRO is not set +# CONFIG_INET_DIAG is not set +# CONFIG_TCP_CONG_ADVANCED is not set +CONFIG_TCP_CONG_CUBIC=y +CONFIG_DEFAULT_TCP_CONG="cubic" +# CONFIG_TCP_MD5SIG is not set +# CONFIG_IP_VS is not set +# CONFIG_IPV6 is not set +# CONFIG_INET6_XFRM_TUNNEL is not set +# CONFIG_INET6_TUNNEL is not set +# CONFIG_NETWORK_SECMARK is not set +CONFIG_NETFILTER=y +# CONFIG_NETFILTER_DEBUG is not set + +# +# Core Netfilter Configuration +# +# CONFIG_NETFILTER_NETLINK is not set +# CONFIG_NF_CONNTRACK_ENABLED is not set +# CONFIG_NF_CONNTRACK is not set +# CONFIG_NETFILTER_XTABLES is not set + +# +# IP: Netfilter Configuration +# +# CONFIG_IP_NF_QUEUE is not set +# CONFIG_IP_NF_IPTABLES is not set +# CONFIG_IP_NF_ARPTABLES is not set +# CONFIG_IP_DCCP is not set +# CONFIG_IP_SCTP is not set +# CONFIG_TIPC is not set +# CONFIG_ATM is not set +# CONFIG_BRIDGE is not set +# CONFIG_VLAN_8021Q is not set +# CONFIG_DECNET is not set +# CONFIG_LLC2 is not set +# CONFIG_IPX is not set +# CONFIG_ATALK is not set +# CONFIG_X25 is not set +# CONFIG_LAPB is not set +# CONFIG_ECONET is not set +# CONFIG_WAN_ROUTER is not set +# CONFIG_NET_SCHED is not set + +# +# Network testing +# +# CONFIG_NET_PKTGEN is not set +# CONFIG_HAMRADIO is not set +# CONFIG_IRDA is not set +# CONFIG_BT is not set +# CONFIG_AF_RXRPC is not set + +# +# Wireless +# +# CONFIG_CFG80211 is not set +CONFIG_WIRELESS_EXT=y +# CONFIG_MAC80211 is not set +# CONFIG_IEEE80211 is not set +# CONFIG_RFKILL is not set +# CONFIG_NET_9P is not set + +# +# Device Drivers +# + +# +# Generic Driver Options +# +CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +CONFIG_STANDALONE=y +CONFIG_PREVENT_FIRMWARE_BUILD=y +CONFIG_FW_LOADER=y +# CONFIG_SYS_HYPERVISOR is not set +# CONFIG_CONNECTOR is not set +CONFIG_MTD=y +# CONFIG_MTD_DEBUG is not set +# CONFIG_MTD_CONCAT is not set +CONFIG_MTD_PARTITIONS=y +# CONFIG_MTD_REDBOOT_PARTS is not set +# CONFIG_MTD_CMDLINE_PARTS is not set +# CONFIG_MTD_AFS_PARTS is not set + +# +# User Modules And Translation Layers +# +CONFIG_MTD_CHAR=y +CONFIG_MTD_BLKDEVS=y +CONFIG_MTD_BLOCK=y +# CONFIG_FTL is not set +# CONFIG_NFTL is not set +# CONFIG_INFTL is not set +# CONFIG_RFD_FTL is not set +# CONFIG_SSFDC is not set +# CONFIG_MTD_OOPS 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=y +# CONFIG_MTD_ABSENT is not set + +# +# Mapping drivers for chip access +# +# CONFIG_MTD_COMPLEX_MAPPINGS is not set +# CONFIG_MTD_PHYSMAP is not set +CONFIG_MTD_SHARP_SL=y +# CONFIG_MTD_PLATRAM is not set + +# +# Self-contained MTD device drivers +# +# CONFIG_MTD_SLRAM is not set +# CONFIG_MTD_PHRAM is not set +# CONFIG_MTD_MTDRAM is not set +# CONFIG_MTD_BLOCK2MTD is not set + +# +# Disk-On-Chip Device Drivers +# +# CONFIG_MTD_DOC2000 is not set +# CONFIG_MTD_DOC2001 is not set +# CONFIG_MTD_DOC2001PLUS is not set +CONFIG_MTD_NAND=y +CONFIG_MTD_NAND_VERIFY_WRITE=y +# CONFIG_MTD_NAND_ECC_SMC is not set +# CONFIG_MTD_NAND_MUSEUM_IDS is not set +# CONFIG_MTD_NAND_H1900 is not set +CONFIG_MTD_NAND_IDS=y +# CONFIG_MTD_NAND_DISKONCHIP is not set +# CONFIG_MTD_NAND_SHARPSL is not set +CONFIG_MTD_NAND_TMIO=y +# CONFIG_MTD_NAND_NANDSIM is not set +# CONFIG_MTD_NAND_PLATFORM is not set +# CONFIG_MTD_ONENAND is not set + +# +# UBI - Unsorted block images +# +# CONFIG_MTD_UBI is not set +# CONFIG_PARPORT is not set +CONFIG_BLK_DEV=y +# CONFIG_BLK_DEV_COW_COMMON is not set +CONFIG_BLK_DEV_LOOP=y +# CONFIG_BLK_DEV_CRYPTOLOOP is not set +# CONFIG_BLK_DEV_NBD is not set +# CONFIG_BLK_DEV_RAM is not set +# CONFIG_CDROM_PKTCDVD is not set +# CONFIG_ATA_OVER_ETH is not set +CONFIG_MISC_DEVICES=y +# CONFIG_EEPROM_93CX6 is not set +CONFIG_IDE=y +CONFIG_IDE_MAX_HWIFS=4 +CONFIG_BLK_DEV_IDE=y + +# +# Please see Documentation/ide.txt for help/info on IDE drives +# +# CONFIG_BLK_DEV_IDE_SATA is not set +CONFIG_BLK_DEV_IDEDISK=y +# CONFIG_IDEDISK_MULTI_MODE is not set +CONFIG_BLK_DEV_IDECS=y +# CONFIG_BLK_DEV_IDECD is not set +# CONFIG_BLK_DEV_IDETAPE is not set +# CONFIG_BLK_DEV_IDEFLOPPY is not set +# CONFIG_IDE_TASK_IOCTL is not set +CONFIG_IDE_PROC_FS=y + +# +# IDE chipset support/bugfixes +# +# CONFIG_IDE_GENERIC is not set +# CONFIG_BLK_DEV_PLATFORM is not set +# CONFIG_IDE_ARM is not set +# CONFIG_BLK_DEV_IDEDMA is not set +CONFIG_IDE_ARCH_OBSOLETE_INIT=y +# CONFIG_BLK_DEV_HD is not set + +# +# SCSI device support +# +# CONFIG_RAID_ATTRS is not set +# CONFIG_SCSI is not set +# CONFIG_SCSI_DMA is not set +# CONFIG_SCSI_NETLINK is not set +# CONFIG_ATA is not set +# CONFIG_MD is not set +CONFIG_NETDEVICES=y +# CONFIG_NETDEVICES_MULTIQUEUE is not set +# CONFIG_DUMMY is not set +# CONFIG_BONDING is not set +# CONFIG_MACVLAN is not set +# CONFIG_EQUALIZER is not set +# CONFIG_TUN is not set +# CONFIG_VETH is not set +# CONFIG_PHYLIB is not set +CONFIG_NET_ETHERNET=y +# CONFIG_MII is not set +# CONFIG_AX88796 is not set +# CONFIG_SMC91X is not set +# CONFIG_DM9000 is not set +# CONFIG_SMC911X is not set +# CONFIG_IBM_NEW_EMAC_ZMII is not set +# CONFIG_IBM_NEW_EMAC_RGMII is not set +# CONFIG_IBM_NEW_EMAC_TAH is not set +# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +# CONFIG_B44 is not set +CONFIG_NETDEV_1000=y +CONFIG_NETDEV_10000=y + +# +# Wireless LAN +# +# CONFIG_WLAN_PRE80211 is not set +# CONFIG_WLAN_80211 is not set +CONFIG_NET_PCMCIA=y +# CONFIG_PCMCIA_3C589 is not set +# CONFIG_PCMCIA_3C574 is not set +# CONFIG_PCMCIA_FMVJ18X is not set +# CONFIG_PCMCIA_PCNET is not set +# CONFIG_PCMCIA_NMCLAN is not set +# CONFIG_PCMCIA_SMC91C92 is not set +# CONFIG_PCMCIA_XIRC2PS is not set +# CONFIG_PCMCIA_AXNET is not set +# CONFIG_WAN is not set +# CONFIG_PPP is not set +# CONFIG_SLIP is not set +# CONFIG_SHAPER is not set +# CONFIG_NETCONSOLE is not set +# CONFIG_NETPOLL is not set +# CONFIG_NET_POLL_CONTROLLER is not set +# CONFIG_ISDN is not set + +# +# Input device support +# +CONFIG_INPUT=y +# CONFIG_INPUT_FF_MEMLESS is not set +# CONFIG_INPUT_POLLDEV is not set + +# +# Userland interfaces +# +# CONFIG_INPUT_MOUSEDEV is not set +# CONFIG_INPUT_JOYDEV is not set +CONFIG_INPUT_EVDEV=y +# CONFIG_INPUT_EVBUG is not set +# CONFIG_INPUT_POWER is not set + +# +# Input Device Drivers +# +CONFIG_INPUT_KEYBOARD=y +# CONFIG_KEYBOARD_ATKBD is not set +# CONFIG_KEYBOARD_SUNKBD is not set +# CONFIG_KEYBOARD_LKKBD is not set +# CONFIG_KEYBOARD_XTKBD is not set +# CONFIG_KEYBOARD_NEWTON is not set +# CONFIG_KEYBOARD_STOWAWAY is not set +# CONFIG_KEYBOARD_CORGI is not set +# CONFIG_KEYBOARD_SPITZ is not set +# CONFIG_SHARPSL_RC is not set +CONFIG_KEYBOARD_TOSA=y +# CONFIG_KEYBOARD_TOSA_USE_EXT_KEYCODES is not set +# CONFIG_KEYBOARD_GPIO is not set +# CONFIG_INPUT_MOUSE is not set +# CONFIG_INPUT_JOYSTICK is not set +# CONFIG_INPUT_TABLET is not set +# CONFIG_INPUT_TOUCHSCREEN is not set +CONFIG_INPUT_MISC=y +# CONFIG_INPUT_ATI_REMOTE is not set +# CONFIG_INPUT_ATI_REMOTE2 is not set +# CONFIG_INPUT_KEYSPAN_REMOTE is not set +# CONFIG_INPUT_POWERMATE is not set +# CONFIG_INPUT_YEALINK is not set +# CONFIG_INPUT_UINPUT is not set + +# +# Hardware I/O ports +# +# CONFIG_SERIO is not set +# CONFIG_GAMEPORT is not set + +# +# Character devices +# +CONFIG_VT=y +CONFIG_VT_CONSOLE=y +CONFIG_HW_CONSOLE=y +CONFIG_VT_HW_CONSOLE_BINDING=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 is not set +# CONFIG_IPMI_HANDLER is not set +# CONFIG_HW_RANDOM is not set +# CONFIG_NVRAM is not set +# CONFIG_R3964 is not set + +# +# PCMCIA character devices +# +# CONFIG_SYNCLINK_CS is not set +# CONFIG_CARDMAN_4000 is not set +# CONFIG_CARDMAN_4040 is not set +# CONFIG_RAW_DRIVER is not set +# CONFIG_TCG_TPM is not set +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +# CONFIG_I2C_CHARDEV is not set + +# +# I2C Algorithms +# +# CONFIG_I2C_ALGOBIT is not set +# CONFIG_I2C_ALGOPCF is not set +# CONFIG_I2C_ALGOPCA is not set + +# +# I2C Hardware Bus support +# +# CONFIG_I2C_GPIO is not set +CONFIG_I2C_PXA=y +# CONFIG_I2C_PXA_SLAVE is not set +# CONFIG_I2C_OCORES is not set +# CONFIG_I2C_PARPORT_LIGHT is not set +# CONFIG_I2C_SIMTEC is not set +# CONFIG_I2C_TAOS_EVM is not set + +# +# Miscellaneous I2C Chip support +# +# CONFIG_SENSORS_DS1337 is not set +# CONFIG_SENSORS_DS1374 is not set +# CONFIG_DS1682 is not set +# CONFIG_SENSORS_EEPROM is not set +# CONFIG_SENSORS_PCF8574 is not set +# CONFIG_SENSORS_PCA9539 is not set +# CONFIG_SENSORS_PCF8591 is not set +# CONFIG_SENSORS_MAX6875 is not set +# CONFIG_SENSORS_TSL2550 is not set +# CONFIG_I2C_DEBUG_CORE is not set +# CONFIG_I2C_DEBUG_ALGO is not set +# CONFIG_I2C_DEBUG_BUS is not set +# CONFIG_I2C_DEBUG_CHIP is not set + +# +# SPI support +# +# CONFIG_SPI is not set +# CONFIG_SPI_MASTER is not set +CONFIG_HAVE_GPIO_LIB=y + +# +# GPIO Support +# + +# +# I2C GPIO expanders: +# + +# +# SPI GPIO expanders: +# +# CONFIG_W1 is not set +# CONFIG_POWER_SUPPLY is not set +# CONFIG_HWMON is not set +# CONFIG_WATCHDOG is not set + +# +# Sonics Silicon Backplane +# +CONFIG_SSB_POSSIBLE=y +# CONFIG_SSB is not set + +# +# Multifunction device drivers +# +CONFIG_MFD_CORE=y +# CONFIG_MFD_T7L66XB is not set +# CONFIG_MFD_TC6387XB is not set +CONFIG_MFD_TC6393XB=y +# CONFIG_MFD_SM501 is not set +# CONFIG_HTC_ASIC3 is not set +# CONFIG_HTC_ASIC3_DS1WM is not set + +# +# Multimedia devices +# +# CONFIG_VIDEO_DEV is not set +# CONFIG_DVB_CORE is not set +# CONFIG_DAB is not set + +# +# Graphics support +# +# CONFIG_VGASTATE is not set +# CONFIG_VIDEO_OUTPUT_CONTROL is not set +CONFIG_FB=y +# CONFIG_FIRMWARE_EDID is not set +# CONFIG_FB_DDC is not set +CONFIG_FB_CFB_FILLRECT=y +CONFIG_FB_CFB_COPYAREA=y +CONFIG_FB_CFB_IMAGEBLIT=y +# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +# CONFIG_FB_SYS_FILLRECT is not set +# CONFIG_FB_SYS_COPYAREA is not set +# CONFIG_FB_SYS_IMAGEBLIT is not set +# CONFIG_FB_SYS_FOPS is not set +# CONFIG_FB_DEFERRED_IO is not set +# CONFIG_FB_SVGALIB is not set +# CONFIG_FB_MACMODES is not set +# CONFIG_FB_BACKLIGHT is not set +# CONFIG_FB_MODE_HELPERS is not set +# CONFIG_FB_TILEBLITTING is not set + +# +# Frame buffer hardware drivers +# +# CONFIG_FB_S1D13XXX is not set +# CONFIG_FB_PXA is not set +# CONFIG_FB_MBX is not set +# CONFIG_FB_W100 is not set +CONFIG_FB_TMIO=y +# CONFIG_FB_TMIO_ACCELL is not set +# CONFIG_FB_VIRTUAL is not set +CONFIG_BACKLIGHT_LCD_SUPPORT=y +# CONFIG_LCD_CLASS_DEVICE is not set +CONFIG_BACKLIGHT_CLASS_DEVICE=y +# CONFIG_BACKLIGHT_CORGI is not set +CONFIG_BACKLIGHT_TOSA=y + +# +# Display device support +# +# CONFIG_DISPLAY_SUPPORT is not set + +# +# Console display driver support +# +# CONFIG_VGA_CONSOLE is not set +CONFIG_DUMMY_CONSOLE=y +CONFIG_FRAMEBUFFER_CONSOLE=y +# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set +# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set +CONFIG_FONTS=y +CONFIG_FONT_8x8=y +# CONFIG_FONT_8x16 is not set +# CONFIG_FONT_6x11 is not set +# CONFIG_FONT_7x14 is not set +# CONFIG_FONT_PEARL_8x8 is not set +# CONFIG_FONT_ACORN_8x8 is not set +# CONFIG_FONT_MINI_4x6 is not set +# CONFIG_FONT_SUN8x16 is not set +# CONFIG_FONT_SUN12x22 is not set +# CONFIG_FONT_10x18 is not set +# CONFIG_LOGO is not set + +# +# Sound +# +CONFIG_SOUND=y + +# +# Advanced Linux Sound Architecture +# +CONFIG_SND=y +CONFIG_SND_TIMER=y +CONFIG_SND_PCM=y +# CONFIG_SND_SEQUENCER is not set +# CONFIG_SND_MIXER_OSS is not set +# CONFIG_SND_PCM_OSS is not set +# CONFIG_SND_DYNAMIC_MINORS is not set +# CONFIG_SND_SUPPORT_OLD_API is not set +# CONFIG_SND_VERBOSE_PROCFS is not set +# CONFIG_SND_VERBOSE_PRINTK is not set +# CONFIG_SND_DEBUG is not set + +# +# Generic devices +# +# CONFIG_SND_DUMMY is not set +# CONFIG_SND_MTPAV is not set +# CONFIG_SND_SERIAL_U16550 is not set +# CONFIG_SND_MPU401 is not set + +# +# ALSA ARM devices +# +# CONFIG_SND_PXA2XX_AC97 is not set + +# +# PCMCIA devices +# +# CONFIG_SND_VXPOCKET is not set +# CONFIG_SND_PDAUDIOCF is not set + +# +# System on Chip audio support +# +CONFIG_SND_SOC_AC97_BUS=y +CONFIG_SND_SOC=y +CONFIG_SND_PXA2XX_SOC=y +CONFIG_SND_PXA2XX_SOC_AC97=y +CONFIG_SND_PXA2XX_SOC_TOSA=y + +# +# SoC Audio support for SuperH +# +CONFIG_SND_SOC_WM9712=y + +# +# Open Sound System +# +# CONFIG_SOUND_PRIME is not set +CONFIG_AC97_BUS=y +# CONFIG_HID_SUPPORT is not set +CONFIG_USB_SUPPORT=y +CONFIG_USB_ARCH_HAS_HCD=y +CONFIG_USB_ARCH_HAS_OHCI=y +# CONFIG_USB_ARCH_HAS_EHCI is not set +# CONFIG_USB is not set + +# +# NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support' +# + +# +# USB Gadget Support +# +# CONFIG_USB_GADGET is not set +CONFIG_MMC=y +# CONFIG_MMC_DEBUG is not set +CONFIG_MMC_UNSAFE_RESUME=y + +# +# MMC/SD Card Drivers +# +CONFIG_MMC_BLOCK=y +CONFIG_MMC_BLOCK_BOUNCE=y +# CONFIG_SDIO_UART is not set + +# +# MMC/SD Host Controller Drivers +# +CONFIG_MMC_PXA=y +# CONFIG_MMC_TMIO is not set +CONFIG_NEW_LEDS=y +CONFIG_LEDS_CLASS=y + +# +# LED drivers +# +CONFIG_LEDS_TOSA=y +# CONFIG_LEDS_GPIO is not set + +# +# LED Triggers +# +CONFIG_LEDS_TRIGGERS=y +# CONFIG_LEDS_TRIGGER_TIMER is not set +CONFIG_LEDS_TRIGGER_IDE_DISK=y +# CONFIG_LEDS_TRIGGER_HEARTBEAT is not set +CONFIG_RTC_LIB=y +CONFIG_RTC_CLASS=y +CONFIG_RTC_HCTOSYS=y +CONFIG_RTC_HCTOSYS_DEVICE="rtc0" +# CONFIG_RTC_DEBUG is not set + +# +# RTC interfaces +# +CONFIG_RTC_INTF_SYSFS=y +CONFIG_RTC_INTF_PROC=y +CONFIG_RTC_INTF_DEV=y +# CONFIG_RTC_INTF_DEV_UIE_EMUL is not set +# CONFIG_RTC_DRV_TEST is not set + +# +# I2C RTC drivers +# +# CONFIG_RTC_DRV_DS1307 is not set +# CONFIG_RTC_DRV_DS1374 is not set +# CONFIG_RTC_DRV_DS1672 is not set +# CONFIG_RTC_DRV_MAX6900 is not set +# CONFIG_RTC_DRV_RS5C372 is not set +# CONFIG_RTC_DRV_ISL1208 is not set +# CONFIG_RTC_DRV_X1205 is not set +# CONFIG_RTC_DRV_PCF8563 is not set +# CONFIG_RTC_DRV_PCF8583 is not set +# CONFIG_RTC_DRV_M41T80 is not set + +# +# SPI RTC drivers +# + +# +# Platform RTC drivers +# +# CONFIG_RTC_DRV_CMOS is not set +# CONFIG_RTC_DRV_DS1553 is not set +# CONFIG_RTC_DRV_STK17TA8 is not set +# CONFIG_RTC_DRV_DS1742 is not set +# CONFIG_RTC_DRV_M48T86 is not set +# CONFIG_RTC_DRV_M48T59 is not set +# CONFIG_RTC_DRV_V3020 is not set + +# +# on-CPU RTC drivers +# +CONFIG_RTC_DRV_SA1100=y + +# +# File systems +# +CONFIG_EXT2_FS=y +# CONFIG_EXT2_FS_XATTR is not set +# CONFIG_EXT2_FS_XIP is not set +CONFIG_EXT3_FS=y +# CONFIG_EXT3_FS_XATTR is not set +# CONFIG_EXT4DEV_FS is not set +CONFIG_JBD=y +# CONFIG_REISERFS_FS is not set +# CONFIG_JFS_FS is not set +# CONFIG_FS_POSIX_ACL is not set +# CONFIG_XFS_FS is not set +# CONFIG_GFS2_FS is not set +# CONFIG_OCFS2_FS is not set +# CONFIG_MINIX_FS is not set +# CONFIG_ROMFS_FS is not set +CONFIG_INOTIFY=y +CONFIG_INOTIFY_USER=y +# CONFIG_QUOTA is not set +CONFIG_DNOTIFY=y +# CONFIG_AUTOFS_FS is not set +# CONFIG_AUTOFS4_FS is not set +# CONFIG_FUSE_FS is not set + +# +# CD-ROM/DVD Filesystems +# +# CONFIG_ISO9660_FS is not set +# CONFIG_UDF_FS is not set + +# +# DOS/FAT/NT Filesystems +# +# CONFIG_MSDOS_FS is not set +# CONFIG_VFAT_FS is not set +# CONFIG_NTFS_FS is not set + +# +# Pseudo filesystems +# +CONFIG_PROC_FS=y +CONFIG_PROC_SYSCTL=y +CONFIG_SYSFS=y +CONFIG_TMPFS=y +# CONFIG_TMPFS_POSIX_ACL is not set +# CONFIG_HUGETLB_PAGE is not set +# CONFIG_CONFIGFS_FS is not set + +# +# Miscellaneous filesystems +# +# CONFIG_ADFS_FS is not set +# CONFIG_AFFS_FS is not set +# CONFIG_HFS_FS is not set +# CONFIG_HFSPLUS_FS is not set +# CONFIG_BEFS_FS is not set +# CONFIG_BFS_FS is not set +# CONFIG_EFS_FS is not set +CONFIG_JFFS2_FS=y +CONFIG_JFFS2_FS_DEBUG=0 +CONFIG_JFFS2_FS_WRITEBUFFER=y +# CONFIG_JFFS2_FS_WBUF_VERIFY is not set +CONFIG_JFFS2_SUMMARY=y +# CONFIG_JFFS2_FS_XATTR is not set +# CONFIG_JFFS2_SYSFS is not set +CONFIG_JFFS2_COMPRESSION_OPTIONS=y +CONFIG_JFFS2_ZLIB=y +CONFIG_JFFS2_LZO=y +CONFIG_JFFS2_RTIME=y +CONFIG_JFFS2_RUBIN=y +# CONFIG_JFFS2_CMODE_NONE is not set +CONFIG_JFFS2_CMODE_PRIORITY=y +# CONFIG_JFFS2_CMODE_SIZE is not set +# CONFIG_JFFS2_CMODE_FAVOURLZO is not set +# CONFIG_CRAMFS is not set +# CONFIG_SQUASHFS 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 +# CONFIG_NETWORK_FILESYSTEMS 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_SGI_PARTITION is not set +# CONFIG_ULTRIX_PARTITION is not set +# CONFIG_SUN_PARTITION is not set +# CONFIG_KARMA_PARTITION is not set +# CONFIG_EFI_PARTITION is not set +# CONFIG_SYSV68_PARTITION is not set +# CONFIG_NLS is not set +# CONFIG_DLM is not set +# CONFIG_INSTRUMENTATION is not set + +# +# Kernel hacking +# +# CONFIG_PRINTK_TIME is not set +CONFIG_ENABLE_WARN_DEPRECATED=y +CONFIG_ENABLE_MUST_CHECK=y +CONFIG_MAGIC_SYSRQ=y +# CONFIG_UNUSED_SYMBOLS is not set +# CONFIG_DEBUG_FS is not set +# CONFIG_HEADERS_CHECK is not set +# CONFIG_DEBUG_KERNEL is not set +# CONFIG_DEBUG_BUGVERBOSE is not set +CONFIG_FRAME_POINTER=y +# CONFIG_SAMPLES is not set +# CONFIG_DEBUG_USER is not set + +# +# Security options +# +# CONFIG_KEYS is not set +# CONFIG_SECURITY is not set +# CONFIG_SECURITY_FILE_CAPABILITIES is not set +CONFIG_CRYPTO=y +# CONFIG_CRYPTO_MANAGER is not set +# CONFIG_CRYPTO_HMAC is not set +# CONFIG_CRYPTO_XCBC is not set +# CONFIG_CRYPTO_NULL is not set +# CONFIG_CRYPTO_MD4 is not set +# CONFIG_CRYPTO_MD5 is not set +# CONFIG_CRYPTO_SHA1 is not set +# CONFIG_CRYPTO_SHA256 is not set +# CONFIG_CRYPTO_SHA512 is not set +# CONFIG_CRYPTO_WP512 is not set +# CONFIG_CRYPTO_TGR192 is not set +# CONFIG_CRYPTO_GF128MUL is not set +# CONFIG_CRYPTO_ECB is not set +# CONFIG_CRYPTO_CBC is not set +# CONFIG_CRYPTO_PCBC is not set +# CONFIG_CRYPTO_LRW is not set +# CONFIG_CRYPTO_XTS is not set +# CONFIG_CRYPTO_CRYPTD is not set +# CONFIG_CRYPTO_DES is not set +# CONFIG_CRYPTO_FCRYPT is not set +# CONFIG_CRYPTO_BLOWFISH is not set +# CONFIG_CRYPTO_TWOFISH is not set +# CONFIG_CRYPTO_SERPENT is not set +# CONFIG_CRYPTO_AES is not set +# CONFIG_CRYPTO_CAST5 is not set +# CONFIG_CRYPTO_CAST6 is not set +# CONFIG_CRYPTO_TEA is not set +# CONFIG_CRYPTO_ARC4 is not set +# CONFIG_CRYPTO_KHAZAD is not set +# CONFIG_CRYPTO_ANUBIS is not set +# CONFIG_CRYPTO_SEED is not set +# CONFIG_CRYPTO_DEFLATE is not set +# CONFIG_CRYPTO_LZO is not set +# CONFIG_CRYPTO_MICHAEL_MIC is not set +# CONFIG_CRYPTO_CRC32C is not set +# CONFIG_CRYPTO_CAMELLIA is not set +# CONFIG_CRYPTO_AUTHENC is not set +CONFIG_CRYPTO_HW=y + +# +# Library routines +# +CONFIG_BITREVERSE=y +# CONFIG_CRC_CCITT is not set +# CONFIG_CRC16 is not set +# CONFIG_CRC_ITU_T is not set +CONFIG_CRC32=y +# CONFIG_CRC7 is not set +# CONFIG_LIBCRC32C is not set +CONFIG_ZLIB_INFLATE=y +CONFIG_ZLIB_DEFLATE=y +CONFIG_LZO_COMPRESS=y +CONFIG_LZO_DECOMPRESS=y +CONFIG_PLIST=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +CONFIG_HAS_DMA=y diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/tmiofb-fix-unaccel.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/tmiofb-fix-unaccel.patch new file mode 100644 index 0000000000..d611342170 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/tmiofb-fix-unaccel.patch @@ -0,0 +1,33 @@ +Index: linux-2.6.24/drivers/video/tmiofb.c +=================================================================== +--- linux-2.6.24.orig/drivers/video/tmiofb.c 2008-12-07 22:30:26.600128369 +0300 ++++ linux-2.6.24/drivers/video/tmiofb.c 2008-12-07 22:31:42.537112538 +0300 +@@ -689,14 +689,14 @@ static irqreturn_t tmiofb_irq(int irq, v + unsigned int bbisc = ioread16(&lcr->bbisc); + + ++ iowrite16(bbisc, &lcr->bbisc); ++ ++#ifdef CONFIG_FB_TMIO_ACCELL + if (unlikely(par->use_polling && irq != -1)) { + printk(KERN_INFO "tmiofb: switching to waitq\n"); + par->use_polling = false; + } + +- iowrite16(bbisc, &lcr->bbisc); +- +-#ifdef CONFIG_FB_TMIO_ACCELL + if (bbisc & 1) + wake_up(&par->wait_acc); + #endif +@@ -972,8 +972,10 @@ static int tmiofb_suspend(struct platfor + info->fbops->fb_sync(info); + + ++#ifdef CONFIG_FB_TMIO_ACCELL + printk(KERN_INFO "tmiofb: switching to polling\n"); + par->use_polling = true; ++#endif + tmiofb_hw_stop(dev); + + if (cell->suspend) diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/tosa-bl-fixup.diff b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/tosa-bl-fixup.diff new file mode 100644 index 0000000000..c4a23d1f49 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/tosa/tosa-bl-fixup.diff @@ -0,0 +1,103 @@ +Index: linux-2.6.24/drivers/video/backlight/tosa_bl.c +=================================================================== +--- linux-2.6.24.orig/drivers/video/backlight/tosa_bl.c 2008-11-15 22:59:51.592985003 +0300 ++++ linux-2.6.24/drivers/video/backlight/tosa_bl.c 2008-11-18 04:08:13.021416618 +0300 +@@ -76,6 +76,8 @@ static void pxa_nssp_output(struct tosa_ + + static void tosa_set_backlight(struct tosa_bl_data *data, int brightness) + { ++ pr_debug("tosa_set_backlight\n"); ++ + /* SetBacklightDuty */ + i2c_smbus_write_byte_data(&data->client, DAC_CH2, (unsigned char)brightness); + +@@ -91,7 +93,7 @@ static void tosa_set_backlight(struct to + + static void tosa_lcd_tg_init(struct tosa_bl_data *data) + { +- dev_dbg(&data->bl_dev->dev, "tosa_lcd_init\n"); ++ pr_debug("tosa_lcd_init\n"); + + /* L3V On */ + set_scoop_gpio( &tosascoop_jc_device.dev,TOSA_SCOOP_JC_TC6393XB_L3V_ON); +@@ -116,7 +118,7 @@ static void tosa_lcd_tg_on(struct tosa_b + + tosa_lcd_tg_init(data); + +- dev_dbg(&data->bl_dev->dev, "tosa_lcd_on: %04x (%d)\n", value, mode->yres); ++ pr_debug("tosa_lcd_on: %04x (%d)\n", value, mode->yres); + pxa_nssp_output(data, TG_PNLCTL, value); + + /* TG LCD pannel power up */ +@@ -129,12 +131,15 @@ static void tosa_lcd_tg_on(struct tosa_b + + /* set common voltage */ + i2c_smbus_write_byte_data(&data->client, DAC_CH1, data->comadj); ++ ++ tosa_set_backlight(data, data->bl_dev->props.brightness); ++ + } + + static void tosa_lcd_tg_off(struct tosa_bl_data *data) + { + tosa_set_backlight(data, 0); +- dev_dbg(&data->bl_dev->dev, "tosa_lcd_off\n"); ++ pr_debug("tosa_lcd_off\n"); + /* TG LCD VHSA off */ + pxa_nssp_output(data, TG_PINICTL,0x4); + mdelay(50); +@@ -158,8 +163,6 @@ static int tosa_bl_update_status(struct + struct tosa_bl_data *data = dev_get_drvdata(&dev->dev); + int new_power = max(props->power, props->fb_blank); + +- tosa_set_backlight(data, props->brightness); +- + if (new_power) + tosa_lcd_tg_off(data); + else +@@ -223,22 +226,26 @@ static int tosa_bl_detect_client(struct + + err = gpio_request(TOSA_TC6393XB_BL_C20MA, "backlight"); + if (err) { +- dev_dbg(&data->bl_dev->dev, "Unable to request gpio!\n"); ++ printk(KERN_ERR "tosa-bl; Unable to request gpio!\n"); + goto err_gpio_bl; + } + + err = gpio_request(TOSA_TC6393XB_TG_ON, "tg"); + if (err) { +- dev_dbg(&data->bl_dev->dev, "Unable to request gpio!\n"); ++ printk(KERN_ERR "tosa-bl: Unable to request gpio!\n"); + goto err_gpio_tg; + } + + err = ssp_init(&data->nssp_dev,2,0); + if (err) { +- dev_err(&data->bl_dev->dev, "Unable to register NSSP handler!\n"); ++ printk(KERN_ERR "tosa-bl: Unable to register NSSP handler!\n"); + goto err_ssp_init; + } + ++ pxa_gpio_mode(GPIO83_NSSP_TX); ++ pxa_gpio_mode(GPIO81_NSSP_CLK_OUT); ++ pxa_gpio_mode(GPIO82_NSSP_FRM_OUT); ++ + /* Tell the i2c layer a new client has arrived */ + err = i2c_attach_client(client); + if (err) +@@ -269,7 +276,6 @@ static int tosa_bl_detect_client(struct + err_fb_register: + backlight_device_unregister(data->bl_dev); + err_bl_register: +- tosa_set_backlight(data, 0); + tosa_lcd_tg_off(data); + + err = i2c_detach_client(client); +@@ -296,7 +302,6 @@ static int tosa_bl_detach_client(struct + + backlight_device_unregister(data->bl_dev); + +- tosa_set_backlight(data, 0); + tosa_lcd_tg_off(data); + + /* Try to detach the client from i2c space */ diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/versatile-armv6.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/versatile-armv6.patch new file mode 100644 index 0000000000..e2d0060ac3 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/versatile-armv6.patch @@ -0,0 +1,19 @@ +--- + arch/arm/mm/Kconfig | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +--- linux-2.6.23.orig/arch/arm/mm/Kconfig ++++ linux-2.6.23/arch/arm/mm/Kconfig +@@ -343,11 +343,11 @@ config CPU_XSC3 + select IO_36 + + # ARMv6 + config CPU_V6 + bool "Support ARM V6 processor" +- depends on ARCH_INTEGRATOR || MACH_REALVIEW_EB || ARCH_OMAP2 || ARCH_MX3 ++ depends on ARCH_INTEGRATOR || MACH_REALVIEW_EB || ARCH_OMAP2 || ARCH_MX3 || ARCH_VERSATILE_PB + default y if ARCH_MX3 + select CPU_32v6 + select CPU_ABRT_EV6 + select CPU_CACHE_V6 + select CPU_CACHE_VIPT diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/wm8750-treble.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/wm8750-treble.patch new file mode 100644 index 0000000000..07a8d8e141 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/wm8750-treble.patch @@ -0,0 +1,11 @@ +--- linux-2.6.23/sound/soc/codecs/wm8750.c 2007-10-09 22:31:38.000000000 +0200 ++++ linux-2.6.23/sound/soc/codecs/wm8750.c 2007-11-02 16:47:35.000000000 +0100 +@@ -189,7 +189,7 @@ + SOC_ENUM("Bass Filter", wm8750_enum[1]), + SOC_SINGLE("Bass Volume", WM8750_BASS, 0, 15, 1), + +-SOC_SINGLE("Treble Volume", WM8750_TREBLE, 0, 15, 0), ++SOC_SINGLE("Treble Volume", WM8750_TREBLE, 0, 15, 1), + SOC_ENUM("Treble Cut-off", wm8750_enum[2]), + + SOC_SINGLE("3D Switch", WM8750_3D, 0, 1, 0), diff --git a/recipes/kexecboot/linux-kexecboot-2.6.24/zylonite-boot.patch b/recipes/kexecboot/linux-kexecboot-2.6.24/zylonite-boot.patch new file mode 100644 index 0000000000..f41928eca5 --- /dev/null +++ b/recipes/kexecboot/linux-kexecboot-2.6.24/zylonite-boot.patch @@ -0,0 +1,45 @@ +From 04c42f566c68b757fdadf54e0e0f9dfe9f3f9b06 Mon Sep 17 00:00:00 2001 +From: eric miao <eric.miao@marvell.com> +Date: Tue, 19 Jun 2007 16:42:53 +0800 +Subject: [PATCH] [PATCH] make zylonite boot + +1. reuse head-xscale.S for XSC3 + +2. add a workaround for machine ID assignment, which should be done + by boot loader +--- + arch/arm/boot/compressed/Makefile | 4 ++++ + arch/arm/boot/compressed/head-xscale.S | 5 +++++ + 2 files changed, 9 insertions(+) + +Index: linux-2.6-pxa3/arch/arm/boot/compressed/Makefile +=================================================================== +--- linux-2.6-pxa3.orig/arch/arm/boot/compressed/Makefile 2007-09-24 11:25:57.000000000 +0200 ++++ linux-2.6-pxa3/arch/arm/boot/compressed/Makefile 2007-09-24 12:26:53.000000000 +0200 +@@ -40,6 +40,10 @@ + OBJS += head-xscale.o + endif + ++ifeq ($(CONFIG_CPU_XSC3),y) ++OBJS += head-xscale.o ++endif ++ + ifeq ($(CONFIG_PXA_SHARPSL),y) + OBJS += head-sharpsl.o + endif +Index: linux-2.6-pxa3/arch/arm/boot/compressed/head-xscale.S +=================================================================== +--- linux-2.6-pxa3.orig/arch/arm/boot/compressed/head-xscale.S 2007-09-24 11:42:27.000000000 +0200 ++++ linux-2.6-pxa3/arch/arm/boot/compressed/head-xscale.S 2007-09-24 12:26:02.000000000 +0200 +@@ -33,6 +33,11 @@ + bic r0, r0, #0x1000 @ clear Icache + mcr p15, 0, r0, c1, c0, 0 + ++#ifdef CONFIG_MACH_ZYLONITE ++ mov r7, #(MACH_TYPE_ZYLONITE & 0xff) ++ orr r7, r7, #(MACH_TYPE_ZYLONITE & 0xff00) ++#endif ++ + #ifdef CONFIG_ARCH_COTULLA_IDP + mov r7, #MACH_TYPE_COTULLA_IDP + #endif |