diff options
311 files changed, 10342 insertions, 1658 deletions
diff --git a/Documentation/00-INDEX b/Documentation/00-INDEX index edef85ce119..2a39aeba146 100644 --- a/Documentation/00-INDEX +++ b/Documentation/00-INDEX @@ -42,14 +42,8 @@ IRQ.txt - description of what an IRQ is. ManagementStyle - how to (attempt to) manage kernel hackers. -MSI-HOWTO.txt - - the Message Signaled Interrupts (MSI) Driver Guide HOWTO and FAQ. RCU/ - directory with info on RCU (read-copy update). -README.DAC960 - - info on Mylex DAC960/DAC1100 PCI RAID Controller Driver for Linux. -README.cycladesZ - - info on Cyclades-Z firmware loading. SAK.txt - info on Secure Attention Keys. SM501.txt @@ -86,20 +80,16 @@ blackfin/ - directory with documentation for the Blackfin arch. block/ - info on the Block I/O (BIO) layer. +blockdev/ + - info on block devices & drivers cachetlb.txt - describes the cache/TLB flushing interfaces Linux uses. -cciss.txt - - info, major/minor #'s for Compaq's SMART Array Controllers. cdrom/ - directory with information on the CD-ROM drivers that Linux has. -computone.txt - - info on Computone Intelliport II/Plus Multiport Serial Driver. connector/ - docs on the netlink based userspace<->kernel space communication mod. console/ - documentation on Linux console drivers. -cpqarray.txt - - info on using Compaq's SMART2 Intelligent Disk Array Controllers. cpu-freq/ - info on CPU frequency and voltage scaling. cpu-hotplug.txt @@ -126,8 +116,6 @@ device-mapper/ - directory with info on Device Mapper. devices.txt - plain ASCII listing of all the nodes in /dev/ with major minor #'s. -digiepca.txt - - info on Digi Intl. {PC,PCI,EISA}Xx and Xem series cards. dontdiff - file containing a list of files that should never be diff'ed. driver-model/ @@ -152,14 +140,10 @@ filesystems/ - info on the vfs and the various filesystems that Linux supports. firmware_class/ - request_firmware() hotplug interface info. -floppy.txt - - notes and driver options for the floppy disk driver. frv/ - Fujitsu FR-V Linux documentation. gpio.txt - overview of GPIO (General Purpose Input/Output) access conventions. -hayes-esp.txt - - info on using the Hayes ESP serial driver. highuid.txt - notes on the change from 16 bit to 32 bit user/group IDs. timers/ @@ -186,8 +170,6 @@ io_ordering.txt - info on ordering I/O writes to memory-mapped addresses. ioctl/ - directory with documents describing various IOCTL calls. -ioctl-number.txt - - how to implement and register device/driver ioctl calls. iostats.txt - info on I/O statistics Linux kernel provides. irqflags-tracing.txt @@ -250,14 +232,10 @@ mips/ - directory with info about Linux on MIPS architecture. mono.txt - how to execute Mono-based .NET binaries with the help of BINFMT_MISC. -moxa-smartio - - file with info on installing/using Moxa multiport serial driver. mutex-design.txt - info on the generic mutex subsystem. namespaces/ - directory with various information about namespaces -nbd.txt - - info on a TCP implementation of a network block device. netlabel/ - directory with information on the NetLabel subsystem. networking/ @@ -270,8 +248,6 @@ numastat.txt - info on how to read Numa policy hit/miss statistics in sysfs. oops-tracing.txt - how to decode those nasty internal kernel error dump messages. -paride.txt - - information about the parallel port IDE subsystem. parisc/ - directory with info on using Linux on PA-RISC architecture. parport.txt @@ -290,20 +266,16 @@ powerpc/ - directory with info on using Linux with the PowerPC. preempt-locking.txt - info on locking under a preemptive kernel. +printk-formats.txt + - how to get printk format specifiers right prio_tree.txt - info on radix-priority-search-tree use for indexing vmas. -ramdisk.txt - - short guide on how to set up and use the RAM disk. rbtree.txt - info on what red-black trees are and what they are for. -riscom8.txt - - notes on using the RISCom/8 multi-port serial driver. robust-futex-ABI.txt - documentation of the robust futex ABI. robust-futexes.txt - a description of what robust futexes are. -rocket.txt - - info on the Comtrol RocketPort multiport serial driver. rt-mutex-design.txt - description of the RealTime mutex implementation design. rt-mutex.txt @@ -332,8 +304,6 @@ sparc/ - directory with info on using Linux on Sparc architecture. sparse.txt - info on how to obtain and use the sparse tool for typechecking. -specialix.txt - - info on hardware/driver for specialix IO8+ multiport serial card. spi/ - overview of Linux kernel Serial Peripheral Interface (SPI) support. spinlocks.txt @@ -342,14 +312,10 @@ stable_api_nonsense.txt - info on why the kernel does not have a stable in-kernel api or abi. stable_kernel_rules.txt - rules and procedures for the -stable kernel releases. -stallion.txt - - info on using the Stallion multiport serial driver. svga.txt - short guide on selecting video modes at boot via VGA BIOS. sysfs-rules.txt - How not to use sysfs. -sx.txt - - info on the Specialix SX/SI multiport serial driver. sysctl/ - directory with info on the /proc/sys/* files. sysrq.txt @@ -358,8 +324,6 @@ telephony/ - directory with info on telephony (e.g. voice over IP) support. time_interpolators.txt - info on time interpolators. -tty.txt - - guide to the locking policies of the tty layer. uml/ - directory with information about User Mode Linux. unicode.txt diff --git a/Documentation/ABI/testing/sysfs-c2port b/Documentation/ABI/testing/sysfs-c2port new file mode 100644 index 00000000000..716cffc457e --- /dev/null +++ b/Documentation/ABI/testing/sysfs-c2port @@ -0,0 +1,88 @@ +What: /sys/class/c2port/ +Date: October 2008 +Contact: Rodolfo Giometti <giometti@linux.it> +Description: + The /sys/class/c2port/ directory will contain files and + directories that will provide a unified interface to + the C2 port interface. + +What: /sys/class/c2port/c2portX +Date: October 2008 +Contact: Rodolfo Giometti <giometti@linux.it> +Description: + The /sys/class/c2port/c2portX/ directory is related to X-th + C2 port into the system. Each directory will contain files to + manage and control its C2 port. + +What: /sys/class/c2port/c2portX/access +Date: October 2008 +Contact: Rodolfo Giometti <giometti@linux.it> +Description: + The /sys/class/c2port/c2portX/access file enable the access + to the C2 port from the system. No commands can be sent + till this entry is set to 0. + +What: /sys/class/c2port/c2portX/dev_id +Date: October 2008 +Contact: Rodolfo Giometti <giometti@linux.it> +Description: + The /sys/class/c2port/c2portX/dev_id file show the device ID + of the connected micro. + +What: /sys/class/c2port/c2portX/flash_access +Date: October 2008 +Contact: Rodolfo Giometti <giometti@linux.it> +Description: + The /sys/class/c2port/c2portX/flash_access file enable the + access to the on-board flash of the connected micro. + No commands can be sent till this entry is set to 0. + +What: /sys/class/c2port/c2portX/flash_block_size +Date: October 2008 +Contact: Rodolfo Giometti <giometti@linux.it> +Description: + The /sys/class/c2port/c2portX/flash_block_size file show + the on-board flash block size of the connected micro. + +What: /sys/class/c2port/c2portX/flash_blocks_num +Date: October 2008 +Contact: Rodolfo Giometti <giometti@linux.it> +Description: + The /sys/class/c2port/c2portX/flash_blocks_num file show + the on-board flash blocks number of the connected micro. + +What: /sys/class/c2port/c2portX/flash_data +Date: October 2008 +Contact: Rodolfo Giometti <giometti@linux.it> +Description: + The /sys/class/c2port/c2portX/flash_data file export + the content of the on-board flash of the connected micro. + +What: /sys/class/c2port/c2portX/flash_erase +Date: October 2008 +Contact: Rodolfo Giometti <giometti@linux.it> +Description: + The /sys/class/c2port/c2portX/flash_erase file execute + the "erase" command on the on-board flash of the connected + micro. + +What: /sys/class/c2port/c2portX/flash_erase +Date: October 2008 +Contact: Rodolfo Giometti <giometti@linux.it> +Description: + The /sys/class/c2port/c2portX/flash_erase file show the + on-board flash size of the connected micro. + +What: /sys/class/c2port/c2portX/reset +Date: October 2008 +Contact: Rodolfo Giometti <giometti@linux.it> +Description: + The /sys/class/c2port/c2portX/reset file execute a "reset" + command on the connected micro. + +What: /sys/class/c2port/c2portX/rev_id +Date: October 2008 +Contact: Rodolfo Giometti <giometti@linux.it> +Description: + The /sys/class/c2port/c2portX/rev_id file show the revision ID + of the connected micro. diff --git a/Documentation/ABI/testing/sysfs-firmware-acpi b/Documentation/ABI/testing/sysfs-firmware-acpi index f27be7d1a49..e8ffc70ffe1 100644 --- a/Documentation/ABI/testing/sysfs-firmware-acpi +++ b/Documentation/ABI/testing/sysfs-firmware-acpi @@ -89,7 +89,7 @@ Description: error - an interrupt that can't be accounted for above. - invalid: it's either a wakeup GPE or a GPE/Fixed Event that + invalid: it's either a GPE or a Fixed Event that doesn't have an event handler. disable: the GPE/Fixed Event is valid but disabled. @@ -117,30 +117,30 @@ Description: and other user space applications so that the machine won't shutdown when pressing the power button. # cat ff_pwr_btn - 0 + 0 enabled # press the power button for 3 times; # cat ff_pwr_btn - 3 + 3 enabled # echo disable > ff_pwr_btn # cat ff_pwr_btn - disable + 3 disabled # press the power button for 3 times; # cat ff_pwr_btn - disable + 3 disabled # echo enable > ff_pwr_btn # cat ff_pwr_btn - 4 + 4 enabled /* * this is because the status bit is set even if the enable bit is cleared, * and it triggers an ACPI fixed event when the enable bit is set again */ # press the power button for 3 times; # cat ff_pwr_btn - 7 + 7 enabled # echo disable > ff_pwr_btn # press the power button for 3 times; # echo clear > ff_pwr_btn /* clear the status bit */ # echo disable > ff_pwr_btn # cat ff_pwr_btn - 7 + 7 enabled diff --git a/Documentation/PCI/00-INDEX b/Documentation/PCI/00-INDEX index 49f43946c6b..812b17fe3ed 100644 --- a/Documentation/PCI/00-INDEX +++ b/Documentation/PCI/00-INDEX @@ -1,5 +1,7 @@ 00-INDEX - this file +MSI-HOWTO.txt + - the Message Signaled Interrupts (MSI) Driver Guide HOWTO and FAQ. PCI-DMA-mapping.txt - info for PCI drivers using DMA portably across all platforms PCIEBUS-HOWTO.txt diff --git a/Documentation/MSI-HOWTO.txt b/Documentation/PCI/MSI-HOWTO.txt index 256defd7e17..256defd7e17 100644 --- a/Documentation/MSI-HOWTO.txt +++ b/Documentation/PCI/MSI-HOWTO.txt diff --git a/Documentation/acpi/debug.txt b/Documentation/acpi/debug.txt new file mode 100644 index 00000000000..65bf47c46b6 --- /dev/null +++ b/Documentation/acpi/debug.txt @@ -0,0 +1,148 @@ + ACPI Debug Output + + +The ACPI CA, the Linux ACPI core, and some ACPI drivers can generate debug +output. This document describes how to use this facility. + +Compile-time configuration +-------------------------- + +ACPI debug output is globally enabled by CONFIG_ACPI_DEBUG. If this config +option is turned off, the debug messages are not even built into the +kernel. + +Boot- and run-time configuration +-------------------------------- + +When CONFIG_ACPI_DEBUG=y, you can select the component and level of messages +you're interested in. At boot-time, use the acpi.debug_layer and +acpi.debug_level kernel command line options. After boot, you can use the +debug_layer and debug_level files in /sys/module/acpi/parameters/ to control +the debug messages. + +debug_layer (component) +----------------------- + +The "debug_layer" is a mask that selects components of interest, e.g., a +specific driver or part of the ACPI interpreter. To build the debug_layer +bitmask, look for the "#define _COMPONENT" in an ACPI source file. + +You can set the debug_layer mask at boot-time using the acpi.debug_layer +command line argument, and you can change it after boot by writing values +to /sys/module/acpi/parameters/debug_layer. + +The possible components are defined in include/acpi/acoutput.h and +include/acpi/acpi_drivers.h. Reading /sys/module/acpi/parameters/debug_layer +shows the supported mask values, currently these: + + ACPI_UTILITIES 0x00000001 + ACPI_HARDWARE 0x00000002 + ACPI_EVENTS 0x00000004 + ACPI_TABLES 0x00000008 + ACPI_NAMESPACE 0x00000010 + ACPI_PARSER 0x00000020 + ACPI_DISPATCHER 0x00000040 + ACPI_EXECUTER 0x00000080 + ACPI_RESOURCES 0x00000100 + ACPI_CA_DEBUGGER 0x00000200 + ACPI_OS_SERVICES 0x00000400 + ACPI_CA_DISASSEMBLER 0x00000800 + ACPI_COMPILER 0x00001000 + ACPI_TOOLS 0x00002000 + ACPI_BUS_COMPONENT 0x00010000 + ACPI_AC_COMPONENT 0x00020000 + ACPI_BATTERY_COMPONENT 0x00040000 + ACPI_BUTTON_COMPONENT 0x00080000 + ACPI_SBS_COMPONENT 0x00100000 + ACPI_FAN_COMPONENT 0x00200000 + ACPI_PCI_COMPONENT 0x00400000 + ACPI_POWER_COMPONENT 0x00800000 + ACPI_CONTAINER_COMPONENT 0x01000000 + ACPI_SYSTEM_COMPONENT 0x02000000 + ACPI_THERMAL_COMPONENT 0x04000000 + ACPI_MEMORY_DEVICE_COMPONENT 0x08000000 + ACPI_VIDEO_COMPONENT 0x10000000 + ACPI_PROCESSOR_COMPONENT 0x20000000 + +debug_level +----------- + +The "debug_level" is a mask that selects different types of messages, e.g., +those related to initialization, method execution, informational messages, etc. +To build debug_level, look at the level specified in an ACPI_DEBUG_PRINT() +statement. + +The ACPI interpreter uses several different levels, but the Linux +ACPI core and ACPI drivers generally only use ACPI_LV_INFO. + +You can set the debug_level mask at boot-time using the acpi.debug_level +command line argument, and you can change it after boot by writing values +to /sys/module/acpi/parameters/debug_level. + +The possible levels are defined in include/acpi/acoutput.h. Reading +/sys/module/acpi/parameters/debug_level shows the supported mask values, +currently these: + + ACPI_LV_INIT 0x00000001 + ACPI_LV_DEBUG_OBJECT 0x00000002 + ACPI_LV_INFO 0x00000004 + ACPI_LV_INIT_NAMES 0x00000020 + ACPI_LV_PARSE 0x00000040 + ACPI_LV_LOAD 0x00000080 + ACPI_LV_DISPATCH 0x00000100 + ACPI_LV_EXEC 0x00000200 + ACPI_LV_NAMES 0x00000400 + ACPI_LV_OPREGION 0x00000800 + ACPI_LV_BFIELD 0x00001000 + ACPI_LV_TABLES 0x00002000 + ACPI_LV_VALUES 0x00004000 + ACPI_LV_OBJECTS 0x00008000 + ACPI_LV_RESOURCES 0x00010000 + ACPI_LV_USER_REQUESTS 0x00020000 + ACPI_LV_PACKAGE 0x00040000 + ACPI_LV_ALLOCATIONS 0x00100000 + ACPI_LV_FUNCTIONS 0x00200000 + ACPI_LV_OPTIMIZATIONS 0x00400000 + ACPI_LV_MUTEX 0x01000000 + ACPI_LV_THREADS 0x02000000 + ACPI_LV_IO 0x04000000 + ACPI_LV_INTERRUPTS 0x08000000 + ACPI_LV_AML_DISASSEMBLE 0x10000000 + ACPI_LV_VERBOSE_INFO 0x20000000 + ACPI_LV_FULL_TABLES 0x40000000 + ACPI_LV_EVENTS 0x80000000 + +Examples +-------- + +For example, drivers/acpi/bus.c contains this: + + #define _COMPONENT ACPI_BUS_COMPONENT + ... + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device insertion detected\n")); + +To turn on this message, set the ACPI_BUS_COMPONENT bit in acpi.debug_layer +and the ACPI_LV_INFO bit in acpi.debug_level. (The ACPI_DEBUG_PRINT +statement uses ACPI_DB_INFO, which is macro based on the ACPI_LV_INFO +definition.) + +Enable all AML "Debug" output (stores to the Debug object while interpreting +AML) during boot: + + acpi.debug_layer=0xffffffff acpi.debug_level=0x2 + +Enable PCI and PCI interrupt routing debug messages: + + acpi.debug_layer=0x400000 acpi.debug_level=0x4 + +Enable all ACPI hardware-related messages: + + acpi.debug_layer=0x2 acpi.debug_level=0xffffffff + +Enable all ACPI_DB_INFO messages after boot: + + # echo 0x4 > /sys/module/acpi/parameters/debug_level + +Show all valid component values: + + # cat /sys/module/acpi/parameters/debug_layer diff --git a/Documentation/blockdev/00-INDEX b/Documentation/blockdev/00-INDEX new file mode 100644 index 00000000000..86f054c4701 --- /dev/null +++ b/Documentation/blockdev/00-INDEX @@ -0,0 +1,16 @@ +00-INDEX + - this file +README.DAC960 + - info on Mylex DAC960/DAC1100 PCI RAID Controller Driver for Linux. +cciss.txt + - info, major/minor #'s for Compaq's SMART Array Controllers. +cpqarray.txt + - info on using Compaq's SMART2 Intelligent Disk Array Controllers. +floppy.txt + - notes and driver options for the floppy disk driver. +nbd.txt + - info on a TCP implementation of a network block device. +paride.txt + - information about the parallel port IDE subsystem. +ramdisk.txt + - short guide on how to set up and use the RAM disk. diff --git a/Documentation/README.DAC960 b/Documentation/blockdev/README.DAC960 index 0e8f618ab53..0e8f618ab53 100644 --- a/Documentation/README.DAC960 +++ b/Documentation/blockdev/README.DAC960 diff --git a/Documentation/cciss.txt b/Documentation/blockdev/cciss.txt index 89698e8df7d..89698e8df7d 100644 --- a/Documentation/cciss.txt +++ b/Documentation/blockdev/cciss.txt diff --git a/Documentation/cpqarray.txt b/Documentation/blockdev/cpqarray.txt index c7154e20ef5..c7154e20ef5 100644 --- a/Documentation/cpqarray.txt +++ b/Documentation/blockdev/cpqarray.txt diff --git a/Documentation/floppy.txt b/Documentation/blockdev/floppy.txt index 6ccab88705c..6ccab88705c 100644 --- a/Documentation/floppy.txt +++ b/Documentation/blockdev/floppy.txt diff --git a/Documentation/nbd.txt b/Documentation/blockdev/nbd.txt index aeb93ffe641..aeb93ffe641 100644 --- a/Documentation/nbd.txt +++ b/Documentation/blockdev/nbd.txt diff --git a/Documentation/paride.txt b/Documentation/blockdev/paride.txt index e4312676bdd..e4312676bdd 100644 --- a/Documentation/paride.txt +++ b/Documentation/blockdev/paride.txt diff --git a/Documentation/ramdisk.txt b/Documentation/blockdev/ramdisk.txt index 6c820baa19a..6c820baa19a 100644 --- a/Documentation/ramdisk.txt +++ b/Documentation/blockdev/ramdisk.txt diff --git a/Documentation/c2port.txt b/Documentation/c2port.txt new file mode 100644 index 00000000000..d9bf93ea439 --- /dev/null +++ b/Documentation/c2port.txt @@ -0,0 +1,90 @@ + C2 port support + --------------- + +(C) Copyright 2007 Rodolfo Giometti <giometti@enneenne.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. + +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. + + + +Overview +-------- + +This driver implements the support for Linux of Silicon Labs (Silabs) +C2 Interface used for in-system programming of micro controllers. + +By using this driver you can reprogram the in-system flash without EC2 +or EC3 debug adapter. This solution is also useful in those systems +where the micro controller is connected via special GPIOs pins. + +References +---------- + +The C2 Interface main references are at (http://www.silabs.com) +Silicon Laboratories site], see: + +- AN127: FLASH Programming via the C2 Interface at +http://www.silabs.com/public/documents/tpub_doc/anote/Microcontrollers/Small_Form_Factor/en/an127.pdf, and + +- C2 Specification at +http://www.silabs.com/public/documents/tpub_doc/spec/Microcontrollers/en/C2spec.pdf, + +however it implements a two wire serial communication protocol (bit +banging) designed to enable in-system programming, debugging, and +boundary-scan testing on low pin-count Silicon Labs devices. Currently +this code supports only flash programming but extensions are easy to +add. + +Using the driver +---------------- + +Once the driver is loaded you can use sysfs support to get C2port's +info or read/write in-system flash. + +# ls /sys/class/c2port/c2port0/ +access flash_block_size flash_erase rev_id +dev_id flash_blocks_num flash_size subsystem/ +flash_access flash_data reset uevent + +Initially the C2port access is disabled since you hardware may have +such lines multiplexed with other devices so, to get access to the +C2port, you need the command: + +# echo 1 > /sys/class/c2port/c2port0/access + +after that you should read the device ID and revision ID of the +connected micro controller: + +# cat /sys/class/c2port/c2port0/dev_id +8 +# cat /sys/class/c2port/c2port0/rev_id +1 + +However, for security reasons, the in-system flash access in not +enabled yet, to do so you need the command: + +# echo 1 > /sys/class/c2port/c2port0/flash_access + +After that you can read the whole flash: + +# cat /sys/class/c2port/c2port0/flash_data > image + +erase it: + +# echo 1 > /sys/class/c2port/c2port0/flash_erase + +and write it: + +# cat image > /sys/class/c2port/c2port0/flash_data + +after writing you have to reset the device to execute the new code: + +# echo 1 > /sys/class/c2port/c2port0/reset diff --git a/Documentation/cgroups/freezer-subsystem.txt b/Documentation/cgroups/freezer-subsystem.txt index c50ab58b72e..41f37fea127 100644 --- a/Documentation/cgroups/freezer-subsystem.txt +++ b/Documentation/cgroups/freezer-subsystem.txt @@ -1,4 +1,4 @@ - The cgroup freezer is useful to batch job management system which start +The cgroup freezer is useful to batch job management system which start and stop sets of tasks in order to schedule the resources of a machine according to the desires of a system administrator. This sort of program is often used on HPC clusters to schedule access to the cluster as a @@ -6,7 +6,7 @@ whole. The cgroup freezer uses cgroups to describe the set of tasks to be started/stopped by the batch job management system. It also provides a means to start and stop the tasks composing the job. - The cgroup freezer will also be useful for checkpointing running groups +The cgroup freezer will also be useful for checkpointing running groups of tasks. The freezer allows the checkpoint code to obtain a consistent image of the tasks by attempting to force the tasks in a cgroup into a quiescent state. Once the tasks are quiescent another task can @@ -16,7 +16,7 @@ recoverable error occur. This also allows the checkpointed tasks to be migrated between nodes in a cluster by copying the gathered information to another node and restarting the tasks there. - Sequences of SIGSTOP and SIGCONT are not always sufficient for stopping +Sequences of SIGSTOP and SIGCONT are not always sufficient for stopping and resuming tasks in userspace. Both of these signals are observable from within the tasks we wish to freeze. While SIGSTOP cannot be caught, blocked, or ignored it can be seen by waiting or ptracing parent tasks. @@ -37,26 +37,29 @@ demonstrate this problem using nested bash shells: <at this point 16990 exits and causes 16644 to exit too> - This happens because bash can observe both signals and choose how it +This happens because bash can observe both signals and choose how it responds to them. - Another example of a program which catches and responds to these +Another example of a program which catches and responds to these signals is gdb. In fact any program designed to use ptrace is likely to have a problem with this method of stopping and resuming tasks. - In contrast, the cgroup freezer uses the kernel freezer code to +In contrast, the cgroup freezer uses the kernel freezer code to prevent the freeze/unfreeze cycle from becoming visible to the tasks being frozen. This allows the bash example above and gdb to run as expected. - The freezer subsystem in the container filesystem defines a file named +The freezer subsystem in the container filesystem defines a file named freezer.state. Writing "FROZEN" to the state file will freeze all tasks in the cgroup. Subsequently writing "THAWED" will unfreeze the tasks in the cgroup. Reading will return the current state. +Note freezer.state doesn't exist in root cgroup, which means root cgroup +is non-freezable. + * Examples of usage : - # mkdir /containers/freezer + # mkdir /containers # mount -t cgroup -ofreezer freezer /containers # mkdir /containers/0 # echo $some_pid > /containers/0/tasks @@ -94,6 +97,6 @@ things happens: the freezer.state file 2) Userspace retries the freezing operation by writing "FROZEN" to the freezer.state file (writing "FREEZING" is not legal - and returns EIO) + and returns EINVAL) 3) The tasks that blocked the cgroup from entering the "FROZEN" state disappear from the cgroup's set of tasks. diff --git a/Documentation/filesystems/xip.txt b/Documentation/filesystems/xip.txt index 3cc4010521a..0466ee56927 100644 --- a/Documentation/filesystems/xip.txt +++ b/Documentation/filesystems/xip.txt @@ -39,10 +39,11 @@ The block device operation is optional, these block devices support it as of today: - dcssblk: s390 dcss block device driver -An address space operation named get_xip_page is used to retrieve reference -to a struct page. To address the target page, a reference to an address_space, -and a sector number is provided. A 3rd argument indicates whether the -function should allocate blocks if needed. +An address space operation named get_xip_mem is used to retrieve references +to a page frame number and a kernel address. To obtain these values a reference +to an address_space is provided. This function assigns values to the kmem and +pfn parameters. The third argument indicates whether the function should allocate +blocks if needed. This address space operation is mutually exclusive with readpage&writepage that do page cache read/write operations. diff --git a/Documentation/hwmon/adt7462 b/Documentation/hwmon/adt7462 new file mode 100644 index 00000000000..ec660b32827 --- /dev/null +++ b/Documentation/hwmon/adt7462 @@ -0,0 +1,67 @@ +Kernel driver adt7462 +====================== + +Supported chips: + * Analog Devices ADT7462 + Prefix: 'adt7462' + Addresses scanned: I2C 0x58, 0x5C + Datasheet: Publicly available at the Analog Devices website + +Author: Darrick J. Wong + +Description +----------- + +This driver implements support for the Analog Devices ADT7462 chip family. + +This chip is a bit of a beast. It has 8 counters for measuring fan speed. It +can also measure 13 voltages or 4 temperatures, or various combinations of the +two. See the chip documentation for more details about the exact set of +configurations. This driver does not allow one to configure the chip; that is +left to the system designer. + +A sophisticated control system for the PWM outputs is designed into the ADT7462 +that allows fan speed to be adjusted automatically based on any of the three +temperature sensors. Each PWM output is individually adjustable and +programmable. Once configured, the ADT7462 will adjust the PWM outputs in +response to the measured temperatures without further host intervention. This +feature can also be disabled for manual control of the PWM's. + +Each of the measured inputs (voltage, temperature, fan speed) has +corresponding high/low limit values. The ADT7462 will signal an ALARM if +any measured value exceeds either limit. + +The ADT7462 samples all inputs continuously. The driver will not read +the registers more often than once every other second. Further, +configuration data is only read once per minute. + +Special Features +---------------- + +The ADT7462 have a 10-bit ADC and can therefore measure temperatures +with 0.25 degC resolution. + +The Analog Devices datasheet is very detailed and describes a procedure for +determining an optimal configuration for the automatic PWM control. + +The driver will report sensor labels when it is able to determine that +information from the configuration registers. + +Configuration Notes +------------------- + +Besides standard interfaces driver adds the following: + +* PWM Control + +* pwm#_auto_point1_pwm and temp#_auto_point1_temp and +* pwm#_auto_point2_pwm and temp#_auto_point2_temp - + +point1: Set the pwm speed at a lower temperature bound. +point2: Set the pwm speed at a higher temperature bound. + +The ADT7462 will scale the pwm between the lower and higher pwm speed when +the temperature is between the two temperature boundaries. PWM values range +from 0 (off) to 255 (full speed). Fan speed will be set to maximum when the +temperature sensor associated with the PWM control exceeds temp#_max. + diff --git a/Documentation/hwmon/lis3lv02d b/Documentation/hwmon/lis3lv02d new file mode 100644 index 00000000000..65dfb0c0fd6 --- /dev/null +++ b/Documentation/hwmon/lis3lv02d @@ -0,0 +1,49 @@ +Kernel driver lis3lv02d +================== + +Supported chips: + + * STMicroelectronics LIS3LV02DL and LIS3LV02DQ + +Author: + Yan Burman <burman.yan@gmail.com> + Eric Piel <eric.piel@tremplin-utc.net> + + +Description +----------- + +This driver provides support for the accelerometer found in various HP laptops +sporting the feature officially called "HP Mobile Data Protection System 3D" or +"HP 3D DriveGuard". It detect automatically laptops with this sensor. Known models +(for now the HP 2133, nc6420, nc2510, nc8510, nc84x0, nw9440 and nx9420) will +have their axis automatically oriented on standard way (eg: you can directly +play neverball). The accelerometer data is readable via +/sys/devices/platform/lis3lv02d. + +Sysfs attributes under /sys/devices/platform/lis3lv02d/: +position - 3D position that the accelerometer reports. Format: "(x,y,z)" +calibrate - read: values (x, y, z) that are used as the base for input class device operation. + write: forces the base to be recalibrated with the current position. +rate - reports the sampling rate of the accelerometer device in HZ + +This driver also provides an absolute input class device, allowing +the laptop to act as a pinball machine-esque joystick. + +Axes orientation +---------------- + +For better compatibility between the various laptops. The values reported by +the accelerometer are converted into a "standard" organisation of the axes +(aka "can play neverball out of the box"): + * When the laptop is horizontal the position reported is about 0 for X and Y +and a positive value for Z + * If the left side is elevated, X increases (becomes positive) + * If the front side (where the touchpad is) is elevated, Y decreases (becomes negative) + * If the laptop is put upside-down, Z becomes negative + +If your laptop model is not recognized (cf "dmesg"), you can send an email to the +authors to add it to the database. When reporting a new laptop, please include +the output of "dmidecode" plus the value of /sys/devices/platform/lis3lv02d/position +in these four cases. + diff --git a/Documentation/ics932s401 b/Documentation/ics932s401 new file mode 100644 index 00000000000..07a739f406d --- /dev/null +++ b/Documentation/ics932s401 @@ -0,0 +1,31 @@ +Kernel driver ics932s401 +====================== + +Supported chips: + * IDT ICS932S401 + Prefix: 'ics932s401' + Addresses scanned: I2C 0x69 + Datasheet: Publically available at the IDT website + +Author: Darrick J. Wong + +Description +----------- + +This driver implements support for the IDT ICS932S401 chip family. + +This chip has 4 clock outputs--a base clock for the CPU (which is likely +multiplied to get the real CPU clock), a system clock, a PCI clock, a USB +clock, and a reference clock. The driver reports selected and actual +frequency. If spread spectrum mode is enabled, the driver also reports by what +percent the clock signal is being spread, which should be between 0 and -0.5%. +All frequencies are reported in KHz. + +The ICS932S401 monitors all inputs continuously. The driver will not read +the registers more often than once every other second. + +Special Features +---------------- + +The clocks could be reprogrammed to increase system speed. I will not help you +do this, as you risk damaging your system! diff --git a/Documentation/ioctl/00-INDEX b/Documentation/ioctl/00-INDEX new file mode 100644 index 00000000000..d2fe4d4729e --- /dev/null +++ b/Documentation/ioctl/00-INDEX @@ -0,0 +1,10 @@ +00-INDEX + - this file +cdrom.txt + - summary of CDROM ioctl calls +hdio.txt + - summary of HDIO_ ioctl calls +ioctl-decoding.txt + - how to decode the bits of an IOCTL code +ioctl-number.txt + - how to implement and register device/driver ioctl calls diff --git a/Documentation/ioctl-number.txt b/Documentation/ioctl/ioctl-number.txt index b880ce5dbd3..b880ce5dbd3 100644 --- a/Documentation/ioctl-number.txt +++ b/Documentation/ioctl/ioctl-number.txt diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index c86c0745971..9fa6508892c 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -198,59 +198,42 @@ and is between 256 and 4096 characters. It is defined in the file that require a timer override, but don't have HPET - acpi.debug_layer= [HW,ACPI] + acpi_backlight= [HW,ACPI] + acpi_backlight=vendor + acpi_backlight=video + If set to vendor, prefer vendor specific driver + (e.g. thinkpad_acpi, sony_acpi, etc.) instead + of the ACPI video.ko driver. + + acpi_display_output= [HW,ACPI] + acpi_display_output=vendor + acpi_display_output=video + See above. + + acpi.debug_layer= [HW,ACPI,ACPI_DEBUG] + acpi.debug_level= [HW,ACPI,ACPI_DEBUG] Format: <int> - Each bit of the <int> indicates an ACPI debug layer, - 1: enable, 0: disable. It is useful for boot time - debugging. After system has booted up, it can be set - via /sys/module/acpi/parameters/debug_layer. - CONFIG_ACPI_DEBUG must be enabled for this to produce any output. - Available bits (add the numbers together) to enable debug output - for specific parts of the ACPI subsystem: - 0x01 utilities 0x02 hardware 0x04 events 0x08 tables - 0x10 namespace 0x20 parser 0x40 dispatcher - 0x80 executer 0x100 resources 0x200 acpica debugger - 0x400 os services 0x800 acpica disassembler. - The number can be in decimal or prefixed with 0x in hex. - Warning: Many of these options can produce a lot of - output and make your system unusable. Be very careful. - - acpi.debug_level= [HW,ACPI] - Format: <int> - Each bit of the <int> indicates an ACPI debug level, - which corresponds to the level in an ACPI_DEBUG_PRINT - statement. After system has booted up, this mask - can be set via /sys/module/acpi/parameters/debug_level. - - CONFIG_ACPI_DEBUG must be enabled for this to produce - any output. The number can be in decimal or prefixed - with 0x in hex. Some of these options produce so much - output that the system is unusable. - - The following global components are defined by the - ACPI CA: - 0x01 error - 0x02 warn - 0x04 init - 0x08 debug object - 0x10 info - 0x20 init names - 0x40 parse - 0x80 load - 0x100 dispatch - 0x200 execute - 0x400 names - 0x800 operation region - 0x1000 bfield - 0x2000 tables - 0x4000 values - 0x8000 objects - 0x10000 resources - 0x20000 user requests - 0x40000 package - The number can be in decimal or prefixed with 0x in hex. - Warning: Many of these options can produce a lot of - output and make your system unusable. Be very careful. + CONFIG_ACPI_DEBUG must be enabled to produce any ACPI + debug output. Bits in debug_layer correspond to a + _COMPONENT in an ACPI source file, e.g., + #define _COMPONENT ACPI_PCI_COMPONENT + Bits in debug_level correspond to a level in + ACPI_DEBUG_PRINT statements, e.g., + ACPI_DEBUG_PRINT((ACPI_DB_INFO, ... + See Documentation/acpi/debug.txt for more information + about debug layers and levels. + + Enable AML "Debug" output, i.e., stores to the Debug + object while interpreting AML: + acpi.debug_layer=0xffffffff acpi.debug_level=0x2 + Enable PCI/PCI interrupt routing info messages: + acpi.debug_layer=0x400000 acpi.debug_level=0x4 + Enable all messages related to ACPI hardware: + acpi.debug_layer=0x2 acpi.debug_level=0xffffffff + + Some values produce so much output that the system is + unusable. The "log_buf_len" parameter may be useful + if you need to capture more output. acpi.power_nocheck= [HW,ACPI] Format: 1/0 enable/disable the check of power state. @@ -646,7 +629,7 @@ and is between 256 and 4096 characters. It is defined in the file digiepca= [HW,SERIAL] See drivers/char/README.epca and - Documentation/digiepca.txt. + Documentation/serial/digiepca.txt. disable_mtrr_cleanup [X86] enable_mtrr_cleanup [X86] @@ -757,7 +740,7 @@ and is between 256 and 4096 characters. It is defined in the file See header of drivers/scsi/fdomain.c. floppy= [HW] - See Documentation/floppy.txt. + See Documentation/blockdev/floppy.txt. force_pal_cache_flush [IA-64] Avoid check_sal_cache_flush which may hang on @@ -1118,7 +1101,7 @@ and is between 256 and 4096 characters. It is defined in the file the same attribute, the last one is used. load_ramdisk= [RAM] List of ramdisks to load from floppy - See Documentation/ramdisk.txt. + See Documentation/blockdev/ramdisk.txt. lockd.nlm_grace_period=P [NFS] Assign grace period. Format: <integer> @@ -1613,7 +1596,7 @@ and is between 256 and 4096 characters. It is defined in the file pcd. [PARIDE] See header of drivers/block/paride/pcd.c. - See also Documentation/paride.txt. + See also Documentation/blockdev/paride.txt. pci=option[,option...] [PCI] various PCI subsystem options: off [X86] don't probe for the PCI bus @@ -1714,7 +1697,7 @@ and is between 256 and 4096 characters. It is defined in the file pcmv= [HW,PCMCIA] BadgePAD 4 pd. [PARIDE] - See Documentation/paride.txt. + See Documentation/blockdev/paride.txt. pdcchassis= [PARISC,HW] Disable/Enable PDC Chassis Status codes at boot time. @@ -1722,10 +1705,10 @@ and is between 256 and 4096 characters. It is defined in the file See arch/parisc/kernel/pdc_chassis.c pf. [PARIDE] - See Documentation/paride.txt. + See Documentation/blockdev/paride.txt. pg. [PARIDE] - See Documentation/paride.txt. + See Documentation/blockdev/paride.txt. pirq= [SMP,APIC] Manual mp-table setup See Documentation/x86/i386/IO-APIC.txt. @@ -1795,7 +1778,7 @@ and is between 256 and 4096 characters. It is defined in the file prompt_ramdisk= [RAM] List of RAM disks to prompt for floppy disk before loading. - See Documentation/ramdisk.txt. + See Documentation/blockdev/ramdisk.txt. psmouse.proto= [HW,MOUSE] Highest PS2 mouse protocol extension to probe for; one of (bare|imps|exps|lifebook|any). @@ -1815,7 +1798,7 @@ and is between 256 and 4096 characters. It is defined in the file <io>,<mss_io>,<mss_irq>,<mss_dma>,<mpu_io>,<mpu_irq> pt. [PARIDE] - See Documentation/paride.txt. + See Documentation/blockdev/paride.txt. pty.legacy_count= [KNL] Number of legacy pty's. Overwrites compiled-in @@ -1829,10 +1812,10 @@ and is between 256 and 4096 characters. It is defined in the file See Documentation/md.txt. ramdisk_blocksize= [RAM] - See Documentation/ramdisk.txt. + See Documentation/blockdev/ramdisk.txt. ramdisk_size= [RAM] Sizes of RAM disks in kilobytes - See Documentation/ramdisk.txt. + See Documentation/blockdev/ramdisk.txt. rcupdate.blimit= [KNL,BOOT] Set maximum number of finished RCU callbacks to process @@ -2164,7 +2147,7 @@ and is between 256 and 4096 characters. It is defined in the file See Documentation/sonypi.txt specialix= [HW,SERIAL] Specialix multi-serial port adapter - See Documentation/specialix.txt. + See Documentation/serial/specialix.txt. spia_io_base= [HW,MTD] spia_fio_base= diff --git a/Documentation/printk-formats.txt b/Documentation/printk-formats.txt new file mode 100644 index 00000000000..1b5a5ddbc3e --- /dev/null +++ b/Documentation/printk-formats.txt @@ -0,0 +1,35 @@ +If variable is of Type, use printk format specifier: +--------------------------------------------------------- + int %d or %x + unsigned int %u or %x + long %ld or %lx + unsigned long %lu or %lx + long long %lld or %llx + unsigned long long %llu or %llx + size_t %zu or %zx + ssize_t %zd or %zx + +Raw pointer value SHOULD be printed with %p. + +u64 SHOULD be printed with %llu/%llx, (unsigned long long): + + printk("%llu", (unsigned long long)u64_var); + +s64 SHOULD be printed with %lld/%llx, (long long): + + printk("%lld", (long long)s64_var); + +If <type> is dependent on a config option for its size (e.g., sector_t, +blkcnt_t, phys_addr_t, resource_size_t) or is architecture-dependent +for its size (e.g., tcflag_t), use a format specifier of its largest +possible type and explicitly cast to it. Example: + + printk("test: sector number/total blocks: %llu/%llu\n", + (unsigned long long)sector, (unsigned long long)blockcount); + +Reminder: sizeof() result is of type size_t. + +Thank you for your cooperation and attention. + + +By Randy Dunlap <rdunlap@xenotime.net> diff --git a/Documentation/serial/00-INDEX b/Documentation/serial/00-INDEX new file mode 100644 index 00000000000..07dcdb0d2a3 --- /dev/null +++ b/Documentation/serial/00-INDEX @@ -0,0 +1,24 @@ +00-INDEX + - this file. +README.cycladesZ + - info on Cyclades-Z firmware loading. +computone.txt + - info on Computone Intelliport II/Plus Multiport Serial Driver. +digiepca.txt + - info on Digi Intl. {PC,PCI,EISA}Xx and Xem series cards. +hayes-esp.txt + - info on using the Hayes ESP serial driver. +moxa-smartio + - file with info on installing/using Moxa multiport serial driver. +riscom8.txt + - notes on using the RISCom/8 multi-port serial driver. +rocket.txt + - info on the Comtrol RocketPort multiport serial driver. +specialix.txt + - info on hardware/driver for specialix IO8+ multiport serial card. +stallion.txt + - info on using the Stallion multiport serial driver. +sx.txt + - info on the Specialix SX/SI multiport serial driver. +tty.txt + - guide to the locking policies of the tty layer. diff --git a/Documentation/README.cycladesZ b/Documentation/serial/README.cycladesZ index 024a69443cc..024a69443cc 100644 --- a/Documentation/README.cycladesZ +++ b/Documentation/serial/README.cycladesZ diff --git a/Documentation/computone.txt b/Documentation/serial/computone.txt index 5e2a0c76bfa..c57ea4781e5 100644 --- a/Documentation/computone.txt +++ b/Documentation/serial/computone.txt @@ -247,7 +247,7 @@ shar archive to make it easier to extract the script from the documentation. To create the ip2mkdev shell script change to a convenient directory (/tmp works just fine) and run the following command: - unshar Documentation/computone.txt + unshar Documentation/serial/computone.txt (This file) You should now have a file ip2mkdev in your current working directory with diff --git a/Documentation/digiepca.txt b/Documentation/serial/digiepca.txt index f2560e22f2c..f2560e22f2c 100644 --- a/Documentation/digiepca.txt +++ b/Documentation/serial/digiepca.txt diff --git a/Documentation/hayes-esp.txt b/Documentation/serial/hayes-esp.txt index 09b5d585675..09b5d585675 100644 --- a/Documentation/hayes-esp.txt +++ b/Documentation/serial/hayes-esp.txt diff --git a/Documentation/moxa-smartio b/Documentation/serial/moxa-smartio index 5337e80a5b9..5337e80a5b9 100644 --- a/Documentation/moxa-smartio +++ b/Documentation/serial/moxa-smartio diff --git a/Documentation/riscom8.txt b/Documentation/serial/riscom8.txt index 14f61fdad7c..14f61fdad7c 100644 --- a/Documentation/riscom8.txt +++ b/Documentation/serial/riscom8.txt diff --git a/Documentation/rocket.txt b/Documentation/serial/rocket.txt index 1d858299043..1d858299043 100644 --- a/Documentation/rocket.txt +++ b/Documentation/serial/rocket.txt diff --git a/Documentation/specialix.txt b/Documentation/serial/specialix.txt index 6eb6f3a3331..6eb6f3a3331 100644 --- a/Documentation/specialix.txt +++ b/Documentation/serial/specialix.txt diff --git a/Documentation/stallion.txt b/Documentation/serial/stallion.txt index 5c4902d9a5b..5c4902d9a5b 100644 --- a/Documentation/stallion.txt +++ b/Documentation/serial/stallion.txt diff --git a/Documentation/sx.txt b/Documentation/serial/sx.txt index cb4efa0fb5c..cb4efa0fb5c 100644 --- a/Documentation/sx.txt +++ b/Documentation/serial/sx.txt diff --git a/Documentation/tty.txt b/Documentation/serial/tty.txt index 8e65c4498c5..8e65c4498c5 100644 --- a/Documentation/tty.txt +++ b/Documentation/serial/tty.txt diff --git a/Documentation/w1/masters/omap-hdq b/Documentation/w1/masters/omap-hdq new file mode 100644 index 00000000000..ca722e09b6a --- /dev/null +++ b/Documentation/w1/masters/omap-hdq @@ -0,0 +1,46 @@ +Kernel driver for omap HDQ/1-wire module. +======================================== + +Supported chips: +================ + HDQ/1-wire controller on the TI OMAP 2430/3430 platforms. + +A useful link about HDQ basics: +=============================== +http://focus.ti.com/lit/an/slua408/slua408.pdf + +Description: +============ +The HDQ/1-Wire module of TI OMAP2430/3430 platforms implement the hardware +protocol of the master functions of the Benchmark HDQ and the Dallas +Semiconductor 1-Wire protocols. These protocols use a single wire for +communication between the master (HDQ/1-Wire controller) and the slave +(HDQ/1-Wire external compliant device). + +A typical application of the HDQ/1-Wire module is the communication with battery +monitor (gas gauge) integrated circuits. + +The controller supports operation in both HDQ and 1-wire mode. The essential +difference between the HDQ and 1-wire mode is how the slave device responds to +initialization pulse.In HDQ mode, the firmware does not require the host to +create an initialization pulse to the slave.However, the slave can be reset by +using an initialization pulse (also referred to as a break pulse).The slave +does not respond with a presence pulse as it does in the 1-Wire protocol. + +Remarks: +======== +The driver (drivers/w1/masters/omap_hdq.c) supports the HDQ mode of the +controller. In this mode, as we can not read the ID which obeys the W1 +spec(family:id:crc), a module parameter can be passed to the driver which will +be used to calculate the CRC and pass back an appropriate slave ID to the W1 +core. + +By default the master driver and the BQ slave i/f +driver(drivers/w1/slaves/w1_bq27000.c) sets the ID to 1. +Please note to load both the modules with a different ID if required, but note +that the ID used should be same for both master and slave driver loading. + +e.g: +insmod omap_hdq.ko W1_ID=2 +inamod w1_bq27000.ko F_ID=2 + diff --git a/MAINTAINERS b/MAINTAINERS index 53e78c83cc7..8e0777fae3c 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -2216,6 +2216,13 @@ M: adaplas@gmail.com L: linux-fbdev-devel@lists.sourceforge.net (moderated for non-subscribers) S: Maintained +INTEL MENLOW THERMAL DRIVER +P: Sujith Thomas +M: sujith.thomas@intel.com +L: linux-acpi@vger.kernel.org +W: http://www.lesswatts.org/projects/acpi/ +S: Supported + INTEL IA32 MICROCODE UPDATE SUPPORT P: Tigran Aivazian M: tigran@aivazian.fsnet.co.uk @@ -2696,6 +2703,11 @@ P: Arnaldo Carvalho de Melo M: acme@ghostprotocols.net S: Maintained +LIS3LV02D ACCELEROMETER DRIVER +P: Eric Piel +M: eric.piel@tremplin-utc.net +S: Maintained + LM83 HARDWARE MONITOR DRIVER P: Jean Delvare M: khali@linux-fr.org @@ -3377,7 +3389,9 @@ S: Maintained PNP SUPPORT P: Adam Belay -M: ambx1@neo.rr.com +M: abelay@mit.edu +P: Bjorn Helgaas +M: bjorn.helgaas@hp.com S: Maintained PNXxxxx I2C DRIVER @@ -1,7 +1,7 @@ VERSION = 2 PATCHLEVEL = 6 SUBLEVEL = 28 -EXTRAVERSION = -rc4 +EXTRAVERSION = -rc5 NAME = Killer Bat of Doom # *DOCUMENTATION* diff --git a/arch/arm/include/asm/dma-mapping.h b/arch/arm/include/asm/dma-mapping.h index 1cb8602dd9d..4ed149cbb32 100644 --- a/arch/arm/include/asm/dma-mapping.h +++ b/arch/arm/include/asm/dma-mapping.h @@ -256,8 +256,17 @@ int dmabounce_sync_for_cpu(struct device *, dma_addr_t, unsigned long, int dmabounce_sync_for_device(struct device *, dma_addr_t, unsigned long, size_t, enum dma_data_direction); #else -#define dmabounce_sync_for_cpu(dev,dma,off,sz,dir) (1) -#define dmabounce_sync_for_device(dev,dma,off,sz,dir) (1) +static inline int dmabounce_sync_for_cpu(struct device *d, dma_addr_t addr, + unsigned long offset, size_t size, enum dma_data_direction dir) +{ + return 1; +} + +static inline int dmabounce_sync_for_device(struct device *d, dma_addr_t addr, + unsigned long offset, size_t size, enum dma_data_direction dir) +{ + return 1; +} /** diff --git a/arch/arm/include/asm/hardware/iop3xx-adma.h b/arch/arm/include/asm/hardware/iop3xx-adma.h index 87bff09633a..83e6ba338e2 100644 --- a/arch/arm/include/asm/hardware/iop3xx-adma.h +++ b/arch/arm/include/asm/hardware/iop3xx-adma.h @@ -730,7 +730,8 @@ static inline void iop_desc_set_next_desc(struct iop_adma_desc_slot *desc, { /* hw_desc->next_desc is the same location for all channels */ union iop3xx_desc hw_desc = { .ptr = desc->hw_desc, }; - BUG_ON(hw_desc.dma->next_desc); + + iop_paranoia(hw_desc.dma->next_desc); hw_desc.dma->next_desc = next_desc_addr; } @@ -760,7 +761,7 @@ static inline int iop_desc_get_zero_result(struct iop_adma_desc_slot *desc) struct iop3xx_desc_aau *hw_desc = desc->hw_desc; struct iop3xx_aau_desc_ctrl desc_ctrl = hw_desc->desc_ctrl_field; - BUG_ON(!(desc_ctrl.tx_complete && desc_ctrl.zero_result_en)); + iop_paranoia(!(desc_ctrl.tx_complete && desc_ctrl.zero_result_en)); return desc_ctrl.zero_result_err; } diff --git a/arch/arm/include/asm/hardware/iop_adma.h b/arch/arm/include/asm/hardware/iop_adma.h index cb7e3611bcb..385c6e8cbbd 100644 --- a/arch/arm/include/asm/hardware/iop_adma.h +++ b/arch/arm/include/asm/hardware/iop_adma.h @@ -23,6 +23,12 @@ #define IOP_ADMA_SLOT_SIZE 32 #define IOP_ADMA_THRESHOLD 4 +#ifdef DEBUG +#define IOP_PARANOIA 1 +#else +#define IOP_PARANOIA 0 +#endif +#define iop_paranoia(x) BUG_ON(IOP_PARANOIA && (x)) /** * struct iop_adma_device - internal representation of an ADMA device diff --git a/arch/arm/include/asm/mach/map.h b/arch/arm/include/asm/mach/map.h index cb1139ac194..39d949b63e8 100644 --- a/arch/arm/include/asm/mach/map.h +++ b/arch/arm/include/asm/mach/map.h @@ -19,12 +19,13 @@ struct map_desc { }; /* types 0-3 are defined in asm/io.h */ -#define MT_CACHECLEAN 4 -#define MT_MINICLEAN 5 -#define MT_LOW_VECTORS 6 -#define MT_HIGH_VECTORS 7 -#define MT_MEMORY 8 -#define MT_ROM 9 +#define MT_UNCACHED 4 +#define MT_CACHECLEAN 5 +#define MT_MINICLEAN 6 +#define MT_LOW_VECTORS 7 +#define MT_HIGH_VECTORS 8 +#define MT_MEMORY 9 +#define MT_ROM 10 #ifdef CONFIG_MMU extern void iotable_init(struct map_desc *, int); diff --git a/arch/arm/mach-clps711x/include/mach/hardware.h b/arch/arm/mach-clps711x/include/mach/hardware.h index 4c3e101b96c..b3ebe9e4871 100644 --- a/arch/arm/mach-clps711x/include/mach/hardware.h +++ b/arch/arm/mach-clps711x/include/mach/hardware.h @@ -94,20 +94,6 @@ #include <asm/hardware/ep7212.h> #include <asm/hardware/cs89712.h> -/* dynamic ioremap() areas */ -#define FLASH_START 0x00000000 -#define FLASH_SIZE 0x800000 -#define FLASH_WIDTH 4 - -#define SRAM_START 0x60000000 -#define SRAM_SIZE 0xc000 -#define SRAM_WIDTH 4 - -#define BOOTROM_START 0x70000000 -#define BOOTROM_SIZE 0x80 -#define BOOTROM_WIDTH 4 - - /* static cdb89712_map_io() areas */ #define REGISTER_START 0x80000000 #define REGISTER_SIZE 0x4000 @@ -198,14 +184,6 @@ #define CEIVA_FLASH_SIZE 0x100000 #define CEIVA_FLASH_WIDTH 2 -#define SRAM_START 0x60000000 -#define SRAM_SIZE 0xc000 -#define SRAM_WIDTH 4 - -#define BOOTROM_START 0x70000000 -#define BOOTROM_SIZE 0x80 -#define BOOTROM_WIDTH 4 - /* * SED1355 LCD controller */ diff --git a/arch/arm/mach-clps7500/core.c b/arch/arm/mach-clps7500/core.c index c3a33b8a5aa..7e247c04d41 100644 --- a/arch/arm/mach-clps7500/core.c +++ b/arch/arm/mach-clps7500/core.c @@ -275,9 +275,9 @@ static struct map_desc cl7500_io_desc[] __initdata = { .length = ISA_SIZE, .type = MT_DEVICE }, { /* Flash */ - .virtual = FLASH_BASE, - .pfn = __phys_to_pfn(FLASH_START), - .length = FLASH_SIZE, + .virtual = CLPS7500_FLASH_BASE, + .pfn = __phys_to_pfn(CLPS7500_FLASH_START), + .length = CLPS7500_FLASH_SIZE, .type = MT_DEVICE }, { /* LED */ .virtual = LED_BASE, diff --git a/arch/arm/mach-clps7500/include/mach/hardware.h b/arch/arm/mach-clps7500/include/mach/hardware.h index d66578a3371..a6ad1d44bad 100644 --- a/arch/arm/mach-clps7500/include/mach/hardware.h +++ b/arch/arm/mach-clps7500/include/mach/hardware.h @@ -39,9 +39,9 @@ #define ISA_SIZE 0x00010000 #define ISA_BASE 0xe1000000 -#define FLASH_START 0x01000000 /* XXX */ -#define FLASH_SIZE 0x01000000 -#define FLASH_BASE 0xe2000000 +#define CLPS7500_FLASH_START 0x01000000 /* XXX */ +#define CLPS7500_FLASH_SIZE 0x01000000 +#define CLPS7500_FLASH_BASE 0xe2000000 #define LED_START 0x0302B000 #define LED_SIZE 0x00001000 diff --git a/arch/arm/mach-h720x/include/mach/boards.h b/arch/arm/mach-h720x/include/mach/boards.h index 079b279e124..38b8e0d61fb 100644 --- a/arch/arm/mach-h720x/include/mach/boards.h +++ b/arch/arm/mach-h720x/include/mach/boards.h @@ -19,9 +19,9 @@ #ifdef CONFIG_ARCH_H7202 /* FLASH */ -#define FLASH_VIRT 0xd0000000 -#define FLASH_PHYS 0x00000000 -#define FLASH_SIZE 0x02000000 +#define H720X_FLASH_VIRT 0xd0000000 +#define H720X_FLASH_PHYS 0x00000000 +#define H720X_FLASH_SIZE 0x02000000 /* onboard LAN controller */ # define ETH0_PHYS 0x08000000 diff --git a/arch/arm/mach-integrator/include/mach/platform.h b/arch/arm/mach-integrator/include/mach/platform.h index 028b87839c0..e00a2624f26 100644 --- a/arch/arm/mach-integrator/include/mach/platform.h +++ b/arch/arm/mach-integrator/include/mach/platform.h @@ -408,27 +408,10 @@ #define uHAL_MEMORY_SIZE INTEGRATOR_SSRAM_SIZE /* - * Application Flash - * - */ -#define FLASH_BASE INTEGRATOR_FLASH_BASE -#define FLASH_SIZE INTEGRATOR_FLASH_SIZE -#define FLASH_END (FLASH_BASE + FLASH_SIZE - 1) -#define FLASH_BLOCK_SIZE SZ_128K - -/* - * Boot Flash - * - */ -#define EPROM_BASE INTEGRATOR_BOOT_ROM_HI -#define EPROM_SIZE INTEGRATOR_BOOT_ROM_SIZE -#define EPROM_END (EPROM_BASE + EPROM_SIZE - 1) - -/* * Clean base - dummy * */ -#define CLEAN_BASE EPROM_BASE +#define CLEAN_BASE INTEGRATOR_BOOT_ROM_HI /* * Timer definitions diff --git a/arch/arm/mach-iop13xx/include/mach/adma.h b/arch/arm/mach-iop13xx/include/mach/adma.h index 60019c8e646..5722e86f217 100644 --- a/arch/arm/mach-iop13xx/include/mach/adma.h +++ b/arch/arm/mach-iop13xx/include/mach/adma.h @@ -404,7 +404,8 @@ static inline void iop_desc_set_next_desc(struct iop_adma_desc_slot *desc, u32 next_desc_addr) { struct iop13xx_adma_desc_hw *hw_desc = desc->hw_desc; - BUG_ON(hw_desc->next_desc); + + iop_paranoia(hw_desc->next_desc); hw_desc->next_desc = next_desc_addr; } diff --git a/arch/arm/mach-realview/clock.c b/arch/arm/mach-realview/clock.c index 3e706c57833..3347c4236a6 100644 --- a/arch/arm/mach-realview/clock.c +++ b/arch/arm/mach-realview/clock.c @@ -104,7 +104,7 @@ static struct clk uart_clk = { static struct clk mmci_clk = { .name = "MCLK", - .rate = 33000000, + .rate = 24000000, }; int clk_register(struct clk *clk) diff --git a/arch/arm/mach-realview/include/mach/platform.h b/arch/arm/mach-realview/include/mach/platform.h index 4034b54950c..793a3a33271 100644 --- a/arch/arm/mach-realview/include/mach/platform.h +++ b/arch/arm/mach-realview/include/mach/platform.h @@ -239,27 +239,10 @@ #define REALVIEW_DECODE_OFFSET 0xC /* Fitted logic modules */ /* - * Application Flash - * - */ -#define FLASH_BASE REALVIEW_FLASH_BASE -#define FLASH_SIZE REALVIEW_FLASH_SIZE -#define FLASH_END (FLASH_BASE + FLASH_SIZE - 1) -#define FLASH_BLOCK_SIZE SZ_128K - -/* - * Boot Flash - * - */ -#define EPROM_BASE REALVIEW_BOOT_ROM_HI -#define EPROM_SIZE REALVIEW_BOOT_ROM_SIZE -#define EPROM_END (EPROM_BASE + EPROM_SIZE - 1) - -/* * Clean base - dummy * */ -#define CLEAN_BASE EPROM_BASE +#define CLEAN_BASE REALVIEW_BOOT_ROM_HI /* * System controller bit assignment diff --git a/arch/arm/mach-versatile/clock.c b/arch/arm/mach-versatile/clock.c index 9336508ec0b..58937f1fb38 100644 --- a/arch/arm/mach-versatile/clock.c +++ b/arch/arm/mach-versatile/clock.c @@ -105,7 +105,7 @@ static struct clk uart_clk = { static struct clk mmci_clk = { .name = "MCLK", - .rate = 33000000, + .rate = 24000000, }; int clk_register(struct clk *clk) diff --git a/arch/arm/mach-versatile/include/mach/platform.h b/arch/arm/mach-versatile/include/mach/platform.h index 27cbe6a3f22..f91ba930ca8 100644 --- a/arch/arm/mach-versatile/include/mach/platform.h +++ b/arch/arm/mach-versatile/include/mach/platform.h @@ -436,28 +436,12 @@ #define SIC_INTMASK_PCI1 (1 << SIC_INT_PCI1) #define SIC_INTMASK_PCI2 (1 << SIC_INT_PCI2) #define SIC_INTMASK_PCI3 (1 << SIC_INT_PCI3) -/* - * Application Flash - * - */ -#define FLASH_BASE VERSATILE_FLASH_BASE -#define FLASH_SIZE VERSATILE_FLASH_SIZE -#define FLASH_END (FLASH_BASE + FLASH_SIZE - 1) -#define FLASH_BLOCK_SIZE SZ_128K - -/* - * Boot Flash - * - */ -#define EPROM_BASE VERSATILE_BOOT_ROM_HI -#define EPROM_SIZE VERSATILE_BOOT_ROM_SIZE -#define EPROM_END (EPROM_BASE + EPROM_SIZE - 1) /* * Clean base - dummy * */ -#define CLEAN_BASE EPROM_BASE +#define CLEAN_BASE VERSATILE_BOOT_ROM_HI /* * System controller bit assignment diff --git a/arch/arm/mm/cache-feroceon-l2.c b/arch/arm/mm/cache-feroceon-l2.c index 13cdae8b0d4..80cd207cbae 100644 --- a/arch/arm/mm/cache-feroceon-l2.c +++ b/arch/arm/mm/cache-feroceon-l2.c @@ -150,7 +150,7 @@ static void feroceon_l2_inv_range(unsigned long start, unsigned long end) /* * Clean and invalidate partial last cache line. */ - if (end & (CACHE_LINE_SIZE - 1)) { + if (start < end && end & (CACHE_LINE_SIZE - 1)) { l2_clean_inv_pa(end & ~(CACHE_LINE_SIZE - 1)); end &= ~(CACHE_LINE_SIZE - 1); } @@ -158,7 +158,7 @@ static void feroceon_l2_inv_range(unsigned long start, unsigned long end) /* * Invalidate all full cache lines between 'start' and 'end'. */ - while (start != end) { + while (start < end) { unsigned long range_end = calc_range_end(start, end); l2_inv_pa_range(start, range_end - CACHE_LINE_SIZE); start = range_end; diff --git a/arch/arm/mm/mmu.c b/arch/arm/mm/mmu.c index e63db11f16a..7f36c825718 100644 --- a/arch/arm/mm/mmu.c +++ b/arch/arm/mm/mmu.c @@ -208,6 +208,12 @@ static struct mem_type mem_types[] = { .prot_sect = PROT_SECT_DEVICE, .domain = DOMAIN_IO, }, + [MT_UNCACHED] = { + .prot_pte = PROT_PTE_DEVICE, + .prot_l1 = PMD_TYPE_TABLE, + .prot_sect = PMD_TYPE_SECT | PMD_SECT_XN, + .domain = DOMAIN_IO, + }, [MT_CACHECLEAN] = { .prot_sect = PMD_TYPE_SECT | PMD_SECT_XN, .domain = DOMAIN_KERNEL, diff --git a/arch/arm/plat-iop/setup.c b/arch/arm/plat-iop/setup.c index 4689db638e9..9e573e78176 100644 --- a/arch/arm/plat-iop/setup.c +++ b/arch/arm/plat-iop/setup.c @@ -16,14 +16,15 @@ #include <asm/hardware/iop3xx.h> /* - * Standard IO mapping for all IOP3xx based systems + * Standard IO mapping for all IOP3xx based systems. Note that + * the IOP3xx OCCDR must be mapped uncached and unbuffered. */ static struct map_desc iop3xx_std_desc[] __initdata = { { /* mem mapped registers */ .virtual = IOP3XX_PERIPHERAL_VIRT_BASE, .pfn = __phys_to_pfn(IOP3XX_PERIPHERAL_PHYS_BASE), .length = IOP3XX_PERIPHERAL_SIZE, - .type = MT_DEVICE, + .type = MT_UNCACHED, }, { /* PCI IO space */ .virtual = IOP3XX_PCI_LOWER_IO_VA, .pfn = __phys_to_pfn(IOP3XX_PCI_LOWER_IO_PA), diff --git a/arch/ia64/kvm/Kconfig b/arch/ia64/kvm/Kconfig index 8e99fed6b3f..f833a0b4188 100644 --- a/arch/ia64/kvm/Kconfig +++ b/arch/ia64/kvm/Kconfig @@ -20,6 +20,8 @@ if VIRTUALIZATION config KVM tristate "Kernel-based Virtual Machine (KVM) support" depends on HAVE_KVM && EXPERIMENTAL + # for device assignment: + depends on PCI select PREEMPT_NOTIFIERS select ANON_INODES ---help--- diff --git a/arch/ia64/kvm/kvm-ia64.c b/arch/ia64/kvm/kvm-ia64.c index 3caac477de9..af1464f7a6a 100644 --- a/arch/ia64/kvm/kvm-ia64.c +++ b/arch/ia64/kvm/kvm-ia64.c @@ -673,16 +673,16 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) vcpu_load(vcpu); + if (vcpu->sigset_active) + sigprocmask(SIG_SETMASK, &vcpu->sigset, &sigsaved); + if (unlikely(vcpu->arch.mp_state == KVM_MP_STATE_UNINITIALIZED)) { kvm_vcpu_block(vcpu); clear_bit(KVM_REQ_UNHALT, &vcpu->requests); - vcpu_put(vcpu); - return -EAGAIN; + r = -EAGAIN; + goto out; } - if (vcpu->sigset_active) - sigprocmask(SIG_SETMASK, &vcpu->sigset, &sigsaved); - if (vcpu->mmio_needed) { memcpy(vcpu->mmio_data, kvm_run->mmio.data, 8); kvm_set_mmio_data(vcpu); @@ -690,7 +690,7 @@ int kvm_arch_vcpu_ioctl_run(struct kvm_vcpu *vcpu, struct kvm_run *kvm_run) vcpu->mmio_needed = 0; } r = __vcpu_run(vcpu, kvm_run); - +out: if (vcpu->sigset_active) sigprocmask(SIG_SETMASK, &sigsaved, NULL); diff --git a/arch/ia64/kvm/vcpu.h b/arch/ia64/kvm/vcpu.h index 341e3fee280..e9b2a4e121c 100644 --- a/arch/ia64/kvm/vcpu.h +++ b/arch/ia64/kvm/vcpu.h @@ -384,6 +384,10 @@ static inline u64 __gpfn_is_io(u64 gpfn) #define MODE_IND(psr) \ (((psr).it << 2) + ((psr).dt << 1) + (psr).rt) +#ifndef CONFIG_SMP +#define _vmm_raw_spin_lock(x) do {}while(0) +#define _vmm_raw_spin_unlock(x) do {}while(0) +#else #define _vmm_raw_spin_lock(x) \ do { \ __u32 *ia64_spinlock_ptr = (__u32 *) (x); \ @@ -403,6 +407,7 @@ static inline u64 __gpfn_is_io(u64 gpfn) do { barrier(); \ ((spinlock_t *)x)->raw_lock.lock = 0; } \ while (0) +#endif void vmm_spin_lock(spinlock_t *lock); void vmm_spin_unlock(spinlock_t *lock); diff --git a/arch/m68k/kernel/ints.c b/arch/m68k/kernel/ints.c index 7e8a0d394e6..761ee0440c9 100644 --- a/arch/m68k/kernel/ints.c +++ b/arch/m68k/kernel/ints.c @@ -133,7 +133,7 @@ void __init m68k_setup_user_interrupt(unsigned int vec, unsigned int cnt, { int i; - BUG_ON(IRQ_USER + cnt >= NR_IRQS); + BUG_ON(IRQ_USER + cnt > NR_IRQS); m68k_first_user_vec = vec; for (i = 0; i < cnt; i++) irq_controller[IRQ_USER + i] = &user_irq_controller; diff --git a/arch/mn10300/Kconfig.debug b/arch/mn10300/Kconfig.debug index 524e33819f3..ff80e86b9bd 100644 --- a/arch/mn10300/Kconfig.debug +++ b/arch/mn10300/Kconfig.debug @@ -15,6 +15,15 @@ config DEBUG_DECOMPRESS_KERNEL decompressing Linux seeing "Uncompressing Linux... " and "Ok, booting the kernel.\n" on console. +config TEST_MISALIGNMENT_HANDLER + bool "Run tests on the misalignment handler" + depends on DEBUG_KERNEL + default n + help + If you say Y here the kernel will execute a list of misaligned memory + accesses to make sure the misalignment handler deals them with + correctly. If it does not, the kernel will throw a BUG. + config KPROBES bool "Kprobes" depends on DEBUG_KERNEL diff --git a/arch/mn10300/mm/misalignment.c b/arch/mn10300/mm/misalignment.c index 32aa89dc384..94c4a435806 100644 --- a/arch/mn10300/mm/misalignment.c +++ b/arch/mn10300/mm/misalignment.c @@ -37,26 +37,22 @@ #include <asm/asm-offsets.h> #if 0 -#define kdebug(FMT, ...) printk(KERN_DEBUG FMT, ##__VA_ARGS__) +#define kdebug(FMT, ...) printk(KERN_DEBUG "MISALIGN: "FMT"\n", ##__VA_ARGS__) #else #define kdebug(FMT, ...) do {} while (0) #endif -static int misalignment_addr(unsigned long *registers, unsigned params, - unsigned opcode, unsigned disp, - void **_address, unsigned long **_postinc); +static int misalignment_addr(unsigned long *registers, unsigned long sp, + unsigned params, unsigned opcode, + unsigned long disp, + void **_address, unsigned long **_postinc, + unsigned long *_inc); static int misalignment_reg(unsigned long *registers, unsigned params, - unsigned opcode, unsigned disp, + unsigned opcode, unsigned long disp, unsigned long **_register); -static inline unsigned int_log2(unsigned x) -{ - unsigned y; - asm("bsch %1,%0" : "=r"(y) : "r"(x), "0"(0)); - return y; -} -#define log2(x) int_log2(x) +static void misalignment_MOV_Lcc(struct pt_regs *regs, uint32_t opcode); static const unsigned Dreg_index[] = { REG_D0 >> 2, REG_D1 >> 2, REG_D2 >> 2, REG_D3 >> 2 @@ -86,9 +82,10 @@ enum format_id { FMT_D7, FMT_D8, FMT_D9, + FMT_D10, }; -struct { +static const struct { u_int8_t opsz, dispsz; } format_tbl[16] = { [FMT_S0] = { 8, 0 }, @@ -103,6 +100,7 @@ struct { [FMT_D7] = { 24, 8 }, [FMT_D8] = { 24, 24 }, [FMT_D9] = { 24, 32 }, + [FMT_D10] = { 32, 0 }, }; enum value_id { @@ -128,9 +126,14 @@ enum value_id { SD24, /* 24-bit signed displacement */ SIMM4_2, /* 4-bit signed displacement in opcode bits 4-7 */ SIMM8, /* 8-bit signed immediate */ + IMM8, /* 8-bit unsigned immediate */ + IMM16, /* 16-bit unsigned immediate */ IMM24, /* 24-bit unsigned immediate */ IMM32, /* 32-bit unsigned immediate */ - IMM32_HIGH8, /* 32-bit unsigned immediate, high 8-bits in opcode */ + IMM32_HIGH8, /* 32-bit unsigned immediate, LSB in opcode */ + + IMM32_MEM, /* 32-bit unsigned displacement */ + IMM32_HIGH8_MEM, /* 32-bit unsigned displacement, LSB in opcode */ DN0 = DM0, DN1 = DM1, @@ -149,7 +152,7 @@ enum value_id { }; struct mn10300_opcode { - const char *name; + const char name[8]; u_int32_t opcode; u_int32_t opmask; unsigned exclusion; @@ -185,6 +188,10 @@ struct mn10300_opcode { Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ static const struct mn10300_opcode mn10300_opcodes[] = { +{ "mov", 0x4200, 0xf300, 0, FMT_S1, 0, {DM1, MEM2(IMM8, SP)}}, +{ "mov", 0x4300, 0xf300, 0, FMT_S1, 0, {AM1, MEM2(IMM8, SP)}}, +{ "mov", 0x5800, 0xfc00, 0, FMT_S1, 0, {MEM2(IMM8, SP), DN0}}, +{ "mov", 0x5c00, 0xfc00, 0, FMT_S1, 0, {MEM2(IMM8, SP), AN0}}, { "mov", 0x60, 0xf0, 0, FMT_S0, 0, {DM1, MEM(AN0)}}, { "mov", 0x70, 0xf0, 0, FMT_S0, 0, {MEM(AM0), DN1}}, { "mov", 0xf000, 0xfff0, 0, FMT_D0, 0, {MEM(AM0), AN1}}, @@ -197,8 +204,6 @@ static const struct mn10300_opcode mn10300_opcodes[] = { { "mov", 0xf81000, 0xfff000, 0, FMT_D1, 0, {DM1, MEM2(SD8, AN0)}}, { "mov", 0xf82000, 0xfff000, 0, FMT_D1, 0, {MEM2(SD8,AM0), AN1}}, { "mov", 0xf83000, 0xfff000, 0, FMT_D1, 0, {AM1, MEM2(SD8, AN0)}}, -{ "mov", 0xf8f000, 0xfffc00, 0, FMT_D1, AM33, {MEM2(SD8, AM0), SP}}, -{ "mov", 0xf8f400, 0xfffc00, 0, FMT_D1, AM33, {SP, MEM2(SD8, AN0)}}, { "mov", 0xf90a00, 0xffff00, 0, FMT_D6, AM33, {MEM(RM0), RN2}}, { "mov", 0xf91a00, 0xffff00, 0, FMT_D6, AM33, {RM2, MEM(RN0)}}, { "mov", 0xf96a00, 0xffff00, 0x12, FMT_D6, AM33, {MEMINC(RM0), RN2}}, @@ -207,24 +212,46 @@ static const struct mn10300_opcode mn10300_opcodes[] = { { "mov", 0xfa100000, 0xfff00000, 0, FMT_D2, 0, {DM1, MEM2(SD16, AN0)}}, { "mov", 0xfa200000, 0xfff00000, 0, FMT_D2, 0, {MEM2(SD16, AM0), AN1}}, { "mov", 0xfa300000, 0xfff00000, 0, FMT_D2, 0, {AM1, MEM2(SD16, AN0)}}, +{ "mov", 0xfa900000, 0xfff30000, 0, FMT_D2, 0, {AM1, MEM2(IMM16, SP)}}, +{ "mov", 0xfa910000, 0xfff30000, 0, FMT_D2, 0, {DM1, MEM2(IMM16, SP)}}, +{ "mov", 0xfab00000, 0xfffc0000, 0, FMT_D2, 0, {MEM2(IMM16, SP), AN0}}, +{ "mov", 0xfab40000, 0xfffc0000, 0, FMT_D2, 0, {MEM2(IMM16, SP), DN0}}, { "mov", 0xfb0a0000, 0xffff0000, 0, FMT_D7, AM33, {MEM2(SD8, RM0), RN2}}, { "mov", 0xfb1a0000, 0xffff0000, 0, FMT_D7, AM33, {RM2, MEM2(SD8, RN0)}}, { "mov", 0xfb6a0000, 0xffff0000, 0x22, FMT_D7, AM33, {MEMINC2 (RM0, SIMM8), RN2}}, { "mov", 0xfb7a0000, 0xffff0000, 0, FMT_D7, AM33, {RM2, MEMINC2 (RN0, SIMM8)}}, +{ "mov", 0xfb8a0000, 0xffff0f00, 0, FMT_D7, AM33, {MEM2(IMM8, SP), RN2}}, { "mov", 0xfb8e0000, 0xffff000f, 0, FMT_D7, AM33, {MEM2(RI, RM0), RD2}}, +{ "mov", 0xfb9a0000, 0xffff0f00, 0, FMT_D7, AM33, {RM2, MEM2(IMM8, SP)}}, { "mov", 0xfb9e0000, 0xffff000f, 0, FMT_D7, AM33, {RD2, MEM2(RI, RN0)}}, { "mov", 0xfc000000, 0xfff00000, 0, FMT_D4, 0, {MEM2(IMM32,AM0), DN1}}, { "mov", 0xfc100000, 0xfff00000, 0, FMT_D4, 0, {DM1, MEM2(IMM32,AN0)}}, { "mov", 0xfc200000, 0xfff00000, 0, FMT_D4, 0, {MEM2(IMM32,AM0), AN1}}, { "mov", 0xfc300000, 0xfff00000, 0, FMT_D4, 0, {AM1, MEM2(IMM32,AN0)}}, +{ "mov", 0xfc800000, 0xfff30000, 0, FMT_D4, 0, {AM1, MEM(IMM32_MEM)}}, +{ "mov", 0xfc810000, 0xfff30000, 0, FMT_D4, 0, {DM1, MEM(IMM32_MEM)}}, +{ "mov", 0xfc900000, 0xfff30000, 0, FMT_D4, 0, {AM1, MEM2(IMM32, SP)}}, +{ "mov", 0xfc910000, 0xfff30000, 0, FMT_D4, 0, {DM1, MEM2(IMM32, SP)}}, +{ "mov", 0xfca00000, 0xfffc0000, 0, FMT_D4, 0, {MEM(IMM32_MEM), AN0}}, +{ "mov", 0xfca40000, 0xfffc0000, 0, FMT_D4, 0, {MEM(IMM32_MEM), DN0}}, +{ "mov", 0xfcb00000, 0xfffc0000, 0, FMT_D4, 0, {MEM2(IMM32, SP), AN0}}, +{ "mov", 0xfcb40000, 0xfffc0000, 0, FMT_D4, 0, {MEM2(IMM32, SP), DN0}}, { "mov", 0xfd0a0000, 0xffff0000, 0, FMT_D8, AM33, {MEM2(SD24, RM0), RN2}}, { "mov", 0xfd1a0000, 0xffff0000, 0, FMT_D8, AM33, {RM2, MEM2(SD24, RN0)}}, { "mov", 0xfd6a0000, 0xffff0000, 0x22, FMT_D8, AM33, {MEMINC2 (RM0, IMM24), RN2}}, { "mov", 0xfd7a0000, 0xffff0000, 0, FMT_D8, AM33, {RM2, MEMINC2 (RN0, IMM24)}}, +{ "mov", 0xfd8a0000, 0xffff0f00, 0, FMT_D8, AM33, {MEM2(IMM24, SP), RN2}}, +{ "mov", 0xfd9a0000, 0xffff0f00, 0, FMT_D8, AM33, {RM2, MEM2(IMM24, SP)}}, { "mov", 0xfe0a0000, 0xffff0000, 0, FMT_D9, AM33, {MEM2(IMM32_HIGH8,RM0), RN2}}, +{ "mov", 0xfe0a0000, 0xffff0000, 0, FMT_D9, AM33, {MEM2(IMM32_HIGH8,RM0), RN2}}, +{ "mov", 0xfe0e0000, 0xffff0f00, 0, FMT_D9, AM33, {MEM(IMM32_HIGH8_MEM), RN2}}, +{ "mov", 0xfe1a0000, 0xffff0000, 0, FMT_D9, AM33, {RM2, MEM2(IMM32_HIGH8, RN0)}}, { "mov", 0xfe1a0000, 0xffff0000, 0, FMT_D9, AM33, {RM2, MEM2(IMM32_HIGH8, RN0)}}, +{ "mov", 0xfe1e0000, 0xffff0f00, 0, FMT_D9, AM33, {RM2, MEM(IMM32_HIGH8_MEM)}}, { "mov", 0xfe6a0000, 0xffff0000, 0x22, FMT_D9, AM33, {MEMINC2 (RM0, IMM32_HIGH8), RN2}}, { "mov", 0xfe7a0000, 0xffff0000, 0, FMT_D9, AM33, {RN2, MEMINC2 (RM0, IMM32_HIGH8)}}, +{ "mov", 0xfe8a0000, 0xffff0f00, 0, FMT_D9, AM33, {MEM2(IMM32_HIGH8, SP), RN2}}, +{ "mov", 0xfe9a0000, 0xffff0f00, 0, FMT_D9, AM33, {RM2, MEM2(IMM32_HIGH8, SP)}}, { "movhu", 0xf060, 0xfff0, 0, FMT_D0, 0, {MEM(AM0), DN1}}, { "movhu", 0xf070, 0xfff0, 0, FMT_D0, 0, {DM1, MEM(AN0)}}, @@ -232,29 +259,58 @@ static const struct mn10300_opcode mn10300_opcodes[] = { { "movhu", 0xf4c0, 0xffc0, 0, FMT_D0, 0, {DM2, MEM2(DI, AN0)}}, { "movhu", 0xf86000, 0xfff000, 0, FMT_D1, 0, {MEM2(SD8, AM0), DN1}}, { "movhu", 0xf87000, 0xfff000, 0, FMT_D1, 0, {DM1, MEM2(SD8, AN0)}}, +{ "movhu", 0xf89300, 0xfff300, 0, FMT_D1, 0, {DM1, MEM2(IMM8, SP)}}, +{ "movhu", 0xf8bc00, 0xfffc00, 0, FMT_D1, 0, {MEM2(IMM8, SP), DN0}}, { "movhu", 0xf94a00, 0xffff00, 0, FMT_D6, AM33, {MEM(RM0), RN2}}, { "movhu", 0xf95a00, 0xffff00, 0, FMT_D6, AM33, {RM2, MEM(RN0)}}, { "movhu", 0xf9ea00, 0xffff00, 0x12, FMT_D6, AM33, {MEMINC(RM0), RN2}}, { "movhu", 0xf9fa00, 0xffff00, 0, FMT_D6, AM33, {RM2, MEMINC(RN0)}}, { "movhu", 0xfa600000, 0xfff00000, 0, FMT_D2, 0, {MEM2(SD16, AM0), DN1}}, { "movhu", 0xfa700000, 0xfff00000, 0, FMT_D2, 0, {DM1, MEM2(SD16, AN0)}}, +{ "movhu", 0xfa930000, 0xfff30000, 0, FMT_D2, 0, {DM1, MEM2(IMM16, SP)}}, +{ "movhu", 0xfabc0000, 0xfffc0000, 0, FMT_D2, 0, {MEM2(IMM16, SP), DN0}}, { "movhu", 0xfb4a0000, 0xffff0000, 0, FMT_D7, AM33, {MEM2(SD8, RM0), RN2}}, { "movhu", 0xfb5a0000, 0xffff0000, 0, FMT_D7, AM33, {RM2, MEM2(SD8, RN0)}}, +{ "movhu", 0xfbca0000, 0xffff0f00, 0, FMT_D7, AM33, {MEM2(IMM8, SP), RN2}}, { "movhu", 0xfbce0000, 0xffff000f, 0, FMT_D7, AM33, {MEM2(RI, RM0), RD2}}, +{ "movhu", 0xfbda0000, 0xffff0f00, 0, FMT_D7, AM33, {RM2, MEM2(IMM8, SP)}}, { "movhu", 0xfbde0000, 0xffff000f, 0, FMT_D7, AM33, {RD2, MEM2(RI, RN0)}}, { "movhu", 0xfbea0000, 0xffff0000, 0x22, FMT_D7, AM33, {MEMINC2 (RM0, SIMM8), RN2}}, { "movhu", 0xfbfa0000, 0xffff0000, 0, FMT_D7, AM33, {RM2, MEMINC2 (RN0, SIMM8)}}, { "movhu", 0xfc600000, 0xfff00000, 0, FMT_D4, 0, {MEM2(IMM32,AM0), DN1}}, { "movhu", 0xfc700000, 0xfff00000, 0, FMT_D4, 0, {DM1, MEM2(IMM32,AN0)}}, +{ "movhu", 0xfc830000, 0xfff30000, 0, FMT_D4, 0, {DM1, MEM(IMM32_MEM)}}, +{ "movhu", 0xfc930000, 0xfff30000, 0, FMT_D4, 0, {DM1, MEM2(IMM32, SP)}}, +{ "movhu", 0xfcac0000, 0xfffc0000, 0, FMT_D4, 0, {MEM(IMM32_MEM), DN0}}, +{ "movhu", 0xfcbc0000, 0xfffc0000, 0, FMT_D4, 0, {MEM2(IMM32, SP), DN0}}, { "movhu", 0xfd4a0000, 0xffff0000, 0, FMT_D8, AM33, {MEM2(SD24, RM0), RN2}}, { "movhu", 0xfd5a0000, 0xffff0000, 0, FMT_D8, AM33, {RM2, MEM2(SD24, RN0)}}, +{ "movhu", 0xfdca0000, 0xffff0f00, 0, FMT_D8, AM33, {MEM2(IMM24, SP), RN2}}, +{ "movhu", 0xfdda0000, 0xffff0f00, 0, FMT_D8, AM33, {RM2, MEM2(IMM24, SP)}}, { "movhu", 0xfdea0000, 0xffff0000, 0x22, FMT_D8, AM33, {MEMINC2 (RM0, IMM24), RN2}}, { "movhu", 0xfdfa0000, 0xffff0000, 0, FMT_D8, AM33, {RM2, MEMINC2 (RN0, IMM24)}}, { "movhu", 0xfe4a0000, 0xffff0000, 0, FMT_D9, AM33, {MEM2(IMM32_HIGH8,RM0), RN2}}, +{ "movhu", 0xfe4e0000, 0xffff0f00, 0, FMT_D9, AM33, {MEM(IMM32_HIGH8_MEM), RN2}}, { "movhu", 0xfe5a0000, 0xffff0000, 0, FMT_D9, AM33, {RM2, MEM2(IMM32_HIGH8, RN0)}}, +{ "movhu", 0xfe5e0000, 0xffff0f00, 0, FMT_D9, AM33, {RM2, MEM(IMM32_HIGH8_MEM)}}, +{ "movhu", 0xfeca0000, 0xffff0f00, 0, FMT_D9, AM33, {MEM2(IMM32_HIGH8, SP), RN2}}, +{ "movhu", 0xfeda0000, 0xffff0f00, 0, FMT_D9, AM33, {RM2, MEM2(IMM32_HIGH8, SP)}}, { "movhu", 0xfeea0000, 0xffff0000, 0x22, FMT_D9, AM33, {MEMINC2 (RM0, IMM32_HIGH8), RN2}}, { "movhu", 0xfefa0000, 0xffff0000, 0, FMT_D9, AM33, {RN2, MEMINC2 (RM0, IMM32_HIGH8)}}, -{ 0, 0, 0, 0, 0, 0, {0}}, + +{ "mov_llt", 0xf7e00000, 0xffff000f, 0x22, FMT_D10, AM33, {MEMINC2 (RN4,SIMM4_2), RM6}}, +{ "mov_lgt", 0xf7e00001, 0xffff000f, 0x22, FMT_D10, AM33, {MEMINC2 (RN4,SIMM4_2), RM6}}, +{ "mov_lge", 0xf7e00002, 0xffff000f, 0x22, FMT_D10, AM33, {MEMINC2 (RN4,SIMM4_2), RM6}}, +{ "mov_lle", 0xf7e00003, 0xffff000f, 0x22, FMT_D10, AM33, {MEMINC2 (RN4,SIMM4_2), RM6}}, +{ "mov_lcs", 0xf7e00004, 0xffff000f, 0x22, FMT_D10, AM33, {MEMINC2 (RN4,SIMM4_2), RM6}}, +{ "mov_lhi", 0xf7e00005, 0xffff000f, 0x22, FMT_D10, AM33, {MEMINC2 (RN4,SIMM4_2), RM6}}, +{ "mov_lcc", 0xf7e00006, 0xffff000f, 0x22, FMT_D10, AM33, {MEMINC2 (RN4,SIMM4_2), RM6}}, +{ "mov_lls", 0xf7e00007, 0xffff000f, 0x22, FMT_D10, AM33, {MEMINC2 (RN4,SIMM4_2), RM6}}, +{ "mov_leq", 0xf7e00008, 0xffff000f, 0x22, FMT_D10, AM33, {MEMINC2 (RN4,SIMM4_2), RM6}}, +{ "mov_lne", 0xf7e00009, 0xffff000f, 0x22, FMT_D10, AM33, {MEMINC2 (RN4,SIMM4_2), RM6}}, +{ "mov_lra", 0xf7e0000a, 0xffff000f, 0x22, FMT_D10, AM33, {MEMINC2 (RN4,SIMM4_2), RM6}}, + +{ "", 0, 0, 0, 0, 0, {0}}, }; /* @@ -265,18 +321,21 @@ asmlinkage void misalignment(struct pt_regs *regs, enum exception_code code) const struct exception_table_entry *fixup; const struct mn10300_opcode *pop; unsigned long *registers = (unsigned long *) regs; - unsigned long data, *store, *postinc; + unsigned long data, *store, *postinc, disp, inc, sp; mm_segment_t seg; siginfo_t info; - uint32_t opcode, disp, noc, xo, xm; - uint8_t *pc, byte; + uint32_t opcode, noc, xo, xm; + uint8_t *pc, byte, datasz; void *address; - unsigned tmp, npop; + unsigned tmp, npop, dispsz, loop; + + /* we don't fix up userspace misalignment faults */ + if (user_mode(regs)) + goto bus_error; - kdebug("MISALIGN at %lx\n", regs->pc); + sp = (unsigned long) regs + sizeof(*regs); - if (in_interrupt()) - die("Misalignment trap in interrupt context", regs, code); + kdebug("==>misalignment({pc=%lx,sp=%lx})", regs->pc, sp); if (regs->epsw & EPSW_IE) asm volatile("or %0,epsw" : : "i"(EPSW_IE)); @@ -294,8 +353,8 @@ asmlinkage void misalignment(struct pt_regs *regs, enum exception_code code) opcode = byte; noc = 8; - for (pop = mn10300_opcodes; pop->name; pop++) { - npop = log2(pop->opcode | pop->opmask); + for (pop = mn10300_opcodes; pop->name[0]; pop++) { + npop = ilog2(pop->opcode | pop->opmask); if (npop <= 0 || npop > 31) continue; npop = (npop + 8) & ~7; @@ -328,15 +387,15 @@ asmlinkage void misalignment(struct pt_regs *regs, enum exception_code code) } /* didn't manage to find a fixup */ - if (!user_mode(regs)) - printk(KERN_CRIT "MISALIGN: %lx: unsupported instruction %x\n", - regs->pc, opcode); + printk(KERN_CRIT "MISALIGN: %lx: unsupported instruction %x\n", + regs->pc, opcode); failed: set_fs(seg); if (die_if_no_fixup("misalignment error", regs, code)) return; +bus_error: info.si_signo = SIGBUS; info.si_errno = 0; info.si_code = BUS_ADRALN; @@ -346,31 +405,27 @@ failed: /* error reading opcodes */ fetch_error: - if (!user_mode(regs)) - printk(KERN_CRIT - "MISALIGN: %p: fault whilst reading instruction data\n", - pc); + printk(KERN_CRIT + "MISALIGN: %p: fault whilst reading instruction data\n", + pc); goto failed; bad_addr_mode: - if (!user_mode(regs)) - printk(KERN_CRIT - "MISALIGN: %lx: unsupported addressing mode %x\n", - regs->pc, opcode); + printk(KERN_CRIT + "MISALIGN: %lx: unsupported addressing mode %x\n", + regs->pc, opcode); goto failed; bad_reg_mode: - if (!user_mode(regs)) - printk(KERN_CRIT - "MISALIGN: %lx: unsupported register mode %x\n", - regs->pc, opcode); + printk(KERN_CRIT + "MISALIGN: %lx: unsupported register mode %x\n", + regs->pc, opcode); goto failed; unsupported_instruction: - if (!user_mode(regs)) - printk(KERN_CRIT - "MISALIGN: %lx: unsupported instruction %x (%s)\n", - regs->pc, opcode, pop->name); + printk(KERN_CRIT + "MISALIGN: %lx: unsupported instruction %x (%s)\n", + regs->pc, opcode, pop->name); goto failed; transfer_failed: @@ -391,7 +446,7 @@ transfer_failed: /* we matched the opcode */ found_opcode: - kdebug("MISALIGN: %lx: %x==%x { %x, %x }\n", + kdebug("%lx: %x==%x { %x, %x }", regs->pc, opcode, pop->opcode, pop->params[0], pop->params[1]); tmp = format_tbl[pop->format].opsz; @@ -406,106 +461,108 @@ found_opcode: /* grab the extra displacement (note it's LSB first) */ disp = 0; - tmp = format_tbl[pop->format].dispsz >> 3; - while (tmp > 0) { - tmp--; - disp <<= 8; - + dispsz = format_tbl[pop->format].dispsz; + for (loop = 0; loop < dispsz; loop += 8) { pc++; if (__get_user(byte, pc) != 0) goto fetch_error; - disp |= byte; + disp |= byte << loop; + kdebug("{%p} disp[%02x]=%02x", pc, loop, byte); } + kdebug("disp=%lx", disp); + set_fs(KERNEL_XDS); - if (fixup || regs->epsw & EPSW_nSL) + if (fixup) set_fs(seg); tmp = (pop->params[0] ^ pop->params[1]) & 0x80000000; if (!tmp) { - if (!user_mode(regs)) - printk(KERN_CRIT - "MISALIGN: %lx:" - " insn not move to/from memory %x\n", - regs->pc, opcode); + printk(KERN_CRIT + "MISALIGN: %lx: insn not move to/from memory %x\n", + regs->pc, opcode); goto failed; } + /* determine the data transfer size of the move */ + if (pop->name[3] == 0 || /* "mov" */ + pop->name[4] == 'l') /* mov_lcc */ + inc = datasz = 4; + else if (pop->name[3] == 'h') /* movhu */ + inc = datasz = 2; + else + goto unsupported_instruction; + if (pop->params[0] & 0x80000000) { /* move memory to register */ - if (!misalignment_addr(registers, pop->params[0], opcode, disp, - &address, &postinc)) + if (!misalignment_addr(registers, sp, + pop->params[0], opcode, disp, + &address, &postinc, &inc)) goto bad_addr_mode; if (!misalignment_reg(registers, pop->params[1], opcode, disp, &store)) goto bad_reg_mode; - if (strcmp(pop->name, "mov") == 0) { - kdebug("FIXUP: mov (%p),DARn\n", address); - if (copy_from_user(&data, (void *) address, 4) != 0) - goto transfer_failed; - if (pop->params[0] & 0x1000000) - *postinc += 4; - } else if (strcmp(pop->name, "movhu") == 0) { - kdebug("FIXUP: movhu (%p),DARn\n", address); - data = 0; - if (copy_from_user(&data, (void *) address, 2) != 0) - goto transfer_failed; - if (pop->params[0] & 0x1000000) - *postinc += 2; - } else { - goto unsupported_instruction; + kdebug("mov%u (%p),DARn", datasz, address); + if (copy_from_user(&data, (void *) address, datasz) != 0) + goto transfer_failed; + if (pop->params[0] & 0x1000000) { + kdebug("inc=%lx", inc); + *postinc += inc; } *store = data; + kdebug("loaded %lx", data); } else { /* move register to memory */ if (!misalignment_reg(registers, pop->params[0], opcode, disp, &store)) goto bad_reg_mode; - if (!misalignment_addr(registers, pop->params[1], opcode, disp, - &address, &postinc)) + if (!misalignment_addr(registers, sp, + pop->params[1], opcode, disp, + &address, &postinc, &inc)) goto bad_addr_mode; data = *store; - if (strcmp(pop->name, "mov") == 0) { - kdebug("FIXUP: mov %lx,(%p)\n", data, address); - if (copy_to_user((void *) address, &data, 4) != 0) - goto transfer_failed; - if (pop->params[1] & 0x1000000) - *postinc += 4; - } else if (strcmp(pop->name, "movhu") == 0) { - kdebug("FIXUP: movhu %hx,(%p)\n", - (uint16_t) data, address); - if (copy_to_user((void *) address, &data, 2) != 0) - goto transfer_failed; - if (pop->params[1] & 0x1000000) - *postinc += 2; - } else { - goto unsupported_instruction; - } + kdebug("mov%u %lx,(%p)", datasz, data, address); + if (copy_to_user((void *) address, &data, datasz) != 0) + goto transfer_failed; + if (pop->params[1] & 0x1000000) + *postinc += inc; } tmp = format_tbl[pop->format].opsz + format_tbl[pop->format].dispsz; regs->pc += tmp >> 3; + /* handle MOV_Lcc, which are currently the only FMT_D10 insns that + * access memory */ + if (pop->format == FMT_D10) + misalignment_MOV_Lcc(regs, opcode); + set_fs(seg); - return; } /* * determine the address that was being accessed */ -static int misalignment_addr(unsigned long *registers, unsigned params, - unsigned opcode, unsigned disp, - void **_address, unsigned long **_postinc) +static int misalignment_addr(unsigned long *registers, unsigned long sp, + unsigned params, unsigned opcode, + unsigned long disp, + void **_address, unsigned long **_postinc, + unsigned long *_inc) { unsigned long *postinc = NULL, address = 0, tmp; - params &= 0x7fffffff; + if (!(params & 0x1000000)) { + kdebug("noinc"); + *_inc = 0; + _inc = NULL; + } + + params &= 0x00ffffff; do { switch (params & 0xff) { @@ -514,11 +571,11 @@ static int misalignment_addr(unsigned long *registers, unsigned params, address += *postinc; break; case DM1: - postinc = ®isters[Dreg_index[opcode >> 2 & 0x0c]]; + postinc = ®isters[Dreg_index[opcode >> 2 & 0x03]]; address += *postinc; break; case DM2: - postinc = ®isters[Dreg_index[opcode >> 4 & 0x30]]; + postinc = ®isters[Dreg_index[opcode >> 4 & 0x03]]; address += *postinc; break; case AM0: @@ -526,11 +583,11 @@ static int misalignment_addr(unsigned long *registers, unsigned params, address += *postinc; break; case AM1: - postinc = ®isters[Areg_index[opcode >> 2 & 0x0c]]; + postinc = ®isters[Areg_index[opcode >> 2 & 0x03]]; address += *postinc; break; case AM2: - postinc = ®isters[Areg_index[opcode >> 4 & 0x30]]; + postinc = ®isters[Areg_index[opcode >> 4 & 0x03]]; address += *postinc; break; case RM0: @@ -561,33 +618,53 @@ static int misalignment_addr(unsigned long *registers, unsigned params, postinc = ®isters[Rreg_index[disp >> 4 & 0x0f]]; address += *postinc; break; + case SP: + address += sp; + break; + /* displacements are either to be added to the address + * before use, or, in the case of post-inc addressing, + * to be added into the base register after use */ case SD8: case SIMM8: - address += (int32_t) (int8_t) (disp & 0xff); - break; + disp = (long) (int8_t) (disp & 0xff); + goto displace_or_inc; case SD16: - address += (int32_t) (int16_t) (disp & 0xffff); - break; + disp = (long) (int16_t) (disp & 0xffff); + goto displace_or_inc; case SD24: tmp = disp << 8; asm("asr 8,%0" : "=r"(tmp) : "0"(tmp)); - address += tmp; - break; + disp = (long) tmp; + goto displace_or_inc; case SIMM4_2: tmp = opcode >> 4 & 0x0f; tmp <<= 28; asm("asr 28,%0" : "=r"(tmp) : "0"(tmp)); - address += tmp; - break; + disp = (long) tmp; + goto displace_or_inc; + case IMM8: + disp &= 0x000000ff; + goto displace_or_inc; + case IMM16: + disp &= 0x0000ffff; + goto displace_or_inc; case IMM24: - address += disp & 0x00ffffff; - break; + disp &= 0x00ffffff; + goto displace_or_inc; case IMM32: + case IMM32_MEM: case IMM32_HIGH8: - address += disp; + case IMM32_HIGH8_MEM: + displace_or_inc: + kdebug("%s %lx", _inc ? "incr" : "disp", disp); + if (!_inc) + address += disp; + else + *_inc = disp; break; default: + BUG(); return 0; } } while ((params >>= 8)); @@ -601,7 +678,7 @@ static int misalignment_addr(unsigned long *registers, unsigned params, * determine the register that is acting as source/dest */ static int misalignment_reg(unsigned long *registers, unsigned params, - unsigned opcode, unsigned disp, + unsigned opcode, unsigned long disp, unsigned long **_register) { params &= 0x7fffffff; @@ -654,8 +731,239 @@ static int misalignment_reg(unsigned long *registers, unsigned params, break; default: + BUG(); return 0; } return 1; } + +/* + * handle the conditional loop part of the move-and-loop instructions + */ +static void misalignment_MOV_Lcc(struct pt_regs *regs, uint32_t opcode) +{ + unsigned long epsw = regs->epsw; + unsigned long NxorV; + + kdebug("MOV_Lcc %x [flags=%lx]", opcode, epsw & 0xf); + + /* calculate N^V and shift onto the same bit position as Z */ + NxorV = ((epsw >> 3) ^ epsw >> 1) & 1; + + switch (opcode & 0xf) { + case 0x0: /* MOV_LLT: N^V */ + if (NxorV) + goto take_the_loop; + return; + case 0x1: /* MOV_LGT: ~(Z or (N^V))*/ + if (!((epsw & EPSW_FLAG_Z) | NxorV)) + goto take_the_loop; + return; + case 0x2: /* MOV_LGE: ~(N^V) */ + if (!NxorV) + goto take_the_loop; + return; + case 0x3: /* MOV_LLE: Z or (N^V) */ + if ((epsw & EPSW_FLAG_Z) | NxorV) + goto take_the_loop; + return; + + case 0x4: /* MOV_LCS: C */ + if (epsw & EPSW_FLAG_C) + goto take_the_loop; + return; + case 0x5: /* MOV_LHI: ~(C or Z) */ + if (!(epsw & (EPSW_FLAG_C | EPSW_FLAG_Z))) + goto take_the_loop; + return; + case 0x6: /* MOV_LCC: ~C */ + if (!(epsw & EPSW_FLAG_C)) + goto take_the_loop; + return; + case 0x7: /* MOV_LLS: C or Z */ + if (epsw & (EPSW_FLAG_C | EPSW_FLAG_Z)) + goto take_the_loop; + return; + + case 0x8: /* MOV_LEQ: Z */ + if (epsw & EPSW_FLAG_Z) + goto take_the_loop; + return; + case 0x9: /* MOV_LNE: ~Z */ + if (!(epsw & EPSW_FLAG_Z)) + goto take_the_loop; + return; + case 0xa: /* MOV_LRA: always */ + goto take_the_loop; + + default: + BUG(); + } + +take_the_loop: + /* wind the PC back to just after the SETLB insn */ + kdebug("loop LAR=%lx", regs->lar); + regs->pc = regs->lar - 4; +} + +/* + * misalignment handler tests + */ +#ifdef CONFIG_TEST_MISALIGNMENT_HANDLER +static u8 __initdata testbuf[512] __attribute__((aligned(16))) = { + [257] = 0x11, + [258] = 0x22, + [259] = 0x33, + [260] = 0x44, +}; + +#define ASSERTCMP(X, OP, Y) \ +do { \ + if (unlikely(!((X) OP (Y)))) { \ + printk(KERN_ERR "\n"); \ + printk(KERN_ERR "MISALIGN: Assertion failed at line %u\n", \ + __LINE__); \ + printk(KERN_ERR "0x%lx " #OP " 0x%lx is false\n", \ + (unsigned long)(X), (unsigned long)(Y)); \ + BUG(); \ + } \ +} while(0) + +static int __init test_misalignment(void) +{ + register void *r asm("e0"); + register u32 y asm("e1"); + void *p = testbuf, *q; + u32 tmp, tmp2, x; + + printk(KERN_NOTICE "==>test_misalignment() [testbuf=%p]\n", p); + p++; + + printk(KERN_NOTICE "___ MOV (Am),Dn ___\n"); + q = p + 256; + asm volatile("mov (%0),%1" : "+a"(q), "=d"(x)); + ASSERTCMP(q, ==, p + 256); + ASSERTCMP(x, ==, 0x44332211); + + printk(KERN_NOTICE "___ MOV (256,Am),Dn ___\n"); + q = p; + asm volatile("mov (256,%0),%1" : "+a"(q), "=d"(x)); + ASSERTCMP(q, ==, p); + ASSERTCMP(x, ==, 0x44332211); + + printk(KERN_NOTICE "___ MOV (Di,Am),Dn ___\n"); + tmp = 256; + q = p; + asm volatile("mov (%2,%0),%1" : "+a"(q), "=d"(x), "+d"(tmp)); + ASSERTCMP(q, ==, p); + ASSERTCMP(x, ==, 0x44332211); + ASSERTCMP(tmp, ==, 256); + + printk(KERN_NOTICE "___ MOV (256,Rm),Rn ___\n"); + r = p; + asm volatile("mov (256,%0),%1" : "+r"(r), "=r"(y)); + ASSERTCMP(r, ==, p); + ASSERTCMP(y, ==, 0x44332211); + + printk(KERN_NOTICE "___ MOV (Rm+),Rn ___\n"); + r = p + 256; + asm volatile("mov (%0+),%1" : "+r"(r), "=r"(y)); + ASSERTCMP(r, ==, p + 256 + 4); + ASSERTCMP(y, ==, 0x44332211); + + printk(KERN_NOTICE "___ MOV (Rm+,8),Rn ___\n"); + r = p + 256; + asm volatile("mov (%0+,8),%1" : "+r"(r), "=r"(y)); + ASSERTCMP(r, ==, p + 256 + 8); + ASSERTCMP(y, ==, 0x44332211); + + printk(KERN_NOTICE "___ MOV (7,SP),Rn ___\n"); + asm volatile( + "add -16,sp \n" + "mov +0x11,%0 \n" + "movbu %0,(7,sp) \n" + "mov +0x22,%0 \n" + "movbu %0,(8,sp) \n" + "mov +0x33,%0 \n" + "movbu %0,(9,sp) \n" + "mov +0x44,%0 \n" + "movbu %0,(10,sp) \n" + "mov (7,sp),%1 \n" + "add +16,sp \n" + : "+a"(q), "=d"(x)); + ASSERTCMP(x, ==, 0x44332211); + + printk(KERN_NOTICE "___ MOV (259,SP),Rn ___\n"); + asm volatile( + "add -264,sp \n" + "mov +0x11,%0 \n" + "movbu %0,(259,sp) \n" + "mov +0x22,%0 \n" + "movbu %0,(260,sp) \n" + "mov +0x33,%0 \n" + "movbu %0,(261,sp) \n" + "mov +0x55,%0 \n" + "movbu %0,(262,sp) \n" + "mov (259,sp),%1 \n" + "add +264,sp \n" + : "+d"(tmp), "=d"(x)); + ASSERTCMP(x, ==, 0x55332211); + + printk(KERN_NOTICE "___ MOV (260,SP),Rn ___\n"); + asm volatile( + "add -264,sp \n" + "mov +0x11,%0 \n" + "movbu %0,(260,sp) \n" + "mov +0x22,%0 \n" + "movbu %0,(261,sp) \n" + "mov +0x33,%0 \n" + "movbu %0,(262,sp) \n" + "mov +0x55,%0 \n" + "movbu %0,(263,sp) \n" + "mov (260,sp),%1 \n" + "add +264,sp \n" + : "+d"(tmp), "=d"(x)); + ASSERTCMP(x, ==, 0x55332211); + + + printk(KERN_NOTICE "___ MOV_LNE ___\n"); + tmp = 1; + tmp2 = 2; + q = p + 256; + asm volatile( + "setlb \n" + "mov %2,%3 \n" + "mov %1,%2 \n" + "cmp +0,%1 \n" + "mov_lne (%0+,4),%1" + : "+r"(q), "+d"(tmp), "+d"(tmp2), "=d"(x) + : + : "cc"); + ASSERTCMP(q, ==, p + 256 + 12); + ASSERTCMP(x, ==, 0x44332211); + + printk(KERN_NOTICE "___ MOV in SETLB ___\n"); + tmp = 1; + tmp2 = 2; + q = p + 256; + asm volatile( + "setlb \n" + "mov %1,%3 \n" + "mov (%0+),%1 \n" + "cmp +0,%1 \n" + "lne " + : "+a"(q), "+d"(tmp), "+d"(tmp2), "=d"(x) + : + : "cc"); + + ASSERTCMP(q, ==, p + 256 + 8); + ASSERTCMP(x, ==, 0x44332211); + + printk(KERN_NOTICE "<==test_misalignment()\n"); + return 0; +} + +arch_initcall(test_misalignment); + +#endif /* CONFIG_TEST_MISALIGNMENT_HANDLER */ diff --git a/arch/parisc/include/asm/smp.h b/arch/parisc/include/asm/smp.h index 398cdbaf4e5..409e698f436 100644 --- a/arch/parisc/include/asm/smp.h +++ b/arch/parisc/include/asm/smp.h @@ -44,8 +44,6 @@ extern void arch_send_call_function_ipi(cpumask_t mask); #define PROC_CHANGE_PENALTY 15 /* Schedule penalty */ -extern unsigned long cpu_present_mask; - #define raw_smp_processor_id() (current_thread_info()->cpu) #else /* CONFIG_SMP */ diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S index ed500ef799b..08844fc24a2 100644 --- a/arch/s390/kernel/entry.S +++ b/arch/s390/kernel/entry.S @@ -61,22 +61,25 @@ STACK_SIZE = 1 << STACK_SHIFT #ifdef CONFIG_TRACE_IRQFLAGS .macro TRACE_IRQS_ON - l %r1,BASED(.Ltrace_irq_on) + basr %r2,%r0 + l %r1,BASED(.Ltrace_irq_on_caller) basr %r14,%r1 .endm .macro TRACE_IRQS_OFF - l %r1,BASED(.Ltrace_irq_off) + basr %r2,%r0 + l %r1,BASED(.Ltrace_irq_off_caller) basr %r14,%r1 .endm .macro TRACE_IRQS_CHECK + basr %r2,%r0 tm SP_PSW(%r15),0x03 # irqs enabled? jz 0f - l %r1,BASED(.Ltrace_irq_on) + l %r1,BASED(.Ltrace_irq_on_caller) basr %r14,%r1 j 1f -0: l %r1,BASED(.Ltrace_irq_off) +0: l %r1,BASED(.Ltrace_irq_off_caller) basr %r14,%r1 1: .endm @@ -1113,9 +1116,12 @@ cleanup_io_leave_insn: .Lschedtail: .long schedule_tail .Lsysc_table: .long sys_call_table #ifdef CONFIG_TRACE_IRQFLAGS -.Ltrace_irq_on: .long trace_hardirqs_on -.Ltrace_irq_off: - .long trace_hardirqs_off +.Ltrace_irq_on_caller: + .long trace_hardirqs_on_caller +.Ltrace_irq_off_caller: + .long trace_hardirqs_off_caller +#endif +#ifdef CONFIG_LOCKDEP .Llockdep_sys_exit: .long lockdep_sys_exit #endif diff --git a/arch/s390/kernel/entry64.S b/arch/s390/kernel/entry64.S index d7ce150453f..41aca06682a 100644 --- a/arch/s390/kernel/entry64.S +++ b/arch/s390/kernel/entry64.S @@ -61,19 +61,22 @@ _TIF_WORK_INT = (_TIF_SIGPENDING | _TIF_NOTIFY_RESUME | _TIF_NEED_RESCHED | \ #ifdef CONFIG_TRACE_IRQFLAGS .macro TRACE_IRQS_ON - brasl %r14,trace_hardirqs_on + basr %r2,%r0 + brasl %r14,trace_hardirqs_on_caller .endm .macro TRACE_IRQS_OFF - brasl %r14,trace_hardirqs_off + basr %r2,%r0 + brasl %r14,trace_hardirqs_off_caller .endm .macro TRACE_IRQS_CHECK + basr %r2,%r0 tm SP_PSW(%r15),0x03 # irqs enabled? jz 0f - brasl %r14,trace_hardirqs_on + brasl %r14,trace_hardirqs_on_caller j 1f -0: brasl %r14,trace_hardirqs_off +0: brasl %r14,trace_hardirqs_off_caller 1: .endm #else diff --git a/arch/s390/kernel/process.c b/arch/s390/kernel/process.c index 3e2c05cb6a8..04f8c67a610 100644 --- a/arch/s390/kernel/process.c +++ b/arch/s390/kernel/process.c @@ -136,9 +136,12 @@ static void default_idle(void) return; } trace_hardirqs_on(); + /* Don't trace preempt off for idle. */ + stop_critical_timings(); /* Wait for external, I/O or machine check interrupt. */ __load_psw_mask(psw_kernel_bits | PSW_MASK_WAIT | PSW_MASK_IO | PSW_MASK_EXT); + start_critical_timings(); } void cpu_idle(void) diff --git a/arch/s390/kernel/setup.c b/arch/s390/kernel/setup.c index 62122bad1e3..400b040df7f 100644 --- a/arch/s390/kernel/setup.c +++ b/arch/s390/kernel/setup.c @@ -604,13 +604,13 @@ setup_memory(void) if (memory_chunk[i].type != CHUNK_READ_WRITE) continue; start_chunk = PFN_DOWN(memory_chunk[i].addr); - end_chunk = start_chunk + PFN_DOWN(memory_chunk[i].size) - 1; + end_chunk = start_chunk + PFN_DOWN(memory_chunk[i].size); end_chunk = min(end_chunk, end_pfn); if (start_chunk >= end_chunk) continue; add_active_range(0, start_chunk, end_chunk); pfn = max(start_chunk, start_pfn); - for (; pfn <= end_chunk; pfn++) + for (; pfn < end_chunk; pfn++) page_set_storage_key(PFN_PHYS(pfn), PAGE_DEFAULT_KEY); } diff --git a/arch/s390/kernel/sys_s390.c b/arch/s390/kernel/sys_s390.c index 5fdb799062b..4fe952e557a 100644 --- a/arch/s390/kernel/sys_s390.c +++ b/arch/s390/kernel/sys_s390.c @@ -198,7 +198,7 @@ asmlinkage long s390x_newuname(struct new_utsname __user *name) { int ret = sys_newuname(name); - if (current->personality == PER_LINUX32 && !ret) { + if (personality(current->personality) == PER_LINUX32 && !ret) { ret = copy_to_user(name->machine, "s390\0\0\0\0", 8); if (ret) ret = -EFAULT; } diff --git a/arch/s390/kernel/topology.c b/arch/s390/kernel/topology.c index 632b13e1005..a947899dcba 100644 --- a/arch/s390/kernel/topology.c +++ b/arch/s390/kernel/topology.c @@ -65,18 +65,21 @@ static int machine_has_topology_irq; static struct timer_list topology_timer; static void set_topology_timer(void); static DECLARE_WORK(topology_work, topology_work_fn); +/* topology_lock protects the core linked list */ +static DEFINE_SPINLOCK(topology_lock); cpumask_t cpu_core_map[NR_CPUS]; cpumask_t cpu_coregroup_map(unsigned int cpu) { struct core_info *core = &core_info; + unsigned long flags; cpumask_t mask; cpus_clear(mask); if (!machine_has_topology) return cpu_present_map; - mutex_lock(&smp_cpu_state_mutex); + spin_lock_irqsave(&topology_lock, flags); while (core) { if (cpu_isset(cpu, core->mask)) { mask = core->mask; @@ -84,7 +87,7 @@ cpumask_t cpu_coregroup_map(unsigned int cpu) } core = core->next; } - mutex_unlock(&smp_cpu_state_mutex); + spin_unlock_irqrestore(&topology_lock, flags); if (cpus_empty(mask)) mask = cpumask_of_cpu(cpu); return mask; @@ -133,7 +136,7 @@ static void tl_to_cores(struct tl_info *info) union tl_entry *tle, *end; struct core_info *core = &core_info; - mutex_lock(&smp_cpu_state_mutex); + spin_lock_irq(&topology_lock); clear_cores(); tle = info->tle; end = (union tl_entry *)((unsigned long)info + info->length); @@ -157,7 +160,7 @@ static void tl_to_cores(struct tl_info *info) } tle = next_tle(tle); } - mutex_unlock(&smp_cpu_state_mutex); + spin_unlock_irq(&topology_lock); } static void topology_update_polarization_simple(void) diff --git a/arch/sh/include/asm/io.h b/arch/sh/include/asm/io.h index 436c2853957..65eaae34e75 100644 --- a/arch/sh/include/asm/io.h +++ b/arch/sh/include/asm/io.h @@ -293,6 +293,10 @@ __ioremap_mode(unsigned long offset, unsigned long size, unsigned long flags) */ #define xlate_dev_kmem_ptr(p) p +#define ARCH_HAS_VALID_PHYS_ADDR_RANGE +int valid_phys_addr_range(unsigned long addr, size_t size); +int valid_mmap_phys_addr_range(unsigned long pfn, size_t size); + #endif /* __KERNEL__ */ #endif /* __ASM_SH_IO_H */ diff --git a/arch/sh/include/asm/pgtable.h b/arch/sh/include/asm/pgtable.h index 52220d70a09..b517ae08b9c 100644 --- a/arch/sh/include/asm/pgtable.h +++ b/arch/sh/include/asm/pgtable.h @@ -148,6 +148,12 @@ extern void paging_init(void); extern void page_table_range_init(unsigned long start, unsigned long end, pgd_t *pgd); +#if !defined(CONFIG_CACHE_OFF) && defined(CONFIG_CPU_SH4) && defined(CONFIG_MMU) +extern void kmap_coherent_init(void); +#else +#define kmap_coherent_init() do { } while (0) +#endif + #include <asm-generic/pgtable.h> #endif /* __ASM_SH_PGTABLE_H */ diff --git a/arch/sh/kernel/cpu/sh4a/setup-sh7723.c b/arch/sh/kernel/cpu/sh4a/setup-sh7723.c index a7412cede53..6d9e6972cfc 100644 --- a/arch/sh/kernel/cpu/sh4a/setup-sh7723.c +++ b/arch/sh/kernel/cpu/sh4a/setup-sh7723.c @@ -119,17 +119,17 @@ static struct plat_sci_port sci_platform_data[] = { },{ .mapbase = 0xa4e30000, .flags = UPF_BOOT_AUTOCONF, - .type = PORT_SCI, + .type = PORT_SCIFA, .irqs = { 56, 56, 56, 56 }, },{ .mapbase = 0xa4e40000, .flags = UPF_BOOT_AUTOCONF, - .type = PORT_SCI, + .type = PORT_SCIFA, .irqs = { 88, 88, 88, 88 }, },{ .mapbase = 0xa4e50000, .flags = UPF_BOOT_AUTOCONF, - .type = PORT_SCI, + .type = PORT_SCIFA, .irqs = { 109, 109, 109, 109 }, }, { .flags = 0, diff --git a/arch/sh/kernel/early_printk.c b/arch/sh/kernel/early_printk.c index 6b7d166694e..a952dcf9999 100644 --- a/arch/sh/kernel/early_printk.c +++ b/arch/sh/kernel/early_printk.c @@ -75,6 +75,7 @@ static struct console bios_console = { #endif static struct uart_port scif_port = { + .type = PORT_SCIF, .mapbase = CONFIG_EARLY_SCIF_CONSOLE_PORT, .membase = (char __iomem *)CONFIG_EARLY_SCIF_CONSOLE_PORT, }; @@ -84,9 +85,9 @@ static void scif_sercon_putc(int c) while (((sci_in(&scif_port, SCFDR) & EPK_FIFO_BITS) >= EPK_FIFO_SIZE)) ; - sci_out(&scif_port, SCxTDR, c); sci_in(&scif_port, SCxSR); sci_out(&scif_port, SCxSR, 0xf3 & ~(0x20 | 0x40)); + sci_out(&scif_port, SCxTDR, c); while ((sci_in(&scif_port, SCxSR) & 0x40) == 0) ; diff --git a/arch/sh/kernel/timers/timer-tmu.c b/arch/sh/kernel/timers/timer-tmu.c index aaaf90d06b8..3c61ddd4d43 100644 --- a/arch/sh/kernel/timers/timer-tmu.c +++ b/arch/sh/kernel/timers/timer-tmu.c @@ -120,7 +120,7 @@ static void tmu_set_mode(enum clock_event_mode mode, { switch (mode) { case CLOCK_EVT_MODE_PERIODIC: - ctrl_outl(ctrl_inl(TMU0_TCNT), TMU0_TCOR); + ctrl_outl(tmu_latest_interval[TMU0], TMU0_TCOR); break; case CLOCK_EVT_MODE_ONESHOT: ctrl_outl(0, TMU0_TCOR); diff --git a/arch/sh/lib/copy_page.S b/arch/sh/lib/copy_page.S index 5d12e657be3..43de7e8e4e1 100644 --- a/arch/sh/lib/copy_page.S +++ b/arch/sh/lib/copy_page.S @@ -80,6 +80,11 @@ ENTRY(copy_page) .section __ex_table, "a"; \ .long 9999b, 6000f ; \ .previous +#define EX_NO_POP(...) \ + 9999: __VA_ARGS__ ; \ + .section __ex_table, "a"; \ + .long 9999b, 6005f ; \ + .previous ENTRY(__copy_user) ! Check if small number of bytes mov #11,r0 @@ -139,9 +144,9 @@ EX( mov.b r1,@r4 ) bt 1f 2: -EX( mov.b @r5+,r0 ) +EX_NO_POP( mov.b @r5+,r0 ) dt r6 -EX( mov.b r0,@r4 ) +EX_NO_POP( mov.b r0,@r4 ) bf/s 2b add #1,r4 @@ -150,7 +155,7 @@ EX( mov.b r0,@r4 ) # Exception handler: .section .fixup, "ax" -6000: +6005: mov.l 8000f,r1 mov r3,r0 jmp @r1 diff --git a/arch/sh/mm/Makefile_32 b/arch/sh/mm/Makefile_32 index 70e0906023c..f066e76da20 100644 --- a/arch/sh/mm/Makefile_32 +++ b/arch/sh/mm/Makefile_32 @@ -2,7 +2,7 @@ # Makefile for the Linux SuperH-specific parts of the memory manager. # -obj-y := init.o extable_32.o consistent.o +obj-y := init.o extable_32.o consistent.o mmap.o ifndef CONFIG_CACHE_OFF cache-$(CONFIG_CPU_SH2) := cache-sh2.o diff --git a/arch/sh/mm/Makefile_64 b/arch/sh/mm/Makefile_64 index 0d92a8a3ac9..9481d0f54ef 100644 --- a/arch/sh/mm/Makefile_64 +++ b/arch/sh/mm/Makefile_64 @@ -2,7 +2,7 @@ # Makefile for the Linux SuperH-specific parts of the memory manager. # -obj-y := init.o consistent.o +obj-y := init.o consistent.o mmap.o mmu-y := tlb-nommu.o pg-nommu.o extable_32.o mmu-$(CONFIG_MMU) := fault_64.o ioremap_64.o tlbflush_64.o tlb-sh5.o \ diff --git a/arch/sh/mm/init.c b/arch/sh/mm/init.c index 4abf00031da..6cbef8caeb5 100644 --- a/arch/sh/mm/init.c +++ b/arch/sh/mm/init.c @@ -137,6 +137,7 @@ void __init page_table_range_init(unsigned long start, unsigned long end, void __init paging_init(void) { unsigned long max_zone_pfns[MAX_NR_ZONES]; + unsigned long vaddr; int nid; /* We don't need to map the kernel through the TLB, as @@ -148,10 +149,15 @@ void __init paging_init(void) * check for a null value. */ set_TTB(swapper_pg_dir); - /* Populate the relevant portions of swapper_pg_dir so that + /* + * Populate the relevant portions of swapper_pg_dir so that * we can use the fixmap entries without calling kmalloc. - * pte's will be filled in by __set_fixmap(). */ - page_table_range_init(FIXADDR_START, FIXADDR_TOP, swapper_pg_dir); + * pte's will be filled in by __set_fixmap(). + */ + vaddr = __fix_to_virt(__end_of_fixed_addresses - 1) & PMD_MASK; + page_table_range_init(vaddr, 0, swapper_pg_dir); + + kmap_coherent_init(); memset(max_zone_pfns, 0, sizeof(max_zone_pfns)); diff --git a/arch/sh/mm/mmap.c b/arch/sh/mm/mmap.c new file mode 100644 index 00000000000..8837d511710 --- /dev/null +++ b/arch/sh/mm/mmap.c @@ -0,0 +1,31 @@ +/* + * arch/sh/mm/mmap.c + * + * Copyright (C) 2008 Paul Mundt + * + * 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/io.h> +#include <linux/mm.h> +#include <asm/page.h> + +/* + * You really shouldn't be using read() or write() on /dev/mem. This + * might go away in the future. + */ +int valid_phys_addr_range(unsigned long addr, size_t count) +{ + if (addr < __MEMORY_START) + return 0; + if (addr + count > __pa(high_memory)) + return 0; + + return 1; +} + +int valid_mmap_phys_addr_range(unsigned long pfn, size_t size) +{ + return 1; +} diff --git a/arch/sh/mm/pg-sh4.c b/arch/sh/mm/pg-sh4.c index 38870e0fc18..2fe14da1f83 100644 --- a/arch/sh/mm/pg-sh4.c +++ b/arch/sh/mm/pg-sh4.c @@ -7,6 +7,7 @@ * Released under the terms of the GNU GPL v2.0. */ #include <linux/mm.h> +#include <linux/init.h> #include <linux/mutex.h> #include <linux/fs.h> #include <linux/highmem.h> @@ -16,6 +17,20 @@ #define CACHE_ALIAS (current_cpu_data.dcache.alias_mask) +#define kmap_get_fixmap_pte(vaddr) \ + pte_offset_kernel(pmd_offset(pud_offset(pgd_offset_k(vaddr), (vaddr)), (vaddr)), (vaddr)) + +static pte_t *kmap_coherent_pte; + +void __init kmap_coherent_init(void) +{ + unsigned long vaddr; + + /* cache the first coherent kmap pte */ + vaddr = __fix_to_virt(FIX_CMAP_BEGIN); + kmap_coherent_pte = kmap_get_fixmap_pte(vaddr); +} + static inline void *kmap_coherent(struct page *page, unsigned long addr) { enum fixed_addresses idx; @@ -34,6 +49,8 @@ static inline void *kmap_coherent(struct page *page, unsigned long addr) update_mmu_cache(NULL, vaddr, pte); + set_pte(kmap_coherent_pte - (FIX_CMAP_END - idx), pte); + return (void *)vaddr; } diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 93224b56918..3cf457f90e8 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -167,9 +167,12 @@ config GENERIC_PENDING_IRQ config X86_SMP bool depends on SMP && ((X86_32 && !X86_VOYAGER) || X86_64) - select USE_GENERIC_SMP_HELPERS default y +config USE_GENERIC_SMP_HELPERS + def_bool y + depends on SMP + config X86_32_SMP def_bool y depends on X86_32 && SMP @@ -239,21 +242,13 @@ config X86_FIND_SMP_CONFIG def_bool y depends on X86_MPPARSE || X86_VOYAGER -if ACPI config X86_MPPARSE - def_bool y - bool "Enable MPS table" + bool "Enable MPS table" if ACPI + default y depends on X86_LOCAL_APIC help For old smp systems that do not have proper acpi support. Newer systems (esp with 64bit cpus) with acpi support, MADT and DSDT will override it -endif - -if !ACPI -config X86_MPPARSE - def_bool y - depends on X86_LOCAL_APIC -endif choice prompt "Subarchitecture Type" diff --git a/arch/x86/ia32/ia32_signal.c b/arch/x86/ia32/ia32_signal.c index 4bc02b23674..e82ebd65226 100644 --- a/arch/x86/ia32/ia32_signal.c +++ b/arch/x86/ia32/ia32_signal.c @@ -572,11 +572,6 @@ int ia32_setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info, regs->dx = (unsigned long) &frame->info; regs->cx = (unsigned long) &frame->uc; - /* Make -mregparm=3 work */ - regs->ax = sig; - regs->dx = (unsigned long) &frame->info; - regs->cx = (unsigned long) &frame->uc; - loadsegment(ds, __USER32_DS); loadsegment(es, __USER32_DS); diff --git a/arch/x86/include/asm/acpi.h b/arch/x86/include/asm/acpi.h index 8d676d8ecde..9830681446a 100644 --- a/arch/x86/include/asm/acpi.h +++ b/arch/x86/include/asm/acpi.h @@ -113,7 +113,6 @@ static inline void acpi_disable_pci(void) acpi_pci_disabled = 1; acpi_noirq_set(); } -extern int acpi_irq_balance_set(char *str); /* routines for saving/restoring kernel state */ extern int acpi_save_state_mem(void); diff --git a/arch/x86/include/asm/bitops.h b/arch/x86/include/asm/bitops.h index 36001032271..9fa9dcdf344 100644 --- a/arch/x86/include/asm/bitops.h +++ b/arch/x86/include/asm/bitops.h @@ -168,7 +168,15 @@ static inline void __change_bit(int nr, volatile unsigned long *addr) */ static inline void change_bit(int nr, volatile unsigned long *addr) { - asm volatile(LOCK_PREFIX "btc %1,%0" : ADDR : "Ir" (nr)); + if (IS_IMMEDIATE(nr)) { + asm volatile(LOCK_PREFIX "xorb %1,%0" + : CONST_MASK_ADDR(nr, addr) + : "iq" ((u8)CONST_MASK(nr))); + } else { + asm volatile(LOCK_PREFIX "btc %1,%0" + : BITOP_ADDR(addr) + : "Ir" (nr)); + } } /** diff --git a/arch/x86/include/asm/byteorder.h b/arch/x86/include/asm/byteorder.h index e02ae2d89ac..f110ad417df 100644 --- a/arch/x86/include/asm/byteorder.h +++ b/arch/x86/include/asm/byteorder.h @@ -4,26 +4,33 @@ #include <asm/types.h> #include <linux/compiler.h> -#ifdef __GNUC__ +#define __LITTLE_ENDIAN -#ifdef __i386__ - -static inline __attribute_const__ __u32 ___arch__swab32(__u32 x) +static inline __attribute_const__ __u32 __arch_swab32(__u32 val) { -#ifdef CONFIG_X86_BSWAP - asm("bswap %0" : "=r" (x) : "0" (x)); -#else +#ifdef __i386__ +# ifdef CONFIG_X86_BSWAP + asm("bswap %0" : "=r" (val) : "0" (val)); +# else asm("xchgb %b0,%h0\n\t" /* swap lower bytes */ "rorl $16,%0\n\t" /* swap words */ "xchgb %b0,%h0" /* swap higher bytes */ - : "=q" (x) - : "0" (x)); + : "=q" (val) + : "0" (val)); +# endif + +#else /* __i386__ */ + asm("bswapl %0" + : "=r" (val) + : "0" (val)); #endif - return x; + return val; } +#define __arch_swab32 __arch_swab32 -static inline __attribute_const__ __u64 ___arch__swab64(__u64 val) +static inline __attribute_const__ __u64 __arch_swab64(__u64 val) { +#ifdef __i386__ union { struct { __u32 a; @@ -32,50 +39,27 @@ static inline __attribute_const__ __u64 ___arch__swab64(__u64 val) __u64 u; } v; v.u = val; -#ifdef CONFIG_X86_BSWAP +# ifdef CONFIG_X86_BSWAP asm("bswapl %0 ; bswapl %1 ; xchgl %0,%1" : "=r" (v.s.a), "=r" (v.s.b) : "0" (v.s.a), "1" (v.s.b)); -#else - v.s.a = ___arch__swab32(v.s.a); - v.s.b = ___arch__swab32(v.s.b); +# else + v.s.a = __arch_swab32(v.s.a); + v.s.b = __arch_swab32(v.s.b); asm("xchgl %0,%1" : "=r" (v.s.a), "=r" (v.s.b) : "0" (v.s.a), "1" (v.s.b)); -#endif +# endif return v.u; -} - #else /* __i386__ */ - -static inline __attribute_const__ __u64 ___arch__swab64(__u64 x) -{ asm("bswapq %0" - : "=r" (x) - : "0" (x)); - return x; -} - -static inline __attribute_const__ __u32 ___arch__swab32(__u32 x) -{ - asm("bswapl %0" - : "=r" (x) - : "0" (x)); - return x; -} - + : "=r" (val) + : "0" (val)); + return val; #endif +} +#define __arch_swab64 __arch_swab64 -/* Do not define swab16. Gcc is smart enough to recognize "C" version and - convert it into rotation or exhange. */ - -#define __arch__swab64(x) ___arch__swab64(x) -#define __arch__swab32(x) ___arch__swab32(x) - -#define __BYTEORDER_HAS_U64__ - -#endif /* __GNUC__ */ - -#include <linux/byteorder/little_endian.h> +#include <linux/byteorder.h> #endif /* _ASM_X86_BYTEORDER_H */ diff --git a/include/asm-x86/iomap.h b/arch/x86/include/asm/iomap.h index c1f06289b14..c1f06289b14 100644 --- a/include/asm-x86/iomap.h +++ b/arch/x86/include/asm/iomap.h diff --git a/arch/x86/include/asm/iommu.h b/arch/x86/include/asm/iommu.h index e4a552d4446..0b500c5b644 100644 --- a/arch/x86/include/asm/iommu.h +++ b/arch/x86/include/asm/iommu.h @@ -6,7 +6,6 @@ extern void no_iommu_init(void); extern struct dma_mapping_ops nommu_dma_ops; extern int force_iommu, no_iommu; extern int iommu_detected; -extern int dmar_disabled; extern unsigned long iommu_nr_pages(unsigned long addr, unsigned long len); diff --git a/arch/x86/include/asm/irq.h b/arch/x86/include/asm/irq.h index bae0eda9548..28e409fc73f 100644 --- a/arch/x86/include/asm/irq.h +++ b/arch/x86/include/asm/irq.h @@ -31,10 +31,6 @@ static inline int irq_canonicalize(int irq) # endif #endif -#ifdef CONFIG_IRQBALANCE -extern int irqbalance_disable(char *str); -#endif - #ifdef CONFIG_HOTPLUG_CPU #include <linux/cpumask.h> extern void fixup_irqs(cpumask_t map); diff --git a/arch/x86/include/asm/irq_regs_32.h b/arch/x86/include/asm/irq_regs_32.h index af2f02d27fc..86afd747345 100644 --- a/arch/x86/include/asm/irq_regs_32.h +++ b/arch/x86/include/asm/irq_regs_32.h @@ -9,6 +9,8 @@ #include <asm/percpu.h> +#define ARCH_HAS_OWN_IRQ_REGS + DECLARE_PER_CPU(struct pt_regs *, irq_regs); static inline struct pt_regs *get_irq_regs(void) diff --git a/arch/x86/include/asm/tsc.h b/arch/x86/include/asm/tsc.h index 9cd83a8e40d..38ae163cc91 100644 --- a/arch/x86/include/asm/tsc.h +++ b/arch/x86/include/asm/tsc.h @@ -34,8 +34,6 @@ static inline cycles_t get_cycles(void) static __always_inline cycles_t vget_cycles(void) { - cycles_t cycles; - /* * We only do VDSOs on TSC capable CPUs, so this shouldnt * access boot_cpu_data (which is not VDSO-safe): @@ -44,11 +42,7 @@ static __always_inline cycles_t vget_cycles(void) if (!cpu_has_tsc) return 0; #endif - rdtsc_barrier(); - cycles = (cycles_t)__native_read_tsc(); - rdtsc_barrier(); - - return cycles; + return (cycles_t)__native_read_tsc(); } extern void tsc_init(void); diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c index 8c1f76abae9..4c51a2f8fd3 100644 --- a/arch/x86/kernel/acpi/boot.c +++ b/arch/x86/kernel/acpi/boot.c @@ -1343,7 +1343,6 @@ static void __init acpi_process_madt(void) error = acpi_parse_madt_ioapic_entries(); if (!error) { acpi_irq_model = ACPI_IRQ_MODEL_IOAPIC; - acpi_irq_balance_set(NULL); acpi_ioapic = 1; smp_found_config = 1; diff --git a/arch/x86/kernel/ds.c b/arch/x86/kernel/ds.c index 2b69994fd3a..d1a121443bd 100644 --- a/arch/x86/kernel/ds.c +++ b/arch/x86/kernel/ds.c @@ -236,17 +236,33 @@ static inline struct ds_context *ds_alloc_context(struct task_struct *task) struct ds_context *context = *p_context; if (!context) { + spin_unlock(&ds_lock); + context = kzalloc(sizeof(*context), GFP_KERNEL); - if (!context) + if (!context) { + spin_lock(&ds_lock); return NULL; + } context->ds = kzalloc(ds_cfg.sizeof_ds, GFP_KERNEL); if (!context->ds) { kfree(context); + spin_lock(&ds_lock); return NULL; } + spin_lock(&ds_lock); + /* + * Check for race - another CPU could have allocated + * it meanwhile: + */ + if (*p_context) { + kfree(context->ds); + kfree(context); + return *p_context; + } + *p_context = context; context->this = p_context; @@ -384,14 +400,15 @@ static int ds_request(struct task_struct *task, void *base, size_t size, spin_lock(&ds_lock); - if (!check_tracer(task)) - return -EPERM; - error = -ENOMEM; context = ds_alloc_context(task); if (!context) goto out_unlock; + error = -EPERM; + if (!check_tracer(task)) + goto out_unlock; + error = -EALREADY; if (context->owner[qual] == current) goto out_unlock; diff --git a/arch/x86/kernel/early-quirks.c b/arch/x86/kernel/early-quirks.c index 3ce029ffaa5..1b894b72c0f 100644 --- a/arch/x86/kernel/early-quirks.c +++ b/arch/x86/kernel/early-quirks.c @@ -188,20 +188,6 @@ static void __init ati_bugs_contd(int num, int slot, int func) } #endif -#ifdef CONFIG_DMAR -static void __init intel_g33_dmar(int num, int slot, int func) -{ - struct acpi_table_header *dmar_tbl; - acpi_status status; - - status = acpi_get_table(ACPI_SIG_DMAR, 0, &dmar_tbl); - if (ACPI_SUCCESS(status)) { - printk(KERN_INFO "BIOS BUG: DMAR advertised on Intel G31/G33 chipset -- ignoring\n"); - dmar_disabled = 1; - } -} -#endif - #define QFLAG_APPLY_ONCE 0x1 #define QFLAG_APPLIED 0x2 #define QFLAG_DONE (QFLAG_APPLY_ONCE|QFLAG_APPLIED) @@ -225,10 +211,6 @@ static struct chipset early_qrk[] __initdata = { PCI_CLASS_SERIAL_SMBUS, PCI_ANY_ID, 0, ati_bugs }, { PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_SBX00_SMBUS, PCI_CLASS_SERIAL_SMBUS, PCI_ANY_ID, 0, ati_bugs_contd }, -#ifdef CONFIG_DMAR - { PCI_VENDOR_ID_INTEL, 0x29c0, - PCI_CLASS_BRIDGE_HOST, PCI_ANY_ID, 0, intel_g33_dmar }, -#endif {} }; diff --git a/arch/x86/kernel/entry_64.S b/arch/x86/kernel/entry_64.S index 369de6973c5..dbf06a0ef3d 100644 --- a/arch/x86/kernel/entry_64.S +++ b/arch/x86/kernel/entry_64.S @@ -11,15 +11,15 @@ * * NOTE: This code handles signal-recognition, which happens every time * after an interrupt and after each system call. - * - * Normal syscalls and interrupts don't save a full stack frame, this is + * + * Normal syscalls and interrupts don't save a full stack frame, this is * only done for syscall tracing, signals or fork/exec et.al. - * - * A note on terminology: - * - top of stack: Architecture defined interrupt frame from SS to RIP - * at the top of the kernel process stack. + * + * A note on terminology: + * - top of stack: Architecture defined interrupt frame from SS to RIP + * at the top of the kernel process stack. * - partial stack frame: partially saved registers upto R11. - * - full stack frame: Like partial stack frame, but all register saved. + * - full stack frame: Like partial stack frame, but all register saved. * * Some macro usage: * - CFI macros are used to generate dwarf2 unwind information for better @@ -142,7 +142,7 @@ END(mcount) #ifndef CONFIG_PREEMPT #define retint_kernel retint_restore_args -#endif +#endif #ifdef CONFIG_PARAVIRT ENTRY(native_usergs_sysret64) @@ -161,14 +161,14 @@ ENTRY(native_usergs_sysret64) .endm /* - * C code is not supposed to know about undefined top of stack. Every time - * a C function with an pt_regs argument is called from the SYSCALL based + * C code is not supposed to know about undefined top of stack. Every time + * a C function with an pt_regs argument is called from the SYSCALL based * fast path FIXUP_TOP_OF_STACK is needed. * RESTORE_TOP_OF_STACK syncs the syscall state after any possible ptregs * manipulation. - */ - - /* %rsp:at FRAMEEND */ + */ + + /* %rsp:at FRAMEEND */ .macro FIXUP_TOP_OF_STACK tmp movq %gs:pda_oldrsp,\tmp movq \tmp,RSP(%rsp) @@ -244,8 +244,8 @@ ENTRY(native_usergs_sysret64) .endm /* * A newly forked process directly context switches into this. - */ -/* rdi: prev */ + */ +/* rdi: prev */ ENTRY(ret_from_fork) CFI_DEFAULT_STACK push kernel_eflags(%rip) @@ -256,7 +256,7 @@ ENTRY(ret_from_fork) GET_THREAD_INFO(%rcx) testl $(_TIF_SYSCALL_TRACE|_TIF_SYSCALL_AUDIT),TI_flags(%rcx) jnz rff_trace -rff_action: +rff_action: RESTORE_REST testl $3,CS-ARGOFFSET(%rsp) # from kernel_thread? je int_ret_from_sys_call @@ -267,7 +267,7 @@ rff_action: rff_trace: movq %rsp,%rdi call syscall_trace_leave - GET_THREAD_INFO(%rcx) + GET_THREAD_INFO(%rcx) jmp rff_action CFI_ENDPROC END(ret_from_fork) @@ -278,20 +278,20 @@ END(ret_from_fork) * SYSCALL does not save anything on the stack and does not change the * stack pointer. */ - + /* - * Register setup: + * Register setup: * rax system call number * rdi arg0 - * rcx return address for syscall/sysret, C arg3 + * rcx return address for syscall/sysret, C arg3 * rsi arg1 - * rdx arg2 + * rdx arg2 * r10 arg3 (--> moved to rcx for C) * r8 arg4 * r9 arg5 * r11 eflags for syscall/sysret, temporary for C - * r12-r15,rbp,rbx saved by C code, not touched. - * + * r12-r15,rbp,rbx saved by C code, not touched. + * * Interrupts are off on entry. * Only called from user space. * @@ -301,7 +301,7 @@ END(ret_from_fork) * When user can change the frames always force IRET. That is because * it deals with uncanonical addresses better. SYSRET has trouble * with them due to bugs in both AMD and Intel CPUs. - */ + */ ENTRY(system_call) CFI_STARTPROC simple @@ -317,7 +317,7 @@ ENTRY(system_call) */ ENTRY(system_call_after_swapgs) - movq %rsp,%gs:pda_oldrsp + movq %rsp,%gs:pda_oldrsp movq %gs:pda_kernelstack,%rsp /* * No need to follow this irqs off/on section - it's straight @@ -325,7 +325,7 @@ ENTRY(system_call_after_swapgs) */ ENABLE_INTERRUPTS(CLBR_NONE) SAVE_ARGS 8,1 - movq %rax,ORIG_RAX-ARGOFFSET(%rsp) + movq %rax,ORIG_RAX-ARGOFFSET(%rsp) movq %rcx,RIP-ARGOFFSET(%rsp) CFI_REL_OFFSET rip,RIP-ARGOFFSET GET_THREAD_INFO(%rcx) @@ -339,19 +339,19 @@ system_call_fastpath: movq %rax,RAX-ARGOFFSET(%rsp) /* * Syscall return path ending with SYSRET (fast path) - * Has incomplete stack frame and undefined top of stack. - */ + * Has incomplete stack frame and undefined top of stack. + */ ret_from_sys_call: movl $_TIF_ALLWORK_MASK,%edi /* edi: flagmask */ -sysret_check: +sysret_check: LOCKDEP_SYS_EXIT GET_THREAD_INFO(%rcx) DISABLE_INTERRUPTS(CLBR_NONE) TRACE_IRQS_OFF movl TI_flags(%rcx),%edx andl %edi,%edx - jnz sysret_careful + jnz sysret_careful CFI_REMEMBER_STATE /* * sysretq will re-enable interrupts: @@ -366,7 +366,7 @@ sysret_check: CFI_RESTORE_STATE /* Handle reschedules */ - /* edx: work, edi: workmask */ + /* edx: work, edi: workmask */ sysret_careful: bt $TIF_NEED_RESCHED,%edx jnc sysret_signal @@ -379,7 +379,7 @@ sysret_careful: CFI_ADJUST_CFA_OFFSET -8 jmp sysret_check - /* Handle a signal */ + /* Handle a signal */ sysret_signal: TRACE_IRQS_ON ENABLE_INTERRUPTS(CLBR_NONE) @@ -398,7 +398,7 @@ sysret_signal: DISABLE_INTERRUPTS(CLBR_NONE) TRACE_IRQS_OFF jmp int_with_check - + badsys: movq $-ENOSYS,RAX-ARGOFFSET(%rsp) jmp ret_from_sys_call @@ -437,7 +437,7 @@ sysret_audit: #endif /* CONFIG_AUDITSYSCALL */ /* Do syscall tracing */ -tracesys: +tracesys: #ifdef CONFIG_AUDITSYSCALL testl $(_TIF_WORK_SYSCALL_ENTRY & ~_TIF_SYSCALL_AUDIT),TI_flags(%rcx) jz auditsys @@ -460,8 +460,8 @@ tracesys: call *sys_call_table(,%rax,8) movq %rax,RAX-ARGOFFSET(%rsp) /* Use IRET because user could have changed frame */ - -/* + +/* * Syscall return path ending with IRET. * Has correct top of stack, but partial stack frame. */ @@ -505,18 +505,18 @@ int_very_careful: TRACE_IRQS_ON ENABLE_INTERRUPTS(CLBR_NONE) SAVE_REST - /* Check for syscall exit trace */ + /* Check for syscall exit trace */ testl $_TIF_WORK_SYSCALL_EXIT,%edx jz int_signal pushq %rdi CFI_ADJUST_CFA_OFFSET 8 - leaq 8(%rsp),%rdi # &ptregs -> arg1 + leaq 8(%rsp),%rdi # &ptregs -> arg1 call syscall_trace_leave popq %rdi CFI_ADJUST_CFA_OFFSET -8 andl $~(_TIF_WORK_SYSCALL_EXIT|_TIF_SYSCALL_EMU),%edi jmp int_restore_rest - + int_signal: testl $_TIF_DO_NOTIFY_MASK,%edx jz 1f @@ -531,11 +531,11 @@ int_restore_rest: jmp int_with_check CFI_ENDPROC END(system_call) - -/* + +/* * Certain special system calls that need to save a complete full stack frame. - */ - + */ + .macro PTREGSCALL label,func,arg .globl \label \label: @@ -572,7 +572,7 @@ ENTRY(ptregscall_common) ret CFI_ENDPROC END(ptregscall_common) - + ENTRY(stub_execve) CFI_STARTPROC popq %r11 @@ -588,11 +588,11 @@ ENTRY(stub_execve) jmp int_ret_from_sys_call CFI_ENDPROC END(stub_execve) - + /* * sigreturn is special because it needs to restore all registers on return. * This cannot be done with SYSRET, so use the IRET return path instead. - */ + */ ENTRY(stub_rt_sigreturn) CFI_STARTPROC addq $8, %rsp @@ -731,12 +731,12 @@ exit_intr: GET_THREAD_INFO(%rcx) testl $3,CS-ARGOFFSET(%rsp) je retint_kernel - + /* Interrupt came from user space */ /* * Has a correct top of stack, but a partial stack frame * %rcx: thread info. Interrupts off. - */ + */ retint_with_reschedule: movl $_TIF_WORK_MASK,%edi retint_check: @@ -809,20 +809,20 @@ retint_careful: pushq %rdi CFI_ADJUST_CFA_OFFSET 8 call schedule - popq %rdi + popq %rdi CFI_ADJUST_CFA_OFFSET -8 GET_THREAD_INFO(%rcx) DISABLE_INTERRUPTS(CLBR_NONE) TRACE_IRQS_OFF jmp retint_check - + retint_signal: testl $_TIF_DO_NOTIFY_MASK,%edx jz retint_swapgs TRACE_IRQS_ON ENABLE_INTERRUPTS(CLBR_NONE) SAVE_REST - movq $-1,ORIG_RAX(%rsp) + movq $-1,ORIG_RAX(%rsp) xorl %esi,%esi # oldset movq %rsp,%rdi # &pt_regs call do_notify_resume @@ -844,14 +844,14 @@ ENTRY(retint_kernel) jnc retint_restore_args call preempt_schedule_irq jmp exit_intr -#endif +#endif CFI_ENDPROC END(common_interrupt) - + /* * APIC interrupts. - */ + */ .macro apicinterrupt num,func INTR_FRAME pushq $~(\num) @@ -869,14 +869,14 @@ ENTRY(threshold_interrupt) apicinterrupt THRESHOLD_APIC_VECTOR,mce_threshold_interrupt END(threshold_interrupt) -#ifdef CONFIG_SMP +#ifdef CONFIG_SMP ENTRY(reschedule_interrupt) apicinterrupt RESCHEDULE_VECTOR,smp_reschedule_interrupt END(reschedule_interrupt) .macro INVALIDATE_ENTRY num ENTRY(invalidate_interrupt\num) - apicinterrupt INVALIDATE_TLB_VECTOR_START+\num,smp_invalidate_interrupt + apicinterrupt INVALIDATE_TLB_VECTOR_START+\num,smp_invalidate_interrupt END(invalidate_interrupt\num) .endm @@ -915,22 +915,22 @@ END(error_interrupt) ENTRY(spurious_interrupt) apicinterrupt SPURIOUS_APIC_VECTOR,smp_spurious_interrupt END(spurious_interrupt) - + /* * Exception entry points. - */ + */ .macro zeroentry sym INTR_FRAME PARAVIRT_ADJUST_EXCEPTION_FRAME - pushq $0 /* push error code/oldrax */ + pushq $0 /* push error code/oldrax */ CFI_ADJUST_CFA_OFFSET 8 - pushq %rax /* push real oldrax to the rdi slot */ + pushq %rax /* push real oldrax to the rdi slot */ CFI_ADJUST_CFA_OFFSET 8 CFI_REL_OFFSET rax,0 leaq \sym(%rip),%rax jmp error_entry CFI_ENDPROC - .endm + .endm .macro errorentry sym XCPT_FRAME @@ -1044,13 +1044,13 @@ paranoid_schedule\trace: /* * Exception entry point. This expects an error code/orig_rax on the stack - * and the exception handler in %rax. - */ + * and the exception handler in %rax. + */ KPROBE_ENTRY(error_entry) _frame RDI CFI_REL_OFFSET rax,0 /* rdi slot contains rax, oldrax contains error code */ - cld + cld subq $14*8,%rsp CFI_ADJUST_CFA_OFFSET (14*8) movq %rsi,13*8(%rsp) @@ -1061,7 +1061,7 @@ KPROBE_ENTRY(error_entry) CFI_REL_OFFSET rdx,RDX movq %rcx,11*8(%rsp) CFI_REL_OFFSET rcx,RCX - movq %rsi,10*8(%rsp) /* store rax */ + movq %rsi,10*8(%rsp) /* store rax */ CFI_REL_OFFSET rax,RAX movq %r8, 9*8(%rsp) CFI_REL_OFFSET r8,R8 @@ -1071,29 +1071,29 @@ KPROBE_ENTRY(error_entry) CFI_REL_OFFSET r10,R10 movq %r11,6*8(%rsp) CFI_REL_OFFSET r11,R11 - movq %rbx,5*8(%rsp) + movq %rbx,5*8(%rsp) CFI_REL_OFFSET rbx,RBX - movq %rbp,4*8(%rsp) + movq %rbp,4*8(%rsp) CFI_REL_OFFSET rbp,RBP - movq %r12,3*8(%rsp) + movq %r12,3*8(%rsp) CFI_REL_OFFSET r12,R12 - movq %r13,2*8(%rsp) + movq %r13,2*8(%rsp) CFI_REL_OFFSET r13,R13 - movq %r14,1*8(%rsp) + movq %r14,1*8(%rsp) CFI_REL_OFFSET r14,R14 - movq %r15,(%rsp) + movq %r15,(%rsp) CFI_REL_OFFSET r15,R15 - xorl %ebx,%ebx + xorl %ebx,%ebx testl $3,CS(%rsp) je error_kernelspace -error_swapgs: +error_swapgs: SWAPGS error_sti: TRACE_IRQS_OFF - movq %rdi,RDI(%rsp) + movq %rdi,RDI(%rsp) CFI_REL_OFFSET rdi,RDI movq %rsp,%rdi - movq ORIG_RAX(%rsp),%rsi /* get error code */ + movq ORIG_RAX(%rsp),%rsi /* get error code */ movq $-1,ORIG_RAX(%rsp) call *%rax /* ebx: no swapgs flag (1: don't need swapgs, 0: need it) */ @@ -1102,7 +1102,7 @@ error_exit: RESTORE_REST DISABLE_INTERRUPTS(CLBR_NONE) TRACE_IRQS_OFF - GET_THREAD_INFO(%rcx) + GET_THREAD_INFO(%rcx) testl %eax,%eax jne retint_kernel LOCKDEP_SYS_EXIT_IRQ @@ -1118,7 +1118,7 @@ error_kernelspace: /* There are two places in the kernel that can potentially fault with usergs. Handle them here. The exception handlers after iret run with kernel gs again, so don't set the user space flag. - B stepping K8s sometimes report an truncated RIP for IRET + B stepping K8s sometimes report an truncated RIP for IRET exceptions returning to compat mode. Check for these here too. */ leaq irq_return(%rip),%rcx cmpq %rcx,RIP(%rsp) @@ -1130,17 +1130,17 @@ error_kernelspace: je error_swapgs jmp error_sti KPROBE_END(error_entry) - + /* Reload gs selector with exception handling */ - /* edi: new selector */ + /* edi: new selector */ ENTRY(native_load_gs_index) CFI_STARTPROC pushf CFI_ADJUST_CFA_OFFSET 8 DISABLE_INTERRUPTS(CLBR_ANY | ~(CLBR_RDI)) SWAPGS -gs_change: - movl %edi,%gs +gs_change: + movl %edi,%gs 2: mfence /* workaround */ SWAPGS popf @@ -1148,20 +1148,20 @@ gs_change: ret CFI_ENDPROC ENDPROC(native_load_gs_index) - + .section __ex_table,"a" .align 8 .quad gs_change,bad_gs .previous .section .fixup,"ax" /* running with kernelgs */ -bad_gs: +bad_gs: SWAPGS /* switch back to user gs */ xorl %eax,%eax movl %eax,%gs jmp 2b - .previous - + .previous + /* * Create a kernel thread. * @@ -1184,7 +1184,7 @@ ENTRY(kernel_thread) xorl %r8d,%r8d xorl %r9d,%r9d - + # clone now call do_fork movq %rax,RAX(%rsp) @@ -1195,14 +1195,14 @@ ENTRY(kernel_thread) * so internally to the x86_64 port you can rely on kernel_thread() * not to reschedule the child before returning, this avoids the need * of hacks for example to fork off the per-CPU idle tasks. - * [Hopefully no generic code relies on the reschedule -AK] + * [Hopefully no generic code relies on the reschedule -AK] */ RESTORE_ALL UNFAKE_STACK_FRAME ret CFI_ENDPROC ENDPROC(kernel_thread) - + child_rip: pushq $0 # fake return address CFI_STARTPROC @@ -1237,10 +1237,10 @@ ENDPROC(child_rip) ENTRY(kernel_execve) CFI_STARTPROC FAKE_STACK_FRAME $0 - SAVE_ALL + SAVE_ALL movq %rsp,%rcx call sys_execve - movq %rax, RAX(%rsp) + movq %rax, RAX(%rsp) RESTORE_REST testq %rax,%rax je int_ret_from_sys_call @@ -1259,7 +1259,7 @@ ENTRY(coprocessor_error) END(coprocessor_error) ENTRY(simd_coprocessor_error) - zeroentry do_simd_coprocessor_error + zeroentry do_simd_coprocessor_error END(simd_coprocessor_error) ENTRY(device_not_available) @@ -1271,12 +1271,12 @@ KPROBE_ENTRY(debug) INTR_FRAME PARAVIRT_ADJUST_EXCEPTION_FRAME pushq $0 - CFI_ADJUST_CFA_OFFSET 8 + CFI_ADJUST_CFA_OFFSET 8 paranoidentry do_debug, DEBUG_STACK paranoidexit KPROBE_END(debug) - /* runs on exception stack */ + /* runs on exception stack */ KPROBE_ENTRY(nmi) INTR_FRAME PARAVIRT_ADJUST_EXCEPTION_FRAME @@ -1310,7 +1310,7 @@ ENTRY(bounds) END(bounds) ENTRY(invalid_op) - zeroentry do_invalid_op + zeroentry do_invalid_op END(invalid_op) ENTRY(coprocessor_segment_overrun) @@ -1365,7 +1365,7 @@ ENTRY(machine_check) INTR_FRAME PARAVIRT_ADJUST_EXCEPTION_FRAME pushq $0 - CFI_ADJUST_CFA_OFFSET 8 + CFI_ADJUST_CFA_OFFSET 8 paranoidentry do_machine_check jmp paranoid_exit1 CFI_ENDPROC diff --git a/arch/x86/kernel/es7000_32.c b/arch/x86/kernel/es7000_32.c index f454c78fcef..0aa2c443d60 100644 --- a/arch/x86/kernel/es7000_32.c +++ b/arch/x86/kernel/es7000_32.c @@ -250,31 +250,24 @@ int __init find_unisys_acpi_oem_table(unsigned long *oem_addr) { struct acpi_table_header *header = NULL; int i = 0; - acpi_size tbl_size; - while (ACPI_SUCCESS(acpi_get_table_with_size("OEM1", i++, &header, &tbl_size))) { + while (ACPI_SUCCESS(acpi_get_table("OEM1", i++, &header))) { if (!memcmp((char *) &header->oem_id, "UNISYS", 6)) { struct oem_table *t = (struct oem_table *)header; oem_addrX = t->OEMTableAddr; oem_size = t->OEMTableSize; - early_acpi_os_unmap_memory(header, tbl_size); *oem_addr = (unsigned long)__acpi_map_table(oem_addrX, oem_size); return 0; } - early_acpi_os_unmap_memory(header, tbl_size); } return -1; } void __init unmap_unisys_acpi_oem_table(unsigned long oem_addr) { - if (!oem_addr) - return; - - __acpi_unmap_table((char *)oem_addr, oem_size); } #endif diff --git a/arch/x86/kernel/kvmclock.c b/arch/x86/kernel/kvmclock.c index 774ac499156..1c9cc431ea4 100644 --- a/arch/x86/kernel/kvmclock.c +++ b/arch/x86/kernel/kvmclock.c @@ -128,7 +128,7 @@ static int kvm_register_clock(char *txt) } #ifdef CONFIG_X86_LOCAL_APIC -static void kvm_setup_secondary_clock(void) +static void __devinit kvm_setup_secondary_clock(void) { /* * Now that the first cpu already had this clocksource initialized, diff --git a/arch/x86/kernel/tsc_sync.c b/arch/x86/kernel/tsc_sync.c index 9ffb01c31c4..1c0dfbca87c 100644 --- a/arch/x86/kernel/tsc_sync.c +++ b/arch/x86/kernel/tsc_sync.c @@ -46,7 +46,9 @@ static __cpuinit void check_tsc_warp(void) cycles_t start, now, prev, end; int i; + rdtsc_barrier(); start = get_cycles(); + rdtsc_barrier(); /* * The measurement runs for 20 msecs: */ @@ -61,7 +63,9 @@ static __cpuinit void check_tsc_warp(void) */ __raw_spin_lock(&sync_lock); prev = last_tsc; + rdtsc_barrier(); now = get_cycles(); + rdtsc_barrier(); last_tsc = now; __raw_spin_unlock(&sync_lock); diff --git a/arch/x86/kernel/vsyscall_64.c b/arch/x86/kernel/vsyscall_64.c index 0b8b6690a86..ebf2f12900f 100644 --- a/arch/x86/kernel/vsyscall_64.c +++ b/arch/x86/kernel/vsyscall_64.c @@ -128,7 +128,16 @@ static __always_inline void do_vgettimeofday(struct timeval * tv) gettimeofday(tv,NULL); return; } + + /* + * Surround the RDTSC by barriers, to make sure it's not + * speculated to outside the seqlock critical section and + * does not cause time warps: + */ + rdtsc_barrier(); now = vread(); + rdtsc_barrier(); + base = __vsyscall_gtod_data.clock.cycle_last; mask = __vsyscall_gtod_data.clock.mask; mult = __vsyscall_gtod_data.clock.mult; diff --git a/arch/x86/kvm/Kconfig b/arch/x86/kvm/Kconfig index ce3251ce550..b81125f0bde 100644 --- a/arch/x86/kvm/Kconfig +++ b/arch/x86/kvm/Kconfig @@ -20,6 +20,8 @@ if VIRTUALIZATION config KVM tristate "Kernel-based Virtual Machine (KVM) support" depends on HAVE_KVM + # for device assignment: + depends on PCI select PREEMPT_NOTIFIERS select MMU_NOTIFIER select ANON_INODES diff --git a/arch/x86/kvm/i8254.c b/arch/x86/kvm/i8254.c index 8772dc94682..59ebd37ad79 100644 --- a/arch/x86/kvm/i8254.c +++ b/arch/x86/kvm/i8254.c @@ -548,8 +548,10 @@ struct kvm_pit *kvm_create_pit(struct kvm *kvm) mutex_lock(&kvm->lock); pit->irq_source_id = kvm_request_irq_source_id(kvm); mutex_unlock(&kvm->lock); - if (pit->irq_source_id < 0) + if (pit->irq_source_id < 0) { + kfree(pit); return NULL; + } mutex_init(&pit->pit_state.lock); mutex_lock(&pit->pit_state.lock); diff --git a/arch/x86/kvm/mmu.c b/arch/x86/kvm/mmu.c index 2a5e64881d9..f1983d9477c 100644 --- a/arch/x86/kvm/mmu.c +++ b/arch/x86/kvm/mmu.c @@ -314,7 +314,7 @@ static int mmu_topup_memory_caches(struct kvm_vcpu *vcpu) if (r) goto out; r = mmu_topup_memory_cache(&vcpu->arch.mmu_rmap_desc_cache, - rmap_desc_cache, 1); + rmap_desc_cache, 4); if (r) goto out; r = mmu_topup_memory_cache_page(&vcpu->arch.mmu_page_cache, 8); diff --git a/arch/x86/kvm/vmx.c b/arch/x86/kvm/vmx.c index 2643b430d83..d06b4dc0e2e 100644 --- a/arch/x86/kvm/vmx.c +++ b/arch/x86/kvm/vmx.c @@ -3564,7 +3564,8 @@ static int __init vmx_init(void) bypass_guest_pf = 0; kvm_mmu_set_base_ptes(VMX_EPT_READABLE_MASK | VMX_EPT_WRITABLE_MASK | - VMX_EPT_DEFAULT_MT << VMX_EPT_MT_EPTE_SHIFT); + VMX_EPT_DEFAULT_MT << VMX_EPT_MT_EPTE_SHIFT | + VMX_EPT_IGMT_BIT); kvm_mmu_set_mask_ptes(0ull, 0ull, 0ull, 0ull, VMX_EPT_EXECUTABLE_MASK); kvm_enable_tdp(); diff --git a/arch/x86/kvm/vmx.h b/arch/x86/kvm/vmx.h index 3e010d21fdd..ec5edc339da 100644 --- a/arch/x86/kvm/vmx.h +++ b/arch/x86/kvm/vmx.h @@ -352,6 +352,7 @@ enum vmcs_field { #define VMX_EPT_READABLE_MASK 0x1ull #define VMX_EPT_WRITABLE_MASK 0x2ull #define VMX_EPT_EXECUTABLE_MASK 0x4ull +#define VMX_EPT_IGMT_BIT (1ull << 6) #define VMX_EPT_IDENTITY_PAGETABLE_ADDR 0xfffbc000ul diff --git a/arch/x86/mach-voyager/voyager_smp.c b/arch/x86/mach-voyager/voyager_smp.c index 0e331652681..52145007bd7 100644 --- a/arch/x86/mach-voyager/voyager_smp.c +++ b/arch/x86/mach-voyager/voyager_smp.c @@ -7,6 +7,7 @@ * This file provides all the same external entries as smp.c but uses * the voyager hal to provide the functionality */ +#include <linux/cpu.h> #include <linux/module.h> #include <linux/mm.h> #include <linux/kernel_stat.h> @@ -1790,6 +1791,17 @@ void __init smp_setup_processor_id(void) x86_write_percpu(cpu_number, hard_smp_processor_id()); } +static void voyager_send_call_func(cpumask_t callmask) +{ + __u32 mask = cpus_addr(callmask)[0] & ~(1 << smp_processor_id()); + send_CPI(mask, VIC_CALL_FUNCTION_CPI); +} + +static void voyager_send_call_func_single(int cpu) +{ + send_CPI(1 << cpu, VIC_CALL_FUNCTION_SINGLE_CPI); +} + struct smp_ops smp_ops = { .smp_prepare_boot_cpu = voyager_smp_prepare_boot_cpu, .smp_prepare_cpus = voyager_smp_prepare_cpus, @@ -1799,6 +1811,6 @@ struct smp_ops smp_ops = { .smp_send_stop = voyager_smp_send_stop, .smp_send_reschedule = voyager_smp_send_reschedule, - .send_call_func_ipi = native_send_call_func_ipi, - .send_call_func_single_ipi = native_send_call_func_single_ipi, + .send_call_func_ipi = voyager_send_call_func, + .send_call_func_single_ipi = voyager_send_call_func_single, }; diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c index c483f424207..3ffed259883 100644 --- a/arch/x86/mm/init_32.c +++ b/arch/x86/mm/init_32.c @@ -102,6 +102,8 @@ static pmd_t * __init one_md_table_init(pgd_t *pgd) set_pgd(pgd, __pgd(__pa(pmd_table) | _PAGE_PRESENT)); pud = pud_offset(pgd, 0); BUG_ON(pmd_table != pmd_offset(pud, 0)); + + return pmd_table; } #endif pud = pud_offset(pgd, 0); diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index f4f63291750..b0243fd55ac 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -312,9 +312,13 @@ config ACPI_DEBUG bool "Debug Statements" default n help - The ACPI driver can optionally report errors with a great deal - of verbosity. Saying Y enables these statements. This will increase - your kernel size by around 50K. + The ACPI subsystem can produce debug output. Saying Y enables this + output and increases the kernel size by around 50K. + + Use the acpi.debug_layer and acpi.debug_level kernel command-line + parameters documented in Documentation/acpi/debug.txt and + Documentation/kernel-parameters.txt to control the type and + amount of debug output. config ACPI_DEBUG_FUNC_TRACE bool "Additionally enable ACPI function tracing" @@ -324,14 +328,6 @@ config ACPI_DEBUG_FUNC_TRACE ACPI Debug Statements slow down ACPI processing. Function trace is about half of the penalty and is rarely useful. -config ACPI_EC - bool - default y - help - This driver is required on some systems for the proper operation of - the battery and thermal drivers. If you are compiling for a - mobile system, say Y. - config ACPI_PCI_SLOT tristate "PCI slot detection driver" default n @@ -341,10 +337,6 @@ config ACPI_PCI_SLOT help you correlate PCI bus addresses with the physical geography of your slots. If you are unsure, say N. -config ACPI_POWER - bool - default y - config ACPI_SYSTEM bool default y diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index d91c027ece8..3c0c93300f1 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -39,19 +39,23 @@ obj-y += sleep/ obj-y += bus.o glue.o obj-y += scan.o # Keep EC driver first. Initialization of others depend on it. -obj-$(CONFIG_ACPI_EC) += ec.o +obj-y += ec.o obj-$(CONFIG_ACPI_AC) += ac.o obj-$(CONFIG_ACPI_BATTERY) += battery.o obj-$(CONFIG_ACPI_BUTTON) += button.o obj-$(CONFIG_ACPI_FAN) += fan.o obj-$(CONFIG_ACPI_DOCK) += dock.o obj-$(CONFIG_ACPI_VIDEO) += video.o +ifdef CONFIG_ACPI_VIDEO +obj-y += video_detect.o +endif + obj-y += pci_root.o pci_link.o pci_irq.o pci_bind.o obj-$(CONFIG_ACPI_PCI_SLOT) += pci_slot.o obj-$(CONFIG_ACPI_PROCESSOR) += processor.o obj-$(CONFIG_ACPI_CONTAINER) += container.o obj-$(CONFIG_ACPI_THERMAL) += thermal.o -obj-$(CONFIG_ACPI_POWER) += power.o +obj-y += power.o obj-$(CONFIG_ACPI_SYSTEM) += system.o event.o obj-$(CONFIG_ACPI_DEBUG) += debug.o obj-$(CONFIG_ACPI_NUMA) += numa.o diff --git a/drivers/acpi/ac.c b/drivers/acpi/ac.c index d72a1b6c8a9..9b917dac773 100644 --- a/drivers/acpi/ac.c +++ b/drivers/acpi/ac.c @@ -37,7 +37,6 @@ #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> -#define ACPI_AC_COMPONENT 0x00020000 #define ACPI_AC_CLASS "ac_adapter" #define ACPI_AC_DEVICE_NAME "AC Adapter" #define ACPI_AC_FILE_STATE "state" @@ -242,7 +241,7 @@ static void acpi_ac_notify(acpi_handle handle, u32 event, void *data) acpi_ac_get_state(ac); acpi_bus_generate_proc_event(device, event, (u32) ac->state); acpi_bus_generate_netlink_event(device->pnp.device_class, - device->dev.bus_id, event, + dev_name(&device->dev), event, (u32) ac->state); #ifdef CONFIG_ACPI_SYSFS_POWER kobject_uevent(&ac->charger.dev->kobj, KOBJ_CHANGE); diff --git a/drivers/acpi/acpi_memhotplug.c b/drivers/acpi/acpi_memhotplug.c index 71d21c51c45..63a17b55b39 100644 --- a/drivers/acpi/acpi_memhotplug.c +++ b/drivers/acpi/acpi_memhotplug.c @@ -32,7 +32,6 @@ #include <linux/memory_hotplug.h> #include <acpi/acpi_drivers.h> -#define ACPI_MEMORY_DEVICE_COMPONENT 0x08000000UL #define ACPI_MEMORY_DEVICE_CLASS "memory" #define ACPI_MEMORY_DEVICE_HID "PNP0C80" #define ACPI_MEMORY_DEVICE_NAME "Hotplug Mem Device" diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index b2133e89ad9..1423b0c0cd2 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -46,7 +46,6 @@ #define ACPI_BATTERY_VALUE_UNKNOWN 0xFFFFFFFF -#define ACPI_BATTERY_COMPONENT 0x00040000 #define ACPI_BATTERY_CLASS "battery" #define ACPI_BATTERY_DEVICE_NAME "Battery" #define ACPI_BATTERY_NOTIFY_STATUS 0x80 @@ -782,7 +781,7 @@ static void acpi_battery_notify(acpi_handle handle, u32 event, void *data) acpi_bus_generate_proc_event(device, event, acpi_battery_present(battery)); acpi_bus_generate_netlink_event(device->pnp.device_class, - device->dev.bus_id, event, + dev_name(&device->dev), event, acpi_battery_present(battery)); #ifdef CONFIG_ACPI_SYSFS_POWER /* acpi_batter_update could remove power_supply object */ diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index c797c6473f3..7edf6d913c1 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -688,6 +688,14 @@ void __init acpi_early_init(void) if (acpi_disabled) return; + /* + * ACPI CA initializes acpi_dbg_level to non-zero, which means + * we get debug output merely by turning on CONFIG_ACPI_DEBUG. + * Turn it off so we don't get output unless the user specifies + * acpi.debug_level. + */ + acpi_dbg_level = 0; + printk(KERN_INFO PREFIX "Core revision %08x\n", ACPI_CA_VERSION); /* enable workarounds, unless strict ACPI spec. compliance */ @@ -774,7 +782,7 @@ static int __init acpi_bus_init(void) "Unable to initialize ACPI OS objects\n"); goto error1; } -#ifdef CONFIG_ACPI_EC + /* * ACPI 2.0 requires the EC driver to be loaded and work before * the EC device is found in the namespace (i.e. before acpi_initialize_objects() @@ -785,7 +793,6 @@ static int __init acpi_bus_init(void) */ status = acpi_ec_ecdt_probe(); /* Ignore result. Not having an ECDT is not fatal. */ -#endif status = acpi_initialize_objects(ACPI_FULL_INITIALIZATION); if (ACPI_FAILURE(status)) { diff --git a/drivers/acpi/button.c b/drivers/acpi/button.c index cb046c3fc3f..171fd914f43 100644 --- a/drivers/acpi/button.c +++ b/drivers/acpi/button.c @@ -33,7 +33,6 @@ #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> -#define ACPI_BUTTON_COMPONENT 0x00080000 #define ACPI_BUTTON_CLASS "button" #define ACPI_BUTTON_FILE_INFO "info" #define ACPI_BUTTON_FILE_STATE "state" @@ -479,7 +478,7 @@ static int acpi_button_add(struct acpi_device *device) device->wakeup.gpe_number, ACPI_GPE_TYPE_WAKE_RUN); acpi_enable_gpe(device->wakeup.gpe_device, - device->wakeup.gpe_number, ACPI_NOT_ISR); + device->wakeup.gpe_number); device->wakeup.state.enabled = 1; } diff --git a/drivers/acpi/cm_sbs.c b/drivers/acpi/cm_sbs.c index 4441e84b28a..307963bd104 100644 --- a/drivers/acpi/cm_sbs.c +++ b/drivers/acpi/cm_sbs.c @@ -34,7 +34,6 @@ ACPI_MODULE_NAME("cm_sbs"); #define ACPI_AC_CLASS "ac_adapter" #define ACPI_BATTERY_CLASS "battery" -#define ACPI_SBS_COMPONENT 0x00080000 #define _COMPONENT ACPI_SBS_COMPONENT static struct proc_dir_entry *acpi_ac_dir; static struct proc_dir_entry *acpi_battery_dir; @@ -105,9 +104,3 @@ void acpi_unlock_battery_dir(struct proc_dir_entry *acpi_battery_dir_param) return; } EXPORT_SYMBOL(acpi_unlock_battery_dir); - -static int __init acpi_cm_sbs_init(void) -{ - return 0; -} -subsys_initcall(acpi_cm_sbs_init); diff --git a/drivers/acpi/container.c b/drivers/acpi/container.c index 134818b265a..17020c12623 100644 --- a/drivers/acpi/container.c +++ b/drivers/acpi/container.c @@ -41,7 +41,6 @@ #define INSTALL_NOTIFY_HANDLER 1 #define UNINSTALL_NOTIFY_HANDLER 2 -#define ACPI_CONTAINER_COMPONENT 0x01000000 #define _COMPONENT ACPI_CONTAINER_COMPONENT ACPI_MODULE_NAME("container"); diff --git a/drivers/acpi/debug.c b/drivers/acpi/debug.c index abf36b4b1d1..c4839689200 100644 --- a/drivers/acpi/debug.c +++ b/drivers/acpi/debug.c @@ -44,6 +44,21 @@ static const struct acpi_dlayer acpi_debug_layers[] = { ACPI_DEBUG_INIT(ACPI_CA_DISASSEMBLER), ACPI_DEBUG_INIT(ACPI_COMPILER), ACPI_DEBUG_INIT(ACPI_TOOLS), + + ACPI_DEBUG_INIT(ACPI_BUS_COMPONENT), + ACPI_DEBUG_INIT(ACPI_AC_COMPONENT), + ACPI_DEBUG_INIT(ACPI_BATTERY_COMPONENT), + ACPI_DEBUG_INIT(ACPI_BUTTON_COMPONENT), + ACPI_DEBUG_INIT(ACPI_SBS_COMPONENT), + ACPI_DEBUG_INIT(ACPI_FAN_COMPONENT), + ACPI_DEBUG_INIT(ACPI_PCI_COMPONENT), + ACPI_DEBUG_INIT(ACPI_POWER_COMPONENT), + ACPI_DEBUG_INIT(ACPI_CONTAINER_COMPONENT), + ACPI_DEBUG_INIT(ACPI_SYSTEM_COMPONENT), + ACPI_DEBUG_INIT(ACPI_THERMAL_COMPONENT), + ACPI_DEBUG_INIT(ACPI_MEMORY_DEVICE_COMPONENT), + ACPI_DEBUG_INIT(ACPI_VIDEO_COMPONENT), + ACPI_DEBUG_INIT(ACPI_PROCESSOR_COMPONENT), }; static const struct acpi_dlevel acpi_debug_levels[] = { diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index ef42316f89f..cf41f9fc24a 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -70,7 +70,7 @@ enum ec_command { #define ACPI_EC_UDELAY_GLK 1000 /* Wait 1ms max. to get global lock */ #define ACPI_EC_UDELAY 100 /* Wait 100us before polling EC again */ -#define ACPI_EC_STORM_THRESHOLD 20 /* number of false interrupts +#define ACPI_EC_STORM_THRESHOLD 8 /* number of false interrupts per one transaction */ enum { @@ -100,8 +100,11 @@ struct transaction { u8 *rdata; unsigned short irq_count; u8 command; + u8 wi; + u8 ri; u8 wlen; u8 rlen; + bool done; }; static struct acpi_ec { @@ -178,34 +181,45 @@ static int ec_transaction_done(struct acpi_ec *ec) unsigned long flags; int ret = 0; spin_lock_irqsave(&ec->curr_lock, flags); - if (!ec->curr || (!ec->curr->wlen && !ec->curr->rlen)) + if (!ec->curr || ec->curr->done) ret = 1; spin_unlock_irqrestore(&ec->curr_lock, flags); return ret; } +static void start_transaction(struct acpi_ec *ec) +{ + ec->curr->irq_count = ec->curr->wi = ec->curr->ri = 0; + ec->curr->done = false; + acpi_ec_write_cmd(ec, ec->curr->command); +} + static void gpe_transaction(struct acpi_ec *ec, u8 status) { unsigned long flags; spin_lock_irqsave(&ec->curr_lock, flags); if (!ec->curr) goto unlock; - if (ec->curr->wlen > 0) { - if ((status & ACPI_EC_FLAG_IBF) == 0) { - acpi_ec_write_data(ec, *(ec->curr->wdata++)); - --ec->curr->wlen; - } else - /* false interrupt, state didn't change */ - ++ec->curr->irq_count; - - } else if (ec->curr->rlen > 0) { + if (ec->curr->wlen > ec->curr->wi) { + if ((status & ACPI_EC_FLAG_IBF) == 0) + acpi_ec_write_data(ec, + ec->curr->wdata[ec->curr->wi++]); + else + goto err; + } else if (ec->curr->rlen > ec->curr->ri) { if ((status & ACPI_EC_FLAG_OBF) == 1) { - *(ec->curr->rdata++) = acpi_ec_read_data(ec); - --ec->curr->rlen; + ec->curr->rdata[ec->curr->ri++] = acpi_ec_read_data(ec); + if (ec->curr->rlen == ec->curr->ri) + ec->curr->done = true; } else - /* false interrupt, state didn't change */ - ++ec->curr->irq_count; - } + goto err; + } else if (ec->curr->wlen == ec->curr->wi && + (status & ACPI_EC_FLAG_IBF) == 0) + ec->curr->done = true; + goto unlock; +err: + /* false interrupt, state didn't change */ + ++ec->curr->irq_count; unlock: spin_unlock_irqrestore(&ec->curr_lock, flags); } @@ -215,6 +229,15 @@ static int acpi_ec_wait(struct acpi_ec *ec) if (wait_event_timeout(ec->wait, ec_transaction_done(ec), msecs_to_jiffies(ACPI_EC_DELAY))) return 0; + /* try restart command if we get any false interrupts */ + if (ec->curr->irq_count && + (acpi_ec_read_status(ec) & ACPI_EC_FLAG_IBF) == 0) { + pr_debug(PREFIX "controller reset, restart transaction\n"); + start_transaction(ec); + if (wait_event_timeout(ec->wait, ec_transaction_done(ec), + msecs_to_jiffies(ACPI_EC_DELAY))) + return 0; + } /* missing GPEs, switch back to poll mode */ if (printk_ratelimit()) pr_info(PREFIX "missing confirmations, " @@ -239,10 +262,10 @@ static int ec_check_sci(struct acpi_ec *ec, u8 state) static int ec_poll(struct acpi_ec *ec) { unsigned long delay = jiffies + msecs_to_jiffies(ACPI_EC_DELAY); - msleep(1); + udelay(ACPI_EC_UDELAY); while (time_before(jiffies, delay)) { gpe_transaction(ec, acpi_ec_read_status(ec)); - msleep(1); + udelay(ACPI_EC_UDELAY); if (ec_transaction_done(ec)) return 0; } @@ -259,14 +282,13 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, /* disable GPE during transaction if storm is detected */ if (test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) { clear_bit(EC_FLAGS_GPE_MODE, &ec->flags); - acpi_disable_gpe(NULL, ec->gpe, ACPI_NOT_ISR); + acpi_disable_gpe(NULL, ec->gpe); } /* start transaction */ spin_lock_irqsave(&ec->curr_lock, tmp); /* following two actions should be kept atomic */ - t->irq_count = 0; ec->curr = t; - acpi_ec_write_cmd(ec, ec->curr->command); + start_transaction(ec); if (ec->curr->command == ACPI_EC_COMMAND_QUERY) clear_bit(EC_FLAGS_QUERY_PENDING, &ec->flags); spin_unlock_irqrestore(&ec->curr_lock, tmp); @@ -283,10 +305,11 @@ static int acpi_ec_transaction_unlocked(struct acpi_ec *ec, /* check if we received SCI during transaction */ ec_check_sci(ec, acpi_ec_read_status(ec)); /* it is safe to enable GPE outside of transaction */ - acpi_enable_gpe(NULL, ec->gpe, ACPI_NOT_ISR); + acpi_enable_gpe(NULL, ec->gpe); } else if (test_bit(EC_FLAGS_GPE_MODE, &ec->flags) && t->irq_count > ACPI_EC_STORM_THRESHOLD) { - pr_debug(PREFIX "GPE storm detected\n"); + pr_info(PREFIX "GPE storm detected, " + "transactions will use polling mode\n"); set_bit(EC_FLAGS_GPE_STORM, &ec->flags); } return ret; @@ -558,17 +581,26 @@ static u32 acpi_ec_gpe_handler(void *data) pr_debug(PREFIX "~~~> interrupt\n"); status = acpi_ec_read_status(ec); - gpe_transaction(ec, status); - if (ec_transaction_done(ec) && (status & ACPI_EC_FLAG_IBF) == 0) - wake_up(&ec->wait); + if (test_bit(EC_FLAGS_GPE_MODE, &ec->flags)) { + gpe_transaction(ec, status); + if (ec_transaction_done(ec) && + (status & ACPI_EC_FLAG_IBF) == 0) + wake_up(&ec->wait); + } ec_check_sci(ec, status); if (!test_bit(EC_FLAGS_GPE_MODE, &ec->flags) && !test_bit(EC_FLAGS_NO_GPE, &ec->flags)) { /* this is non-query, must be confirmation */ - if (printk_ratelimit()) - pr_info(PREFIX "non-query interrupt received," + if (!test_bit(EC_FLAGS_GPE_STORM, &ec->flags)) { + if (printk_ratelimit()) + pr_info(PREFIX "non-query interrupt received," + " switching to interrupt mode\n"); + } else { + /* hush, STORM switches the mode every transaction */ + pr_debug(PREFIX "non-query interrupt received," " switching to interrupt mode\n"); + } set_bit(EC_FLAGS_GPE_MODE, &ec->flags); } return ACPI_INTERRUPT_HANDLED; @@ -736,7 +768,7 @@ static acpi_status ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval) { acpi_status status; - unsigned long long tmp; + unsigned long long tmp = 0; struct acpi_ec *ec = context; status = acpi_walk_resources(handle, METHOD_NAME__CRS, @@ -751,6 +783,7 @@ ec_parse_device(acpi_handle handle, u32 Level, void *context, void **retval) return status; ec->gpe = tmp; /* Use the global lock for all EC transactions? */ + tmp = 0; acpi_evaluate_integer(handle, "_GLK", NULL, &tmp); ec->global_lock = tmp; ec->handle = handle; @@ -868,7 +901,7 @@ static int ec_install_handlers(struct acpi_ec *ec) if (ACPI_FAILURE(status)) return -ENODEV; acpi_set_gpe_type(NULL, ec->gpe, ACPI_GPE_TYPE_RUNTIME); - acpi_enable_gpe(NULL, ec->gpe, ACPI_NOT_ISR); + acpi_enable_gpe(NULL, ec->gpe); status = acpi_install_address_space_handler(ec->handle, ACPI_ADR_SPACE_EC, &acpi_ec_space_handler, @@ -1007,7 +1040,7 @@ static int acpi_ec_suspend(struct acpi_device *device, pm_message_t state) /* Stop using GPE */ set_bit(EC_FLAGS_NO_GPE, &ec->flags); clear_bit(EC_FLAGS_GPE_MODE, &ec->flags); - acpi_disable_gpe(NULL, ec->gpe, ACPI_NOT_ISR); + acpi_disable_gpe(NULL, ec->gpe); return 0; } @@ -1016,7 +1049,7 @@ static int acpi_ec_resume(struct acpi_device *device) struct acpi_ec *ec = acpi_driver_data(device); /* Enable use of GPE back */ clear_bit(EC_FLAGS_NO_GPE, &ec->flags); - acpi_enable_gpe(NULL, ec->gpe, ACPI_NOT_ISR); + acpi_enable_gpe(NULL, ec->gpe); return 0; } diff --git a/drivers/acpi/events/evgpe.c b/drivers/acpi/events/evgpe.c index c5e53aae86f..f45c74fe745 100644 --- a/drivers/acpi/events/evgpe.c +++ b/drivers/acpi/events/evgpe.c @@ -289,8 +289,6 @@ acpi_status acpi_ev_disable_gpe(struct acpi_gpe_event_info *gpe_event_info) */ status = acpi_hw_low_disable_gpe(gpe_event_info); return_ACPI_STATUS(status); - - return_ACPI_STATUS(AE_OK); } /******************************************************************************* diff --git a/drivers/acpi/events/evxfevnt.c b/drivers/acpi/events/evxfevnt.c index 73bfd6bf962..41554f736b6 100644 --- a/drivers/acpi/events/evxfevnt.c +++ b/drivers/acpi/events/evxfevnt.c @@ -248,21 +248,15 @@ ACPI_EXPORT_SYMBOL(acpi_set_gpe_type) * DESCRIPTION: Enable an ACPI event (general purpose) * ******************************************************************************/ -acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number, u32 flags) +acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number) { acpi_status status = AE_OK; + acpi_cpu_flags flags; struct acpi_gpe_event_info *gpe_event_info; ACPI_FUNCTION_TRACE(acpi_enable_gpe); - /* Use semaphore lock if not executing at interrupt level */ - - if (flags & ACPI_NOT_ISR) { - status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); - if (ACPI_FAILURE(status)) { - return_ACPI_STATUS(status); - } - } + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); /* Ensure that we have a valid GPE number */ @@ -277,9 +271,7 @@ acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number, u32 flags) status = acpi_ev_enable_gpe(gpe_event_info, TRUE); unlock_and_exit: - if (flags & ACPI_NOT_ISR) { - (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); - } + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); return_ACPI_STATUS(status); } @@ -299,22 +291,15 @@ ACPI_EXPORT_SYMBOL(acpi_enable_gpe) * DESCRIPTION: Disable an ACPI event (general purpose) * ******************************************************************************/ -acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number, u32 flags) +acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number) { acpi_status status = AE_OK; + acpi_cpu_flags flags; struct acpi_gpe_event_info *gpe_event_info; ACPI_FUNCTION_TRACE(acpi_disable_gpe); - /* Use semaphore lock if not executing at interrupt level */ - - if (flags & ACPI_NOT_ISR) { - status = acpi_ut_acquire_mutex(ACPI_MTX_EVENTS); - if (ACPI_FAILURE(status)) { - return_ACPI_STATUS(status); - } - } - + flags = acpi_os_acquire_lock(acpi_gbl_gpe_lock); /* Ensure that we have a valid GPE number */ gpe_event_info = acpi_ev_get_gpe_event_info(gpe_device, gpe_number); @@ -325,10 +310,8 @@ acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number, u32 flags) status = acpi_ev_disable_gpe(gpe_event_info); - unlock_and_exit: - if (flags & ACPI_NOT_ISR) { - (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); - } +unlock_and_exit: + acpi_os_release_lock(acpi_gbl_gpe_lock, flags); return_ACPI_STATUS(status); } @@ -521,6 +504,9 @@ acpi_status acpi_get_event_status(u32 event, acpi_event_status * event_status) if (value) *event_status |= ACPI_EVENT_FLAG_SET; + if (acpi_gbl_fixed_event_handlers[event].handler) + *event_status |= ACPI_EVENT_FLAG_HANDLE; + return_ACPI_STATUS(status); } @@ -571,6 +557,9 @@ acpi_get_gpe_status(acpi_handle gpe_device, status = acpi_hw_get_gpe_status(gpe_event_info, event_status); + if (gpe_event_info->flags & ACPI_GPE_DISPATCH_MASK) + *event_status |= ACPI_EVENT_FLAG_HANDLE; + unlock_and_exit: if (flags & ACPI_NOT_ISR) { (void)acpi_ut_release_mutex(ACPI_MTX_EVENTS); diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c index 60d54d1f6b1..eaaee1660bd 100644 --- a/drivers/acpi/fan.c +++ b/drivers/acpi/fan.c @@ -34,7 +34,6 @@ #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> -#define ACPI_FAN_COMPONENT 0x00200000 #define ACPI_FAN_CLASS "fan" #define ACPI_FAN_FILE_STATE "state" diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index 24649ada08d..adec3d15810 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -140,6 +140,46 @@ struct device *acpi_get_physical_device(acpi_handle handle) EXPORT_SYMBOL(acpi_get_physical_device); +/* ToDo: When a PCI bridge is found, return the PCI device behind the bridge + * This should work in general, but did not on a Lenovo T61 for the + * graphics card. But this must be fixed when the PCI device is + * bound and the kernel device struct is attached to the acpi device + * Note: A success call will increase reference count by one + * Do call put_device(dev) on the returned device then + */ +struct device *acpi_get_physical_pci_device(acpi_handle handle) +{ + struct device *dev; + long long device_id; + acpi_status status; + + status = + acpi_evaluate_integer(handle, "_ADR", NULL, &device_id); + + if (ACPI_FAILURE(status)) + return NULL; + + /* We need to attempt to determine whether the _ADR refers to a + PCI device or not. There's no terribly good way to do this, + so the best we can hope for is to assume that there'll never + be a device in the host bridge */ + if (device_id >= 0x10000) { + /* It looks like a PCI device. Does it exist? */ + dev = acpi_get_physical_device(handle); + } else { + /* It doesn't look like a PCI device. Does its parent + exist? */ + acpi_handle phandle; + if (acpi_get_parent(handle, &phandle)) + return NULL; + dev = acpi_get_physical_device(phandle); + } + if (!dev) + return NULL; + return dev; +} +EXPORT_SYMBOL(acpi_get_physical_pci_device); + static int acpi_bind_one(struct device *dev, acpi_handle handle) { struct acpi_device *acpi_dev; diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c index fcfdef7b4fd..e52ad91ce2d 100644 --- a/drivers/acpi/pci_link.c +++ b/drivers/acpi/pci_link.c @@ -531,7 +531,7 @@ int __init acpi_irq_penalty_init(void) return 0; } -static int acpi_irq_balance; /* 0: static, 1: balance */ +static int acpi_irq_balance = -1; /* 0: static, 1: balance */ static int acpi_pci_link_allocate(struct acpi_pci_link *link) { @@ -950,10 +950,17 @@ device_initcall(irqrouter_init_sysfs); static int __init acpi_pci_link_init(void) { - if (acpi_noirq) return 0; + if (acpi_irq_balance == -1) { + /* no command line switch: enable balancing in IOAPIC mode */ + if (acpi_irq_model == ACPI_IRQ_MODEL_IOAPIC) + acpi_irq_balance = 1; + else + acpi_irq_balance = 0; + } + acpi_link.count = 0; INIT_LIST_HEAD(&acpi_link.entries); diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 1b8f67d21d5..642554b1b60 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -376,15 +376,9 @@ static int acpi_pci_root_remove(struct acpi_device *device, int type) static int __init acpi_pci_root_init(void) { - if (acpi_pci_disabled) return 0; - /* DEBUG: - acpi_dbg_layer = ACPI_PCI_COMPONENT; - acpi_dbg_level = 0xFFFFFFFF; - */ - if (acpi_bus_register_driver(&acpi_pci_root_driver) < 0) return -ENODEV; diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index a1718e56103..bb7d50dd281 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -44,9 +44,8 @@ #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> -#define _COMPONENT ACPI_POWER_COMPONENT +#define _COMPONENT ACPI_POWER_COMPONENT ACPI_MODULE_NAME("power"); -#define ACPI_POWER_COMPONENT 0x00800000 #define ACPI_POWER_CLASS "power_resource" #define ACPI_POWER_DEVICE_NAME "Power Resource" #define ACPI_POWER_FILE_INFO "info" @@ -153,7 +152,8 @@ static int acpi_power_get_state(acpi_handle handle, int *state) ACPI_POWER_RESOURCE_STATE_OFF; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] is %s\n", - acpi_ut_get_node_name(handle), state ? "on" : "off")); + acpi_ut_get_node_name(handle), + *state ? "on" : "off")); return 0; } @@ -516,11 +516,6 @@ int acpi_power_transition(struct acpi_device *device, int state) cl = &device->power.states[device->power.state].resources; tl = &device->power.states[state].resources; - if (!cl->count && !tl->count) { - result = -ENODEV; - goto end; - } - /* TBD: Resources must be ordered. */ /* diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c index 24a362f8034..34948362f41 100644 --- a/drivers/acpi/processor_core.c +++ b/drivers/acpi/processor_core.c @@ -59,7 +59,6 @@ #include <acpi/acpi_drivers.h> #include <acpi/processor.h> -#define ACPI_PROCESSOR_COMPONENT 0x01000000 #define ACPI_PROCESSOR_CLASS "processor" #define ACPI_PROCESSOR_DEVICE_NAME "Processor" #define ACPI_PROCESSOR_FILE_INFO "info" @@ -89,6 +88,7 @@ static int acpi_processor_handle_eject(struct acpi_processor *pr); static const struct acpi_device_id processor_device_ids[] = { + {ACPI_PROCESSOR_OBJECT_HID, 0}, {ACPI_PROCESSOR_HID, 0}, {"", 0}, }; @@ -409,7 +409,7 @@ static int acpi_processor_remove_fs(struct acpi_device *device) /* Use the acpiid in MADT to map cpus in case of SMP */ #ifndef CONFIG_SMP -static int get_cpu_id(acpi_handle handle, u32 acpi_id) {return -1;} +static int get_cpu_id(acpi_handle handle, int type, u32 acpi_id) { return -1; } #else static struct acpi_table_madt *madt; @@ -428,27 +428,35 @@ static int map_lapic_id(struct acpi_subtable_header *entry, } static int map_lsapic_id(struct acpi_subtable_header *entry, - u32 acpi_id, int *apic_id) + int device_declaration, u32 acpi_id, int *apic_id) { struct acpi_madt_local_sapic *lsapic = (struct acpi_madt_local_sapic *)entry; + u32 tmp = (lsapic->id << 8) | lsapic->eid; + /* Only check enabled APICs*/ - if (lsapic->lapic_flags & ACPI_MADT_ENABLED) { - /* First check against id */ - if (lsapic->processor_id == acpi_id) { - *apic_id = (lsapic->id << 8) | lsapic->eid; - return 1; - /* Check against optional uid */ - } else if (entry->length >= 16 && - lsapic->uid == acpi_id) { - *apic_id = lsapic->uid; - return 1; - } - } + if (!(lsapic->lapic_flags & ACPI_MADT_ENABLED)) + return 0; + + /* Device statement declaration type */ + if (device_declaration) { + if (entry->length < 16) + printk(KERN_ERR PREFIX + "Invalid LSAPIC with Device type processor (SAPIC ID %#x)\n", + tmp); + else if (lsapic->uid == acpi_id) + goto found; + /* Processor statement declaration type */ + } else if (lsapic->processor_id == acpi_id) + goto found; + return 0; +found: + *apic_id = tmp; + return 1; } -static int map_madt_entry(u32 acpi_id) +static int map_madt_entry(int type, u32 acpi_id) { unsigned long madt_end, entry; int apic_id = -1; @@ -469,7 +477,7 @@ static int map_madt_entry(u32 acpi_id) if (map_lapic_id(header, acpi_id, &apic_id)) break; } else if (header->type == ACPI_MADT_TYPE_LOCAL_SAPIC) { - if (map_lsapic_id(header, acpi_id, &apic_id)) + if (map_lsapic_id(header, type, acpi_id, &apic_id)) break; } entry += header->length; @@ -477,7 +485,7 @@ static int map_madt_entry(u32 acpi_id) return apic_id; } -static int map_mat_entry(acpi_handle handle, u32 acpi_id) +static int map_mat_entry(acpi_handle handle, int type, u32 acpi_id) { struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object *obj; @@ -500,7 +508,7 @@ static int map_mat_entry(acpi_handle handle, u32 acpi_id) if (header->type == ACPI_MADT_TYPE_LOCAL_APIC) { map_lapic_id(header, acpi_id, &apic_id); } else if (header->type == ACPI_MADT_TYPE_LOCAL_SAPIC) { - map_lsapic_id(header, acpi_id, &apic_id); + map_lsapic_id(header, type, acpi_id, &apic_id); } exit: @@ -509,14 +517,14 @@ exit: return apic_id; } -static int get_cpu_id(acpi_handle handle, u32 acpi_id) +static int get_cpu_id(acpi_handle handle, int type, u32 acpi_id) { int i; int apic_id = -1; - apic_id = map_mat_entry(handle, acpi_id); + apic_id = map_mat_entry(handle, type, acpi_id); if (apic_id == -1) - apic_id = map_madt_entry(acpi_id); + apic_id = map_madt_entry(type, acpi_id); if (apic_id == -1) return apic_id; @@ -532,15 +540,16 @@ static int get_cpu_id(acpi_handle handle, u32 acpi_id) Driver Interface -------------------------------------------------------------------------- */ -static int acpi_processor_get_info(struct acpi_processor *pr, unsigned has_uid) +static int acpi_processor_get_info(struct acpi_device *device) { acpi_status status = 0; union acpi_object object = { 0 }; struct acpi_buffer buffer = { sizeof(union acpi_object), &object }; - int cpu_index; + struct acpi_processor *pr; + int cpu_index, device_declaration = 0; static int cpu0_initialized; - + pr = acpi_driver_data(device); if (!pr) return -EINVAL; @@ -561,22 +570,23 @@ static int acpi_processor_get_info(struct acpi_processor *pr, unsigned has_uid) ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No bus mastering arbitration control\n")); - /* Check if it is a Device with HID and UID */ - if (has_uid) { + if (!strcmp(acpi_device_hid(device), ACPI_PROCESSOR_HID)) { + /* + * Declared with "Device" statement; match _UID. + * Note that we don't handle string _UIDs yet. + */ unsigned long long value; status = acpi_evaluate_integer(pr->handle, METHOD_NAME__UID, NULL, &value); if (ACPI_FAILURE(status)) { - printk(KERN_ERR PREFIX "Evaluating processor _UID\n"); + printk(KERN_ERR PREFIX + "Evaluating processor _UID [%#x]\n", status); return -ENODEV; } + device_declaration = 1; pr->acpi_id = value; } else { - /* - * Evalute the processor object. Note that it is common on SMP to - * have the first (boot) processor with a valid PBLK address while - * all others have a NULL address. - */ + /* Declared with "Processor" statement; match ProcessorID */ status = acpi_evaluate_object(pr->handle, NULL, NULL, &buffer); if (ACPI_FAILURE(status)) { printk(KERN_ERR PREFIX "Evaluating processor object\n"); @@ -584,12 +594,13 @@ static int acpi_processor_get_info(struct acpi_processor *pr, unsigned has_uid) } /* - * TBD: Synch processor ID (via LAPIC/LSAPIC structures) on SMP. - * >>> 'acpi_get_processor_id(acpi_id, &id)' in arch/xxx/acpi.c - */ + * TBD: Synch processor ID (via LAPIC/LSAPIC structures) on SMP. + * >>> 'acpi_get_processor_id(acpi_id, &id)' in + * arch/xxx/acpi.c + */ pr->acpi_id = object.processor.proc_id; } - cpu_index = get_cpu_id(pr->handle, pr->acpi_id); + cpu_index = get_cpu_id(pr->handle, device_declaration, pr->acpi_id); /* Handle UP system running SMP kernel, with no LAPIC in MADT */ if (!cpu0_initialized && (cpu_index == -1) && @@ -661,7 +672,7 @@ static int __cpuinit acpi_processor_start(struct acpi_device *device) pr = acpi_driver_data(device); - result = acpi_processor_get_info(pr, device->flags.unique_id); + result = acpi_processor_get_info(device); if (result) { /* Processor is physically not present */ return 0; @@ -761,20 +772,20 @@ static void acpi_processor_notify(acpi_handle handle, u32 event, void *data) acpi_bus_generate_proc_event(device, event, pr->performance_platform_limit); acpi_bus_generate_netlink_event(device->pnp.device_class, - device->dev.bus_id, event, + dev_name(&device->dev), event, pr->performance_platform_limit); break; case ACPI_PROCESSOR_NOTIFY_POWER: acpi_processor_cst_has_changed(pr); acpi_bus_generate_proc_event(device, event, 0); acpi_bus_generate_netlink_event(device->pnp.device_class, - device->dev.bus_id, event, 0); + dev_name(&device->dev), event, 0); break; case ACPI_PROCESSOR_NOTIFY_THROTTLING: acpi_processor_tstate_has_changed(pr); acpi_bus_generate_proc_event(device, event, 0); acpi_bus_generate_netlink_event(device->pnp.device_class, - device->dev.bus_id, event, 0); + dev_name(&device->dev), event, 0); default: ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Unsupported event [0x%x]\n", event)); diff --git a/drivers/acpi/processor_idle.c b/drivers/acpi/processor_idle.c index 81b40ed5379..5f8d746a9b8 100644 --- a/drivers/acpi/processor_idle.c +++ b/drivers/acpi/processor_idle.c @@ -59,7 +59,6 @@ #include <acpi/processor.h> #include <asm/processor.h> -#define ACPI_PROCESSOR_COMPONENT 0x01000000 #define ACPI_PROCESSOR_CLASS "processor" #define _COMPONENT ACPI_PROCESSOR_COMPONENT ACPI_MODULE_NAME("processor_idle"); diff --git a/drivers/acpi/processor_perflib.c b/drivers/acpi/processor_perflib.c index dbcf260ea93..0d7b772bef5 100644 --- a/drivers/acpi/processor_perflib.c +++ b/drivers/acpi/processor_perflib.c @@ -44,9 +44,9 @@ #endif #include <acpi/acpi_bus.h> +#include <acpi/acpi_drivers.h> #include <acpi/processor.h> -#define ACPI_PROCESSOR_COMPONENT 0x01000000 #define ACPI_PROCESSOR_CLASS "processor" #define ACPI_PROCESSOR_FILE_PERFORMANCE "performance" #define _COMPONENT ACPI_PROCESSOR_COMPONENT diff --git a/drivers/acpi/processor_thermal.c b/drivers/acpi/processor_thermal.c index ef34b18f95c..b1eb376fae4 100644 --- a/drivers/acpi/processor_thermal.c +++ b/drivers/acpi/processor_thermal.c @@ -40,7 +40,6 @@ #include <acpi/processor.h> #include <acpi/acpi_drivers.h> -#define ACPI_PROCESSOR_COMPONENT 0x01000000 #define ACPI_PROCESSOR_CLASS "processor" #define _COMPONENT ACPI_PROCESSOR_COMPONENT ACPI_MODULE_NAME("processor_thermal"); diff --git a/drivers/acpi/processor_throttling.c b/drivers/acpi/processor_throttling.c index 3da2df93d92..a0c38c94a8a 100644 --- a/drivers/acpi/processor_throttling.c +++ b/drivers/acpi/processor_throttling.c @@ -38,9 +38,9 @@ #include <asm/uaccess.h> #include <acpi/acpi_bus.h> +#include <acpi/acpi_drivers.h> #include <acpi/processor.h> -#define ACPI_PROCESSOR_COMPONENT 0x01000000 #define ACPI_PROCESSOR_CLASS "processor" #define _COMPONENT ACPI_PROCESSOR_COMPONENT ACPI_MODULE_NAME("processor_throttling"); diff --git a/drivers/acpi/reboot.c b/drivers/acpi/reboot.c index 755baf2ca70..a6b662c00b6 100644 --- a/drivers/acpi/reboot.c +++ b/drivers/acpi/reboot.c @@ -15,28 +15,9 @@ void acpi_reboot(void) rr = &acpi_gbl_FADT.reset_register; - /* - * Is the ACPI reset register supported? - * - * According to ACPI 3.0, FADT.flags.RESET_REG_SUP indicates - * whether the ACPI reset mechanism is supported. - * - * However, some boxes have this bit clear, yet a valid - * ACPI_RESET_REG & RESET_VALUE, and ACPI reboot is the only - * mechanism that works for them after S3. - * - * This suggests that other operating systems may not be checking - * the RESET_REG_SUP bit, and are using other means to decide - * whether to use the ACPI reboot mechanism or not. - * - * So when acpi reboot is requested, - * only the reset_register is checked. If the following - * conditions are met, it indicates that the reset register is supported. - * a. reset_register is not zero - * b. the access width is eight - * c. the bit_offset is zero - */ - if (!(rr->address) || rr->bit_width != 8 || rr->bit_offset != 0) + /* Is the reset register supported? */ + if (!(acpi_gbl_FADT.flags & ACPI_FADT_RESET_REGISTER) || + rr->bit_width != 8 || rr->bit_offset != 0) return; reset_value = acpi_gbl_FADT.reset_value; diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index a9dda8e0f9f..bd5253ee5c8 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -109,8 +109,7 @@ static int acpi_bus_hot_remove_device(void *context) return 0; ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Hot-removing device %s...\n", device->dev.bus_id)); - + "Hot-removing device %s...\n", dev_name(&device->dev))); if (acpi_bus_trim(device, 1)) { printk(KERN_ERR PREFIX @@ -460,7 +459,7 @@ static int acpi_device_register(struct acpi_device *device, acpi_device_bus_id->instance_no = 0; list_add_tail(&acpi_device_bus_id->node, &acpi_bus_id_list); } - sprintf(device->dev.bus_id, "%s:%02x", acpi_device_bus_id->bus_id, acpi_device_bus_id->instance_no); + dev_set_name(&device->dev, "%s:%02x", acpi_device_bus_id->bus_id, acpi_device_bus_id->instance_no); if (device->parent) { list_add_tail(&device->node, &device->parent->children); @@ -484,7 +483,8 @@ static int acpi_device_register(struct acpi_device *device, result = acpi_device_setup_files(device); if(result) - printk(KERN_ERR PREFIX "Error creating sysfs interface for device %s\n", device->dev.bus_id); + printk(KERN_ERR PREFIX "Error creating sysfs interface for device %s\n", + dev_name(&device->dev)); device->removal_type = ACPI_BUS_REMOVAL_NORMAL; return 0; @@ -919,36 +919,6 @@ static void acpi_device_get_busid(struct acpi_device *device, } } -static int -acpi_video_bus_match(struct acpi_device *device) -{ - acpi_handle h_dummy; - - if (!device) - return -EINVAL; - - /* Since there is no HID, CID for ACPI Video drivers, we have - * to check well known required nodes for each feature we support. - */ - - /* Does this device able to support video switching ? */ - if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOD", &h_dummy)) && - ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOS", &h_dummy))) - return 0; - - /* Does this device able to retrieve a video ROM ? */ - if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_ROM", &h_dummy))) - return 0; - - /* Does this device able to configure which video head to be POSTed ? */ - if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_VPO", &h_dummy)) && - ACPI_SUCCESS(acpi_get_handle(device->handle, "_GPD", &h_dummy)) && - ACPI_SUCCESS(acpi_get_handle(device->handle, "_SPD", &h_dummy))) - return 0; - - return -ENODEV; -} - /* * acpi_bay_match - see if a device is an ejectable driver bay * @@ -1031,7 +1001,7 @@ static void acpi_device_set_id(struct acpi_device *device, will get autoloaded and the device might still match against another driver. */ - if (ACPI_SUCCESS(acpi_video_bus_match(device))) + if (acpi_is_video_device(device)) cid_add = ACPI_VIDEO_HID; else if (ACPI_SUCCESS(acpi_bay_match(device))) cid_add = ACPI_BAY_HID; @@ -1043,7 +1013,7 @@ static void acpi_device_set_id(struct acpi_device *device, hid = ACPI_POWER_HID; break; case ACPI_BUS_TYPE_PROCESSOR: - hid = ACPI_PROCESSOR_HID; + hid = ACPI_PROCESSOR_OBJECT_HID; break; case ACPI_BUS_TYPE_SYSTEM: hid = ACPI_SYSTEM_HID; diff --git a/drivers/acpi/sleep/proc.c b/drivers/acpi/sleep/proc.c index 631ee2ee2ca..64e591ba86f 100644 --- a/drivers/acpi/sleep/proc.c +++ b/drivers/acpi/sleep/proc.c @@ -366,8 +366,8 @@ acpi_system_wakeup_device_seq_show(struct seq_file *seq, void *offset) dev->wakeup.state.enabled ? "enabled" : "disabled"); if (ldev) seq_printf(seq, "%s:%s", - ldev->bus ? ldev->bus->name : "no-bus", - ldev->bus_id); + dev_name(ldev) ? ldev->bus->name : "no-bus", + dev_name(ldev)); seq_printf(seq, "\n"); put_device(ldev); diff --git a/drivers/acpi/sleep/wakeup.c b/drivers/acpi/sleep/wakeup.c index 38655eb132d..dea4c23df76 100644 --- a/drivers/acpi/sleep/wakeup.c +++ b/drivers/acpi/sleep/wakeup.c @@ -88,7 +88,7 @@ void acpi_enable_wakeup_device(u8 sleep_state) spin_unlock(&acpi_device_lock); if (!dev->wakeup.flags.run_wake) acpi_enable_gpe(dev->wakeup.gpe_device, - dev->wakeup.gpe_number, ACPI_ISR); + dev->wakeup.gpe_number); spin_lock(&acpi_device_lock); } spin_unlock(&acpi_device_lock); @@ -122,7 +122,7 @@ void acpi_disable_wakeup_device(u8 sleep_state) ACPI_GPE_TYPE_WAKE_RUN); /* Re-enable it, since set_gpe_type will disable it */ acpi_enable_gpe(dev->wakeup.gpe_device, - dev->wakeup.gpe_number, ACPI_NOT_ISR); + dev->wakeup.gpe_number); spin_lock(&acpi_device_lock); } continue; @@ -133,7 +133,7 @@ void acpi_disable_wakeup_device(u8 sleep_state) /* Never disable run-wake GPE */ if (!dev->wakeup.flags.run_wake) { acpi_disable_gpe(dev->wakeup.gpe_device, - dev->wakeup.gpe_number, ACPI_NOT_ISR); + dev->wakeup.gpe_number); acpi_clear_gpe(dev->wakeup.gpe_device, dev->wakeup.gpe_number, ACPI_NOT_ISR); } @@ -162,7 +162,7 @@ static int __init acpi_wakeup_device_init(void) dev->wakeup.gpe_number, ACPI_GPE_TYPE_WAKE_RUN); acpi_enable_gpe(dev->wakeup.gpe_device, - dev->wakeup.gpe_number, ACPI_NOT_ISR); + dev->wakeup.gpe_number); dev->wakeup.state.enabled = 1; spin_lock(&acpi_device_lock); } diff --git a/drivers/acpi/system.c b/drivers/acpi/system.c index 1d74171b794..6e4107f8240 100644 --- a/drivers/acpi/system.c +++ b/drivers/acpi/system.c @@ -78,9 +78,15 @@ static ssize_t acpi_table_show(struct kobject *kobj, container_of(bin_attr, struct acpi_table_attr, attr); struct acpi_table_header *table_header = NULL; acpi_status status; + char name[ACPI_NAME_SIZE]; + + if (strncmp(table_attr->name, "NULL", 4)) + memcpy(name, table_attr->name, ACPI_NAME_SIZE); + else + memcpy(name, "\0\0\0\0", 4); status = - acpi_get_table(table_attr->name, table_attr->instance, + acpi_get_table(name, table_attr->instance, &table_header); if (ACPI_FAILURE(status)) return -ENODEV; @@ -95,21 +101,24 @@ static void acpi_table_attr_init(struct acpi_table_attr *table_attr, struct acpi_table_header *header = NULL; struct acpi_table_attr *attr = NULL; - memcpy(table_attr->name, table_header->signature, ACPI_NAME_SIZE); + if (table_header->signature[0] != '\0') + memcpy(table_attr->name, table_header->signature, + ACPI_NAME_SIZE); + else + memcpy(table_attr->name, "NULL", 4); list_for_each_entry(attr, &acpi_table_attr_list, node) { - if (!memcmp(table_header->signature, attr->name, - ACPI_NAME_SIZE)) + if (!memcmp(table_attr->name, attr->name, ACPI_NAME_SIZE)) if (table_attr->instance < attr->instance) table_attr->instance = attr->instance; } table_attr->instance++; if (table_attr->instance > 1 || (table_attr->instance == 1 && - !acpi_get_table(table_header-> - signature, 2, - &header))) - sprintf(table_attr->name + 4, "%d", table_attr->instance); + !acpi_get_table + (table_header->signature, 2, &header))) + sprintf(table_attr->name + ACPI_NAME_SIZE, "%d", + table_attr->instance); table_attr->attr.size = 0; table_attr->attr.read = acpi_table_show; @@ -167,7 +176,6 @@ static int acpi_system_sysfs_init(void) #define COUNT_ERROR 2 /* other */ #define NUM_COUNTERS_EXTRA 3 -#define ACPI_EVENT_VALID 0x01 struct event_counter { u32 count; u32 flags; @@ -312,12 +320,6 @@ static int get_status(u32 index, acpi_event_status *status, acpi_handle *handle) } else if (index < (num_gpes + ACPI_NUM_FIXED_EVENTS)) result = acpi_get_event_status(index - num_gpes, status); - /* - * sleep/power button GPE/Fixed Event is enabled after acpi_system_init, - * check the status at runtime and mark it as valid once it's enabled - */ - if (!result && (*status & ACPI_EVENT_FLAG_ENABLED)) - all_counters[index].flags |= ACPI_EVENT_VALID; end: return result; } @@ -346,12 +348,14 @@ static ssize_t counter_show(struct kobject *kobj, if (result) goto end; - if (!(all_counters[index].flags & ACPI_EVENT_VALID)) - size += sprintf(buf + size, " invalid"); + if (!(status & ACPI_EVENT_FLAG_HANDLE)) + size += sprintf(buf + size, " invalid"); else if (status & ACPI_EVENT_FLAG_ENABLED) - size += sprintf(buf + size, " enable"); + size += sprintf(buf + size, " enabled"); + else if (status & ACPI_EVENT_FLAG_WAKE_ENABLED) + size += sprintf(buf + size, " wake_enabled"); else - size += sprintf(buf + size, " disable"); + size += sprintf(buf + size, " disabled"); end: size += sprintf(buf + size, "\n"); @@ -385,7 +389,7 @@ static ssize_t counter_set(struct kobject *kobj, if (result) goto end; - if (!(all_counters[index].flags & ACPI_EVENT_VALID)) { + if (!(status & ACPI_EVENT_FLAG_HANDLE)) { printk(KERN_WARNING PREFIX "Can not change Invalid GPE/Fixed Event status\n"); return -EINVAL; @@ -394,10 +398,10 @@ static ssize_t counter_set(struct kobject *kobj, if (index < num_gpes) { if (!strcmp(buf, "disable\n") && (status & ACPI_EVENT_FLAG_ENABLED)) - result = acpi_disable_gpe(handle, index, ACPI_NOT_ISR); + result = acpi_disable_gpe(handle, index); else if (!strcmp(buf, "enable\n") && !(status & ACPI_EVENT_FLAG_ENABLED)) - result = acpi_enable_gpe(handle, index, ACPI_NOT_ISR); + result = acpi_enable_gpe(handle, index); else if (!strcmp(buf, "clear\n") && (status & ACPI_EVENT_FLAG_SET)) result = acpi_clear_gpe(handle, index, ACPI_NOT_ISR); diff --git a/drivers/acpi/tables/tbfadt.c b/drivers/acpi/tables/tbfadt.c index 2c7885e7ffb..2817158fb6a 100644 --- a/drivers/acpi/tables/tbfadt.c +++ b/drivers/acpi/tables/tbfadt.c @@ -304,7 +304,7 @@ static void acpi_tb_convert_fadt(void) * The ACPI 1.0 reserved fields that will be zeroed are the bytes located at * offset 45, 55, 95, and the word located at offset 109, 110. */ - if (acpi_gbl_FADT.header.revision < 3) { + if (acpi_gbl_FADT.header.revision < FADT2_REVISION_ID) { acpi_gbl_FADT.preferred_profile = 0; acpi_gbl_FADT.pstate_control = 0; acpi_gbl_FADT.cst_control = 0; diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index ad6cae938f0..073ff09218a 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c @@ -47,7 +47,6 @@ #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> -#define ACPI_THERMAL_COMPONENT 0x04000000 #define ACPI_THERMAL_CLASS "thermal_zone" #define ACPI_THERMAL_DEVICE_NAME "Thermal Zone" #define ACPI_THERMAL_FILE_STATE "state" @@ -576,7 +575,7 @@ static int acpi_thermal_critical(struct acpi_thermal *tz) acpi_bus_generate_proc_event(tz->device, ACPI_THERMAL_NOTIFY_CRITICAL, tz->trips.critical.flags.enabled); acpi_bus_generate_netlink_event(tz->device->pnp.device_class, - tz->device->dev.bus_id, + dev_name(&tz->device->dev), ACPI_THERMAL_NOTIFY_CRITICAL, tz->trips.critical.flags.enabled); @@ -605,7 +604,7 @@ static int acpi_thermal_hot(struct acpi_thermal *tz) acpi_bus_generate_proc_event(tz->device, ACPI_THERMAL_NOTIFY_HOT, tz->trips.hot.flags.enabled); acpi_bus_generate_netlink_event(tz->device->pnp.device_class, - tz->device->dev.bus_id, + dev_name(&tz->device->dev), ACPI_THERMAL_NOTIFY_HOT, tz->trips.hot.flags.enabled); @@ -1592,14 +1591,14 @@ static void acpi_thermal_notify(acpi_handle handle, u32 event, void *data) acpi_thermal_check(tz); acpi_bus_generate_proc_event(device, event, 0); acpi_bus_generate_netlink_event(device->pnp.device_class, - device->dev.bus_id, event, 0); + dev_name(&device->dev), event, 0); break; case ACPI_THERMAL_NOTIFY_DEVICES: acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_DEVICES); acpi_thermal_check(tz); acpi_bus_generate_proc_event(device, event, 0); acpi_bus_generate_netlink_event(device->pnp.device_class, - device->dev.bus_id, event, 0); + dev_name(&device->dev), event, 0); break; default: ACPI_DEBUG_PRINT((ACPI_DB_INFO, diff --git a/drivers/acpi/video.c b/drivers/acpi/video.c index a29b0ccac65..baa44192972 100644 --- a/drivers/acpi/video.c +++ b/drivers/acpi/video.c @@ -41,7 +41,6 @@ #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> -#define ACPI_VIDEO_COMPONENT 0x08000000 #define ACPI_VIDEO_CLASS "video" #define ACPI_VIDEO_BUS_NAME "Video Bus" #define ACPI_VIDEO_DEVICE_NAME "Video Device" @@ -739,7 +738,8 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) device->cap._DSS = 1; } - max_level = acpi_video_init_brightness(device); + if (acpi_video_backlight_support()) + max_level = acpi_video_init_brightness(device); if (device->cap._BCL && device->cap._BCM && max_level > 0) { int result; @@ -785,18 +785,21 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device) printk(KERN_ERR PREFIX "Create sysfs link\n"); } - if (device->cap._DCS && device->cap._DSS){ - static int count = 0; - char *name; - name = kzalloc(MAX_NAME_LEN, GFP_KERNEL); - if (!name) - return; - sprintf(name, "acpi_video%d", count++); - device->output_dev = video_output_register(name, - NULL, device, &acpi_output_properties); - kfree(name); + + if (acpi_video_display_switch_support()) { + + if (device->cap._DCS && device->cap._DSS) { + static int count; + char *name; + name = kzalloc(MAX_NAME_LEN, GFP_KERNEL); + if (!name) + return; + sprintf(name, "acpi_video%d", count++); + device->output_dev = video_output_register(name, + NULL, device, &acpi_output_properties); + kfree(name); + } } - return; } /* @@ -842,11 +845,16 @@ static void acpi_video_bus_find_cap(struct acpi_video_bus *video) static int acpi_video_bus_check(struct acpi_video_bus *video) { acpi_status status = -ENOENT; - + struct device *dev; if (!video) return -EINVAL; + dev = acpi_get_physical_pci_device(video->device->handle); + if (!dev) + return -ENODEV; + put_device(dev); + /* Since there is no HID, CID and so on for VGA driver, we have * to check well known required nodes. */ @@ -2094,12 +2102,6 @@ static int __init acpi_video_init(void) { int result = 0; - - /* - acpi_dbg_level = 0xFFFFFFFF; - acpi_dbg_layer = 0x08000000; - */ - acpi_video_dir = proc_mkdir(ACPI_VIDEO_CLASS, acpi_root_dir); if (!acpi_video_dir) return -ENODEV; diff --git a/drivers/acpi/video_detect.c b/drivers/acpi/video_detect.c new file mode 100644 index 00000000000..f022eb6f563 --- /dev/null +++ b/drivers/acpi/video_detect.c @@ -0,0 +1,267 @@ +/* + * Copyright (C) 2008 SuSE Linux Products GmbH + * Thomas Renninger <trenn@suse.de> + * + * May be copied or modified under the terms of the GNU General Public License + * + * video_detect.c: + * Provides acpi_is_video_device() for early scanning of ACPI devices in scan.c + * There a Linux specific (Spec does not provide a HID for video devices) is + * assinged + * + * After PCI devices are glued with ACPI devices + * acpi_get_physical_pci_device() can be called to identify ACPI graphics + * devices for which a real graphics card is plugged in + * + * Now acpi_video_get_capabilities() can be called to check which + * capabilities the graphics cards plugged in support. The check for general + * video capabilities will be triggered by the first caller of + * acpi_video_get_capabilities(NULL); which will happen when the first + * backlight (or display output) switching supporting driver calls: + * acpi_video_backlight_support(); + * + * Depending on whether ACPI graphics extensions (cmp. ACPI spec Appendix B) + * are available, video.ko should be used to handle the device. + * + * Otherwise vendor specific drivers like thinkpad_acpi, asus_acpi, + * sony_acpi,... can take care about backlight brightness and display output + * switching. + * + * If CONFIG_ACPI_VIDEO is neither set as "compiled in" (y) nor as a module (m) + * this file will not be compiled, acpi_video_get_capabilities() and + * acpi_video_backlight_support() will always return 0 and vendor specific + * drivers always can handle backlight. + * + */ + +#include <linux/acpi.h> +#include <linux/dmi.h> + +ACPI_MODULE_NAME("video"); +#define _COMPONENT ACPI_VIDEO_COMPONENT + +static long acpi_video_support; +static bool acpi_video_caps_checked; + +static acpi_status +acpi_backlight_cap_match(acpi_handle handle, u32 level, void *context, + void **retyurn_value) +{ + long *cap = context; + acpi_handle h_dummy; + + if (ACPI_SUCCESS(acpi_get_handle(handle, "_BCM", &h_dummy)) && + ACPI_SUCCESS(acpi_get_handle(handle, "_BCL", &h_dummy))) { + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found generic backlight " + "support\n")); + *cap |= ACPI_VIDEO_BACKLIGHT; + /* We have backlight support, no need to scan further */ + return AE_CTRL_TERMINATE; + } + return 0; +} + +/* Returns true if the device is a video device which can be handled by + * video.ko. + * The device will get a Linux specific CID added in scan.c to + * identify the device as an ACPI graphics device + * Be aware that the graphics device may not be physically present + * Use acpi_video_get_capabilities() to detect general ACPI video + * capabilities of present cards + */ +long acpi_is_video_device(struct acpi_device *device) +{ + acpi_handle h_dummy; + long video_caps = 0; + + if (!device) + return 0; + + /* Does this device able to support video switching ? */ + if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOD", &h_dummy)) && + ACPI_SUCCESS(acpi_get_handle(device->handle, "_DOS", &h_dummy))) + video_caps |= ACPI_VIDEO_OUTPUT_SWITCHING; + + /* Does this device able to retrieve a video ROM ? */ + if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_ROM", &h_dummy))) + video_caps |= ACPI_VIDEO_ROM_AVAILABLE; + + /* Does this device able to configure which video head to be POSTed ? */ + if (ACPI_SUCCESS(acpi_get_handle(device->handle, "_VPO", &h_dummy)) && + ACPI_SUCCESS(acpi_get_handle(device->handle, "_GPD", &h_dummy)) && + ACPI_SUCCESS(acpi_get_handle(device->handle, "_SPD", &h_dummy))) + video_caps |= ACPI_VIDEO_DEVICE_POSTING; + + /* Only check for backlight functionality if one of the above hit. */ + if (video_caps) + acpi_walk_namespace(ACPI_TYPE_DEVICE, device->handle, + ACPI_UINT32_MAX, acpi_backlight_cap_match, + &video_caps, NULL); + + return video_caps; +} +EXPORT_SYMBOL(acpi_is_video_device); + +static acpi_status +find_video(acpi_handle handle, u32 lvl, void *context, void **rv) +{ + long *cap = context; + struct device *dev; + struct acpi_device *acpi_dev; + + const struct acpi_device_id video_ids[] = { + {ACPI_VIDEO_HID, 0}, + {"", 0}, + }; + if (acpi_bus_get_device(handle, &acpi_dev)) + return AE_OK; + + if (!acpi_match_device_ids(acpi_dev, video_ids)) { + dev = acpi_get_physical_pci_device(handle); + if (!dev) + return AE_OK; + put_device(dev); + *cap |= acpi_is_video_device(acpi_dev); + } + return AE_OK; +} + +/* + * Returns the video capabilities of a specific ACPI graphics device + * + * if NULL is passed as argument all ACPI devices are enumerated and + * all graphics capabilities of physically present devices are + * summerized and returned. This is cached and done only once. + */ +long acpi_video_get_capabilities(acpi_handle graphics_handle) +{ + long caps = 0; + struct acpi_device *tmp_dev; + acpi_status status; + + if (acpi_video_caps_checked && graphics_handle == NULL) + return acpi_video_support; + + if (!graphics_handle) { + /* Only do the global walk through all graphics devices once */ + acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, find_video, + &caps, NULL); + /* There might be boot param flags set already... */ + acpi_video_support |= caps; + acpi_video_caps_checked = 1; + /* Add blacklists here. Be careful to use the right *DMI* bits + * to still be able to override logic via boot params, e.g.: + * + * if (dmi_name_in_vendors("XY")) { + * acpi_video_support |= + * ACPI_VIDEO_OUTPUT_SWITCHING_DMI_VENDOR; + * acpi_video_support |= + * ACPI_VIDEO_BACKLIGHT_DMI_VENDOR; + *} + */ + } else { + status = acpi_bus_get_device(graphics_handle, &tmp_dev); + if (ACPI_FAILURE(status)) { + ACPI_EXCEPTION((AE_INFO, status, "Invalid device")); + return 0; + } + acpi_walk_namespace(ACPI_TYPE_DEVICE, graphics_handle, + ACPI_UINT32_MAX, find_video, + &caps, NULL); + } + ACPI_DEBUG_PRINT((ACPI_DB_INFO, "We have 0x%lX video support %s %s\n", + graphics_handle ? caps : acpi_video_support, + graphics_handle ? "on device " : "in general", + graphics_handle ? acpi_device_bid(tmp_dev) : "")); + return caps; +} +EXPORT_SYMBOL(acpi_video_get_capabilities); + +/* Returns true if video.ko can do backlight switching */ +int acpi_video_backlight_support(void) +{ + /* + * We must check whether the ACPI graphics device is physically plugged + * in. Therefore this must be called after binding PCI and ACPI devices + */ + if (!acpi_video_caps_checked) + acpi_video_get_capabilities(NULL); + + /* First check for boot param -> highest prio */ + if (acpi_video_support & ACPI_VIDEO_BACKLIGHT_FORCE_VENDOR) + return 0; + else if (acpi_video_support & ACPI_VIDEO_BACKLIGHT_FORCE_VIDEO) + return 1; + + /* Then check for DMI blacklist -> second highest prio */ + if (acpi_video_support & ACPI_VIDEO_BACKLIGHT_DMI_VENDOR) + return 0; + else if (acpi_video_support & ACPI_VIDEO_BACKLIGHT_DMI_VIDEO) + return 1; + + /* Then go the default way */ + return acpi_video_support & ACPI_VIDEO_BACKLIGHT; +} +EXPORT_SYMBOL(acpi_video_backlight_support); + +/* + * Returns true if video.ko can do display output switching. + * This does not work well/at all with binary graphics drivers + * which disable system io ranges and do it on their own. + */ +int acpi_video_display_switch_support(void) +{ + if (!acpi_video_caps_checked) + acpi_video_get_capabilities(NULL); + + if (acpi_video_support & ACPI_VIDEO_OUTPUT_SWITCHING_FORCE_VENDOR) + return 0; + else if (acpi_video_support & ACPI_VIDEO_OUTPUT_SWITCHING_FORCE_VIDEO) + return 1; + + if (acpi_video_support & ACPI_VIDEO_OUTPUT_SWITCHING_DMI_VENDOR) + return 0; + else if (acpi_video_support & ACPI_VIDEO_OUTPUT_SWITCHING_DMI_VIDEO) + return 1; + + return acpi_video_support & ACPI_VIDEO_OUTPUT_SWITCHING; +} +EXPORT_SYMBOL(acpi_video_display_switch_support); + +/* + * Use acpi_display_output=vendor/video or acpi_backlight=vendor/video + * To force that backlight or display output switching is processed by vendor + * specific acpi drivers or video.ko driver. + */ +int __init acpi_backlight(char *str) +{ + if (str == NULL || *str == '\0') + return 1; + else { + if (!strcmp("vendor", str)) + acpi_video_support |= + ACPI_VIDEO_BACKLIGHT_FORCE_VENDOR; + if (!strcmp("video", str)) + acpi_video_support |= + ACPI_VIDEO_OUTPUT_SWITCHING_FORCE_VIDEO; + } + return 1; +} +__setup("acpi_backlight=", acpi_backlight); + +int __init acpi_display_output(char *str) +{ + if (str == NULL || *str == '\0') + return 1; + else { + if (!strcmp("vendor", str)) + acpi_video_support |= + ACPI_VIDEO_OUTPUT_SWITCHING_FORCE_VENDOR; + if (!strcmp("video", str)) + acpi_video_support |= + ACPI_VIDEO_OUTPUT_SWITCHING_FORCE_VIDEO; + } + return 1; +} +__setup("acpi_display_output=", acpi_display_output); diff --git a/drivers/acpi/wmi.c b/drivers/acpi/wmi.c index 47cd7baf9b1..8a8b377712c 100644 --- a/drivers/acpi/wmi.c +++ b/drivers/acpi/wmi.c @@ -660,7 +660,7 @@ static void acpi_wmi_notify(acpi_handle handle, u32 event, void *data) wblock->handler(event, wblock->handler_data); acpi_bus_generate_netlink_event( - device->pnp.device_class, device->dev.bus_id, + device->pnp.device_class, dev_name(&device->dev), event, 0); break; } diff --git a/drivers/ata/libata-sff.c b/drivers/ata/libata-sff.c index 4b473948632..9033d164c4e 100644 --- a/drivers/ata/libata-sff.c +++ b/drivers/ata/libata-sff.c @@ -1227,10 +1227,19 @@ fsm_start: /* ATA PIO protocol */ if (unlikely((status & ATA_DRQ) == 0)) { /* handle BSY=0, DRQ=0 as error */ - if (likely(status & (ATA_ERR | ATA_DF))) + if (likely(status & (ATA_ERR | ATA_DF))) { /* device stops HSM for abort/error */ qc->err_mask |= AC_ERR_DEV; - else { + + /* If diagnostic failed and this is + * IDENTIFY, it's likely a phantom + * device. Mark hint. + */ + if (qc->dev->horkage & + ATA_HORKAGE_DIAGNOSTIC) + qc->err_mask |= + AC_ERR_NODEV_HINT; + } else { /* HSM violation. Let EH handle this. * Phantom devices also trigger this * condition. Mark hint. diff --git a/drivers/block/Kconfig b/drivers/block/Kconfig index 61ad8d639ba..0344a8a8321 100644 --- a/drivers/block/Kconfig +++ b/drivers/block/Kconfig @@ -21,7 +21,8 @@ config BLK_DEV_FD ---help--- If you want to use the floppy disk drive(s) of your PC under Linux, say Y. Information about this driver, especially important for IBM - Thinkpad users, is contained in <file:Documentation/floppy.txt>. + Thinkpad users, is contained in + <file:Documentation/blockdev/floppy.txt>. That file also contains the location of the Floppy driver FAQ as well as location of the fdutils package used to configure additional parameters of the driver at run time. @@ -76,7 +77,7 @@ config PARIDE your computer's parallel port. Most of them are actually IDE devices using a parallel port IDE adapter. This option enables the PARIDE subsystem which contains drivers for many of these external drives. - Read <file:Documentation/paride.txt> for more information. + Read <file:Documentation/blockdev/paride.txt> for more information. If you have said Y to the "Parallel-port support" configuration option, you may share a single port between your printer and other @@ -114,9 +115,9 @@ config BLK_CPQ_DA help This is the driver for Compaq Smart Array controllers. Everyone using these boards should say Y here. See the file - <file:Documentation/cpqarray.txt> for the current list of boards - supported by this driver, and for further information on the use of - this driver. + <file:Documentation/blockdev/cpqarray.txt> for the current list of + boards supported by this driver, and for further information on the + use of this driver. config BLK_CPQ_CISS_DA tristate "Compaq Smart Array 5xxx support" @@ -124,7 +125,7 @@ config BLK_CPQ_CISS_DA help This is the driver for Compaq Smart Array 5xxx controllers. Everyone using these boards should say Y here. - See <file:Documentation/cciss.txt> for the current list of + See <file:Documentation/blockdev/cciss.txt> for the current list of boards supported by this driver, and for further information on the use of this driver. @@ -135,7 +136,7 @@ config CISS_SCSI_TAPE help When enabled (Y), this option allows SCSI tape drives and SCSI medium changers (tape robots) to be accessed via a Compaq 5xxx array - controller. (See <file:Documentation/cciss.txt> for more details.) + controller. (See <file:Documentation/blockdev/cciss.txt> for more details.) "SCSI support" and "SCSI tape support" must also be enabled for this option to work. @@ -149,8 +150,8 @@ config BLK_DEV_DAC960 help This driver adds support for the Mylex DAC960, AcceleRAID, and eXtremeRAID PCI RAID controllers. See the file - <file:Documentation/README.DAC960> for further information about - this driver. + <file:Documentation/blockdev/README.DAC960> for further information + about this driver. To compile this driver as a module, choose M here: the module will be called DAC960. @@ -278,9 +279,9 @@ config BLK_DEV_NBD userland (making server and client physically the same computer, communicating using the loopback network device). - Read <file:Documentation/nbd.txt> for more information, especially - about where to find the server code, which runs in user space and - does not need special kernel support. + Read <file:Documentation/blockdev/nbd.txt> for more information, + especially about where to find the server code, which runs in user + space and does not need special kernel support. Note that this has nothing to do with the network file systems NFS or Coda; you can say N here even if you intend to use NFS or Coda. @@ -321,8 +322,8 @@ config BLK_DEV_RAM store a copy of a minimal root file system off of a floppy into RAM during the initial install of Linux. - Note that the kernel command line option "ramdisk=XX" is now - obsolete. For details, read <file:Documentation/ramdisk.txt>. + Note that the kernel command line option "ramdisk=XX" is now obsolete. + For details, read <file:Documentation/blockdev/ramdisk.txt>. To compile this driver as a module, choose M here: the module will be called rd. diff --git a/drivers/block/floppy.c b/drivers/block/floppy.c index 14db747a636..cf29cc4e6ab 100644 --- a/drivers/block/floppy.c +++ b/drivers/block/floppy.c @@ -4124,7 +4124,7 @@ static int __init floppy_setup(char *str) printk("\n"); } else DPRINT("botched floppy option\n"); - DPRINT("Read Documentation/floppy.txt\n"); + DPRINT("Read Documentation/blockdev/floppy.txt\n"); return 0; } diff --git a/drivers/block/ub.c b/drivers/block/ub.c index fccac18d311..048d71d244d 100644 --- a/drivers/block/ub.c +++ b/drivers/block/ub.c @@ -1546,8 +1546,6 @@ static void ub_top_sense_done(struct ub_dev *sc, struct ub_scsi_cmd *scmd) /* * Reset management - * XXX Move usb_reset_device to khubd. Hogging kevent is not a good thing. - * XXX Make usb_sync_reset asynchronous. */ static void ub_reset_enter(struct ub_dev *sc, int try) @@ -1633,6 +1631,22 @@ static void ub_reset_task(struct work_struct *work) } /* + * XXX Reset brackets are too much hassle to implement, so just stub them + * in order to prevent forced unbinding (which deadlocks solid when our + * ->disconnect method waits for the reset to complete and this kills keventd). + * + * XXX Tell Alan to move usb_unlock_device inside of usb_reset_device, + * or else the post_reset is invoked, and restats I/O on a locked device. + */ +static int ub_pre_reset(struct usb_interface *iface) { + return 0; +} + +static int ub_post_reset(struct usb_interface *iface) { + return 0; +} + +/* * This is called from a process context. */ static void ub_revalidate(struct ub_dev *sc, struct ub_lun *lun) @@ -2446,6 +2460,8 @@ static struct usb_driver ub_driver = { .probe = ub_probe, .disconnect = ub_disconnect, .id_table = ub_usb_ids, + .pre_reset = ub_pre_reset, + .post_reset = ub_post_reset, }; static int __init ub_init(void) diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index 43b35d0369d..43d6ba83a19 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -124,7 +124,7 @@ config COMPUTONE which give you many serial ports. You would need something like this to connect more than two modems to your Linux box, for instance in order to become a dial-in server. If you have a card like that, say - Y here and read <file:Documentation/computone.txt>. + Y here and read <file:Documentation/serial/computone.txt>. To compile this driver as module, choose M here: the module will be called ip2. @@ -136,7 +136,7 @@ config ROCKETPORT This driver supports Comtrol RocketPort and RocketModem PCI boards. These boards provide 2, 4, 8, 16, or 32 high-speed serial ports or modems. For information about the RocketPort/RocketModem boards - and this driver read <file:Documentation/rocket.txt>. + and this driver read <file:Documentation/serial/rocket.txt>. To compile this driver as a module, choose M here: the module will be called rocket. @@ -154,7 +154,7 @@ config CYCLADES your Linux box, for instance in order to become a dial-in server. For information about the Cyclades-Z card, read - <file:Documentation/README.cycladesZ>. + <file:Documentation/serial/README.cycladesZ>. To compile this driver as a module, choose M here: the module will be called cyclades. @@ -183,7 +183,7 @@ config DIGIEPCA box, for instance in order to become a dial-in server. This driver supports the original PC (ISA) boards as well as PCI, and EISA. If you have a card like this, say Y here and read the file - <file:Documentation/digiepca.txt>. + <file:Documentation/serial/digiepca.txt>. To compile this driver as a module, choose M here: the module will be called epca. @@ -289,7 +289,7 @@ config RISCOM8 which gives you many serial ports. You would need something like this to connect more than two modems to your Linux box, for instance in order to become a dial-in server. If you have a card like that, - say Y here and read the file <file:Documentation/riscom8.txt>. + say Y here and read the file <file:Documentation/serial/riscom8.txt>. Also it's possible to say M here and compile this driver as kernel loadable module; the module will be called riscom8. @@ -304,8 +304,8 @@ config SPECIALIX your Linux box, for instance in order to become a dial-in server. If you have a card like that, say Y here and read the file - <file:Documentation/specialix.txt>. Also it's possible to say M here - and compile this driver as kernel loadable module which will be + <file:Documentation/serial/specialix.txt>. Also it's possible to say + M here and compile this driver as kernel loadable module which will be called specialix. config SX @@ -313,7 +313,7 @@ config SX depends on SERIAL_NONSTANDARD && (PCI || EISA || ISA) help This is a driver for the SX and SI multiport serial cards. - Please read the file <file:Documentation/sx.txt> for details. + Please read the file <file:Documentation/serial/sx.txt> for details. This driver can only be built as a module ( = code which can be inserted in and removed from the running kernel whenever you want). @@ -344,8 +344,8 @@ config STALDRV like this to connect more than two modems to your Linux box, for instance in order to become a dial-in server. If you say Y here, you will be asked for your specific card model in the next - questions. Make sure to read <file:Documentation/stallion.txt> in - this case. If you have never heard about all this, it's safe to + questions. Make sure to read <file:Documentation/serial/stallion.txt> + in this case. If you have never heard about all this, it's safe to say N. config STALLION @@ -354,7 +354,7 @@ config STALLION help If you have an EasyIO or EasyConnection 8/32 multiport Stallion card, then this is for you; say Y. Make sure to read - <file:Documentation/stallion.txt>. + <file:Documentation/serial/stallion.txt>. To compile this driver as a module, choose M here: the module will be called stallion. @@ -365,7 +365,7 @@ config ISTALLION help If you have an EasyConnection 8/64, ONboard, Brumby or Stallion serial multiport card, say Y here. Make sure to read - <file:Documentation/stallion.txt>. + <file:Documentation/serial/stallion.txt>. To compile this driver as a module, choose M here: the module will be called istallion. diff --git a/drivers/char/ipmi/ipmi_msghandler.c b/drivers/char/ipmi/ipmi_msghandler.c index 8a59aaa21be..7a88dfd4427 100644 --- a/drivers/char/ipmi/ipmi_msghandler.c +++ b/drivers/char/ipmi/ipmi_msghandler.c @@ -422,9 +422,11 @@ struct ipmi_smi { /** * The driver model view of the IPMI messaging driver. */ -static struct device_driver ipmidriver = { - .name = "ipmi", - .bus = &platform_bus_type +static struct platform_driver ipmidriver = { + .driver = { + .name = "ipmi", + .bus = &platform_bus_type + } }; static DEFINE_MUTEX(ipmidriver_mutex); @@ -2384,9 +2386,9 @@ static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum, * representing the interfaced BMC already */ if (bmc->guid_set) - old_bmc = ipmi_find_bmc_guid(&ipmidriver, bmc->guid); + old_bmc = ipmi_find_bmc_guid(&ipmidriver.driver, bmc->guid); else - old_bmc = ipmi_find_bmc_prod_dev_id(&ipmidriver, + old_bmc = ipmi_find_bmc_prod_dev_id(&ipmidriver.driver, bmc->id.product_id, bmc->id.device_id); @@ -2416,7 +2418,7 @@ static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum, snprintf(name, sizeof(name), "ipmi_bmc.%4.4x", bmc->id.product_id); - while (ipmi_find_bmc_prod_dev_id(&ipmidriver, + while (ipmi_find_bmc_prod_dev_id(&ipmidriver.driver, bmc->id.product_id, bmc->id.device_id)) { if (!warn_printed) { @@ -2446,7 +2448,7 @@ static int ipmi_bmc_register(ipmi_smi_t intf, int ifnum, " Unable to allocate platform device\n"); return -ENOMEM; } - bmc->dev->dev.driver = &ipmidriver; + bmc->dev->dev.driver = &ipmidriver.driver; dev_set_drvdata(&bmc->dev->dev, bmc); kref_init(&bmc->refcount); @@ -4247,7 +4249,7 @@ static int ipmi_init_msghandler(void) if (initialized) return 0; - rv = driver_register(&ipmidriver); + rv = driver_register(&ipmidriver.driver); if (rv) { printk(KERN_ERR PFX "Could not register IPMI driver\n"); return rv; @@ -4308,7 +4310,7 @@ static __exit void cleanup_ipmi(void) remove_proc_entry(proc_ipmi_root->name, NULL); #endif /* CONFIG_PROC_FS */ - driver_unregister(&ipmidriver); + driver_unregister(&ipmidriver.driver); initialized = 0; diff --git a/drivers/char/ipmi/ipmi_si_intf.c b/drivers/char/ipmi/ipmi_si_intf.c index 3123bf57ad9..3000135f2ea 100644 --- a/drivers/char/ipmi/ipmi_si_intf.c +++ b/drivers/char/ipmi/ipmi_si_intf.c @@ -114,9 +114,11 @@ static char *si_to_str[] = { "kcs", "smic", "bt" }; #define DEVICE_NAME "ipmi_si" -static struct device_driver ipmi_driver = { - .name = DEVICE_NAME, - .bus = &platform_bus_type +static struct platform_driver ipmi_driver = { + .driver = { + .name = DEVICE_NAME, + .bus = &platform_bus_type + } }; @@ -2868,7 +2870,7 @@ static int try_smi_init(struct smi_info *new_smi) goto out_err; } new_smi->dev = &new_smi->pdev->dev; - new_smi->dev->driver = &ipmi_driver; + new_smi->dev->driver = &ipmi_driver.driver; rv = platform_device_add(new_smi->pdev); if (rv) { @@ -2983,7 +2985,7 @@ static __devinit int init_ipmi_si(void) initialized = 1; /* Register the device drivers. */ - rv = driver_register(&ipmi_driver); + rv = driver_register(&ipmi_driver.driver); if (rv) { printk(KERN_ERR "init_ipmi_si: Unable to register driver: %d\n", @@ -3052,7 +3054,7 @@ static __devinit int init_ipmi_si(void) #ifdef CONFIG_PPC_OF of_unregister_platform_driver(&ipmi_of_platform_driver); #endif - driver_unregister(&ipmi_driver); + driver_unregister(&ipmi_driver.driver); printk(KERN_WARNING "ipmi_si: Unable to find any System Interface(s)\n"); return -ENODEV; @@ -3151,7 +3153,7 @@ static __exit void cleanup_ipmi_si(void) cleanup_one_si(e); mutex_unlock(&smi_infos_lock); - driver_unregister(&ipmi_driver); + driver_unregister(&ipmi_driver.driver); } module_exit(cleanup_ipmi_si); diff --git a/drivers/char/sonypi.c b/drivers/char/sonypi.c index 2457b07dabd..f4374437a03 100644 --- a/drivers/char/sonypi.c +++ b/drivers/char/sonypi.c @@ -523,7 +523,7 @@ static int acpi_driver_registered; static int sonypi_ec_write(u8 addr, u8 value) { -#ifdef CONFIG_ACPI_EC +#ifdef CONFIG_ACPI if (SONYPI_ACPI_ACTIVE) return ec_write(addr, value); #endif @@ -539,7 +539,7 @@ static int sonypi_ec_write(u8 addr, u8 value) static int sonypi_ec_read(u8 addr, u8 *value) { -#ifdef CONFIG_ACPI_EC +#ifdef CONFIG_ACPI if (SONYPI_ACPI_ACTIVE) return ec_read(addr, value); #endif diff --git a/drivers/char/specialix.c b/drivers/char/specialix.c index 242fd46fda2..a16b94f12eb 100644 --- a/drivers/char/specialix.c +++ b/drivers/char/specialix.c @@ -72,7 +72,7 @@ /* * There is a bunch of documentation about the card, jumpers, config * settings, restrictions, cables, device names and numbers in - * Documentation/specialix.txt + * Documentation/serial/specialix.txt */ #include <linux/module.h> diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c index dc003a3a787..5317e08221e 100644 --- a/drivers/dma/dmaengine.c +++ b/drivers/dma/dmaengine.c @@ -399,8 +399,8 @@ int dma_async_device_register(struct dma_device *device) chan->chan_id = chancnt++; chan->dev.class = &dma_devclass; chan->dev.parent = device->dev; - snprintf(chan->dev.bus_id, BUS_ID_SIZE, "dma%dchan%d", - device->dev_id, chan->chan_id); + dev_set_name(&chan->dev, "dma%dchan%d", + device->dev_id, chan->chan_id); rc = device_register(&chan->dev); if (rc) { diff --git a/drivers/dma/dmatest.c b/drivers/dma/dmatest.c index d1e381e35a9..ed9636bfb54 100644 --- a/drivers/dma/dmatest.c +++ b/drivers/dma/dmatest.c @@ -20,11 +20,11 @@ static unsigned int test_buf_size = 16384; module_param(test_buf_size, uint, S_IRUGO); MODULE_PARM_DESC(test_buf_size, "Size of the memcpy test buffer"); -static char test_channel[BUS_ID_SIZE]; +static char test_channel[20]; module_param_string(channel, test_channel, sizeof(test_channel), S_IRUGO); MODULE_PARM_DESC(channel, "Bus ID of the channel to test (default: any)"); -static char test_device[BUS_ID_SIZE]; +static char test_device[20]; module_param_string(device, test_device, sizeof(test_device), S_IRUGO); MODULE_PARM_DESC(device, "Bus ID of the DMA Engine to test (default: any)"); @@ -80,14 +80,14 @@ static bool dmatest_match_channel(struct dma_chan *chan) { if (test_channel[0] == '\0') return true; - return strcmp(chan->dev.bus_id, test_channel) == 0; + return strcmp(dev_name(&chan->dev), test_channel) == 0; } static bool dmatest_match_device(struct dma_device *device) { if (test_device[0] == '\0') return true; - return strcmp(device->dev->bus_id, test_device) == 0; + return strcmp(dev_name(device->dev), test_device) == 0; } static unsigned long dmatest_random(void) @@ -332,7 +332,7 @@ static enum dma_state_client dmatest_add_channel(struct dma_chan *chan) dtc = kmalloc(sizeof(struct dmatest_chan), GFP_KERNEL); if (!dtc) { - pr_warning("dmatest: No memory for %s\n", chan->dev.bus_id); + pr_warning("dmatest: No memory for %s\n", dev_name(&chan->dev)); return DMA_NAK; } @@ -343,16 +343,16 @@ static enum dma_state_client dmatest_add_channel(struct dma_chan *chan) thread = kzalloc(sizeof(struct dmatest_thread), GFP_KERNEL); if (!thread) { pr_warning("dmatest: No memory for %s-test%u\n", - chan->dev.bus_id, i); + dev_name(&chan->dev), i); break; } thread->chan = dtc->chan; smp_wmb(); thread->task = kthread_run(dmatest_func, thread, "%s-test%u", - chan->dev.bus_id, i); + dev_name(&chan->dev), i); if (IS_ERR(thread->task)) { pr_warning("dmatest: Failed to run thread %s-test%u\n", - chan->dev.bus_id, i); + dev_name(&chan->dev), i); kfree(thread); break; } @@ -362,7 +362,7 @@ static enum dma_state_client dmatest_add_channel(struct dma_chan *chan) list_add_tail(&thread->node, &dtc->threads); } - pr_info("dmatest: Started %u threads using %s\n", i, chan->dev.bus_id); + pr_info("dmatest: Started %u threads using %s\n", i, dev_name(&chan->dev)); list_add_tail(&dtc->node, &dmatest_channels); nr_channels++; @@ -379,7 +379,7 @@ static enum dma_state_client dmatest_remove_channel(struct dma_chan *chan) list_del(&dtc->node); dmatest_cleanup_channel(dtc); pr_debug("dmatest: lost channel %s\n", - chan->dev.bus_id); + dev_name(&chan->dev)); return DMA_ACK; } } @@ -418,7 +418,7 @@ dmatest_event(struct dma_client *client, struct dma_chan *chan, default: pr_info("dmatest: Unhandled event %u (%s)\n", - state, chan->dev.bus_id); + state, dev_name(&chan->dev)); break; } diff --git a/drivers/dma/iop-adma.c b/drivers/dma/iop-adma.c index 71fba82462c..c7a9306d951 100644 --- a/drivers/dma/iop-adma.c +++ b/drivers/dma/iop-adma.c @@ -411,6 +411,7 @@ iop_adma_tx_submit(struct dma_async_tx_descriptor *tx) int slot_cnt; int slots_per_op; dma_cookie_t cookie; + dma_addr_t next_dma; grp_start = sw_desc->group_head; slot_cnt = grp_start->slot_cnt; @@ -425,12 +426,12 @@ iop_adma_tx_submit(struct dma_async_tx_descriptor *tx) &old_chain_tail->chain_node); /* fix up the hardware chain */ - iop_desc_set_next_desc(old_chain_tail, grp_start->async_tx.phys); + next_dma = grp_start->async_tx.phys; + iop_desc_set_next_desc(old_chain_tail, next_dma); + BUG_ON(iop_desc_get_next_desc(old_chain_tail) != next_dma); /* flush */ - /* 1/ don't add pre-chained descriptors - * 2/ dummy read to flush next_desc write - */ - BUG_ON(iop_desc_get_next_desc(sw_desc)); + /* check for pre-chained descriptors */ + iop_paranoia(iop_desc_get_next_desc(sw_desc)); /* increment the pending count by the number of slots * memcpy operations have a 1:1 (slot:operation) relation diff --git a/drivers/edac/i5000_edac.c b/drivers/edac/i5000_edac.c index f0d9b415db5..d335086f4a2 100644 --- a/drivers/edac/i5000_edac.c +++ b/drivers/edac/i5000_edac.c @@ -1381,6 +1381,7 @@ static int i5000_probe1(struct pci_dev *pdev, int dev_idx) if (mci == NULL) return -ENOMEM; + kobject_get(&mci->edac_mci_kobj); debugf0("MC: " __FILE__ ": %s(): mci = %p\n", __func__, mci); mci->dev = &pdev->dev; /* record ptr to the generic device */ @@ -1453,6 +1454,7 @@ fail1: i5000_put_devices(mci); fail0: + kobject_put(&mci->edac_mci_kobj); edac_mc_free(mci); return -ENODEV; } @@ -1498,7 +1500,7 @@ static void __devexit i5000_remove_one(struct pci_dev *pdev) /* retrieve references to resources, and free those resources */ i5000_put_devices(mci); - + kobject_put(&mci->edac_mci_kobj); edac_mc_free(mci); } diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 7f2ee27fe76..48f49d93d24 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -65,6 +65,14 @@ config GPIO_SYSFS # put expanders in the right section, in alphabetical order +comment "Memory mapped GPIO expanders:" + +config GPIO_XILINX + bool "Xilinx GPIO support" + depends on PPC_OF + help + Say yes here to support the Xilinx FPGA GPIO device + comment "I2C GPIO expanders:" config GPIO_MAX732X diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index 6aafdeb9ad0..49ac64e515e 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -10,4 +10,5 @@ obj-$(CONFIG_GPIO_MCP23S08) += mcp23s08.o obj-$(CONFIG_GPIO_PCA953X) += pca953x.o obj-$(CONFIG_GPIO_PCF857X) += pcf857x.o obj-$(CONFIG_GPIO_TWL4030) += twl4030-gpio.o +obj-$(CONFIG_GPIO_XILINX) += xilinx_gpio.o obj-$(CONFIG_GPIO_BT8XX) += bt8xxgpio.o diff --git a/drivers/gpio/xilinx_gpio.c b/drivers/gpio/xilinx_gpio.c new file mode 100644 index 00000000000..3c1177abebd --- /dev/null +++ b/drivers/gpio/xilinx_gpio.c @@ -0,0 +1,235 @@ +/* + * Xilinx gpio driver + * + * Copyright 2008 Xilinx, 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. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/of_device.h> +#include <linux/of_platform.h> +#include <linux/of_gpio.h> +#include <linux/io.h> +#include <linux/gpio.h> + +/* Register Offset Definitions */ +#define XGPIO_DATA_OFFSET (0x0) /* Data register */ +#define XGPIO_TRI_OFFSET (0x4) /* I/O direction register */ + +struct xgpio_instance { + struct of_mm_gpio_chip mmchip; + u32 gpio_state; /* GPIO state shadow register */ + u32 gpio_dir; /* GPIO direction shadow register */ + spinlock_t gpio_lock; /* Lock used for synchronization */ +}; + +/** + * xgpio_get - Read the specified signal of the GPIO device. + * @gc: Pointer to gpio_chip device structure. + * @gpio: GPIO signal number. + * + * This function reads the specified signal of the GPIO device. It returns 0 if + * the signal clear, 1 if signal is set or negative value on error. + */ +static int xgpio_get(struct gpio_chip *gc, unsigned int gpio) +{ + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + + return (in_be32(mm_gc->regs + XGPIO_DATA_OFFSET) >> gpio) & 1; +} + +/** + * xgpio_set - Write the specified signal of the GPIO device. + * @gc: Pointer to gpio_chip device structure. + * @gpio: GPIO signal number. + * @val: Value to be written to specified signal. + * + * This function writes the specified value in to the specified signal of the + * GPIO device. + */ +static void xgpio_set(struct gpio_chip *gc, unsigned int gpio, int val) +{ + unsigned long flags; + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct xgpio_instance *chip = + container_of(mm_gc, struct xgpio_instance, mmchip); + + spin_lock_irqsave(&chip->gpio_lock, flags); + + /* Write to GPIO signal and set its direction to output */ + if (val) + chip->gpio_state |= 1 << gpio; + else + chip->gpio_state &= ~(1 << gpio); + out_be32(mm_gc->regs + XGPIO_DATA_OFFSET, chip->gpio_state); + + spin_unlock_irqrestore(&chip->gpio_lock, flags); +} + +/** + * xgpio_dir_in - Set the direction of the specified GPIO signal as input. + * @gc: Pointer to gpio_chip device structure. + * @gpio: GPIO signal number. + * + * This function sets the direction of specified GPIO signal as input. + * It returns 0 if direction of GPIO signals is set as input otherwise it + * returns negative error value. + */ +static int xgpio_dir_in(struct gpio_chip *gc, unsigned int gpio) +{ + unsigned long flags; + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct xgpio_instance *chip = + container_of(mm_gc, struct xgpio_instance, mmchip); + + spin_lock_irqsave(&chip->gpio_lock, flags); + + /* Set the GPIO bit in shadow register and set direction as input */ + chip->gpio_dir |= (1 << gpio); + out_be32(mm_gc->regs + XGPIO_TRI_OFFSET, chip->gpio_dir); + + spin_unlock_irqrestore(&chip->gpio_lock, flags); + + return 0; +} + +/** + * xgpio_dir_out - Set the direction of the specified GPIO signal as output. + * @gc: Pointer to gpio_chip device structure. + * @gpio: GPIO signal number. + * @val: Value to be written to specified signal. + * + * This function sets the direction of specified GPIO signal as output. If all + * GPIO signals of GPIO chip is configured as input then it returns + * error otherwise it returns 0. + */ +static int xgpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val) +{ + unsigned long flags; + struct of_mm_gpio_chip *mm_gc = to_of_mm_gpio_chip(gc); + struct xgpio_instance *chip = + container_of(mm_gc, struct xgpio_instance, mmchip); + + spin_lock_irqsave(&chip->gpio_lock, flags); + + /* Write state of GPIO signal */ + if (val) + chip->gpio_state |= 1 << gpio; + else + chip->gpio_state &= ~(1 << gpio); + out_be32(mm_gc->regs + XGPIO_DATA_OFFSET, chip->gpio_state); + + /* Clear the GPIO bit in shadow register and set direction as output */ + chip->gpio_dir &= (~(1 << gpio)); + out_be32(mm_gc->regs + XGPIO_TRI_OFFSET, chip->gpio_dir); + + spin_unlock_irqrestore(&chip->gpio_lock, flags); + + return 0; +} + +/** + * xgpio_save_regs - Set initial values of GPIO pins + * @mm_gc: pointer to memory mapped GPIO chip structure + */ +static void xgpio_save_regs(struct of_mm_gpio_chip *mm_gc) +{ + struct xgpio_instance *chip = + container_of(mm_gc, struct xgpio_instance, mmchip); + + out_be32(mm_gc->regs + XGPIO_DATA_OFFSET, chip->gpio_state); + out_be32(mm_gc->regs + XGPIO_TRI_OFFSET, chip->gpio_dir); +} + +/** + * xgpio_of_probe - Probe method for the GPIO device. + * @np: pointer to device tree node + * + * This function probes the GPIO device in the device tree. It initializes the + * driver data structure. It returns 0, if the driver is bound to the GPIO + * device, or a negative value if there is an error. + */ +static int __devinit xgpio_of_probe(struct device_node *np) +{ + struct xgpio_instance *chip; + struct of_gpio_chip *ofchip; + int status = 0; + const u32 *tree_info; + + chip = kzalloc(sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + ofchip = &chip->mmchip.of_gc; + + /* Update GPIO state shadow register with default value */ + tree_info = of_get_property(np, "xlnx,dout-default", NULL); + if (tree_info) + chip->gpio_state = *tree_info; + + /* Update GPIO direction shadow register with default value */ + chip->gpio_dir = 0xFFFFFFFF; /* By default, all pins are inputs */ + tree_info = of_get_property(np, "xlnx,tri-default", NULL); + if (tree_info) + chip->gpio_dir = *tree_info; + + /* Check device node and parent device node for device width */ + ofchip->gc.ngpio = 32; /* By default assume full GPIO controller */ + tree_info = of_get_property(np, "xlnx,gpio-width", NULL); + if (!tree_info) + tree_info = of_get_property(np->parent, + "xlnx,gpio-width", NULL); + if (tree_info) + ofchip->gc.ngpio = *tree_info; + + spin_lock_init(&chip->gpio_lock); + + ofchip->gpio_cells = 2; + ofchip->gc.direction_input = xgpio_dir_in; + ofchip->gc.direction_output = xgpio_dir_out; + ofchip->gc.get = xgpio_get; + ofchip->gc.set = xgpio_set; + + chip->mmchip.save_regs = xgpio_save_regs; + + /* Call the OF gpio helper to setup and register the GPIO device */ + status = of_mm_gpiochip_add(np, &chip->mmchip); + if (status) { + kfree(chip); + pr_err("%s: error in probe function with status %d\n", + np->full_name, status); + return status; + } + pr_info("XGpio: %s: registered\n", np->full_name); + return 0; +} + +static struct of_device_id xgpio_of_match[] __devinitdata = { + { .compatible = "xlnx,xps-gpio-1.00.a", }, + { /* end of list */ }, +}; + +static int __init xgpio_init(void) +{ + struct device_node *np; + + for_each_matching_node(np, xgpio_of_match) + xgpio_of_probe(np); + + return 0; +} + +/* Make sure we get initialized before anyone else tries to use us */ +subsys_initcall(xgpio_init); +/* No exit call at the moment as we cannot unregister of GPIO chips */ + +MODULE_AUTHOR("Xilinx, Inc."); +MODULE_DESCRIPTION("Xilinx GPIO driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 6de1e0ffd39..c709e821f04 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -159,6 +159,16 @@ config SENSORS_ADM9240 This driver can also be built as a module. If so, the module will be called adm9240. +config SENSORS_ADT7462 + tristate "Analog Devices ADT7462" + depends on I2C && EXPERIMENTAL + help + If you say yes here you get support for the Analog Devices + ADT7462 temperature monitoring chips. + + This driver can also be built as a module. If so, the module + will be called adt7462. + config SENSORS_ADT7470 tristate "Analog Devices ADT7470" depends on I2C && EXPERIMENTAL @@ -825,6 +835,25 @@ config SENSORS_HDAPS Say Y here if you have an applicable laptop and want to experience the awesome power of hdaps. +config SENSORS_LIS3LV02D + tristate "STMicroeletronics LIS3LV02Dx three-axis digital accelerometer" + depends on ACPI && INPUT + default n + help + This driver provides support for the LIS3LV02Dx accelerometer. In + particular, it can be found in a number of HP laptops, which have the + "Mobile Data Protection System 3D" or "3D DriveGuard" feature. On such + systems the driver should load automatically (via ACPI). The + accelerometer might also be found in other systems, connected via SPI + or I2C. The accelerometer data is readable via + /sys/devices/platform/lis3lv02d. + + This driver also provides an absolute input class device, allowing + the laptop to act as a pinball machine-esque joystick. + + This driver can also be built as a module. If so, the module + will be called lis3lv02d. + config SENSORS_APPLESMC tristate "Apple SMC (Motion sensor, light sensor, keyboard backlight)" depends on INPUT && X86 diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile index 042d5a78622..58fc5be5355 100644 --- a/drivers/hwmon/Makefile +++ b/drivers/hwmon/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_SENSORS_ADM1029) += adm1029.o obj-$(CONFIG_SENSORS_ADM1031) += adm1031.o obj-$(CONFIG_SENSORS_ADM9240) += adm9240.o obj-$(CONFIG_SENSORS_ADS7828) += ads7828.o +obj-$(CONFIG_SENSORS_ADT7462) += adt7462.o obj-$(CONFIG_SENSORS_ADT7470) += adt7470.o obj-$(CONFIG_SENSORS_ADT7473) += adt7473.o obj-$(CONFIG_SENSORS_APPLESMC) += applesmc.o @@ -48,6 +49,7 @@ obj-$(CONFIG_SENSORS_IBMAEM) += ibmaem.o obj-$(CONFIG_SENSORS_IBMPEX) += ibmpex.o obj-$(CONFIG_SENSORS_IT87) += it87.o obj-$(CONFIG_SENSORS_K8TEMP) += k8temp.o +obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o obj-$(CONFIG_SENSORS_LM63) += lm63.o obj-$(CONFIG_SENSORS_LM70) += lm70.o obj-$(CONFIG_SENSORS_LM75) += lm75.o diff --git a/drivers/hwmon/adt7462.c b/drivers/hwmon/adt7462.c new file mode 100644 index 00000000000..66107b4dc12 --- /dev/null +++ b/drivers/hwmon/adt7462.c @@ -0,0 +1,2002 @@ +/* + * A hwmon driver for the Analog Devices ADT7462 + * Copyright (C) 2008 IBM + * + * Author: Darrick J. Wong <djwong@us.ibm.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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/module.h> +#include <linux/jiffies.h> +#include <linux/i2c.h> +#include <linux/hwmon.h> +#include <linux/hwmon-sysfs.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include <linux/delay.h> +#include <linux/log2.h> + +/* Addresses to scan */ +static const unsigned short normal_i2c[] = { 0x58, 0x5C, I2C_CLIENT_END }; + +/* Insmod parameters */ +I2C_CLIENT_INSMOD_1(adt7462); + +/* ADT7462 registers */ +#define ADT7462_REG_DEVICE 0x3D +#define ADT7462_REG_VENDOR 0x3E +#define ADT7462_REG_REVISION 0x3F + +#define ADT7462_REG_MIN_TEMP_BASE_ADDR 0x44 +#define ADT7462_REG_MIN_TEMP_MAX_ADDR 0x47 +#define ADT7462_REG_MAX_TEMP_BASE_ADDR 0x48 +#define ADT7462_REG_MAX_TEMP_MAX_ADDR 0x4B +#define ADT7462_REG_TEMP_BASE_ADDR 0x88 +#define ADT7462_REG_TEMP_MAX_ADDR 0x8F + +#define ADT7462_REG_FAN_BASE_ADDR 0x98 +#define ADT7462_REG_FAN_MAX_ADDR 0x9F +#define ADT7462_REG_FAN2_BASE_ADDR 0xA2 +#define ADT7462_REG_FAN2_MAX_ADDR 0xA9 +#define ADT7462_REG_FAN_ENABLE 0x07 +#define ADT7462_REG_FAN_MIN_BASE_ADDR 0x78 +#define ADT7462_REG_FAN_MIN_MAX_ADDR 0x7F + +#define ADT7462_REG_CFG2 0x02 +#define ADT7462_FSPD_MASK 0x20 + +#define ADT7462_REG_PWM_BASE_ADDR 0xAA +#define ADT7462_REG_PWM_MAX_ADDR 0xAD +#define ADT7462_REG_PWM_MIN_BASE_ADDR 0x28 +#define ADT7462_REG_PWM_MIN_MAX_ADDR 0x2B +#define ADT7462_REG_PWM_MAX 0x2C +#define ADT7462_REG_PWM_TEMP_MIN_BASE_ADDR 0x5C +#define ADT7462_REG_PWM_TEMP_MIN_MAX_ADDR 0x5F +#define ADT7462_REG_PWM_TEMP_RANGE_BASE_ADDR 0x60 +#define ADT7462_REG_PWM_TEMP_RANGE_MAX_ADDR 0x63 +#define ADT7462_PWM_HYST_MASK 0x0F +#define ADT7462_PWM_RANGE_MASK 0xF0 +#define ADT7462_PWM_RANGE_SHIFT 4 +#define ADT7462_REG_PWM_CFG_BASE_ADDR 0x21 +#define ADT7462_REG_PWM_CFG_MAX_ADDR 0x24 +#define ADT7462_PWM_CHANNEL_MASK 0xE0 +#define ADT7462_PWM_CHANNEL_SHIFT 5 + +#define ADT7462_REG_PIN_CFG_BASE_ADDR 0x10 +#define ADT7462_REG_PIN_CFG_MAX_ADDR 0x13 +#define ADT7462_PIN7_INPUT 0x01 /* cfg0 */ +#define ADT7462_DIODE3_INPUT 0x20 +#define ADT7462_DIODE1_INPUT 0x40 +#define ADT7462_VID_INPUT 0x80 +#define ADT7462_PIN22_INPUT 0x04 /* cfg1 */ +#define ADT7462_PIN21_INPUT 0x08 +#define ADT7462_PIN19_INPUT 0x10 +#define ADT7462_PIN15_INPUT 0x20 +#define ADT7462_PIN13_INPUT 0x40 +#define ADT7462_PIN8_INPUT 0x80 +#define ADT7462_PIN23_MASK 0x03 +#define ADT7462_PIN23_SHIFT 0 +#define ADT7462_PIN26_MASK 0x0C /* cfg2 */ +#define ADT7462_PIN26_SHIFT 2 +#define ADT7462_PIN25_MASK 0x30 +#define ADT7462_PIN25_SHIFT 4 +#define ADT7462_PIN24_MASK 0xC0 +#define ADT7462_PIN24_SHIFT 6 +#define ADT7462_PIN26_VOLT_INPUT 0x08 +#define ADT7462_PIN25_VOLT_INPUT 0x20 +#define ADT7462_PIN28_SHIFT 6 /* cfg3 */ +#define ADT7462_PIN28_VOLT 0x5 + +#define ADT7462_REG_ALARM1 0xB8 +#define ADT7462_LT_ALARM 0x02 +#define ADT7462_R1T_ALARM 0x04 +#define ADT7462_R2T_ALARM 0x08 +#define ADT7462_R3T_ALARM 0x10 +#define ADT7462_REG_ALARM2 0xBB +#define ADT7462_V0_ALARM 0x01 +#define ADT7462_V1_ALARM 0x02 +#define ADT7462_V2_ALARM 0x04 +#define ADT7462_V3_ALARM 0x08 +#define ADT7462_V4_ALARM 0x10 +#define ADT7462_V5_ALARM 0x20 +#define ADT7462_V6_ALARM 0x40 +#define ADT7462_V7_ALARM 0x80 +#define ADT7462_REG_ALARM3 0xBC +#define ADT7462_V8_ALARM 0x08 +#define ADT7462_V9_ALARM 0x10 +#define ADT7462_V10_ALARM 0x20 +#define ADT7462_V11_ALARM 0x40 +#define ADT7462_V12_ALARM 0x80 +#define ADT7462_REG_ALARM4 0xBD +#define ADT7462_F0_ALARM 0x01 +#define ADT7462_F1_ALARM 0x02 +#define ADT7462_F2_ALARM 0x04 +#define ADT7462_F3_ALARM 0x08 +#define ADT7462_F4_ALARM 0x10 +#define ADT7462_F5_ALARM 0x20 +#define ADT7462_F6_ALARM 0x40 +#define ADT7462_F7_ALARM 0x80 +#define ADT7462_ALARM1 0x0000 +#define ADT7462_ALARM2 0x0100 +#define ADT7462_ALARM3 0x0200 +#define ADT7462_ALARM4 0x0300 +#define ADT7462_ALARM_REG_SHIFT 8 +#define ADT7462_ALARM_FLAG_MASK 0x0F + +#define ADT7462_TEMP_COUNT 4 +#define ADT7462_TEMP_REG(x) (ADT7462_REG_TEMP_BASE_ADDR + (x * 2)) +#define ADT7462_TEMP_MIN_REG(x) (ADT7462_REG_MIN_TEMP_BASE_ADDR + (x)) +#define ADT7462_TEMP_MAX_REG(x) (ADT7462_REG_MAX_TEMP_BASE_ADDR + (x)) +#define TEMP_FRAC_OFFSET 6 + +#define ADT7462_FAN_COUNT 8 +#define ADT7462_REG_FAN_MIN(x) (ADT7462_REG_FAN_MIN_BASE_ADDR + (x)) + +#define ADT7462_PWM_COUNT 4 +#define ADT7462_REG_PWM(x) (ADT7462_REG_PWM_BASE_ADDR + (x)) +#define ADT7462_REG_PWM_MIN(x) (ADT7462_REG_PWM_MIN_BASE_ADDR + (x)) +#define ADT7462_REG_PWM_TMIN(x) \ + (ADT7462_REG_PWM_TEMP_MIN_BASE_ADDR + (x)) +#define ADT7462_REG_PWM_TRANGE(x) \ + (ADT7462_REG_PWM_TEMP_RANGE_BASE_ADDR + (x)) + +#define ADT7462_PIN_CFG_REG_COUNT 4 +#define ADT7462_REG_PIN_CFG(x) (ADT7462_REG_PIN_CFG_BASE_ADDR + (x)) +#define ADT7462_REG_PWM_CFG(x) (ADT7462_REG_PWM_CFG_BASE_ADDR + (x)) + +#define ADT7462_ALARM_REG_COUNT 4 + +/* + * The chip can measure 13 different voltage sources: + * + * 1. +12V1 (pin 7) + * 2. Vccp1/+2.5V/+1.8V/+1.5V (pin 23) + * 3. +12V3 (pin 22) + * 4. +5V (pin 21) + * 5. +1.25V/+0.9V (pin 19) + * 6. +2.5V/+1.8V (pin 15) + * 7. +3.3v (pin 13) + * 8. +12V2 (pin 8) + * 9. Vbatt/FSB_Vtt (pin 26) + * A. +3.3V/+1.2V1 (pin 25) + * B. Vccp2/+2.5V/+1.8V/+1.5V (pin 24) + * C. +1.5V ICH (only if BOTH pin 28/29 are set to +1.5V) + * D. +1.5V 3GPIO (only if BOTH pin 28/29 are set to +1.5V) + * + * Each of these 13 has a factor to convert raw to voltage. Even better, + * the pins can be connected to other sensors (tach/gpio/hot/etc), which + * makes the bookkeeping tricky. + * + * Some, but not all, of these voltages have low/high limits. + */ +#define ADT7462_VOLT_COUNT 12 + +#define ADT7462_VENDOR 0x41 +#define ADT7462_DEVICE 0x62 +/* datasheet only mentions a revision 4 */ +#define ADT7462_REVISION 0x04 + +/* How often do we reread sensors values? (In jiffies) */ +#define SENSOR_REFRESH_INTERVAL (2 * HZ) + +/* How often do we reread sensor limit values? (In jiffies) */ +#define LIMIT_REFRESH_INTERVAL (60 * HZ) + +/* datasheet says to divide this number by the fan reading to get fan rpm */ +#define FAN_PERIOD_TO_RPM(x) ((90000 * 60) / (x)) +#define FAN_RPM_TO_PERIOD FAN_PERIOD_TO_RPM +#define FAN_PERIOD_INVALID 65535 +#define FAN_DATA_VALID(x) ((x) && (x) != FAN_PERIOD_INVALID) + +#define MASK_AND_SHIFT(value, prefix) \ + (((value) & prefix##_MASK) >> prefix##_SHIFT) + +#define ROUND_DIV(x, divisor) (((x) + ((divisor) / 2)) / (divisor)) + +struct adt7462_data { + struct device *hwmon_dev; + struct attribute_group attrs; + struct mutex lock; + char sensors_valid; + char limits_valid; + unsigned long sensors_last_updated; /* In jiffies */ + unsigned long limits_last_updated; /* In jiffies */ + + u8 temp[ADT7462_TEMP_COUNT]; + /* bits 6-7 are quarter pieces of temp */ + u8 temp_frac[ADT7462_TEMP_COUNT]; + u8 temp_min[ADT7462_TEMP_COUNT]; + u8 temp_max[ADT7462_TEMP_COUNT]; + u16 fan[ADT7462_FAN_COUNT]; + u8 fan_enabled; + u8 fan_min[ADT7462_FAN_COUNT]; + u8 cfg2; + u8 pwm[ADT7462_PWM_COUNT]; + u8 pin_cfg[ADT7462_PIN_CFG_REG_COUNT]; + u8 voltages[ADT7462_VOLT_COUNT]; + u8 volt_max[ADT7462_VOLT_COUNT]; + u8 volt_min[ADT7462_VOLT_COUNT]; + u8 pwm_min[ADT7462_PWM_COUNT]; + u8 pwm_tmin[ADT7462_PWM_COUNT]; + u8 pwm_trange[ADT7462_PWM_COUNT]; + u8 pwm_max; /* only one per chip */ + u8 pwm_cfg[ADT7462_PWM_COUNT]; + u8 alarms[ADT7462_ALARM_REG_COUNT]; +}; + +static int adt7462_probe(struct i2c_client *client, + const struct i2c_device_id *id); +static int adt7462_detect(struct i2c_client *client, int kind, + struct i2c_board_info *info); +static int adt7462_remove(struct i2c_client *client); + +static const struct i2c_device_id adt7462_id[] = { + { "adt7462", adt7462 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, adt7462_id); + +static struct i2c_driver adt7462_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "adt7462", + }, + .probe = adt7462_probe, + .remove = adt7462_remove, + .id_table = adt7462_id, + .detect = adt7462_detect, + .address_data = &addr_data, +}; + +/* + * 16-bit registers on the ADT7462 are low-byte first. The data sheet says + * that the low byte must be read before the high byte. + */ +static inline int adt7462_read_word_data(struct i2c_client *client, u8 reg) +{ + u16 foo; + foo = i2c_smbus_read_byte_data(client, reg); + foo |= ((u16)i2c_smbus_read_byte_data(client, reg + 1) << 8); + return foo; +} + +/* For some reason these registers are not contiguous. */ +static int ADT7462_REG_FAN(int fan) +{ + if (fan < 4) + return ADT7462_REG_FAN_BASE_ADDR + (2 * fan); + return ADT7462_REG_FAN2_BASE_ADDR + (2 * (fan - 4)); +} + +/* Voltage registers are scattered everywhere */ +static int ADT7462_REG_VOLT_MAX(struct adt7462_data *data, int which) +{ + switch (which) { + case 0: + if (!(data->pin_cfg[0] & ADT7462_PIN7_INPUT)) + return 0x7C; + break; + case 1: + return 0x69; + case 2: + if (!(data->pin_cfg[1] & ADT7462_PIN22_INPUT)) + return 0x7F; + break; + case 3: + if (!(data->pin_cfg[1] & ADT7462_PIN21_INPUT)) + return 0x7E; + break; + case 4: + if (!(data->pin_cfg[0] & ADT7462_DIODE3_INPUT)) + return 0x4B; + break; + case 5: + if (!(data->pin_cfg[0] & ADT7462_DIODE1_INPUT)) + return 0x49; + break; + case 6: + if (!(data->pin_cfg[1] & ADT7462_PIN13_INPUT)) + return 0x68; + break; + case 7: + if (!(data->pin_cfg[1] & ADT7462_PIN8_INPUT)) + return 0x7D; + break; + case 8: + if (!(data->pin_cfg[2] & ADT7462_PIN26_VOLT_INPUT)) + return 0x6C; + break; + case 9: + if (!(data->pin_cfg[2] & ADT7462_PIN25_VOLT_INPUT)) + return 0x6B; + break; + case 10: + return 0x6A; + case 11: + if (data->pin_cfg[3] >> ADT7462_PIN28_SHIFT == + ADT7462_PIN28_VOLT && + !(data->pin_cfg[0] & ADT7462_VID_INPUT)) + return 0x50; + break; + case 12: + if (data->pin_cfg[3] >> ADT7462_PIN28_SHIFT == + ADT7462_PIN28_VOLT && + !(data->pin_cfg[0] & ADT7462_VID_INPUT)) + return 0x4C; + break; + } + return -ENODEV; +} + +static int ADT7462_REG_VOLT_MIN(struct adt7462_data *data, int which) +{ + switch (which) { + case 0: + if (!(data->pin_cfg[0] & ADT7462_PIN7_INPUT)) + return 0x6D; + break; + case 1: + return 0x72; + case 2: + if (!(data->pin_cfg[1] & ADT7462_PIN22_INPUT)) + return 0x6F; + break; + case 3: + if (!(data->pin_cfg[1] & ADT7462_PIN21_INPUT)) + return 0x71; + break; + case 4: + if (!(data->pin_cfg[0] & ADT7462_DIODE3_INPUT)) + return 0x47; + break; + case 5: + if (!(data->pin_cfg[0] & ADT7462_DIODE1_INPUT)) + return 0x45; + break; + case 6: + if (!(data->pin_cfg[1] & ADT7462_PIN13_INPUT)) + return 0x70; + break; + case 7: + if (!(data->pin_cfg[1] & ADT7462_PIN8_INPUT)) + return 0x6E; + break; + case 8: + if (!(data->pin_cfg[2] & ADT7462_PIN26_VOLT_INPUT)) + return 0x75; + break; + case 9: + if (!(data->pin_cfg[2] & ADT7462_PIN25_VOLT_INPUT)) + return 0x74; + break; + case 10: + return 0x73; + case 11: + if (data->pin_cfg[3] >> ADT7462_PIN28_SHIFT == + ADT7462_PIN28_VOLT && + !(data->pin_cfg[0] & ADT7462_VID_INPUT)) + return 0x76; + break; + case 12: + if (data->pin_cfg[3] >> ADT7462_PIN28_SHIFT == + ADT7462_PIN28_VOLT && + !(data->pin_cfg[0] & ADT7462_VID_INPUT)) + return 0x77; + break; + } + return -ENODEV; +} + +static int ADT7462_REG_VOLT(struct adt7462_data *data, int which) +{ + switch (which) { + case 0: + if (!(data->pin_cfg[0] & ADT7462_PIN7_INPUT)) + return 0xA3; + break; + case 1: + return 0x90; + case 2: + if (!(data->pin_cfg[1] & ADT7462_PIN22_INPUT)) + return 0xA9; + break; + case 3: + if (!(data->pin_cfg[1] & ADT7462_PIN21_INPUT)) + return 0xA7; + break; + case 4: + if (!(data->pin_cfg[0] & ADT7462_DIODE3_INPUT)) + return 0x8F; + break; + case 5: + if (!(data->pin_cfg[0] & ADT7462_DIODE1_INPUT)) + return 0x8B; + break; + case 6: + if (!(data->pin_cfg[1] & ADT7462_PIN13_INPUT)) + return 0x96; + break; + case 7: + if (!(data->pin_cfg[1] & ADT7462_PIN8_INPUT)) + return 0xA5; + break; + case 8: + if (!(data->pin_cfg[2] & ADT7462_PIN26_VOLT_INPUT)) + return 0x93; + break; + case 9: + if (!(data->pin_cfg[2] & ADT7462_PIN25_VOLT_INPUT)) + return 0x92; + break; + case 10: + return 0x91; + case 11: + if (data->pin_cfg[3] >> ADT7462_PIN28_SHIFT == + ADT7462_PIN28_VOLT && + !(data->pin_cfg[0] & ADT7462_VID_INPUT)) + return 0x94; + break; + case 12: + if (data->pin_cfg[3] >> ADT7462_PIN28_SHIFT == + ADT7462_PIN28_VOLT && + !(data->pin_cfg[0] & ADT7462_VID_INPUT)) + return 0x95; + break; + } + return -ENODEV; +} + +/* Provide labels for sysfs */ +static const char *voltage_label(struct adt7462_data *data, int which) +{ + switch (which) { + case 0: + if (!(data->pin_cfg[0] & ADT7462_PIN7_INPUT)) + return "+12V1"; + break; + case 1: + switch (MASK_AND_SHIFT(data->pin_cfg[1], ADT7462_PIN23)) { + case 0: + return "Vccp1"; + case 1: + return "+2.5V"; + case 2: + return "+1.8V"; + case 3: + return "+1.5V"; + } + case 2: + if (!(data->pin_cfg[1] & ADT7462_PIN22_INPUT)) + return "+12V3"; + break; + case 3: + if (!(data->pin_cfg[1] & ADT7462_PIN21_INPUT)) + return "+5V"; + break; + case 4: + if (!(data->pin_cfg[0] & ADT7462_DIODE3_INPUT)) { + if (data->pin_cfg[1] & ADT7462_PIN19_INPUT) + return "+0.9V"; + return "+1.25V"; + } + break; + case 5: + if (!(data->pin_cfg[0] & ADT7462_DIODE1_INPUT)) { + if (data->pin_cfg[1] & ADT7462_PIN19_INPUT) + return "+1.8V"; + return "+2.5V"; + } + break; + case 6: + if (!(data->pin_cfg[1] & ADT7462_PIN13_INPUT)) + return "+3.3V"; + break; + case 7: + if (!(data->pin_cfg[1] & ADT7462_PIN8_INPUT)) + return "+12V2"; + break; + case 8: + switch (MASK_AND_SHIFT(data->pin_cfg[2], ADT7462_PIN26)) { + case 0: + return "Vbatt"; + case 1: + return "FSB_Vtt"; + } + break; + case 9: + switch (MASK_AND_SHIFT(data->pin_cfg[2], ADT7462_PIN25)) { + case 0: + return "+3.3V"; + case 1: + return "+1.2V1"; + } + break; + case 10: + switch (MASK_AND_SHIFT(data->pin_cfg[2], ADT7462_PIN24)) { + case 0: + return "Vccp2"; + case 1: + return "+2.5V"; + case 2: + return "+1.8V"; + case 3: + return "+1.5"; + } + case 11: + if (data->pin_cfg[3] >> ADT7462_PIN28_SHIFT == + ADT7462_PIN28_VOLT && + !(data->pin_cfg[0] & ADT7462_VID_INPUT)) + return "+1.5V ICH"; + break; + case 12: + if (data->pin_cfg[3] >> ADT7462_PIN28_SHIFT == + ADT7462_PIN28_VOLT && + !(data->pin_cfg[0] & ADT7462_VID_INPUT)) + return "+1.5V 3GPIO"; + break; + } + return "N/A"; +} + +/* Multipliers are actually in uV, not mV. */ +static int voltage_multiplier(struct adt7462_data *data, int which) +{ + switch (which) { + case 0: + if (!(data->pin_cfg[0] & ADT7462_PIN7_INPUT)) + return 62500; + break; + case 1: + switch (MASK_AND_SHIFT(data->pin_cfg[1], ADT7462_PIN23)) { + case 0: + if (data->pin_cfg[0] & ADT7462_VID_INPUT) + return 12500; + return 6250; + case 1: + return 13000; + case 2: + return 9400; + case 3: + return 7800; + } + case 2: + if (!(data->pin_cfg[1] & ADT7462_PIN22_INPUT)) + return 62500; + break; + case 3: + if (!(data->pin_cfg[1] & ADT7462_PIN21_INPUT)) + return 26000; + break; + case 4: + if (!(data->pin_cfg[0] & ADT7462_DIODE3_INPUT)) { + if (data->pin_cfg[1] & ADT7462_PIN19_INPUT) + return 4690; + return 6500; + } + break; + case 5: + if (!(data->pin_cfg[0] & ADT7462_DIODE1_INPUT)) { + if (data->pin_cfg[1] & ADT7462_PIN15_INPUT) + return 9400; + return 13000; + } + break; + case 6: + if (!(data->pin_cfg[1] & ADT7462_PIN13_INPUT)) + return 17200; + break; + case 7: + if (!(data->pin_cfg[1] & ADT7462_PIN8_INPUT)) + return 62500; + break; + case 8: + switch (MASK_AND_SHIFT(data->pin_cfg[2], ADT7462_PIN26)) { + case 0: + return 15600; + case 1: + return 6250; + } + break; + case 9: + switch (MASK_AND_SHIFT(data->pin_cfg[2], ADT7462_PIN25)) { + case 0: + return 17200; + case 1: + return 6250; + } + break; + case 10: + switch (MASK_AND_SHIFT(data->pin_cfg[2], ADT7462_PIN24)) { + case 0: + return 6250; + case 1: + return 13000; + case 2: + return 9400; + case 3: + return 7800; + } + case 11: + case 12: + if (data->pin_cfg[3] >> ADT7462_PIN28_SHIFT == + ADT7462_PIN28_VOLT && + !(data->pin_cfg[0] & ADT7462_VID_INPUT)) + return 7800; + } + return 0; +} + +static int temp_enabled(struct adt7462_data *data, int which) +{ + switch (which) { + case 0: + case 2: + return 1; + case 1: + if (data->pin_cfg[0] & ADT7462_DIODE1_INPUT) + return 1; + break; + case 3: + if (data->pin_cfg[0] & ADT7462_DIODE3_INPUT) + return 1; + break; + } + return 0; +} + +static const char *temp_label(struct adt7462_data *data, int which) +{ + switch (which) { + case 0: + return "local"; + case 1: + if (data->pin_cfg[0] & ADT7462_DIODE1_INPUT) + return "remote1"; + break; + case 2: + return "remote2"; + case 3: + if (data->pin_cfg[0] & ADT7462_DIODE3_INPUT) + return "remote3"; + break; + } + return "N/A"; +} + +/* Map Trange register values to mC */ +#define NUM_TRANGE_VALUES 16 +static const int trange_values[NUM_TRANGE_VALUES] = { + 2000, + 2500, + 3300, + 4000, + 5000, + 6700, + 8000, + 10000, + 13300, + 16000, + 20000, + 26700, + 32000, + 40000, + 53300, + 80000 +}; + +static int find_trange_value(int trange) +{ + int i; + + for (i = 0; i < NUM_TRANGE_VALUES; i++) + if (trange_values[i] == trange) + return i; + + return -ENODEV; +} + +static struct adt7462_data *adt7462_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adt7462_data *data = i2c_get_clientdata(client); + unsigned long local_jiffies = jiffies; + int i; + + mutex_lock(&data->lock); + if (time_before(local_jiffies, data->sensors_last_updated + + SENSOR_REFRESH_INTERVAL) + && data->sensors_valid) + goto no_sensor_update; + + for (i = 0; i < ADT7462_TEMP_COUNT; i++) { + /* + * Reading the fractional register locks the integral + * register until both have been read. + */ + data->temp_frac[i] = i2c_smbus_read_byte_data(client, + ADT7462_TEMP_REG(i)); + data->temp[i] = i2c_smbus_read_byte_data(client, + ADT7462_TEMP_REG(i) + 1); + } + + for (i = 0; i < ADT7462_FAN_COUNT; i++) + data->fan[i] = adt7462_read_word_data(client, + ADT7462_REG_FAN(i)); + + data->fan_enabled = i2c_smbus_read_byte_data(client, + ADT7462_REG_FAN_ENABLE); + + for (i = 0; i < ADT7462_PWM_COUNT; i++) + data->pwm[i] = i2c_smbus_read_byte_data(client, + ADT7462_REG_PWM(i)); + + for (i = 0; i < ADT7462_PIN_CFG_REG_COUNT; i++) + data->pin_cfg[i] = i2c_smbus_read_byte_data(client, + ADT7462_REG_PIN_CFG(i)); + + for (i = 0; i < ADT7462_VOLT_COUNT; i++) { + int reg = ADT7462_REG_VOLT(data, i); + if (!reg) + data->voltages[i] = 0; + else + data->voltages[i] = i2c_smbus_read_byte_data(client, + reg); + } + + data->alarms[0] = i2c_smbus_read_byte_data(client, ADT7462_REG_ALARM1); + data->alarms[1] = i2c_smbus_read_byte_data(client, ADT7462_REG_ALARM2); + data->alarms[2] = i2c_smbus_read_byte_data(client, ADT7462_REG_ALARM3); + data->alarms[3] = i2c_smbus_read_byte_data(client, ADT7462_REG_ALARM4); + + data->sensors_last_updated = local_jiffies; + data->sensors_valid = 1; + +no_sensor_update: + if (time_before(local_jiffies, data->limits_last_updated + + LIMIT_REFRESH_INTERVAL) + && data->limits_valid) + goto out; + + for (i = 0; i < ADT7462_TEMP_COUNT; i++) { + data->temp_min[i] = i2c_smbus_read_byte_data(client, + ADT7462_TEMP_MIN_REG(i)); + data->temp_max[i] = i2c_smbus_read_byte_data(client, + ADT7462_TEMP_MAX_REG(i)); + } + + for (i = 0; i < ADT7462_FAN_COUNT; i++) + data->fan_min[i] = i2c_smbus_read_byte_data(client, + ADT7462_REG_FAN_MIN(i)); + + for (i = 0; i < ADT7462_VOLT_COUNT; i++) { + int reg = ADT7462_REG_VOLT_MAX(data, i); + data->volt_max[i] = + (reg ? i2c_smbus_read_byte_data(client, reg) : 0); + + reg = ADT7462_REG_VOLT_MIN(data, i); + data->volt_min[i] = + (reg ? i2c_smbus_read_byte_data(client, reg) : 0); + } + + for (i = 0; i < ADT7462_PWM_COUNT; i++) { + data->pwm_min[i] = i2c_smbus_read_byte_data(client, + ADT7462_REG_PWM_MIN(i)); + data->pwm_tmin[i] = i2c_smbus_read_byte_data(client, + ADT7462_REG_PWM_TMIN(i)); + data->pwm_trange[i] = i2c_smbus_read_byte_data(client, + ADT7462_REG_PWM_TRANGE(i)); + data->pwm_cfg[i] = i2c_smbus_read_byte_data(client, + ADT7462_REG_PWM_CFG(i)); + } + + data->pwm_max = i2c_smbus_read_byte_data(client, ADT7462_REG_PWM_MAX); + + data->cfg2 = i2c_smbus_read_byte_data(client, ADT7462_REG_CFG2); + + data->limits_last_updated = local_jiffies; + data->limits_valid = 1; + +out: + mutex_unlock(&data->lock); + return data; +} + +static ssize_t show_temp_min(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = adt7462_update_device(dev); + + if (!temp_enabled(data, attr->index)) + return sprintf(buf, "0\n"); + + return sprintf(buf, "%d\n", 1000 * (data->temp_min[attr->index] - 64)); +} + +static ssize_t set_temp_min(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7462_data *data = i2c_get_clientdata(client); + long temp; + + if (strict_strtol(buf, 10, &temp) || !temp_enabled(data, attr->index)) + return -EINVAL; + + temp = ROUND_DIV(temp, 1000) + 64; + temp = SENSORS_LIMIT(temp, 0, 255); + + mutex_lock(&data->lock); + data->temp_min[attr->index] = temp; + i2c_smbus_write_byte_data(client, ADT7462_TEMP_MIN_REG(attr->index), + temp); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_temp_max(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = adt7462_update_device(dev); + + if (!temp_enabled(data, attr->index)) + return sprintf(buf, "0\n"); + + return sprintf(buf, "%d\n", 1000 * (data->temp_max[attr->index] - 64)); +} + +static ssize_t set_temp_max(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7462_data *data = i2c_get_clientdata(client); + long temp; + + if (strict_strtol(buf, 10, &temp) || !temp_enabled(data, attr->index)) + return -EINVAL; + + temp = ROUND_DIV(temp, 1000) + 64; + temp = SENSORS_LIMIT(temp, 0, 255); + + mutex_lock(&data->lock); + data->temp_max[attr->index] = temp; + i2c_smbus_write_byte_data(client, ADT7462_TEMP_MAX_REG(attr->index), + temp); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_temp(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = adt7462_update_device(dev); + u8 frac = data->temp_frac[attr->index] >> TEMP_FRAC_OFFSET; + + if (!temp_enabled(data, attr->index)) + return sprintf(buf, "0\n"); + + return sprintf(buf, "%d\n", 1000 * (data->temp[attr->index] - 64) + + 250 * frac); +} + +static ssize_t show_temp_label(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = adt7462_update_device(dev); + + return sprintf(buf, "%s\n", temp_label(data, attr->index)); +} + +static ssize_t show_volt_max(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = adt7462_update_device(dev); + int x = voltage_multiplier(data, attr->index); + + x *= data->volt_max[attr->index]; + x /= 1000; /* convert from uV to mV */ + + return sprintf(buf, "%d\n", x); +} + +static ssize_t set_volt_max(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7462_data *data = i2c_get_clientdata(client); + int x = voltage_multiplier(data, attr->index); + long temp; + + if (strict_strtol(buf, 10, &temp) || !x) + return -EINVAL; + + temp *= 1000; /* convert mV to uV */ + temp = ROUND_DIV(temp, x); + temp = SENSORS_LIMIT(temp, 0, 255); + + mutex_lock(&data->lock); + data->volt_max[attr->index] = temp; + i2c_smbus_write_byte_data(client, + ADT7462_REG_VOLT_MAX(data, attr->index), + temp); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_volt_min(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = adt7462_update_device(dev); + int x = voltage_multiplier(data, attr->index); + + x *= data->volt_min[attr->index]; + x /= 1000; /* convert from uV to mV */ + + return sprintf(buf, "%d\n", x); +} + +static ssize_t set_volt_min(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7462_data *data = i2c_get_clientdata(client); + int x = voltage_multiplier(data, attr->index); + long temp; + + if (strict_strtol(buf, 10, &temp) || !x) + return -EINVAL; + + temp *= 1000; /* convert mV to uV */ + temp = ROUND_DIV(temp, x); + temp = SENSORS_LIMIT(temp, 0, 255); + + mutex_lock(&data->lock); + data->volt_min[attr->index] = temp; + i2c_smbus_write_byte_data(client, + ADT7462_REG_VOLT_MIN(data, attr->index), + temp); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_voltage(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = adt7462_update_device(dev); + int x = voltage_multiplier(data, attr->index); + + x *= data->voltages[attr->index]; + x /= 1000; /* convert from uV to mV */ + + return sprintf(buf, "%d\n", x); +} + +static ssize_t show_voltage_label(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = adt7462_update_device(dev); + + return sprintf(buf, "%s\n", voltage_label(data, attr->index)); +} + +static ssize_t show_alarm(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = adt7462_update_device(dev); + int reg = attr->index >> ADT7462_ALARM_REG_SHIFT; + int mask = attr->index & ADT7462_ALARM_FLAG_MASK; + + if (data->alarms[reg] & mask) + return sprintf(buf, "1\n"); + else + return sprintf(buf, "0\n"); +} + +static int fan_enabled(struct adt7462_data *data, int fan) +{ + return data->fan_enabled & (1 << fan); +} + +static ssize_t show_fan_min(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = adt7462_update_device(dev); + u16 temp; + + /* Only the MSB of the min fan period is stored... */ + temp = data->fan_min[attr->index]; + temp <<= 8; + + if (!fan_enabled(data, attr->index) || + !FAN_DATA_VALID(temp)) + return sprintf(buf, "0\n"); + + return sprintf(buf, "%d\n", FAN_PERIOD_TO_RPM(temp)); +} + +static ssize_t set_fan_min(struct device *dev, + struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7462_data *data = i2c_get_clientdata(client); + long temp; + + if (strict_strtol(buf, 10, &temp) || !temp || + !fan_enabled(data, attr->index)) + return -EINVAL; + + temp = FAN_RPM_TO_PERIOD(temp); + temp >>= 8; + temp = SENSORS_LIMIT(temp, 1, 255); + + mutex_lock(&data->lock); + data->fan_min[attr->index] = temp; + i2c_smbus_write_byte_data(client, ADT7462_REG_FAN_MIN(attr->index), + temp); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_fan(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = adt7462_update_device(dev); + + if (!fan_enabled(data, attr->index) || + !FAN_DATA_VALID(data->fan[attr->index])) + return sprintf(buf, "0\n"); + + return sprintf(buf, "%d\n", + FAN_PERIOD_TO_RPM(data->fan[attr->index])); +} + +static ssize_t show_force_pwm_max(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct adt7462_data *data = adt7462_update_device(dev); + return sprintf(buf, "%d\n", (data->cfg2 & ADT7462_FSPD_MASK ? 1 : 0)); +} + +static ssize_t set_force_pwm_max(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adt7462_data *data = i2c_get_clientdata(client); + long temp; + u8 reg; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + mutex_lock(&data->lock); + reg = i2c_smbus_read_byte_data(client, ADT7462_REG_CFG2); + if (temp) + reg |= ADT7462_FSPD_MASK; + else + reg &= ~ADT7462_FSPD_MASK; + data->cfg2 = reg; + i2c_smbus_write_byte_data(client, ADT7462_REG_CFG2, reg); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_pwm(struct device *dev, struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = adt7462_update_device(dev); + return sprintf(buf, "%d\n", data->pwm[attr->index]); +} + +static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr, + const char *buf, size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7462_data *data = i2c_get_clientdata(client); + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = SENSORS_LIMIT(temp, 0, 255); + + mutex_lock(&data->lock); + data->pwm[attr->index] = temp; + i2c_smbus_write_byte_data(client, ADT7462_REG_PWM(attr->index), temp); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_pwm_max(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct adt7462_data *data = adt7462_update_device(dev); + return sprintf(buf, "%d\n", data->pwm_max); +} + +static ssize_t set_pwm_max(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct i2c_client *client = to_i2c_client(dev); + struct adt7462_data *data = i2c_get_clientdata(client); + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = SENSORS_LIMIT(temp, 0, 255); + + mutex_lock(&data->lock); + data->pwm_max = temp; + i2c_smbus_write_byte_data(client, ADT7462_REG_PWM_MAX, temp); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_pwm_min(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = adt7462_update_device(dev); + return sprintf(buf, "%d\n", data->pwm_min[attr->index]); +} + +static ssize_t set_pwm_min(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7462_data *data = i2c_get_clientdata(client); + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = SENSORS_LIMIT(temp, 0, 255); + + mutex_lock(&data->lock); + data->pwm_min[attr->index] = temp; + i2c_smbus_write_byte_data(client, ADT7462_REG_PWM_MIN(attr->index), + temp); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_pwm_hyst(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = adt7462_update_device(dev); + return sprintf(buf, "%d\n", 1000 * + (data->pwm_trange[attr->index] & ADT7462_PWM_HYST_MASK)); +} + +static ssize_t set_pwm_hyst(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7462_data *data = i2c_get_clientdata(client); + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = ROUND_DIV(temp, 1000); + temp = SENSORS_LIMIT(temp, 0, 15); + + /* package things up */ + temp &= ADT7462_PWM_HYST_MASK; + temp |= data->pwm_trange[attr->index] & ADT7462_PWM_RANGE_MASK; + + mutex_lock(&data->lock); + data->pwm_trange[attr->index] = temp; + i2c_smbus_write_byte_data(client, ADT7462_REG_PWM_TRANGE(attr->index), + temp); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_pwm_tmax(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = adt7462_update_device(dev); + + /* tmax = tmin + trange */ + int trange = trange_values[data->pwm_trange[attr->index] >> + ADT7462_PWM_RANGE_SHIFT]; + int tmin = (data->pwm_tmin[attr->index] - 64) * 1000; + + return sprintf(buf, "%d\n", tmin + trange); +} + +static ssize_t set_pwm_tmax(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + int temp; + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7462_data *data = i2c_get_clientdata(client); + int tmin, trange_value; + long trange; + + if (strict_strtol(buf, 10, &trange)) + return -EINVAL; + + /* trange = tmax - tmin */ + tmin = (data->pwm_tmin[attr->index] - 64) * 1000; + trange_value = find_trange_value(trange - tmin); + + if (trange_value < 0) + return -EINVAL; + + temp = trange_value << ADT7462_PWM_RANGE_SHIFT; + temp |= data->pwm_trange[attr->index] & ADT7462_PWM_HYST_MASK; + + mutex_lock(&data->lock); + data->pwm_trange[attr->index] = temp; + i2c_smbus_write_byte_data(client, ADT7462_REG_PWM_TRANGE(attr->index), + temp); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_pwm_tmin(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = adt7462_update_device(dev); + return sprintf(buf, "%d\n", 1000 * (data->pwm_tmin[attr->index] - 64)); +} + +static ssize_t set_pwm_tmin(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7462_data *data = i2c_get_clientdata(client); + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = ROUND_DIV(temp, 1000) + 64; + temp = SENSORS_LIMIT(temp, 0, 255); + + mutex_lock(&data->lock); + data->pwm_tmin[attr->index] = temp; + i2c_smbus_write_byte_data(client, ADT7462_REG_PWM_TMIN(attr->index), + temp); + mutex_unlock(&data->lock); + + return count; +} + +static ssize_t show_pwm_auto(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = adt7462_update_device(dev); + int cfg = data->pwm_cfg[attr->index] >> ADT7462_PWM_CHANNEL_SHIFT; + + switch (cfg) { + case 4: /* off */ + return sprintf(buf, "0\n"); + case 7: /* manual */ + return sprintf(buf, "1\n"); + default: /* automatic */ + return sprintf(buf, "2\n"); + } +} + +static void set_pwm_channel(struct i2c_client *client, + struct adt7462_data *data, + int which, + int value) +{ + int temp = data->pwm_cfg[which] & ~ADT7462_PWM_CHANNEL_MASK; + temp |= value << ADT7462_PWM_CHANNEL_SHIFT; + + mutex_lock(&data->lock); + data->pwm_cfg[which] = temp; + i2c_smbus_write_byte_data(client, ADT7462_REG_PWM_CFG(which), temp); + mutex_unlock(&data->lock); +} + +static ssize_t set_pwm_auto(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7462_data *data = i2c_get_clientdata(client); + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + switch (temp) { + case 0: /* off */ + set_pwm_channel(client, data, attr->index, 4); + return count; + case 1: /* manual */ + set_pwm_channel(client, data, attr->index, 7); + return count; + default: + return -EINVAL; + } +} + +static ssize_t show_pwm_auto_temp(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct adt7462_data *data = adt7462_update_device(dev); + int channel = data->pwm_cfg[attr->index] >> ADT7462_PWM_CHANNEL_SHIFT; + + switch (channel) { + case 0: /* temp[1234] only */ + case 1: + case 2: + case 3: + return sprintf(buf, "%d\n", (1 << channel)); + case 5: /* temp1 & temp4 */ + return sprintf(buf, "9\n"); + case 6: + return sprintf(buf, "15\n"); + default: + return sprintf(buf, "0\n"); + } +} + +static int cvt_auto_temp(int input) +{ + if (input == 0xF) + return 6; + if (input == 0x9) + return 5; + if (input < 1 || !is_power_of_2(input)) + return -EINVAL; + return ilog2(input); +} + +static ssize_t set_pwm_auto_temp(struct device *dev, + struct device_attribute *devattr, + const char *buf, + size_t count) +{ + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); + struct i2c_client *client = to_i2c_client(dev); + struct adt7462_data *data = i2c_get_clientdata(client); + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = cvt_auto_temp(temp); + if (temp < 0) + return temp; + + set_pwm_channel(client, data, attr->index, temp); + + return count; +} + +static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_temp_max, + set_temp_max, 0); +static SENSOR_DEVICE_ATTR(temp2_max, S_IWUSR | S_IRUGO, show_temp_max, + set_temp_max, 1); +static SENSOR_DEVICE_ATTR(temp3_max, S_IWUSR | S_IRUGO, show_temp_max, + set_temp_max, 2); +static SENSOR_DEVICE_ATTR(temp4_max, S_IWUSR | S_IRUGO, show_temp_max, + set_temp_max, 3); + +static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, show_temp_min, + set_temp_min, 0); +static SENSOR_DEVICE_ATTR(temp2_min, S_IWUSR | S_IRUGO, show_temp_min, + set_temp_min, 1); +static SENSOR_DEVICE_ATTR(temp3_min, S_IWUSR | S_IRUGO, show_temp_min, + set_temp_min, 2); +static SENSOR_DEVICE_ATTR(temp4_min, S_IWUSR | S_IRUGO, show_temp_min, + set_temp_min, 3); + +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, show_temp, NULL, 1); +static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, show_temp, NULL, 2); +static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, show_temp, NULL, 3); + +static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, show_temp_label, NULL, 0); +static SENSOR_DEVICE_ATTR(temp2_label, S_IRUGO, show_temp_label, NULL, 1); +static SENSOR_DEVICE_ATTR(temp3_label, S_IRUGO, show_temp_label, NULL, 2); +static SENSOR_DEVICE_ATTR(temp4_label, S_IRUGO, show_temp_label, NULL, 3); + +static SENSOR_DEVICE_ATTR(temp1_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM1 | ADT7462_LT_ALARM); +static SENSOR_DEVICE_ATTR(temp2_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM1 | ADT7462_R1T_ALARM); +static SENSOR_DEVICE_ATTR(temp3_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM1 | ADT7462_R2T_ALARM); +static SENSOR_DEVICE_ATTR(temp4_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM1 | ADT7462_R3T_ALARM); + +static SENSOR_DEVICE_ATTR(in1_max, S_IWUSR | S_IRUGO, show_volt_max, + set_volt_max, 0); +static SENSOR_DEVICE_ATTR(in2_max, S_IWUSR | S_IRUGO, show_volt_max, + set_volt_max, 1); +static SENSOR_DEVICE_ATTR(in3_max, S_IWUSR | S_IRUGO, show_volt_max, + set_volt_max, 2); +static SENSOR_DEVICE_ATTR(in4_max, S_IWUSR | S_IRUGO, show_volt_max, + set_volt_max, 3); +static SENSOR_DEVICE_ATTR(in5_max, S_IWUSR | S_IRUGO, show_volt_max, + set_volt_max, 4); +static SENSOR_DEVICE_ATTR(in6_max, S_IWUSR | S_IRUGO, show_volt_max, + set_volt_max, 5); +static SENSOR_DEVICE_ATTR(in7_max, S_IWUSR | S_IRUGO, show_volt_max, + set_volt_max, 6); +static SENSOR_DEVICE_ATTR(in8_max, S_IWUSR | S_IRUGO, show_volt_max, + set_volt_max, 7); +static SENSOR_DEVICE_ATTR(in9_max, S_IWUSR | S_IRUGO, show_volt_max, + set_volt_max, 8); +static SENSOR_DEVICE_ATTR(in10_max, S_IWUSR | S_IRUGO, show_volt_max, + set_volt_max, 9); +static SENSOR_DEVICE_ATTR(in11_max, S_IWUSR | S_IRUGO, show_volt_max, + set_volt_max, 10); +static SENSOR_DEVICE_ATTR(in12_max, S_IWUSR | S_IRUGO, show_volt_max, + set_volt_max, 11); +static SENSOR_DEVICE_ATTR(in13_max, S_IWUSR | S_IRUGO, show_volt_max, + set_volt_max, 12); + +static SENSOR_DEVICE_ATTR(in1_min, S_IWUSR | S_IRUGO, show_volt_min, + set_volt_min, 0); +static SENSOR_DEVICE_ATTR(in2_min, S_IWUSR | S_IRUGO, show_volt_min, + set_volt_min, 1); +static SENSOR_DEVICE_ATTR(in3_min, S_IWUSR | S_IRUGO, show_volt_min, + set_volt_min, 2); +static SENSOR_DEVICE_ATTR(in4_min, S_IWUSR | S_IRUGO, show_volt_min, + set_volt_min, 3); +static SENSOR_DEVICE_ATTR(in5_min, S_IWUSR | S_IRUGO, show_volt_min, + set_volt_min, 4); +static SENSOR_DEVICE_ATTR(in6_min, S_IWUSR | S_IRUGO, show_volt_min, + set_volt_min, 5); +static SENSOR_DEVICE_ATTR(in7_min, S_IWUSR | S_IRUGO, show_volt_min, + set_volt_min, 6); +static SENSOR_DEVICE_ATTR(in8_min, S_IWUSR | S_IRUGO, show_volt_min, + set_volt_min, 7); +static SENSOR_DEVICE_ATTR(in9_min, S_IWUSR | S_IRUGO, show_volt_min, + set_volt_min, 8); +static SENSOR_DEVICE_ATTR(in10_min, S_IWUSR | S_IRUGO, show_volt_min, + set_volt_min, 9); +static SENSOR_DEVICE_ATTR(in11_min, S_IWUSR | S_IRUGO, show_volt_min, + set_volt_min, 10); +static SENSOR_DEVICE_ATTR(in12_min, S_IWUSR | S_IRUGO, show_volt_min, + set_volt_min, 11); +static SENSOR_DEVICE_ATTR(in13_min, S_IWUSR | S_IRUGO, show_volt_min, + set_volt_min, 12); + +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_voltage, NULL, 0); +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_voltage, NULL, 1); +static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_voltage, NULL, 2); +static SENSOR_DEVICE_ATTR(in4_input, S_IRUGO, show_voltage, NULL, 3); +static SENSOR_DEVICE_ATTR(in5_input, S_IRUGO, show_voltage, NULL, 4); +static SENSOR_DEVICE_ATTR(in6_input, S_IRUGO, show_voltage, NULL, 5); +static SENSOR_DEVICE_ATTR(in7_input, S_IRUGO, show_voltage, NULL, 6); +static SENSOR_DEVICE_ATTR(in8_input, S_IRUGO, show_voltage, NULL, 7); +static SENSOR_DEVICE_ATTR(in9_input, S_IRUGO, show_voltage, NULL, 8); +static SENSOR_DEVICE_ATTR(in10_input, S_IRUGO, show_voltage, NULL, 9); +static SENSOR_DEVICE_ATTR(in11_input, S_IRUGO, show_voltage, NULL, 10); +static SENSOR_DEVICE_ATTR(in12_input, S_IRUGO, show_voltage, NULL, 11); +static SENSOR_DEVICE_ATTR(in13_input, S_IRUGO, show_voltage, NULL, 12); + +static SENSOR_DEVICE_ATTR(in1_label, S_IRUGO, show_voltage_label, NULL, 0); +static SENSOR_DEVICE_ATTR(in2_label, S_IRUGO, show_voltage_label, NULL, 1); +static SENSOR_DEVICE_ATTR(in3_label, S_IRUGO, show_voltage_label, NULL, 2); +static SENSOR_DEVICE_ATTR(in4_label, S_IRUGO, show_voltage_label, NULL, 3); +static SENSOR_DEVICE_ATTR(in5_label, S_IRUGO, show_voltage_label, NULL, 4); +static SENSOR_DEVICE_ATTR(in6_label, S_IRUGO, show_voltage_label, NULL, 5); +static SENSOR_DEVICE_ATTR(in7_label, S_IRUGO, show_voltage_label, NULL, 6); +static SENSOR_DEVICE_ATTR(in8_label, S_IRUGO, show_voltage_label, NULL, 7); +static SENSOR_DEVICE_ATTR(in9_label, S_IRUGO, show_voltage_label, NULL, 8); +static SENSOR_DEVICE_ATTR(in10_label, S_IRUGO, show_voltage_label, NULL, 9); +static SENSOR_DEVICE_ATTR(in11_label, S_IRUGO, show_voltage_label, NULL, 10); +static SENSOR_DEVICE_ATTR(in12_label, S_IRUGO, show_voltage_label, NULL, 11); +static SENSOR_DEVICE_ATTR(in13_label, S_IRUGO, show_voltage_label, NULL, 12); + +static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM2 | ADT7462_V0_ALARM); +static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM2 | ADT7462_V7_ALARM); +static SENSOR_DEVICE_ATTR(in3_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM2 | ADT7462_V2_ALARM); +static SENSOR_DEVICE_ATTR(in4_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM2 | ADT7462_V6_ALARM); +static SENSOR_DEVICE_ATTR(in5_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM2 | ADT7462_V5_ALARM); +static SENSOR_DEVICE_ATTR(in6_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM2 | ADT7462_V4_ALARM); +static SENSOR_DEVICE_ATTR(in7_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM2 | ADT7462_V3_ALARM); +static SENSOR_DEVICE_ATTR(in8_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM2 | ADT7462_V1_ALARM); +static SENSOR_DEVICE_ATTR(in9_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM3 | ADT7462_V10_ALARM); +static SENSOR_DEVICE_ATTR(in10_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM3 | ADT7462_V9_ALARM); +static SENSOR_DEVICE_ATTR(in11_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM3 | ADT7462_V8_ALARM); +static SENSOR_DEVICE_ATTR(in12_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM3 | ADT7462_V11_ALARM); +static SENSOR_DEVICE_ATTR(in13_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM3 | ADT7462_V12_ALARM); + +static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan_min, + set_fan_min, 0); +static SENSOR_DEVICE_ATTR(fan2_min, S_IWUSR | S_IRUGO, show_fan_min, + set_fan_min, 1); +static SENSOR_DEVICE_ATTR(fan3_min, S_IWUSR | S_IRUGO, show_fan_min, + set_fan_min, 2); +static SENSOR_DEVICE_ATTR(fan4_min, S_IWUSR | S_IRUGO, show_fan_min, + set_fan_min, 3); +static SENSOR_DEVICE_ATTR(fan5_min, S_IWUSR | S_IRUGO, show_fan_min, + set_fan_min, 4); +static SENSOR_DEVICE_ATTR(fan6_min, S_IWUSR | S_IRUGO, show_fan_min, + set_fan_min, 5); +static SENSOR_DEVICE_ATTR(fan7_min, S_IWUSR | S_IRUGO, show_fan_min, + set_fan_min, 6); +static SENSOR_DEVICE_ATTR(fan8_min, S_IWUSR | S_IRUGO, show_fan_min, + set_fan_min, 7); + +static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0); +static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, show_fan, NULL, 1); +static SENSOR_DEVICE_ATTR(fan3_input, S_IRUGO, show_fan, NULL, 2); +static SENSOR_DEVICE_ATTR(fan4_input, S_IRUGO, show_fan, NULL, 3); +static SENSOR_DEVICE_ATTR(fan5_input, S_IRUGO, show_fan, NULL, 4); +static SENSOR_DEVICE_ATTR(fan6_input, S_IRUGO, show_fan, NULL, 5); +static SENSOR_DEVICE_ATTR(fan7_input, S_IRUGO, show_fan, NULL, 6); +static SENSOR_DEVICE_ATTR(fan8_input, S_IRUGO, show_fan, NULL, 7); + +static SENSOR_DEVICE_ATTR(fan1_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM4 | ADT7462_F0_ALARM); +static SENSOR_DEVICE_ATTR(fan2_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM4 | ADT7462_F1_ALARM); +static SENSOR_DEVICE_ATTR(fan3_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM4 | ADT7462_F2_ALARM); +static SENSOR_DEVICE_ATTR(fan4_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM4 | ADT7462_F3_ALARM); +static SENSOR_DEVICE_ATTR(fan5_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM4 | ADT7462_F4_ALARM); +static SENSOR_DEVICE_ATTR(fan6_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM4 | ADT7462_F5_ALARM); +static SENSOR_DEVICE_ATTR(fan7_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM4 | ADT7462_F6_ALARM); +static SENSOR_DEVICE_ATTR(fan8_alarm, S_IRUGO, show_alarm, NULL, + ADT7462_ALARM4 | ADT7462_F7_ALARM); + +static SENSOR_DEVICE_ATTR(force_pwm_max, S_IWUSR | S_IRUGO, + show_force_pwm_max, set_force_pwm_max, 0); + +static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 0); +static SENSOR_DEVICE_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 1); +static SENSOR_DEVICE_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 2); +static SENSOR_DEVICE_ATTR(pwm4, S_IWUSR | S_IRUGO, show_pwm, set_pwm, 3); + +static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IWUSR | S_IRUGO, + show_pwm_min, set_pwm_min, 0); +static SENSOR_DEVICE_ATTR(pwm2_auto_point1_pwm, S_IWUSR | S_IRUGO, + show_pwm_min, set_pwm_min, 1); +static SENSOR_DEVICE_ATTR(pwm3_auto_point1_pwm, S_IWUSR | S_IRUGO, + show_pwm_min, set_pwm_min, 2); +static SENSOR_DEVICE_ATTR(pwm4_auto_point1_pwm, S_IWUSR | S_IRUGO, + show_pwm_min, set_pwm_min, 3); + +static SENSOR_DEVICE_ATTR(pwm1_auto_point2_pwm, S_IWUSR | S_IRUGO, + show_pwm_max, set_pwm_max, 0); +static SENSOR_DEVICE_ATTR(pwm2_auto_point2_pwm, S_IWUSR | S_IRUGO, + show_pwm_max, set_pwm_max, 1); +static SENSOR_DEVICE_ATTR(pwm3_auto_point2_pwm, S_IWUSR | S_IRUGO, + show_pwm_max, set_pwm_max, 2); +static SENSOR_DEVICE_ATTR(pwm4_auto_point2_pwm, S_IWUSR | S_IRUGO, + show_pwm_max, set_pwm_max, 3); + +static SENSOR_DEVICE_ATTR(temp1_auto_point1_hyst, S_IWUSR | S_IRUGO, + show_pwm_hyst, set_pwm_hyst, 0); +static SENSOR_DEVICE_ATTR(temp2_auto_point1_hyst, S_IWUSR | S_IRUGO, + show_pwm_hyst, set_pwm_hyst, 1); +static SENSOR_DEVICE_ATTR(temp3_auto_point1_hyst, S_IWUSR | S_IRUGO, + show_pwm_hyst, set_pwm_hyst, 2); +static SENSOR_DEVICE_ATTR(temp4_auto_point1_hyst, S_IWUSR | S_IRUGO, + show_pwm_hyst, set_pwm_hyst, 3); + +static SENSOR_DEVICE_ATTR(temp1_auto_point2_hyst, S_IWUSR | S_IRUGO, + show_pwm_hyst, set_pwm_hyst, 0); +static SENSOR_DEVICE_ATTR(temp2_auto_point2_hyst, S_IWUSR | S_IRUGO, + show_pwm_hyst, set_pwm_hyst, 1); +static SENSOR_DEVICE_ATTR(temp3_auto_point2_hyst, S_IWUSR | S_IRUGO, + show_pwm_hyst, set_pwm_hyst, 2); +static SENSOR_DEVICE_ATTR(temp4_auto_point2_hyst, S_IWUSR | S_IRUGO, + show_pwm_hyst, set_pwm_hyst, 3); + +static SENSOR_DEVICE_ATTR(temp1_auto_point1_temp, S_IWUSR | S_IRUGO, + show_pwm_tmin, set_pwm_tmin, 0); +static SENSOR_DEVICE_ATTR(temp2_auto_point1_temp, S_IWUSR | S_IRUGO, + show_pwm_tmin, set_pwm_tmin, 1); +static SENSOR_DEVICE_ATTR(temp3_auto_point1_temp, S_IWUSR | S_IRUGO, + show_pwm_tmin, set_pwm_tmin, 2); +static SENSOR_DEVICE_ATTR(temp4_auto_point1_temp, S_IWUSR | S_IRUGO, + show_pwm_tmin, set_pwm_tmin, 3); + +static SENSOR_DEVICE_ATTR(temp1_auto_point2_temp, S_IWUSR | S_IRUGO, + show_pwm_tmax, set_pwm_tmax, 0); +static SENSOR_DEVICE_ATTR(temp2_auto_point2_temp, S_IWUSR | S_IRUGO, + show_pwm_tmax, set_pwm_tmax, 1); +static SENSOR_DEVICE_ATTR(temp3_auto_point2_temp, S_IWUSR | S_IRUGO, + show_pwm_tmax, set_pwm_tmax, 2); +static SENSOR_DEVICE_ATTR(temp4_auto_point2_temp, S_IWUSR | S_IRUGO, + show_pwm_tmax, set_pwm_tmax, 3); + +static SENSOR_DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO, show_pwm_auto, + set_pwm_auto, 0); +static SENSOR_DEVICE_ATTR(pwm2_enable, S_IWUSR | S_IRUGO, show_pwm_auto, + set_pwm_auto, 1); +static SENSOR_DEVICE_ATTR(pwm3_enable, S_IWUSR | S_IRUGO, show_pwm_auto, + set_pwm_auto, 2); +static SENSOR_DEVICE_ATTR(pwm4_enable, S_IWUSR | S_IRUGO, show_pwm_auto, + set_pwm_auto, 3); + +static SENSOR_DEVICE_ATTR(pwm1_auto_channels_temp, S_IWUSR | S_IRUGO, + show_pwm_auto_temp, set_pwm_auto_temp, 0); +static SENSOR_DEVICE_ATTR(pwm2_auto_channels_temp, S_IWUSR | S_IRUGO, + show_pwm_auto_temp, set_pwm_auto_temp, 1); +static SENSOR_DEVICE_ATTR(pwm3_auto_channels_temp, S_IWUSR | S_IRUGO, + show_pwm_auto_temp, set_pwm_auto_temp, 2); +static SENSOR_DEVICE_ATTR(pwm4_auto_channels_temp, S_IWUSR | S_IRUGO, + show_pwm_auto_temp, set_pwm_auto_temp, 3); + +static struct attribute *adt7462_attr[] = +{ + &sensor_dev_attr_temp1_max.dev_attr.attr, + &sensor_dev_attr_temp2_max.dev_attr.attr, + &sensor_dev_attr_temp3_max.dev_attr.attr, + &sensor_dev_attr_temp4_max.dev_attr.attr, + + &sensor_dev_attr_temp1_min.dev_attr.attr, + &sensor_dev_attr_temp2_min.dev_attr.attr, + &sensor_dev_attr_temp3_min.dev_attr.attr, + &sensor_dev_attr_temp4_min.dev_attr.attr, + + &sensor_dev_attr_temp1_input.dev_attr.attr, + &sensor_dev_attr_temp2_input.dev_attr.attr, + &sensor_dev_attr_temp3_input.dev_attr.attr, + &sensor_dev_attr_temp4_input.dev_attr.attr, + + &sensor_dev_attr_temp1_label.dev_attr.attr, + &sensor_dev_attr_temp2_label.dev_attr.attr, + &sensor_dev_attr_temp3_label.dev_attr.attr, + &sensor_dev_attr_temp4_label.dev_attr.attr, + + &sensor_dev_attr_temp1_alarm.dev_attr.attr, + &sensor_dev_attr_temp2_alarm.dev_attr.attr, + &sensor_dev_attr_temp3_alarm.dev_attr.attr, + &sensor_dev_attr_temp4_alarm.dev_attr.attr, + + &sensor_dev_attr_in1_max.dev_attr.attr, + &sensor_dev_attr_in2_max.dev_attr.attr, + &sensor_dev_attr_in3_max.dev_attr.attr, + &sensor_dev_attr_in4_max.dev_attr.attr, + &sensor_dev_attr_in5_max.dev_attr.attr, + &sensor_dev_attr_in6_max.dev_attr.attr, + &sensor_dev_attr_in7_max.dev_attr.attr, + &sensor_dev_attr_in8_max.dev_attr.attr, + &sensor_dev_attr_in9_max.dev_attr.attr, + &sensor_dev_attr_in10_max.dev_attr.attr, + &sensor_dev_attr_in11_max.dev_attr.attr, + &sensor_dev_attr_in12_max.dev_attr.attr, + &sensor_dev_attr_in13_max.dev_attr.attr, + + &sensor_dev_attr_in1_min.dev_attr.attr, + &sensor_dev_attr_in2_min.dev_attr.attr, + &sensor_dev_attr_in3_min.dev_attr.attr, + &sensor_dev_attr_in4_min.dev_attr.attr, + &sensor_dev_attr_in5_min.dev_attr.attr, + &sensor_dev_attr_in6_min.dev_attr.attr, + &sensor_dev_attr_in7_min.dev_attr.attr, + &sensor_dev_attr_in8_min.dev_attr.attr, + &sensor_dev_attr_in9_min.dev_attr.attr, + &sensor_dev_attr_in10_min.dev_attr.attr, + &sensor_dev_attr_in11_min.dev_attr.attr, + &sensor_dev_attr_in12_min.dev_attr.attr, + &sensor_dev_attr_in13_min.dev_attr.attr, + + &sensor_dev_attr_in1_input.dev_attr.attr, + &sensor_dev_attr_in2_input.dev_attr.attr, + &sensor_dev_attr_in3_input.dev_attr.attr, + &sensor_dev_attr_in4_input.dev_attr.attr, + &sensor_dev_attr_in5_input.dev_attr.attr, + &sensor_dev_attr_in6_input.dev_attr.attr, + &sensor_dev_attr_in7_input.dev_attr.attr, + &sensor_dev_attr_in8_input.dev_attr.attr, + &sensor_dev_attr_in9_input.dev_attr.attr, + &sensor_dev_attr_in10_input.dev_attr.attr, + &sensor_dev_attr_in11_input.dev_attr.attr, + &sensor_dev_attr_in12_input.dev_attr.attr, + &sensor_dev_attr_in13_input.dev_attr.attr, + + &sensor_dev_attr_in1_label.dev_attr.attr, + &sensor_dev_attr_in2_label.dev_attr.attr, + &sensor_dev_attr_in3_label.dev_attr.attr, + &sensor_dev_attr_in4_label.dev_attr.attr, + &sensor_dev_attr_in5_label.dev_attr.attr, + &sensor_dev_attr_in6_label.dev_attr.attr, + &sensor_dev_attr_in7_label.dev_attr.attr, + &sensor_dev_attr_in8_label.dev_attr.attr, + &sensor_dev_attr_in9_label.dev_attr.attr, + &sensor_dev_attr_in10_label.dev_attr.attr, + &sensor_dev_attr_in11_label.dev_attr.attr, + &sensor_dev_attr_in12_label.dev_attr.attr, + &sensor_dev_attr_in13_label.dev_attr.attr, + + &sensor_dev_attr_in1_alarm.dev_attr.attr, + &sensor_dev_attr_in2_alarm.dev_attr.attr, + &sensor_dev_attr_in3_alarm.dev_attr.attr, + &sensor_dev_attr_in4_alarm.dev_attr.attr, + &sensor_dev_attr_in5_alarm.dev_attr.attr, + &sensor_dev_attr_in6_alarm.dev_attr.attr, + &sensor_dev_attr_in7_alarm.dev_attr.attr, + &sensor_dev_attr_in8_alarm.dev_attr.attr, + &sensor_dev_attr_in9_alarm.dev_attr.attr, + &sensor_dev_attr_in10_alarm.dev_attr.attr, + &sensor_dev_attr_in11_alarm.dev_attr.attr, + &sensor_dev_attr_in12_alarm.dev_attr.attr, + &sensor_dev_attr_in13_alarm.dev_attr.attr, + + &sensor_dev_attr_fan1_min.dev_attr.attr, + &sensor_dev_attr_fan2_min.dev_attr.attr, + &sensor_dev_attr_fan3_min.dev_attr.attr, + &sensor_dev_attr_fan4_min.dev_attr.attr, + &sensor_dev_attr_fan5_min.dev_attr.attr, + &sensor_dev_attr_fan6_min.dev_attr.attr, + &sensor_dev_attr_fan7_min.dev_attr.attr, + &sensor_dev_attr_fan8_min.dev_attr.attr, + + &sensor_dev_attr_fan1_input.dev_attr.attr, + &sensor_dev_attr_fan2_input.dev_attr.attr, + &sensor_dev_attr_fan3_input.dev_attr.attr, + &sensor_dev_attr_fan4_input.dev_attr.attr, + &sensor_dev_attr_fan5_input.dev_attr.attr, + &sensor_dev_attr_fan6_input.dev_attr.attr, + &sensor_dev_attr_fan7_input.dev_attr.attr, + &sensor_dev_attr_fan8_input.dev_attr.attr, + + &sensor_dev_attr_fan1_alarm.dev_attr.attr, + &sensor_dev_attr_fan2_alarm.dev_attr.attr, + &sensor_dev_attr_fan3_alarm.dev_attr.attr, + &sensor_dev_attr_fan4_alarm.dev_attr.attr, + &sensor_dev_attr_fan5_alarm.dev_attr.attr, + &sensor_dev_attr_fan6_alarm.dev_attr.attr, + &sensor_dev_attr_fan7_alarm.dev_attr.attr, + &sensor_dev_attr_fan8_alarm.dev_attr.attr, + + &sensor_dev_attr_force_pwm_max.dev_attr.attr, + &sensor_dev_attr_pwm1.dev_attr.attr, + &sensor_dev_attr_pwm2.dev_attr.attr, + &sensor_dev_attr_pwm3.dev_attr.attr, + &sensor_dev_attr_pwm4.dev_attr.attr, + + &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr, + &sensor_dev_attr_pwm4_auto_point1_pwm.dev_attr.attr, + + &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_point2_pwm.dev_attr.attr, + &sensor_dev_attr_pwm4_auto_point2_pwm.dev_attr.attr, + + &sensor_dev_attr_temp1_auto_point1_hyst.dev_attr.attr, + &sensor_dev_attr_temp2_auto_point1_hyst.dev_attr.attr, + &sensor_dev_attr_temp3_auto_point1_hyst.dev_attr.attr, + &sensor_dev_attr_temp4_auto_point1_hyst.dev_attr.attr, + + &sensor_dev_attr_temp1_auto_point2_hyst.dev_attr.attr, + &sensor_dev_attr_temp2_auto_point2_hyst.dev_attr.attr, + &sensor_dev_attr_temp3_auto_point2_hyst.dev_attr.attr, + &sensor_dev_attr_temp4_auto_point2_hyst.dev_attr.attr, + + &sensor_dev_attr_temp1_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_temp2_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_temp3_auto_point1_temp.dev_attr.attr, + &sensor_dev_attr_temp4_auto_point1_temp.dev_attr.attr, + + &sensor_dev_attr_temp1_auto_point2_temp.dev_attr.attr, + &sensor_dev_attr_temp2_auto_point2_temp.dev_attr.attr, + &sensor_dev_attr_temp3_auto_point2_temp.dev_attr.attr, + &sensor_dev_attr_temp4_auto_point2_temp.dev_attr.attr, + + &sensor_dev_attr_pwm1_enable.dev_attr.attr, + &sensor_dev_attr_pwm2_enable.dev_attr.attr, + &sensor_dev_attr_pwm3_enable.dev_attr.attr, + &sensor_dev_attr_pwm4_enable.dev_attr.attr, + + &sensor_dev_attr_pwm1_auto_channels_temp.dev_attr.attr, + &sensor_dev_attr_pwm2_auto_channels_temp.dev_attr.attr, + &sensor_dev_attr_pwm3_auto_channels_temp.dev_attr.attr, + &sensor_dev_attr_pwm4_auto_channels_temp.dev_attr.attr, + NULL +}; + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int adt7462_detect(struct i2c_client *client, int kind, + struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + if (kind <= 0) { + int vendor, device, revision; + + vendor = i2c_smbus_read_byte_data(client, ADT7462_REG_VENDOR); + if (vendor != ADT7462_VENDOR) + return -ENODEV; + + device = i2c_smbus_read_byte_data(client, ADT7462_REG_DEVICE); + if (device != ADT7462_DEVICE) + return -ENODEV; + + revision = i2c_smbus_read_byte_data(client, + ADT7462_REG_REVISION); + if (revision != ADT7462_REVISION) + return -ENODEV; + } else + dev_dbg(&adapter->dev, "detection forced\n"); + + strlcpy(info->type, "adt7462", I2C_NAME_SIZE); + + return 0; +} + +static int adt7462_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct adt7462_data *data; + int err; + + data = kzalloc(sizeof(struct adt7462_data), GFP_KERNEL); + if (!data) { + err = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(client, data); + mutex_init(&data->lock); + + dev_info(&client->dev, "%s chip found\n", client->name); + + /* Register sysfs hooks */ + data->attrs.attrs = adt7462_attr; + err = sysfs_create_group(&client->dev.kobj, &data->attrs); + if (err) + goto exit_free; + + data->hwmon_dev = hwmon_device_register(&client->dev); + if (IS_ERR(data->hwmon_dev)) { + err = PTR_ERR(data->hwmon_dev); + goto exit_remove; + } + + return 0; + +exit_remove: + sysfs_remove_group(&client->dev.kobj, &data->attrs); +exit_free: + kfree(data); +exit: + return err; +} + +static int adt7462_remove(struct i2c_client *client) +{ + struct adt7462_data *data = i2c_get_clientdata(client); + + hwmon_device_unregister(data->hwmon_dev); + sysfs_remove_group(&client->dev.kobj, &data->attrs); + kfree(data); + return 0; +} + +static int __init adt7462_init(void) +{ + return i2c_add_driver(&adt7462_driver); +} + +static void __exit adt7462_exit(void) +{ + i2c_del_driver(&adt7462_driver); +} + +MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>"); +MODULE_DESCRIPTION("ADT7462 driver"); +MODULE_LICENSE("GPL"); + +module_init(adt7462_init); +module_exit(adt7462_exit); diff --git a/drivers/hwmon/adt7470.c b/drivers/hwmon/adt7470.c index d368d8f845e..1311a595147 100644 --- a/drivers/hwmon/adt7470.c +++ b/drivers/hwmon/adt7470.c @@ -137,6 +137,8 @@ I2C_CLIENT_INSMOD_1(adt7470); #define FAN_PERIOD_INVALID 65535 #define FAN_DATA_VALID(x) ((x) && (x) != FAN_PERIOD_INVALID) +#define ROUND_DIV(x, divisor) (((x) + ((divisor) / 2)) / (divisor)) + struct adt7470_data { struct device *hwmon_dev; struct attribute_group attrs; @@ -353,7 +355,13 @@ static ssize_t set_temp_min(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7470_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10) / 1000; + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = ROUND_DIV(temp, 1000); + temp = SENSORS_LIMIT(temp, 0, 255); mutex_lock(&data->lock); data->temp_min[attr->index] = temp; @@ -381,7 +389,13 @@ static ssize_t set_temp_max(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7470_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10) / 1000; + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = ROUND_DIV(temp, 1000); + temp = SENSORS_LIMIT(temp, 0, 255); mutex_lock(&data->lock); data->temp_max[attr->index] = temp; @@ -430,11 +444,13 @@ static ssize_t set_fan_max(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7470_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10); + long temp; - if (!temp) + if (strict_strtol(buf, 10, &temp) || !temp) return -EINVAL; + temp = FAN_RPM_TO_PERIOD(temp); + temp = SENSORS_LIMIT(temp, 1, 65534); mutex_lock(&data->lock); data->fan_max[attr->index] = temp; @@ -465,11 +481,13 @@ static ssize_t set_fan_min(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7470_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10); + long temp; - if (!temp) + if (strict_strtol(buf, 10, &temp) || !temp) return -EINVAL; + temp = FAN_RPM_TO_PERIOD(temp); + temp = SENSORS_LIMIT(temp, 1, 65534); mutex_lock(&data->lock); data->fan_min[attr->index] = temp; @@ -507,9 +525,12 @@ static ssize_t set_force_pwm_max(struct device *dev, { struct i2c_client *client = to_i2c_client(dev); struct adt7470_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10); + long temp; u8 reg; + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + mutex_lock(&data->lock); data->force_pwm_max = temp; reg = i2c_smbus_read_byte_data(client, ADT7470_REG_CFG); @@ -537,7 +558,12 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7470_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10); + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = SENSORS_LIMIT(temp, 0, 255); mutex_lock(&data->lock); data->pwm[attr->index] = temp; @@ -564,7 +590,12 @@ static ssize_t set_pwm_max(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7470_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10); + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = SENSORS_LIMIT(temp, 0, 255); mutex_lock(&data->lock); data->pwm_max[attr->index] = temp; @@ -592,7 +623,12 @@ static ssize_t set_pwm_min(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7470_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10); + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = SENSORS_LIMIT(temp, 0, 255); mutex_lock(&data->lock); data->pwm_min[attr->index] = temp; @@ -630,7 +666,13 @@ static ssize_t set_pwm_tmin(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7470_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10) / 1000; + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = ROUND_DIV(temp, 1000); + temp = SENSORS_LIMIT(temp, 0, 255); mutex_lock(&data->lock); data->pwm_tmin[attr->index] = temp; @@ -658,11 +700,14 @@ static ssize_t set_pwm_auto(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7470_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10); int pwm_auto_reg = ADT7470_REG_PWM_CFG(attr->index); int pwm_auto_reg_mask; + long temp; u8 reg; + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + if (attr->index % 2) pwm_auto_reg_mask = ADT7470_PWM2_AUTO_MASK; else @@ -716,10 +761,14 @@ static ssize_t set_pwm_auto_temp(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7470_data *data = i2c_get_clientdata(client); - int temp = cvt_auto_temp(simple_strtol(buf, NULL, 10)); int pwm_auto_reg = ADT7470_REG_PWM_AUTO_TEMP(attr->index); + long temp; u8 reg; + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = cvt_auto_temp(temp); if (temp < 0) return temp; diff --git a/drivers/hwmon/adt7473.c b/drivers/hwmon/adt7473.c index b9a8ea30c99..18aa30866a6 100644 --- a/drivers/hwmon/adt7473.c +++ b/drivers/hwmon/adt7473.c @@ -129,6 +129,8 @@ I2C_CLIENT_INSMOD_1(adt7473); #define FAN_PERIOD_INVALID 65535 #define FAN_DATA_VALID(x) ((x) && (x) != FAN_PERIOD_INVALID) +#define ROUND_DIV(x, divisor) (((x) + ((divisor) / 2)) / (divisor)) + struct adt7473_data { struct device *hwmon_dev; struct attribute_group attrs; @@ -357,7 +359,12 @@ static ssize_t set_volt_min(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7473_data *data = i2c_get_clientdata(client); - int volt = encode_volt(attr->index, simple_strtol(buf, NULL, 10)); + long volt; + + if (strict_strtol(buf, 10, &volt)) + return -EINVAL; + + volt = encode_volt(attr->index, volt); mutex_lock(&data->lock); data->volt_min[attr->index] = volt; @@ -386,7 +393,12 @@ static ssize_t set_volt_max(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7473_data *data = i2c_get_clientdata(client); - int volt = encode_volt(attr->index, simple_strtol(buf, NULL, 10)); + long volt; + + if (strict_strtol(buf, 10, &volt)) + return -EINVAL; + + volt = encode_volt(attr->index, volt); mutex_lock(&data->lock); data->volt_max[attr->index] = volt; @@ -419,7 +431,8 @@ static int decode_temp(u8 twos_complement, u8 raw) static u8 encode_temp(u8 twos_complement, int cooked) { - return twos_complement ? cooked & 0xFF : cooked + 64; + u8 ret = twos_complement ? cooked & 0xFF : cooked + 64; + return SENSORS_LIMIT(ret, 0, 255); } static ssize_t show_temp_min(struct device *dev, @@ -441,7 +454,12 @@ static ssize_t set_temp_min(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7473_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10) / 1000; + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = ROUND_DIV(temp, 1000); temp = encode_temp(data->temp_twos_complement, temp); mutex_lock(&data->lock); @@ -472,7 +490,12 @@ static ssize_t set_temp_max(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7473_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10) / 1000; + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = ROUND_DIV(temp, 1000); temp = encode_temp(data->temp_twos_complement, temp); mutex_lock(&data->lock); @@ -515,11 +538,13 @@ static ssize_t set_fan_min(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7473_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10); + long temp; - if (!temp) + if (strict_strtol(buf, 10, &temp) || !temp) return -EINVAL; + temp = FAN_RPM_TO_PERIOD(temp); + temp = SENSORS_LIMIT(temp, 1, 65534); mutex_lock(&data->lock); data->fan_min[attr->index] = temp; @@ -558,7 +583,10 @@ static ssize_t set_max_duty_at_crit(struct device *dev, u8 reg; struct i2c_client *client = to_i2c_client(dev); struct adt7473_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10); + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; mutex_lock(&data->lock); data->max_duty_at_overheat = !!temp; @@ -587,7 +615,12 @@ static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7473_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10); + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = SENSORS_LIMIT(temp, 0, 255); mutex_lock(&data->lock); data->pwm[attr->index] = temp; @@ -614,7 +647,12 @@ static ssize_t set_pwm_max(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7473_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10); + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = SENSORS_LIMIT(temp, 0, 255); mutex_lock(&data->lock); data->pwm_max[attr->index] = temp; @@ -642,7 +680,12 @@ static ssize_t set_pwm_min(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7473_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10); + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = SENSORS_LIMIT(temp, 0, 255); mutex_lock(&data->lock); data->pwm_min[attr->index] = temp; @@ -672,7 +715,12 @@ static ssize_t set_temp_tmax(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7473_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10) / 1000; + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = ROUND_DIV(temp, 1000); temp = encode_temp(data->temp_twos_complement, temp); mutex_lock(&data->lock); @@ -703,7 +751,12 @@ static ssize_t set_temp_tmin(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7473_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10) / 1000; + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; + + temp = ROUND_DIV(temp, 1000); temp = encode_temp(data->temp_twos_complement, temp); mutex_lock(&data->lock); @@ -741,7 +794,10 @@ static ssize_t set_pwm_enable(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7473_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10); + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; switch (temp) { case 0: @@ -805,7 +861,10 @@ static ssize_t set_pwm_auto_temp(struct device *dev, struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); struct i2c_client *client = to_i2c_client(dev); struct adt7473_data *data = i2c_get_clientdata(client); - int temp = simple_strtol(buf, NULL, 10); + long temp; + + if (strict_strtol(buf, 10, &temp)) + return -EINVAL; switch (temp) { case 1: diff --git a/drivers/hwmon/applesmc.c b/drivers/hwmon/applesmc.c index be3285912cb..488e45cd43d 100644 --- a/drivers/hwmon/applesmc.c +++ b/drivers/hwmon/applesmc.c @@ -1280,7 +1280,7 @@ static __initdata struct dmi_match_data applesmc_dmi_data[] = { { .accelerometer = 0, .light = 0, .temperature_set = 4 }, /* iMac: temperature set 5 */ { .accelerometer = 0, .light = 0, .temperature_set = 5 }, -/* MacBook3: accelerometer and temperature set 6 */ +/* MacBook3, MacBook4: accelerometer and temperature set 6 */ { .accelerometer = 1, .light = 0, .temperature_set = 6 }, /* MacBook Air: accelerometer, backlight and temperature set 7 */ { .accelerometer = 1, .light = 1, .temperature_set = 7 }, @@ -1329,6 +1329,10 @@ static __initdata struct dmi_system_id applesmc_whitelist[] = { DMI_MATCH(DMI_BOARD_VENDOR,"Apple"), DMI_MATCH(DMI_PRODUCT_NAME,"MacBook3") }, &applesmc_dmi_data[6]}, + { applesmc_dmi_match, "Apple MacBook 4", { + DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), + DMI_MATCH(DMI_PRODUCT_NAME, "MacBook4") }, + &applesmc_dmi_data[6]}, { applesmc_dmi_match, "Apple MacBook 5", { DMI_MATCH(DMI_BOARD_VENDOR, "Apple"), DMI_MATCH(DMI_PRODUCT_NAME, "MacBook5") }, diff --git a/drivers/hwmon/ibmaem.c b/drivers/hwmon/ibmaem.c index 7b0ed5dea39..fe74609a7fe 100644 --- a/drivers/hwmon/ibmaem.c +++ b/drivers/hwmon/ibmaem.c @@ -88,9 +88,11 @@ static DEFINE_IDR(aem_idr); static DEFINE_SPINLOCK(aem_idr_lock); -static struct device_driver aem_driver = { - .name = DRVNAME, - .bus = &platform_bus_type, +static struct platform_driver aem_driver = { + .driver = { + .name = DRVNAME, + .bus = &platform_bus_type, + } }; struct aem_ipmi_data { @@ -583,7 +585,7 @@ static int aem_init_aem1_inst(struct aem_ipmi_data *probe, u8 module_handle) data->pdev = platform_device_alloc(DRVNAME, data->id); if (!data->pdev) goto dev_err; - data->pdev->dev.driver = &aem_driver; + data->pdev->dev.driver = &aem_driver.driver; res = platform_device_add(data->pdev); if (res) @@ -716,7 +718,7 @@ static int aem_init_aem2_inst(struct aem_ipmi_data *probe, data->pdev = platform_device_alloc(DRVNAME, data->id); if (!data->pdev) goto dev_err; - data->pdev->dev.driver = &aem_driver; + data->pdev->dev.driver = &aem_driver.driver; res = platform_device_add(data->pdev); if (res) @@ -1085,7 +1087,7 @@ static int __init aem_init(void) { int res; - res = driver_register(&aem_driver); + res = driver_register(&aem_driver.driver); if (res) { printk(KERN_ERR "Can't register aem driver\n"); return res; @@ -1097,7 +1099,7 @@ static int __init aem_init(void) return 0; ipmi_reg_err: - driver_unregister(&aem_driver); + driver_unregister(&aem_driver.driver); return res; } @@ -1107,7 +1109,7 @@ static void __exit aem_exit(void) struct aem_data *p1, *next1; ipmi_smi_watcher_unregister(&driver_data.bmc_events); - driver_unregister(&aem_driver); + driver_unregister(&aem_driver.driver); list_for_each_entry_safe(p1, next1, &driver_data.aem_devices, list) aem_delete(p1); } diff --git a/drivers/hwmon/lis3lv02d.c b/drivers/hwmon/lis3lv02d.c new file mode 100644 index 00000000000..c002144c76b --- /dev/null +++ b/drivers/hwmon/lis3lv02d.c @@ -0,0 +1,581 @@ +/* + * lis3lv02d.c - ST LIS3LV02DL accelerometer driver + * + * Copyright (C) 2007-2008 Yan Burman + * Copyright (C) 2008 Eric Piel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/dmi.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/input.h> +#include <linux/kthread.h> +#include <linux/semaphore.h> +#include <linux/delay.h> +#include <linux/wait.h> +#include <linux/poll.h> +#include <linux/freezer.h> +#include <linux/uaccess.h> +#include <acpi/acpi_drivers.h> +#include <asm/atomic.h> +#include "lis3lv02d.h" + +#define DRIVER_NAME "lis3lv02d" +#define ACPI_MDPS_CLASS "accelerometer" + +/* joystick device poll interval in milliseconds */ +#define MDPS_POLL_INTERVAL 50 +/* + * The sensor can also generate interrupts (DRDY) but it's pretty pointless + * because their are generated even if the data do not change. So it's better + * to keep the interrupt for the free-fall event. The values are updated at + * 40Hz (at the lowest frequency), but as it can be pretty time consuming on + * some low processor, we poll the sensor only at 20Hz... enough for the + * joystick. + */ + +/* Maximum value our axis may get for the input device (signed 12 bits) */ +#define MDPS_MAX_VAL 2048 + +struct axis_conversion { + s8 x; + s8 y; + s8 z; +}; + +struct acpi_lis3lv02d { + struct acpi_device *device; /* The ACPI device */ + struct input_dev *idev; /* input device */ + struct task_struct *kthread; /* kthread for input */ + struct mutex lock; + struct platform_device *pdev; /* platform device */ + atomic_t count; /* interrupt count after last read */ + int xcalib; /* calibrated null value for x */ + int ycalib; /* calibrated null value for y */ + int zcalib; /* calibrated null value for z */ + unsigned char is_on; /* whether the device is on or off */ + unsigned char usage; /* usage counter */ + struct axis_conversion ac; /* hw -> logical axis */ +}; + +static struct acpi_lis3lv02d adev; + +static int lis3lv02d_remove_fs(void); +static int lis3lv02d_add_fs(struct acpi_device *device); + +/* For automatic insertion of the module */ +static struct acpi_device_id lis3lv02d_device_ids[] = { + {"HPQ0004", 0}, /* HP Mobile Data Protection System PNP */ + {"", 0}, +}; +MODULE_DEVICE_TABLE(acpi, lis3lv02d_device_ids); + +/** + * lis3lv02d_acpi_init - ACPI _INI method: initialize the device. + * @handle: the handle of the device + * + * Returns AE_OK on success. + */ +static inline acpi_status lis3lv02d_acpi_init(acpi_handle handle) +{ + return acpi_evaluate_object(handle, METHOD_NAME__INI, NULL, NULL); +} + +/** + * lis3lv02d_acpi_read - ACPI ALRD method: read a register + * @handle: the handle of the device + * @reg: the register to read + * @ret: result of the operation + * + * Returns AE_OK on success. + */ +static acpi_status lis3lv02d_acpi_read(acpi_handle handle, int reg, u8 *ret) +{ + union acpi_object arg0 = { ACPI_TYPE_INTEGER }; + struct acpi_object_list args = { 1, &arg0 }; + unsigned long long lret; + acpi_status status; + + arg0.integer.value = reg; + + status = acpi_evaluate_integer(handle, "ALRD", &args, &lret); + *ret = lret; + return status; +} + +/** + * lis3lv02d_acpi_write - ACPI ALWR method: write to a register + * @handle: the handle of the device + * @reg: the register to write to + * @val: the value to write + * + * Returns AE_OK on success. + */ +static acpi_status lis3lv02d_acpi_write(acpi_handle handle, int reg, u8 val) +{ + unsigned long long ret; /* Not used when writting */ + union acpi_object in_obj[2]; + struct acpi_object_list args = { 2, in_obj }; + + in_obj[0].type = ACPI_TYPE_INTEGER; + in_obj[0].integer.value = reg; + in_obj[1].type = ACPI_TYPE_INTEGER; + in_obj[1].integer.value = val; + + return acpi_evaluate_integer(handle, "ALWR", &args, &ret); +} + +static s16 lis3lv02d_read_16(acpi_handle handle, int reg) +{ + u8 lo, hi; + + lis3lv02d_acpi_read(handle, reg, &lo); + lis3lv02d_acpi_read(handle, reg + 1, &hi); + /* In "12 bit right justified" mode, bit 6, bit 7, bit 8 = bit 5 */ + return (s16)((hi << 8) | lo); +} + +/** + * lis3lv02d_get_axis - For the given axis, give the value converted + * @axis: 1,2,3 - can also be negative + * @hw_values: raw values returned by the hardware + * + * Returns the converted value. + */ +static inline int lis3lv02d_get_axis(s8 axis, int hw_values[3]) +{ + if (axis > 0) + return hw_values[axis - 1]; + else + return -hw_values[-axis - 1]; +} + +/** + * lis3lv02d_get_xyz - Get X, Y and Z axis values from the accelerometer + * @handle: the handle to the device + * @x: where to store the X axis value + * @y: where to store the Y axis value + * @z: where to store the Z axis value + * + * Note that 40Hz input device can eat up about 10% CPU at 800MHZ + */ +static void lis3lv02d_get_xyz(acpi_handle handle, int *x, int *y, int *z) +{ + int position[3]; + + position[0] = lis3lv02d_read_16(handle, OUTX_L); + position[1] = lis3lv02d_read_16(handle, OUTY_L); + position[2] = lis3lv02d_read_16(handle, OUTZ_L); + + *x = lis3lv02d_get_axis(adev.ac.x, position); + *y = lis3lv02d_get_axis(adev.ac.y, position); + *z = lis3lv02d_get_axis(adev.ac.z, position); +} + +static inline void lis3lv02d_poweroff(acpi_handle handle) +{ + adev.is_on = 0; + /* disable X,Y,Z axis and power down */ + lis3lv02d_acpi_write(handle, CTRL_REG1, 0x00); +} + +static void lis3lv02d_poweron(acpi_handle handle) +{ + u8 val; + + adev.is_on = 1; + lis3lv02d_acpi_init(handle); + lis3lv02d_acpi_write(handle, FF_WU_CFG, 0); + /* + * BDU: LSB and MSB values are not updated until both have been read. + * So the value read will always be correct. + * IEN: Interrupt for free-fall and DD, not for data-ready. + */ + lis3lv02d_acpi_read(handle, CTRL_REG2, &val); + val |= CTRL2_BDU | CTRL2_IEN; + lis3lv02d_acpi_write(handle, CTRL_REG2, val); +} + +#ifdef CONFIG_PM +static int lis3lv02d_suspend(struct acpi_device *device, pm_message_t state) +{ + /* make sure the device is off when we suspend */ + lis3lv02d_poweroff(device->handle); + return 0; +} + +static int lis3lv02d_resume(struct acpi_device *device) +{ + /* put back the device in the right state (ACPI might turn it on) */ + mutex_lock(&adev.lock); + if (adev.usage > 0) + lis3lv02d_poweron(device->handle); + else + lis3lv02d_poweroff(device->handle); + mutex_unlock(&adev.lock); + return 0; +} +#else +#define lis3lv02d_suspend NULL +#define lis3lv02d_resume NULL +#endif + + +/* + * To be called before starting to use the device. It makes sure that the + * device will always be on until a call to lis3lv02d_decrease_use(). Not to be + * used from interrupt context. + */ +static void lis3lv02d_increase_use(struct acpi_lis3lv02d *dev) +{ + mutex_lock(&dev->lock); + dev->usage++; + if (dev->usage == 1) { + if (!dev->is_on) + lis3lv02d_poweron(dev->device->handle); + } + mutex_unlock(&dev->lock); +} + +/* + * To be called whenever a usage of the device is stopped. + * It will make sure to turn off the device when there is not usage. + */ +static void lis3lv02d_decrease_use(struct acpi_lis3lv02d *dev) +{ + mutex_lock(&dev->lock); + dev->usage--; + if (dev->usage == 0) + lis3lv02d_poweroff(dev->device->handle); + mutex_unlock(&dev->lock); +} + +/** + * lis3lv02d_joystick_kthread - Kthread polling function + * @data: unused - here to conform to threadfn prototype + */ +static int lis3lv02d_joystick_kthread(void *data) +{ + int x, y, z; + + while (!kthread_should_stop()) { + lis3lv02d_get_xyz(adev.device->handle, &x, &y, &z); + input_report_abs(adev.idev, ABS_X, x - adev.xcalib); + input_report_abs(adev.idev, ABS_Y, y - adev.ycalib); + input_report_abs(adev.idev, ABS_Z, z - adev.zcalib); + + input_sync(adev.idev); + + try_to_freeze(); + msleep_interruptible(MDPS_POLL_INTERVAL); + } + + return 0; +} + +static int lis3lv02d_joystick_open(struct input_dev *input) +{ + lis3lv02d_increase_use(&adev); + adev.kthread = kthread_run(lis3lv02d_joystick_kthread, NULL, "klis3lv02d"); + if (IS_ERR(adev.kthread)) { + lis3lv02d_decrease_use(&adev); + return PTR_ERR(adev.kthread); + } + + return 0; +} + +static void lis3lv02d_joystick_close(struct input_dev *input) +{ + kthread_stop(adev.kthread); + lis3lv02d_decrease_use(&adev); +} + + +static inline void lis3lv02d_calibrate_joystick(void) +{ + lis3lv02d_get_xyz(adev.device->handle, &adev.xcalib, &adev.ycalib, &adev.zcalib); +} + +static int lis3lv02d_joystick_enable(void) +{ + int err; + + if (adev.idev) + return -EINVAL; + + adev.idev = input_allocate_device(); + if (!adev.idev) + return -ENOMEM; + + lis3lv02d_calibrate_joystick(); + + adev.idev->name = "ST LIS3LV02DL Accelerometer"; + adev.idev->phys = DRIVER_NAME "/input0"; + adev.idev->id.bustype = BUS_HOST; + adev.idev->id.vendor = 0; + adev.idev->dev.parent = &adev.pdev->dev; + adev.idev->open = lis3lv02d_joystick_open; + adev.idev->close = lis3lv02d_joystick_close; + + set_bit(EV_ABS, adev.idev->evbit); + input_set_abs_params(adev.idev, ABS_X, -MDPS_MAX_VAL, MDPS_MAX_VAL, 3, 3); + input_set_abs_params(adev.idev, ABS_Y, -MDPS_MAX_VAL, MDPS_MAX_VAL, 3, 3); + input_set_abs_params(adev.idev, ABS_Z, -MDPS_MAX_VAL, MDPS_MAX_VAL, 3, 3); + + err = input_register_device(adev.idev); + if (err) { + input_free_device(adev.idev); + adev.idev = NULL; + } + + return err; +} + +static void lis3lv02d_joystick_disable(void) +{ + if (!adev.idev) + return; + + input_unregister_device(adev.idev); + adev.idev = NULL; +} + + +/* + * Initialise the accelerometer and the various subsystems. + * Should be rather independant of the bus system. + */ +static int lis3lv02d_init_device(struct acpi_lis3lv02d *dev) +{ + mutex_init(&dev->lock); + lis3lv02d_add_fs(dev->device); + lis3lv02d_increase_use(dev); + + if (lis3lv02d_joystick_enable()) + printk(KERN_ERR DRIVER_NAME ": joystick initialization failed\n"); + + lis3lv02d_decrease_use(dev); + return 0; +} + +static int lis3lv02d_dmi_matched(const struct dmi_system_id *dmi) +{ + adev.ac = *((struct axis_conversion *)dmi->driver_data); + printk(KERN_INFO DRIVER_NAME ": hardware type %s found.\n", dmi->ident); + + return 1; +} + +/* Represents, for each axis seen by userspace, the corresponding hw axis (+1). + * If the value is negative, the opposite of the hw value is used. */ +static struct axis_conversion lis3lv02d_axis_normal = {1, 2, 3}; +static struct axis_conversion lis3lv02d_axis_y_inverted = {1, -2, 3}; +static struct axis_conversion lis3lv02d_axis_x_inverted = {-1, 2, 3}; +static struct axis_conversion lis3lv02d_axis_z_inverted = {1, 2, -3}; +static struct axis_conversion lis3lv02d_axis_xy_rotated_left = {-2, 1, 3}; +static struct axis_conversion lis3lv02d_axis_xy_swap_inverted = {-2, -1, 3}; + +#define AXIS_DMI_MATCH(_ident, _name, _axis) { \ + .ident = _ident, \ + .callback = lis3lv02d_dmi_matched, \ + .matches = { \ + DMI_MATCH(DMI_PRODUCT_NAME, _name) \ + }, \ + .driver_data = &lis3lv02d_axis_##_axis \ +} +static struct dmi_system_id lis3lv02d_dmi_ids[] = { + /* product names are truncated to match all kinds of a same model */ + AXIS_DMI_MATCH("NC64x0", "HP Compaq nc64", x_inverted), + AXIS_DMI_MATCH("NC84x0", "HP Compaq nc84", z_inverted), + AXIS_DMI_MATCH("NX9420", "HP Compaq nx9420", x_inverted), + AXIS_DMI_MATCH("NW9440", "HP Compaq nw9440", x_inverted), + AXIS_DMI_MATCH("NC2510", "HP Compaq 2510", y_inverted), + AXIS_DMI_MATCH("NC8510", "HP Compaq 8510", xy_swap_inverted), + AXIS_DMI_MATCH("HP2133", "HP 2133", xy_rotated_left), + { NULL, } +/* Laptop models without axis info (yet): + * "NC651xx" "HP Compaq 651" + * "NC671xx" "HP Compaq 671" + * "NC6910" "HP Compaq 6910" + * HP Compaq 8710x Notebook PC / Mobile Workstation + * "NC2400" "HP Compaq nc2400" + * "NX74x0" "HP Compaq nx74" + * "NX6325" "HP Compaq nx6325" + * "NC4400" "HP Compaq nc4400" + */ +}; + +static int lis3lv02d_add(struct acpi_device *device) +{ + u8 val; + + if (!device) + return -EINVAL; + + adev.device = device; + strcpy(acpi_device_name(device), DRIVER_NAME); + strcpy(acpi_device_class(device), ACPI_MDPS_CLASS); + device->driver_data = &adev; + + lis3lv02d_acpi_read(device->handle, WHO_AM_I, &val); + if ((val != LIS3LV02DL_ID) && (val != LIS302DL_ID)) { + printk(KERN_ERR DRIVER_NAME + ": Accelerometer chip not LIS3LV02D{L,Q}\n"); + } + + /* If possible use a "standard" axes order */ + if (dmi_check_system(lis3lv02d_dmi_ids) == 0) { + printk(KERN_INFO DRIVER_NAME ": laptop model unknown, " + "using default axes configuration\n"); + adev.ac = lis3lv02d_axis_normal; + } + + return lis3lv02d_init_device(&adev); +} + +static int lis3lv02d_remove(struct acpi_device *device, int type) +{ + if (!device) + return -EINVAL; + + lis3lv02d_joystick_disable(); + lis3lv02d_poweroff(device->handle); + + return lis3lv02d_remove_fs(); +} + + +/* Sysfs stuff */ +static ssize_t lis3lv02d_position_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int x, y, z; + + lis3lv02d_increase_use(&adev); + lis3lv02d_get_xyz(adev.device->handle, &x, &y, &z); + lis3lv02d_decrease_use(&adev); + return sprintf(buf, "(%d,%d,%d)\n", x, y, z); +} + +static ssize_t lis3lv02d_calibrate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sprintf(buf, "(%d,%d,%d)\n", adev.xcalib, adev.ycalib, adev.zcalib); +} + +static ssize_t lis3lv02d_calibrate_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + lis3lv02d_increase_use(&adev); + lis3lv02d_calibrate_joystick(); + lis3lv02d_decrease_use(&adev); + return count; +} + +/* conversion btw sampling rate and the register values */ +static int lis3lv02dl_df_val[4] = {40, 160, 640, 2560}; +static ssize_t lis3lv02d_rate_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + u8 ctrl; + int val; + + lis3lv02d_increase_use(&adev); + lis3lv02d_acpi_read(adev.device->handle, CTRL_REG1, &ctrl); + lis3lv02d_decrease_use(&adev); + val = (ctrl & (CTRL1_DF0 | CTRL1_DF1)) >> 4; + return sprintf(buf, "%d\n", lis3lv02dl_df_val[val]); +} + +static DEVICE_ATTR(position, S_IRUGO, lis3lv02d_position_show, NULL); +static DEVICE_ATTR(calibrate, S_IRUGO|S_IWUSR, lis3lv02d_calibrate_show, + lis3lv02d_calibrate_store); +static DEVICE_ATTR(rate, S_IRUGO, lis3lv02d_rate_show, NULL); + +static struct attribute *lis3lv02d_attributes[] = { + &dev_attr_position.attr, + &dev_attr_calibrate.attr, + &dev_attr_rate.attr, + NULL +}; + +static struct attribute_group lis3lv02d_attribute_group = { + .attrs = lis3lv02d_attributes +}; + +static int lis3lv02d_add_fs(struct acpi_device *device) +{ + adev.pdev = platform_device_register_simple(DRIVER_NAME, -1, NULL, 0); + if (IS_ERR(adev.pdev)) + return PTR_ERR(adev.pdev); + + return sysfs_create_group(&adev.pdev->dev.kobj, &lis3lv02d_attribute_group); +} + +static int lis3lv02d_remove_fs(void) +{ + sysfs_remove_group(&adev.pdev->dev.kobj, &lis3lv02d_attribute_group); + platform_device_unregister(adev.pdev); + return 0; +} + +/* For the HP MDPS aka 3D Driveguard */ +static struct acpi_driver lis3lv02d_driver = { + .name = DRIVER_NAME, + .class = ACPI_MDPS_CLASS, + .ids = lis3lv02d_device_ids, + .ops = { + .add = lis3lv02d_add, + .remove = lis3lv02d_remove, + .suspend = lis3lv02d_suspend, + .resume = lis3lv02d_resume, + } +}; + +static int __init lis3lv02d_init_module(void) +{ + int ret; + + if (acpi_disabled) + return -ENODEV; + + ret = acpi_bus_register_driver(&lis3lv02d_driver); + if (ret < 0) + return ret; + + printk(KERN_INFO DRIVER_NAME " driver loaded.\n"); + + return 0; +} + +static void __exit lis3lv02d_exit_module(void) +{ + acpi_bus_unregister_driver(&lis3lv02d_driver); +} + +MODULE_DESCRIPTION("ST LIS3LV02Dx three-axis digital accelerometer driver"); +MODULE_AUTHOR("Yan Burman and Eric Piel"); +MODULE_LICENSE("GPL"); + +module_init(lis3lv02d_init_module); +module_exit(lis3lv02d_exit_module); diff --git a/drivers/hwmon/lis3lv02d.h b/drivers/hwmon/lis3lv02d.h new file mode 100644 index 00000000000..330cfc60e94 --- /dev/null +++ b/drivers/hwmon/lis3lv02d.h @@ -0,0 +1,149 @@ +/* + * lis3lv02d.h - ST LIS3LV02DL accelerometer driver + * + * Copyright (C) 2007-2008 Yan Burman + * Copyright (C) 2008 Eric Piel + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * The actual chip is STMicroelectronics LIS3LV02DL or LIS3LV02DQ that seems to + * be connected via SPI. There exists also several similar chips (such as LIS302DL or + * LIS3L02DQ) but not in the HP laptops and they have slightly different registers. + * They can also be connected via I²C. + */ + +#define LIS3LV02DL_ID 0x3A /* Also the LIS3LV02DQ */ +#define LIS302DL_ID 0x3B /* Also the LIS202DL! */ + +enum lis3lv02d_reg { + WHO_AM_I = 0x0F, + OFFSET_X = 0x16, + OFFSET_Y = 0x17, + OFFSET_Z = 0x18, + GAIN_X = 0x19, + GAIN_Y = 0x1A, + GAIN_Z = 0x1B, + CTRL_REG1 = 0x20, + CTRL_REG2 = 0x21, + CTRL_REG3 = 0x22, + HP_FILTER_RESET = 0x23, + STATUS_REG = 0x27, + OUTX_L = 0x28, + OUTX_H = 0x29, + OUTY_L = 0x2A, + OUTY_H = 0x2B, + OUTZ_L = 0x2C, + OUTZ_H = 0x2D, + FF_WU_CFG = 0x30, + FF_WU_SRC = 0x31, + FF_WU_ACK = 0x32, + FF_WU_THS_L = 0x34, + FF_WU_THS_H = 0x35, + FF_WU_DURATION = 0x36, + DD_CFG = 0x38, + DD_SRC = 0x39, + DD_ACK = 0x3A, + DD_THSI_L = 0x3C, + DD_THSI_H = 0x3D, + DD_THSE_L = 0x3E, + DD_THSE_H = 0x3F, +}; + +enum lis3lv02d_ctrl1 { + CTRL1_Xen = 0x01, + CTRL1_Yen = 0x02, + CTRL1_Zen = 0x04, + CTRL1_ST = 0x08, + CTRL1_DF0 = 0x10, + CTRL1_DF1 = 0x20, + CTRL1_PD0 = 0x40, + CTRL1_PD1 = 0x80, +}; +enum lis3lv02d_ctrl2 { + CTRL2_DAS = 0x01, + CTRL2_SIM = 0x02, + CTRL2_DRDY = 0x04, + CTRL2_IEN = 0x08, + CTRL2_BOOT = 0x10, + CTRL2_BLE = 0x20, + CTRL2_BDU = 0x40, /* Block Data Update */ + CTRL2_FS = 0x80, /* Full Scale selection */ +}; + + +enum lis3lv02d_ctrl3 { + CTRL3_CFS0 = 0x01, + CTRL3_CFS1 = 0x02, + CTRL3_FDS = 0x10, + CTRL3_HPFF = 0x20, + CTRL3_HPDD = 0x40, + CTRL3_ECK = 0x80, +}; + +enum lis3lv02d_status_reg { + STATUS_XDA = 0x01, + STATUS_YDA = 0x02, + STATUS_ZDA = 0x04, + STATUS_XYZDA = 0x08, + STATUS_XOR = 0x10, + STATUS_YOR = 0x20, + STATUS_ZOR = 0x40, + STATUS_XYZOR = 0x80, +}; + +enum lis3lv02d_ff_wu_cfg { + FF_WU_CFG_XLIE = 0x01, + FF_WU_CFG_XHIE = 0x02, + FF_WU_CFG_YLIE = 0x04, + FF_WU_CFG_YHIE = 0x08, + FF_WU_CFG_ZLIE = 0x10, + FF_WU_CFG_ZHIE = 0x20, + FF_WU_CFG_LIR = 0x40, + FF_WU_CFG_AOI = 0x80, +}; + +enum lis3lv02d_ff_wu_src { + FF_WU_SRC_XL = 0x01, + FF_WU_SRC_XH = 0x02, + FF_WU_SRC_YL = 0x04, + FF_WU_SRC_YH = 0x08, + FF_WU_SRC_ZL = 0x10, + FF_WU_SRC_ZH = 0x20, + FF_WU_SRC_IA = 0x40, +}; + +enum lis3lv02d_dd_cfg { + DD_CFG_XLIE = 0x01, + DD_CFG_XHIE = 0x02, + DD_CFG_YLIE = 0x04, + DD_CFG_YHIE = 0x08, + DD_CFG_ZLIE = 0x10, + DD_CFG_ZHIE = 0x20, + DD_CFG_LIR = 0x40, + DD_CFG_IEND = 0x80, +}; + +enum lis3lv02d_dd_src { + DD_SRC_XL = 0x01, + DD_SRC_XH = 0x02, + DD_SRC_YL = 0x04, + DD_SRC_YH = 0x08, + DD_SRC_ZL = 0x10, + DD_SRC_ZH = 0x20, + DD_SRC_IA = 0x40, +}; + diff --git a/drivers/hwmon/lm85.c b/drivers/hwmon/lm85.c index 3ff0285396f..cfc1ee90f5a 100644 --- a/drivers/hwmon/lm85.c +++ b/drivers/hwmon/lm85.c @@ -39,7 +39,8 @@ static const unsigned short normal_i2c[] = { 0x2c, 0x2d, 0x2e, I2C_CLIENT_END }; /* Insmod parameters */ -I2C_CLIENT_INSMOD_6(lm85b, lm85c, adm1027, adt7463, emc6d100, emc6d102); +I2C_CLIENT_INSMOD_7(lm85b, lm85c, adm1027, adt7463, adt7468, emc6d100, + emc6d102); /* The LM85 registers */ @@ -59,6 +60,12 @@ I2C_CLIENT_INSMOD_6(lm85b, lm85c, adm1027, adt7463, emc6d100, emc6d102); #define LM85_REG_COMPANY 0x3e #define LM85_REG_VERSTEP 0x3f + +#define ADT7468_REG_CFG5 0x7c +#define ADT7468_OFF64 0x01 +#define IS_ADT7468_OFF64(data) \ + ((data)->type == adt7468 && !((data)->cfg5 & ADT7468_OFF64)) + /* These are the recognized values for the above regs */ #define LM85_COMPANY_NATIONAL 0x01 #define LM85_COMPANY_ANALOG_DEV 0x41 @@ -70,6 +77,8 @@ I2C_CLIENT_INSMOD_6(lm85b, lm85c, adm1027, adt7463, emc6d100, emc6d102); #define LM85_VERSTEP_ADM1027 0x60 #define LM85_VERSTEP_ADT7463 0x62 #define LM85_VERSTEP_ADT7463C 0x6A +#define LM85_VERSTEP_ADT7468_1 0x71 +#define LM85_VERSTEP_ADT7468_2 0x72 #define LM85_VERSTEP_EMC6D100_A0 0x60 #define LM85_VERSTEP_EMC6D100_A1 0x61 #define LM85_VERSTEP_EMC6D102 0x65 @@ -306,6 +315,7 @@ struct lm85_data { u8 vid; /* Register value */ u8 vrm; /* VRM version */ u32 alarms; /* Register encoding, combined */ + u8 cfg5; /* Config Register 5 on ADT7468 */ struct lm85_autofan autofan[3]; struct lm85_zone zone[3]; }; @@ -685,6 +695,9 @@ static ssize_t set_temp_min(struct device *dev, struct device_attribute *attr, struct lm85_data *data = i2c_get_clientdata(client); long val = simple_strtol(buf, NULL, 10); + if (IS_ADT7468_OFF64(data)) + val += 64; + mutex_lock(&data->update_lock); data->temp_min[nr] = TEMP_TO_REG(val); lm85_write_value(client, LM85_REG_TEMP_MIN(nr), data->temp_min[nr]); @@ -708,6 +721,9 @@ static ssize_t set_temp_max(struct device *dev, struct device_attribute *attr, struct lm85_data *data = i2c_get_clientdata(client); long val = simple_strtol(buf, NULL, 10); + if (IS_ADT7468_OFF64(data)) + val += 64; + mutex_lock(&data->update_lock); data->temp_max[nr] = TEMP_TO_REG(val); lm85_write_value(client, LM85_REG_TEMP_MAX(nr), data->temp_max[nr]); @@ -1163,6 +1179,10 @@ static int lm85_detect(struct i2c_client *client, int kind, case LM85_VERSTEP_ADT7463C: kind = adt7463; break; + case LM85_VERSTEP_ADT7468_1: + case LM85_VERSTEP_ADT7468_2: + kind = adt7468; + break; } } else if (company == LM85_COMPANY_SMSC) { switch (verstep) { @@ -1195,6 +1215,9 @@ static int lm85_detect(struct i2c_client *client, int kind, case adt7463: type_name = "adt7463"; break; + case adt7468: + type_name = "adt7468"; + break; case emc6d100: type_name = "emc6d100"; break; @@ -1246,10 +1269,11 @@ static int lm85_probe(struct i2c_client *client, if (err) goto err_kfree; - /* The ADT7463 has an optional VRM 10 mode where pin 21 is used + /* The ADT7463/68 have an optional VRM 10 mode where pin 21 is used as a sixth digital VID input rather than an analog input. */ data->vid = lm85_read_value(client, LM85_REG_VID); - if (!(data->type == adt7463 && (data->vid & 0x80))) + if (!((data->type == adt7463 || data->type == adt7468) && + (data->vid & 0x80))) if ((err = sysfs_create_group(&client->dev.kobj, &lm85_group_in4))) goto err_remove_files; @@ -1357,7 +1381,8 @@ static struct lm85_data *lm85_update_device(struct device *dev) * There are 2 additional resolution bits per channel and we * have room for 4, so we shift them to the left. */ - if (data->type == adm1027 || data->type == adt7463) { + if (data->type == adm1027 || data->type == adt7463 || + data->type == adt7468) { int ext1 = lm85_read_value(client, ADM1027_REG_EXTEND_ADC1); int ext2 = lm85_read_value(client, @@ -1382,16 +1407,23 @@ static struct lm85_data *lm85_update_device(struct device *dev) lm85_read_value(client, LM85_REG_FAN(i)); } - if (!(data->type == adt7463 && (data->vid & 0x80))) { + if (!((data->type == adt7463 || data->type == adt7468) && + (data->vid & 0x80))) { data->in[4] = lm85_read_value(client, LM85_REG_IN(4)); } + if (data->type == adt7468) + data->cfg5 = lm85_read_value(client, ADT7468_REG_CFG5); + for (i = 0; i <= 2; ++i) { data->temp[i] = lm85_read_value(client, LM85_REG_TEMP(i)); data->pwm[i] = lm85_read_value(client, LM85_REG_PWM(i)); + + if (IS_ADT7468_OFF64(data)) + data->temp[i] -= 64; } data->alarms = lm85_read_value(client, LM85_REG_ALARM1); @@ -1446,7 +1478,8 @@ static struct lm85_data *lm85_update_device(struct device *dev) lm85_read_value(client, LM85_REG_FAN_MIN(i)); } - if (!(data->type == adt7463 && (data->vid & 0x80))) { + if (!((data->type == adt7463 || data->type == adt7468) && + (data->vid & 0x80))) { data->in_min[4] = lm85_read_value(client, LM85_REG_IN_MIN(4)); data->in_max[4] = lm85_read_value(client, @@ -1481,6 +1514,13 @@ static struct lm85_data *lm85_update_device(struct device *dev) lm85_read_value(client, LM85_REG_AFAN_LIMIT(i)); data->zone[i].critical = lm85_read_value(client, LM85_REG_AFAN_CRITICAL(i)); + + if (IS_ADT7468_OFF64(data)) { + data->temp_min[i] -= 64; + data->temp_max[i] -= 64; + data->zone[i].limit -= 64; + data->zone[i].critical -= 64; + } } i = lm85_read_value(client, LM85_REG_AFAN_SPIKE1); diff --git a/drivers/i2c/busses/i2c-sh_mobile.c b/drivers/i2c/busses/i2c-sh_mobile.c index 640cbb23732..3384a717fec 100644 --- a/drivers/i2c/busses/i2c-sh_mobile.c +++ b/drivers/i2c/busses/i2c-sh_mobile.c @@ -318,7 +318,8 @@ static int sh_mobile_i2c_isr_rx(struct sh_mobile_i2c_data *pd) } else data = i2c_op(pd, OP_RX, 0); - pd->msg->buf[real_pos] = data; + if (real_pos >= 0) + pd->msg->buf[real_pos] = data; } while (0); pd->pos++; diff --git a/drivers/idle/Kconfig b/drivers/idle/Kconfig index 108264de0ac..f15e90a453d 100644 --- a/drivers/idle/Kconfig +++ b/drivers/idle/Kconfig @@ -1,5 +1,6 @@ menu "Memory power savings" +depends on X86_64 config I7300_IDLE_IOAT_CHANNEL bool @@ -7,7 +8,7 @@ config I7300_IDLE_IOAT_CHANNEL config I7300_IDLE tristate "Intel chipset idle memory power saving driver" select I7300_IDLE_IOAT_CHANNEL - depends on X86_64 && EXPERIMENTAL + depends on EXPERIMENTAL help Enable memory power savings when idle with certain Intel server chipsets. The chipset must have I/O AT support, such as the diff --git a/drivers/infiniband/hw/cxgb3/iwch_provider.c b/drivers/infiniband/hw/cxgb3/iwch_provider.c index ecff9804358..160ef482712 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_provider.c +++ b/drivers/infiniband/hw/cxgb3/iwch_provider.c @@ -1102,9 +1102,7 @@ static u64 fw_vers_string_to_u64(struct iwch_dev *iwch_dev) char *cp, *next; unsigned fw_maj, fw_min, fw_mic; - rtnl_lock(); lldev->ethtool_ops->get_drvinfo(lldev, &info); - rtnl_unlock(); next = info.fw_version + 1; cp = strsep(&next, "."); @@ -1192,9 +1190,7 @@ static ssize_t show_fw_ver(struct device *dev, struct device_attribute *attr, ch struct net_device *lldev = iwch_dev->rdev.t3cdev_p->lldev; PDBG("%s dev 0x%p\n", __func__, dev); - rtnl_lock(); lldev->ethtool_ops->get_drvinfo(lldev, &info); - rtnl_unlock(); return sprintf(buf, "%s\n", info.fw_version); } @@ -1207,9 +1203,7 @@ static ssize_t show_hca(struct device *dev, struct device_attribute *attr, struct net_device *lldev = iwch_dev->rdev.t3cdev_p->lldev; PDBG("%s dev 0x%p\n", __func__, dev); - rtnl_lock(); lldev->ethtool_ops->get_drvinfo(lldev, &info); - rtnl_unlock(); return sprintf(buf, "%s\n", info.driver); } diff --git a/drivers/infiniband/hw/cxgb3/iwch_qp.c b/drivers/infiniband/hw/cxgb3/iwch_qp.c index 3e4585c2318..19661b2f040 100644 --- a/drivers/infiniband/hw/cxgb3/iwch_qp.c +++ b/drivers/infiniband/hw/cxgb3/iwch_qp.c @@ -745,7 +745,6 @@ int iwch_post_zb_read(struct iwch_qp *qhp) wqe->read.rdmaop = T3_READ_REQ; wqe->read.reserved[0] = 0; wqe->read.reserved[1] = 0; - wqe->read.reserved[2] = 0; wqe->read.rem_stag = cpu_to_be32(1); wqe->read.rem_to = cpu_to_be64(1); wqe->read.local_stag = cpu_to_be32(1); diff --git a/drivers/infiniband/hw/ehca/ehca_irq.c b/drivers/infiniband/hw/ehca/ehca_irq.c index cb55be04442..757035ea246 100644 --- a/drivers/infiniband/hw/ehca/ehca_irq.c +++ b/drivers/infiniband/hw/ehca/ehca_irq.c @@ -359,36 +359,48 @@ static void notify_port_conf_change(struct ehca_shca *shca, int port_num) *old_attr = new_attr; } +/* replay modify_qp for sqps -- return 0 if all is well, 1 if AQP1 destroyed */ +static int replay_modify_qp(struct ehca_sport *sport) +{ + int aqp1_destroyed; + unsigned long flags; + + spin_lock_irqsave(&sport->mod_sqp_lock, flags); + + aqp1_destroyed = !sport->ibqp_sqp[IB_QPT_GSI]; + + if (sport->ibqp_sqp[IB_QPT_SMI]) + ehca_recover_sqp(sport->ibqp_sqp[IB_QPT_SMI]); + if (!aqp1_destroyed) + ehca_recover_sqp(sport->ibqp_sqp[IB_QPT_GSI]); + + spin_unlock_irqrestore(&sport->mod_sqp_lock, flags); + + return aqp1_destroyed; +} + static void parse_ec(struct ehca_shca *shca, u64 eqe) { u8 ec = EHCA_BMASK_GET(NEQE_EVENT_CODE, eqe); u8 port = EHCA_BMASK_GET(NEQE_PORT_NUMBER, eqe); u8 spec_event; struct ehca_sport *sport = &shca->sport[port - 1]; - unsigned long flags; switch (ec) { case 0x30: /* port availability change */ if (EHCA_BMASK_GET(NEQE_PORT_AVAILABILITY, eqe)) { - int suppress_event; - /* replay modify_qp for sqps */ - spin_lock_irqsave(&sport->mod_sqp_lock, flags); - suppress_event = !sport->ibqp_sqp[IB_QPT_GSI]; - if (sport->ibqp_sqp[IB_QPT_SMI]) - ehca_recover_sqp(sport->ibqp_sqp[IB_QPT_SMI]); - if (!suppress_event) - ehca_recover_sqp(sport->ibqp_sqp[IB_QPT_GSI]); - spin_unlock_irqrestore(&sport->mod_sqp_lock, flags); - - /* AQP1 was destroyed, ignore this event */ - if (suppress_event) - break; + /* only replay modify_qp calls in autodetect mode; + * if AQP1 was destroyed, the port is already down + * again and we can drop the event. + */ + if (ehca_nr_ports < 0) + if (replay_modify_qp(sport)) + break; sport->port_state = IB_PORT_ACTIVE; dispatch_port_event(shca, port, IB_EVENT_PORT_ACTIVE, "is active"); - ehca_query_sma_attr(shca, port, - &sport->saved_attr); + ehca_query_sma_attr(shca, port, &sport->saved_attr); } else { sport->port_state = IB_PORT_DOWN; dispatch_port_event(shca, port, IB_EVENT_PORT_ERR, diff --git a/drivers/infiniband/hw/ehca/ehca_qp.c b/drivers/infiniband/hw/ehca/ehca_qp.c index 4d54b9f6456..9e05ee2db39 100644 --- a/drivers/infiniband/hw/ehca/ehca_qp.c +++ b/drivers/infiniband/hw/ehca/ehca_qp.c @@ -860,6 +860,11 @@ static struct ehca_qp *internal_create_qp( if (qp_type == IB_QPT_GSI) { h_ret = ehca_define_sqp(shca, my_qp, init_attr); if (h_ret != H_SUCCESS) { + kfree(my_qp->mod_qp_parm); + my_qp->mod_qp_parm = NULL; + /* the QP pointer is no longer valid */ + shca->sport[init_attr->port_num - 1].ibqp_sqp[qp_type] = + NULL; ret = ehca2ib_return_code(h_ret); goto create_qp_exit6; } diff --git a/drivers/infiniband/hw/ipath/ipath_ruc.c b/drivers/infiniband/hw/ipath/ipath_ruc.c index fc0f6d9e603..2296832f94d 100644 --- a/drivers/infiniband/hw/ipath/ipath_ruc.c +++ b/drivers/infiniband/hw/ipath/ipath_ruc.c @@ -156,7 +156,7 @@ bail: /** * ipath_get_rwqe - copy the next RWQE into the QP's RWQE * @qp: the QP - * @wr_id_only: update wr_id only, not SGEs + * @wr_id_only: update qp->r_wr_id only, not qp->r_sge * * Return 0 if no RWQE is available, otherwise return 1. * @@ -173,8 +173,6 @@ int ipath_get_rwqe(struct ipath_qp *qp, int wr_id_only) u32 tail; int ret; - qp->r_sge.sg_list = qp->r_sg_list; - if (qp->ibqp.srq) { srq = to_isrq(qp->ibqp.srq); handler = srq->ibsrq.event_handler; @@ -206,8 +204,10 @@ int ipath_get_rwqe(struct ipath_qp *qp, int wr_id_only) wqe = get_rwqe_ptr(rq, tail); if (++tail >= rq->size) tail = 0; - } while (!wr_id_only && !ipath_init_sge(qp, wqe, &qp->r_len, - &qp->r_sge)); + if (wr_id_only) + break; + qp->r_sge.sg_list = qp->r_sg_list; + } while (!ipath_init_sge(qp, wqe, &qp->r_len, &qp->r_sge)); qp->r_wr_id = wqe->wr_id; wq->tail = tail; diff --git a/drivers/infiniband/hw/mlx4/mr.c b/drivers/infiniband/hw/mlx4/mr.c index 87f5c5a87b9..8e4d26d56a9 100644 --- a/drivers/infiniband/hw/mlx4/mr.c +++ b/drivers/infiniband/hw/mlx4/mr.c @@ -205,6 +205,7 @@ struct ib_mr *mlx4_ib_alloc_fast_reg_mr(struct ib_pd *pd, goto err_mr; mr->ibmr.rkey = mr->ibmr.lkey = mr->mmr.key; + mr->umem = NULL; return &mr->ibmr; diff --git a/drivers/infiniband/hw/nes/nes.c b/drivers/infiniband/hw/nes/nes.c index a2b04d62b1a..aa1dc41f04c 100644 --- a/drivers/infiniband/hw/nes/nes.c +++ b/drivers/infiniband/hw/nes/nes.c @@ -95,6 +95,10 @@ unsigned int wqm_quanta = 0x10000; module_param(wqm_quanta, int, 0644); MODULE_PARM_DESC(wqm_quanta, "WQM quanta"); +static unsigned int limit_maxrdreqsz; +module_param(limit_maxrdreqsz, bool, 0644); +MODULE_PARM_DESC(limit_maxrdreqsz, "Limit max read request size to 256 Bytes"); + LIST_HEAD(nes_adapter_list); static LIST_HEAD(nes_dev_list); @@ -588,6 +592,18 @@ static int __devinit nes_probe(struct pci_dev *pcidev, const struct pci_device_i nesdev->nesadapter->port_count; } + if ((limit_maxrdreqsz || + ((nesdev->nesadapter->phy_type[0] == NES_PHY_TYPE_GLADIUS) && + (hw_rev == NE020_REV1))) && + (pcie_get_readrq(pcidev) > 256)) { + if (pcie_set_readrq(pcidev, 256)) + printk(KERN_ERR PFX "Unable to set max read request" + " to 256 bytes\n"); + else + nes_debug(NES_DBG_INIT, "Max read request size set" + " to 256 bytes\n"); + } + tasklet_init(&nesdev->dpc_tasklet, nes_dpc, (unsigned long)nesdev); /* bring up the Control QP */ diff --git a/drivers/infiniband/hw/nes/nes_hw.h b/drivers/infiniband/hw/nes/nes_hw.h index 610b9d85959..bc0b4de0445 100644 --- a/drivers/infiniband/hw/nes/nes_hw.h +++ b/drivers/infiniband/hw/nes/nes_hw.h @@ -40,6 +40,7 @@ #define NES_PHY_TYPE_ARGUS 4 #define NES_PHY_TYPE_PUMA_1G 5 #define NES_PHY_TYPE_PUMA_10G 6 +#define NES_PHY_TYPE_GLADIUS 7 #define NES_MULTICAST_PF_MAX 8 diff --git a/drivers/infiniband/hw/nes/nes_verbs.c b/drivers/infiniband/hw/nes/nes_verbs.c index 932e56fcf77..d36c9a0bf1b 100644 --- a/drivers/infiniband/hw/nes/nes_verbs.c +++ b/drivers/infiniband/hw/nes/nes_verbs.c @@ -220,14 +220,14 @@ static int nes_bind_mw(struct ib_qp *ibqp, struct ib_mw *ibmw, if (nesqp->ibqp_state > IB_QPS_RTS) return -EINVAL; - spin_lock_irqsave(&nesqp->lock, flags); + spin_lock_irqsave(&nesqp->lock, flags); head = nesqp->hwqp.sq_head; qsize = nesqp->hwqp.sq_tail; /* Check for SQ overflow */ if (((head + (2 * qsize) - nesqp->hwqp.sq_tail) % qsize) == (qsize - 1)) { - spin_unlock_irqrestore(&nesqp->lock, flags); + spin_unlock_irqrestore(&nesqp->lock, flags); return -EINVAL; } @@ -269,7 +269,7 @@ static int nes_bind_mw(struct ib_qp *ibqp, struct ib_mw *ibmw, nes_write32(nesdev->regs+NES_WQE_ALLOC, (1 << 24) | 0x00800000 | nesqp->hwqp.qp_id); - spin_unlock_irqrestore(&nesqp->lock, flags); + spin_unlock_irqrestore(&nesqp->lock, flags); return 0; } @@ -349,7 +349,7 @@ static struct ib_fmr *nes_alloc_fmr(struct ib_pd *ibpd, if (nesfmr->nesmr.pbls_used > nesadapter->free_4kpbl) { spin_unlock_irqrestore(&nesadapter->pbl_lock, flags); ret = -ENOMEM; - goto failed_vpbl_alloc; + goto failed_vpbl_avail; } else { nesadapter->free_4kpbl -= nesfmr->nesmr.pbls_used; } @@ -357,7 +357,7 @@ static struct ib_fmr *nes_alloc_fmr(struct ib_pd *ibpd, if (nesfmr->nesmr.pbls_used > nesadapter->free_256pbl) { spin_unlock_irqrestore(&nesadapter->pbl_lock, flags); ret = -ENOMEM; - goto failed_vpbl_alloc; + goto failed_vpbl_avail; } else { nesadapter->free_256pbl -= nesfmr->nesmr.pbls_used; } @@ -391,14 +391,14 @@ static struct ib_fmr *nes_alloc_fmr(struct ib_pd *ibpd, goto failed_vpbl_alloc; } - nesfmr->root_vpbl.leaf_vpbl = kzalloc(sizeof(*nesfmr->root_vpbl.leaf_vpbl)*1024, GFP_KERNEL); + nesfmr->leaf_pbl_cnt = nesfmr->nesmr.pbls_used-1; + nesfmr->root_vpbl.leaf_vpbl = kzalloc(sizeof(*nesfmr->root_vpbl.leaf_vpbl)*1024, GFP_ATOMIC); if (!nesfmr->root_vpbl.leaf_vpbl) { spin_unlock_irqrestore(&nesadapter->pbl_lock, flags); ret = -ENOMEM; goto failed_leaf_vpbl_alloc; } - nesfmr->leaf_pbl_cnt = nesfmr->nesmr.pbls_used-1; nes_debug(NES_DBG_MR, "two level pbl, root_vpbl.pbl_vbase=%p" " leaf_pbl_cnt=%d root_vpbl.leaf_vpbl=%p\n", nesfmr->root_vpbl.pbl_vbase, nesfmr->leaf_pbl_cnt, nesfmr->root_vpbl.leaf_vpbl); @@ -519,6 +519,16 @@ static struct ib_fmr *nes_alloc_fmr(struct ib_pd *ibpd, nesfmr->root_vpbl.pbl_pbase); failed_vpbl_alloc: + if (nesfmr->nesmr.pbls_used != 0) { + spin_lock_irqsave(&nesadapter->pbl_lock, flags); + if (nesfmr->nesmr.pbl_4k) + nesadapter->free_4kpbl += nesfmr->nesmr.pbls_used; + else + nesadapter->free_256pbl += nesfmr->nesmr.pbls_used; + spin_unlock_irqrestore(&nesadapter->pbl_lock, flags); + } + +failed_vpbl_avail: kfree(nesfmr); failed_fmr_alloc: @@ -534,18 +544,14 @@ static struct ib_fmr *nes_alloc_fmr(struct ib_pd *ibpd, */ static int nes_dealloc_fmr(struct ib_fmr *ibfmr) { + unsigned long flags; struct nes_mr *nesmr = to_nesmr_from_ibfmr(ibfmr); struct nes_fmr *nesfmr = to_nesfmr(nesmr); struct nes_vnic *nesvnic = to_nesvnic(ibfmr->device); struct nes_device *nesdev = nesvnic->nesdev; - struct nes_mr temp_nesmr = *nesmr; + struct nes_adapter *nesadapter = nesdev->nesadapter; int i = 0; - temp_nesmr.ibmw.device = ibfmr->device; - temp_nesmr.ibmw.pd = ibfmr->pd; - temp_nesmr.ibmw.rkey = ibfmr->rkey; - temp_nesmr.ibmw.uobject = NULL; - /* free the resources */ if (nesfmr->leaf_pbl_cnt == 0) { /* single PBL case */ @@ -561,8 +567,24 @@ static int nes_dealloc_fmr(struct ib_fmr *ibfmr) pci_free_consistent(nesdev->pcidev, 8192, nesfmr->root_vpbl.pbl_vbase, nesfmr->root_vpbl.pbl_pbase); } + nesmr->ibmw.device = ibfmr->device; + nesmr->ibmw.pd = ibfmr->pd; + nesmr->ibmw.rkey = ibfmr->rkey; + nesmr->ibmw.uobject = NULL; - return nes_dealloc_mw(&temp_nesmr.ibmw); + if (nesfmr->nesmr.pbls_used != 0) { + spin_lock_irqsave(&nesadapter->pbl_lock, flags); + if (nesfmr->nesmr.pbl_4k) { + nesadapter->free_4kpbl += nesfmr->nesmr.pbls_used; + WARN_ON(nesadapter->free_4kpbl > nesadapter->max_4kpbl); + } else { + nesadapter->free_256pbl += nesfmr->nesmr.pbls_used; + WARN_ON(nesadapter->free_256pbl > nesadapter->max_256pbl); + } + spin_unlock_irqrestore(&nesadapter->pbl_lock, flags); + } + + return nes_dealloc_mw(&nesmr->ibmw); } @@ -1595,7 +1617,7 @@ static struct ib_cq *nes_create_cq(struct ib_device *ibdev, int entries, nes_ucontext->mcrqf = req.mcrqf; if (nes_ucontext->mcrqf) { if (nes_ucontext->mcrqf & 0x80000000) - nescq->hw_cq.cq_number = nesvnic->nic.qp_id + 12 + (nes_ucontext->mcrqf & 0xf) - 1; + nescq->hw_cq.cq_number = nesvnic->nic.qp_id + 28 + 2 * ((nes_ucontext->mcrqf & 0xf) - 1); else if (nes_ucontext->mcrqf & 0x40000000) nescq->hw_cq.cq_number = nes_ucontext->mcrqf & 0xffff; else @@ -3212,7 +3234,7 @@ static int nes_post_send(struct ib_qp *ibqp, struct ib_send_wr *ib_wr, if (nesqp->ibqp_state > IB_QPS_RTS) return -EINVAL; - spin_lock_irqsave(&nesqp->lock, flags); + spin_lock_irqsave(&nesqp->lock, flags); head = nesqp->hwqp.sq_head; @@ -3337,7 +3359,7 @@ static int nes_post_send(struct ib_qp *ibqp, struct ib_send_wr *ib_wr, (counter << 24) | 0x00800000 | nesqp->hwqp.qp_id); } - spin_unlock_irqrestore(&nesqp->lock, flags); + spin_unlock_irqrestore(&nesqp->lock, flags); if (err) *bad_wr = ib_wr; @@ -3368,7 +3390,7 @@ static int nes_post_recv(struct ib_qp *ibqp, struct ib_recv_wr *ib_wr, if (nesqp->ibqp_state > IB_QPS_RTS) return -EINVAL; - spin_lock_irqsave(&nesqp->lock, flags); + spin_lock_irqsave(&nesqp->lock, flags); head = nesqp->hwqp.rq_head; @@ -3421,7 +3443,7 @@ static int nes_post_recv(struct ib_qp *ibqp, struct ib_recv_wr *ib_wr, nes_write32(nesdev->regs+NES_WQE_ALLOC, (counter<<24) | nesqp->hwqp.qp_id); } - spin_unlock_irqrestore(&nesqp->lock, flags); + spin_unlock_irqrestore(&nesqp->lock, flags); if (err) *bad_wr = ib_wr; @@ -3453,7 +3475,7 @@ static int nes_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry) nes_debug(NES_DBG_CQ, "\n"); - spin_lock_irqsave(&nescq->lock, flags); + spin_lock_irqsave(&nescq->lock, flags); head = nescq->hw_cq.cq_head; cq_size = nescq->hw_cq.cq_size; @@ -3562,7 +3584,7 @@ static int nes_poll_cq(struct ib_cq *ibcq, int num_entries, struct ib_wc *entry) nes_debug(NES_DBG_CQ, "Reporting %u completions for CQ%u.\n", cqe_count, nescq->hw_cq.cq_number); - spin_unlock_irqrestore(&nescq->lock, flags); + spin_unlock_irqrestore(&nescq->lock, flags); return cqe_count; } diff --git a/drivers/infiniband/ulp/ipoib/ipoib_main.c b/drivers/infiniband/ulp/ipoib/ipoib_main.c index fddded7900d..85257f6b957 100644 --- a/drivers/infiniband/ulp/ipoib/ipoib_main.c +++ b/drivers/infiniband/ulp/ipoib/ipoib_main.c @@ -106,12 +106,13 @@ int ipoib_open(struct net_device *dev) ipoib_dbg(priv, "bringing up interface\n"); - napi_enable(&priv->napi); set_bit(IPOIB_FLAG_ADMIN_UP, &priv->flags); if (ipoib_pkey_dev_delay_open(dev)) return 0; + napi_enable(&priv->napi); + if (ipoib_ib_dev_open(dev)) { napi_disable(&priv->napi); return -EINVAL; @@ -546,6 +547,7 @@ static int path_rec_start(struct net_device *dev, if (path->query_id < 0) { ipoib_warn(priv, "ib_sa_path_rec_get failed: %d\n", path->query_id); path->query = NULL; + complete(&path->done); return path->query_id; } @@ -662,7 +664,7 @@ static void unicast_arp_send(struct sk_buff *skb, struct net_device *dev, skb_push(skb, sizeof *phdr); __skb_queue_tail(&path->queue, skb); - if (path_rec_start(dev, path)) { + if (!path->query && path_rec_start(dev, path)) { spin_unlock_irqrestore(&priv->lock, flags); path_free(dev, path); return; diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c index 4840733cd90..3d7f4923cd1 100644 --- a/drivers/md/dm-mpath.c +++ b/drivers/md/dm-mpath.c @@ -441,13 +441,13 @@ static void process_queued_ios(struct work_struct *work) __choose_pgpath(m); pgpath = m->current_pgpath; - m->pgpath_to_activate = m->current_pgpath; if ((pgpath && !m->queue_io) || (!pgpath && !m->queue_if_no_path)) must_queue = 0; - if (m->pg_init_required && !m->pg_init_in_progress) { + if (m->pg_init_required && !m->pg_init_in_progress && pgpath) { + m->pgpath_to_activate = pgpath; m->pg_init_count++; m->pg_init_required = 0; m->pg_init_in_progress = 1; @@ -708,6 +708,10 @@ static int parse_hw_handler(struct arg_set *as, struct multipath *m) m->hw_handler_name = NULL; return -EINVAL; } + + if (hw_argc > 1) + DMWARN("Ignoring user-specified arguments for " + "hardware handler \"%s\"", m->hw_handler_name); consume(as, hw_argc - 1); return 0; diff --git a/drivers/md/dm-raid1.c b/drivers/md/dm-raid1.c index 9d7b53ed75b..ec43f9fa4b2 100644 --- a/drivers/md/dm-raid1.c +++ b/drivers/md/dm-raid1.c @@ -1032,6 +1032,7 @@ static void mirror_dtr(struct dm_target *ti) del_timer_sync(&ms->timer); flush_workqueue(ms->kmirrord_wq); + flush_scheduled_work(); dm_kcopyd_client_destroy(ms->kcopyd_client); destroy_workqueue(ms->kmirrord_wq); free_context(ms, ti, ms->nr_mirrors); diff --git a/drivers/md/dm-stripe.c b/drivers/md/dm-stripe.c index a2d068dbe9e..9e4ef88d421 100644 --- a/drivers/md/dm-stripe.c +++ b/drivers/md/dm-stripe.c @@ -320,8 +320,10 @@ int __init dm_stripe_init(void) int r; r = dm_register_target(&stripe_target); - if (r < 0) + if (r < 0) { DMWARN("target registration failed"); + return r; + } kstriped = create_singlethread_workqueue("kstriped"); if (!kstriped) { diff --git a/drivers/md/dm.c b/drivers/md/dm.c index 6963ad14840..c99e4728ff4 100644 --- a/drivers/md/dm.c +++ b/drivers/md/dm.c @@ -375,7 +375,7 @@ static void start_io_acct(struct dm_io *io) dm_disk(md)->part0.in_flight = atomic_inc_return(&md->pending); } -static int end_io_acct(struct dm_io *io) +static void end_io_acct(struct dm_io *io) { struct mapped_device *md = io->md; struct bio *bio = io->bio; @@ -391,7 +391,9 @@ static int end_io_acct(struct dm_io *io) dm_disk(md)->part0.in_flight = pending = atomic_dec_return(&md->pending); - return !pending; + /* nudge anyone waiting on suspend queue */ + if (!pending) + wake_up(&md->wait); } /* @@ -499,9 +501,7 @@ static void dec_pending(struct dm_io *io, int error) spin_unlock_irqrestore(&io->md->pushback_lock, flags); } - if (end_io_acct(io)) - /* nudge anyone waiting on suspend queue */ - wake_up(&io->md->wait); + end_io_acct(io); if (io->error != DM_ENDIO_REQUEUE) { blk_add_trace_bio(io->md->queue, io->bio, @@ -937,16 +937,24 @@ static void dm_unplug_all(struct request_queue *q) static int dm_any_congested(void *congested_data, int bdi_bits) { - int r; - struct mapped_device *md = (struct mapped_device *) congested_data; - struct dm_table *map = dm_get_table(md); + int r = bdi_bits; + struct mapped_device *md = congested_data; + struct dm_table *map; - if (!map || test_bit(DMF_BLOCK_IO, &md->flags)) - r = bdi_bits; - else - r = dm_table_any_congested(map, bdi_bits); + atomic_inc(&md->pending); + + if (!test_bit(DMF_BLOCK_IO, &md->flags)) { + map = dm_get_table(md); + if (map) { + r = dm_table_any_congested(map, bdi_bits); + dm_table_put(map); + } + } + + if (!atomic_dec_return(&md->pending)) + /* nudge anyone waiting on suspend queue */ + wake_up(&md->wait); - dm_table_put(map); return r; } diff --git a/drivers/media/video/tvaudio.c b/drivers/media/video/tvaudio.c index b59e47272ab..3720f0e03a1 100644 --- a/drivers/media/video/tvaudio.c +++ b/drivers/media/video/tvaudio.c @@ -1,5 +1,5 @@ /* - * experimental driver for simple i2c audio chips. + * Driver for simple i2c audio chips. * * Copyright (c) 2000 Gerd Knorr * based on code by: @@ -7,6 +7,10 @@ * Steve VanDeBogart (vandebo@uclink.berkeley.edu) * Greg Alexander (galexand@acm.org) * + * Copyright(c) 2005-2008 Mauro Carvalho Chehab + * - Some cleanups, code fixes, etc + * - Convert it to V4L2 API + * * This code is placed under the terms of the GNU General Public License * * OPTIONS: @@ -30,6 +34,7 @@ #include <media/tvaudio.h> #include <media/v4l2-common.h> +#include <media/v4l2-ioctl.h> #include <media/v4l2-chip-ident.h> #include <media/v4l2-i2c-drv-legacy.h> @@ -58,7 +63,6 @@ typedef int (*checkit)(struct CHIPSTATE*); typedef int (*initialize)(struct CHIPSTATE*); typedef int (*getmode)(struct CHIPSTATE*); typedef void (*setmode)(struct CHIPSTATE*, int mode); -typedef void (*checkmode)(struct CHIPSTATE*); /* i2c command */ typedef struct AUDIOCMD { @@ -79,6 +83,7 @@ struct CHIPDESC { #define CHIP_HAS_VOLUME 1 #define CHIP_HAS_BASSTREBLE 2 #define CHIP_HAS_INPUTSEL 4 +#define CHIP_NEED_CHECKMODE 8 /* various i2c command sequences */ audiocmd init; @@ -96,23 +101,20 @@ struct CHIPDESC { getmode getmode; setmode setmode; - /* check / autoswitch audio after channel switches */ - checkmode checkmode; - /* input switch register + values for v4l inputs */ int inputreg; int inputmap[4]; int inputmute; int inputmask; }; -static struct CHIPDESC chiplist[]; /* current state of the chip */ struct CHIPSTATE { struct i2c_client *c; - /* index into CHIPDESC array */ - int type; + /* chip-specific description - should point to + an entry at CHIPDESC table */ + struct CHIPDESC *desc; /* shadow register set */ audiocmd shadow; @@ -152,7 +154,7 @@ static int chip_write(struct CHIPSTATE *chip, int subaddr, int val) { unsigned char buffer[2]; - if (-1 == subaddr) { + if (subaddr < 0) { v4l_dbg(1, debug, chip->c, "%s: chip_write: 0x%x\n", chip->c->name, val); chip->shadow.bytes[1] = val; @@ -163,6 +165,13 @@ static int chip_write(struct CHIPSTATE *chip, int subaddr, int val) return -1; } } else { + if (subaddr + 1 >= ARRAY_SIZE(chip->shadow.bytes)) { + v4l_info(chip->c, + "Tried to access a non-existent register: %d\n", + subaddr); + return -EINVAL; + } + v4l_dbg(1, debug, chip->c, "%s: chip_write: reg%d=0x%x\n", chip->c->name, subaddr, val); chip->shadow.bytes[subaddr+1] = val; @@ -177,12 +186,20 @@ static int chip_write(struct CHIPSTATE *chip, int subaddr, int val) return 0; } -static int chip_write_masked(struct CHIPSTATE *chip, int subaddr, int val, int mask) +static int chip_write_masked(struct CHIPSTATE *chip, + int subaddr, int val, int mask) { if (mask != 0) { - if (-1 == subaddr) { + if (subaddr < 0) { val = (chip->shadow.bytes[1] & ~mask) | (val & mask); } else { + if (subaddr + 1 >= ARRAY_SIZE(chip->shadow.bytes)) { + v4l_info(chip->c, + "Tried to access a non-existent register: %d\n", + subaddr); + return -EINVAL; + } + val = (chip->shadow.bytes[subaddr+1] & ~mask) | (val & mask); } } @@ -228,6 +245,15 @@ static int chip_cmd(struct CHIPSTATE *chip, char *name, audiocmd *cmd) if (0 == cmd->count) return 0; + if (cmd->count + cmd->bytes[0] - 1 >= ARRAY_SIZE(chip->shadow.bytes)) { + v4l_info(chip->c, + "Tried to access a non-existent register range: %d to %d\n", + cmd->bytes[0] + 1, cmd->bytes[0] + cmd->count - 1); + return -EINVAL; + } + + /* FIXME: it seems that the shadow bytes are wrong bellow !*/ + /* update our shadow register set; print bytes if (debug > 0) */ v4l_dbg(1, debug, chip->c, "%s: chip_cmd(%s): reg=%d, data:", chip->c->name, name,cmd->bytes[0]); @@ -263,7 +289,8 @@ static void chip_thread_wake(unsigned long data) static int chip_thread(void *data) { struct CHIPSTATE *chip = data; - struct CHIPDESC *desc = chiplist + chip->type; + struct CHIPDESC *desc = chip->desc; + int mode; v4l_dbg(1, debug, chip->c, "%s: thread started\n", chip->c->name); set_freezable(); @@ -282,7 +309,26 @@ static int chip_thread(void *data) continue; /* have a look what's going on */ - desc->checkmode(chip); + mode = desc->getmode(chip); + if (mode == chip->prevmode) + continue; + + /* chip detected a new audio mode - set it */ + v4l_dbg(1, debug, chip->c, "%s: thread checkmode\n", + chip->c->name); + + chip->prevmode = mode; + + if (mode & V4L2_TUNER_MODE_STEREO) + desc->setmode(chip, V4L2_TUNER_MODE_STEREO); + if (mode & V4L2_TUNER_MODE_LANG1_LANG2) + desc->setmode(chip, V4L2_TUNER_MODE_STEREO); + else if (mode & V4L2_TUNER_MODE_LANG1) + desc->setmode(chip, V4L2_TUNER_MODE_LANG1); + else if (mode & V4L2_TUNER_MODE_LANG2) + desc->setmode(chip, V4L2_TUNER_MODE_LANG2); + else + desc->setmode(chip, V4L2_TUNER_MODE_MONO); /* schedule next check */ mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000)); @@ -292,29 +338,6 @@ static int chip_thread(void *data) return 0; } -static void generic_checkmode(struct CHIPSTATE *chip) -{ - struct CHIPDESC *desc = chiplist + chip->type; - int mode = desc->getmode(chip); - - if (mode == chip->prevmode) - return; - - v4l_dbg(1, debug, chip->c, "%s: thread checkmode\n", chip->c->name); - chip->prevmode = mode; - - if (mode & V4L2_TUNER_MODE_STEREO) - desc->setmode(chip,V4L2_TUNER_MODE_STEREO); - if (mode & V4L2_TUNER_MODE_LANG1_LANG2) - desc->setmode(chip,V4L2_TUNER_MODE_STEREO); - else if (mode & V4L2_TUNER_MODE_LANG1) - desc->setmode(chip,V4L2_TUNER_MODE_LANG1); - else if (mode & V4L2_TUNER_MODE_LANG2) - desc->setmode(chip,V4L2_TUNER_MODE_LANG2); - else - desc->setmode(chip,V4L2_TUNER_MODE_MONO); -} - /* ---------------------------------------------------------------------- */ /* audio chip descriptions - defines+functions for tda9840 */ @@ -777,7 +800,7 @@ static struct tda9874a_MODES { char *name; audiocmd cmd; } tda9874a_modelist[9] = { - { "A2, B/G", + { "A2, B/G", /* default */ { 9, { TDA9874A_C1FRA, 0x72,0x95,0x55, 0x77,0xA0,0x00, 0x00,0x00 }} }, { "A2, M (Korea)", { 9, { TDA9874A_C1FRA, 0x5D,0xC0,0x00, 0x62,0x6A,0xAA, 0x20,0x22 }} }, @@ -791,7 +814,7 @@ static struct tda9874a_MODES { { 9, { TDA9874A_C1FRA, 0x7D,0x00,0x00, 0x88,0x8A,0xAA, 0x08,0x33 }} }, { "NICAM, B/G", { 9, { TDA9874A_C1FRA, 0x72,0x95,0x55, 0x79,0xEA,0xAA, 0x08,0x33 }} }, - { "NICAM, D/K", /* default */ + { "NICAM, D/K", { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x79,0xEA,0xAA, 0x08,0x33 }} }, { "NICAM, L", { 9, { TDA9874A_C1FRA, 0x87,0x6A,0xAA, 0x79,0xEA,0xAA, 0x09,0x33 }} } @@ -981,7 +1004,7 @@ static int tda9874a_initialize(struct CHIPSTATE *chip) { if (tda9874a_SIF > 2) tda9874a_SIF = 1; - if (tda9874a_STD > 8) + if (tda9874a_STD >= ARRAY_SIZE(tda9874a_modelist)) tda9874a_STD = 0; if(tda9874a_AMSEL > 1) tda9874a_AMSEL = 0; @@ -1089,7 +1112,7 @@ static int tda8425_shift12(int val) { return (val >> 12) | 0xf0; } static int tda8425_initialize(struct CHIPSTATE *chip) { - struct CHIPDESC *desc = chiplist + chip->type; + struct CHIPDESC *desc = chip->desc; int inputmap[4] = { /* tuner */ TDA8425_S1_CH2, /* radio */ TDA8425_S1_CH1, /* extern */ TDA8425_S1_CH1, /* intern */ TDA8425_S1_OFF}; @@ -1259,27 +1282,28 @@ static struct CHIPDESC chiplist[] = { .addr_lo = I2C_ADDR_TDA9840 >> 1, .addr_hi = I2C_ADDR_TDA9840 >> 1, .registers = 5, + .flags = CHIP_NEED_CHECKMODE, + /* callbacks */ .checkit = tda9840_checkit, .getmode = tda9840_getmode, .setmode = tda9840_setmode, - .checkmode = generic_checkmode, .init = { 2, { TDA9840_TEST, TDA9840_TEST_INT1SN /* ,TDA9840_SW, TDA9840_MONO */} } }, { .name = "tda9873h", - .checkit = tda9873_checkit, .insmodopt = &tda9873, .addr_lo = I2C_ADDR_TDA985x_L >> 1, .addr_hi = I2C_ADDR_TDA985x_H >> 1, .registers = 3, - .flags = CHIP_HAS_INPUTSEL, + .flags = CHIP_HAS_INPUTSEL | CHIP_NEED_CHECKMODE, + /* callbacks */ + .checkit = tda9873_checkit, .getmode = tda9873_getmode, .setmode = tda9873_setmode, - .checkmode = generic_checkmode, .init = { 4, { TDA9873_SW, 0xa4, 0x06, 0x03 } }, .inputreg = TDA9873_SW, @@ -1290,15 +1314,16 @@ static struct CHIPDESC chiplist[] = { }, { .name = "tda9874h/a", - .checkit = tda9874a_checkit, - .initialize = tda9874a_initialize, .insmodopt = &tda9874a, .addr_lo = I2C_ADDR_TDA9874 >> 1, .addr_hi = I2C_ADDR_TDA9874 >> 1, + .flags = CHIP_NEED_CHECKMODE, + /* callbacks */ + .initialize = tda9874a_initialize, + .checkit = tda9874a_checkit, .getmode = tda9874a_getmode, .setmode = tda9874a_setmode, - .checkmode = generic_checkmode, }, { .name = "tda9850", @@ -1324,10 +1349,11 @@ static struct CHIPDESC chiplist[] = { .rightreg = TDA9855_VR, .bassreg = TDA9855_BA, .treblereg = TDA9855_TR, + + /* callbacks */ .volfunc = tda9855_volume, .bassfunc = tda9855_bass, .treblefunc = tda9855_treble, - .getmode = tda985x_getmode, .setmode = tda985x_setmode, @@ -1348,6 +1374,8 @@ static struct CHIPDESC chiplist[] = { .rightreg = TEA6300_VL, .bassreg = TEA6300_BA, .treblereg = TEA6300_TR, + + /* callbacks */ .volfunc = tea6300_shift10, .bassfunc = tea6300_shift12, .treblefunc = tea6300_shift12, @@ -1358,7 +1386,6 @@ static struct CHIPDESC chiplist[] = { }, { .name = "tea6320", - .initialize = tea6320_initialize, .insmodopt = &tea6320, .addr_lo = I2C_ADDR_TEA6300 >> 1, .addr_hi = I2C_ADDR_TEA6300 >> 1, @@ -1369,6 +1396,9 @@ static struct CHIPDESC chiplist[] = { .rightreg = TEA6320_V, .bassreg = TEA6320_BA, .treblereg = TEA6320_TR, + + /* callbacks */ + .initialize = tea6320_initialize, .volfunc = tea6320_volume, .bassfunc = tea6320_shift11, .treblefunc = tea6320_shift11, @@ -1401,16 +1431,18 @@ static struct CHIPDESC chiplist[] = { .rightreg = TDA8425_VR, .bassreg = TDA8425_BA, .treblereg = TDA8425_TR, + + /* callbacks */ + .initialize = tda8425_initialize, .volfunc = tda8425_shift10, .bassfunc = tda8425_shift12, .treblefunc = tda8425_shift12, + .setmode = tda8425_setmode, .inputreg = TDA8425_S1, .inputmap = { TDA8425_S1_CH1, TDA8425_S1_CH1, TDA8425_S1_CH1 }, .inputmute = TDA8425_S1_OFF, - .setmode = tda8425_setmode, - .initialize = tda8425_initialize, }, { .name = "pic16c54 (PV951)", @@ -1434,10 +1466,11 @@ static struct CHIPDESC chiplist[] = { .addr_lo = I2C_ADDR_TDA9840 >> 1, .addr_hi = I2C_ADDR_TDA9840 >> 1, .registers = 2, + .flags = CHIP_NEED_CHECKMODE, + /* callbacks */ .getmode = ta8874z_getmode, .setmode = ta8874z_setmode, - .checkmode = generic_checkmode, .init = {2, { TA8874Z_MONO_SET, TA8874Z_SEPARATION_DEFAULT}}, }, @@ -1481,6 +1514,7 @@ static int chip_probe(struct i2c_client *client, const struct i2c_device_id *id) } if (desc->name == NULL) { v4l_dbg(1, debug, client, "no matching chip description found\n"); + kfree(chip); return -EIO; } v4l_info(client, "%s found @ 0x%x (%s)\n", desc->name, client->addr<<1, client->adapter->name); @@ -1494,7 +1528,7 @@ static int chip_probe(struct i2c_client *client, const struct i2c_device_id *id) /* fill required data structures */ if (!id) strlcpy(client->name, desc->name, I2C_NAME_SIZE); - chip->type = desc-chiplist; + chip->desc = desc; chip->shadow.count = desc->registers+1; chip->prevmode = -1; chip->audmode = V4L2_TUNER_MODE_LANG1; @@ -1506,20 +1540,49 @@ static int chip_probe(struct i2c_client *client, const struct i2c_device_id *id) chip_cmd(chip,"init",&desc->init); if (desc->flags & CHIP_HAS_VOLUME) { - chip->left = desc->leftinit ? desc->leftinit : 65535; - chip->right = desc->rightinit ? desc->rightinit : 65535; - chip_write(chip,desc->leftreg,desc->volfunc(chip->left)); - chip_write(chip,desc->rightreg,desc->volfunc(chip->right)); + if (!desc->volfunc) { + /* This shouldn't be happen. Warn user, but keep working + without volume controls + */ + v4l_info(chip->c, "volume callback undefined!\n"); + desc->flags &= ~CHIP_HAS_VOLUME; + } else { + chip->left = desc->leftinit ? desc->leftinit : 65535; + chip->right = desc->rightinit ? desc->rightinit : 65535; + chip_write(chip, desc->leftreg, + desc->volfunc(chip->left)); + chip_write(chip, desc->rightreg, + desc->volfunc(chip->right)); + } } if (desc->flags & CHIP_HAS_BASSTREBLE) { - chip->treble = desc->trebleinit ? desc->trebleinit : 32768; - chip->bass = desc->bassinit ? desc->bassinit : 32768; - chip_write(chip,desc->bassreg,desc->bassfunc(chip->bass)); - chip_write(chip,desc->treblereg,desc->treblefunc(chip->treble)); + if (!desc->bassfunc || !desc->treblefunc) { + /* This shouldn't be happen. Warn user, but keep working + without bass/treble controls + */ + v4l_info(chip->c, "bass/treble callbacks undefined!\n"); + desc->flags &= ~CHIP_HAS_BASSTREBLE; + } else { + chip->treble = desc->trebleinit ? + desc->trebleinit : 32768; + chip->bass = desc->bassinit ? + desc->bassinit : 32768; + chip_write(chip, desc->bassreg, + desc->bassfunc(chip->bass)); + chip_write(chip, desc->treblereg, + desc->treblefunc(chip->treble)); + } } chip->thread = NULL; - if (desc->checkmode) { + if (desc->flags & CHIP_NEED_CHECKMODE) { + if (!desc->getmode || !desc->setmode) { + /* This shouldn't be happen. Warn user, but keep working + without kthread + */ + v4l_info(chip->c, "set/get mode callbacks undefined!\n"); + return 0; + } /* start async thread */ init_timer(&chip->wt); chip->wt.function = chip_thread_wake; @@ -1552,7 +1615,7 @@ static int chip_remove(struct i2c_client *client) static int tvaudio_get_ctrl(struct CHIPSTATE *chip, struct v4l2_control *ctrl) { - struct CHIPDESC *desc = chiplist + chip->type; + struct CHIPDESC *desc = chip->desc; switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -1576,13 +1639,13 @@ static int tvaudio_get_ctrl(struct CHIPSTATE *chip, return 0; } case V4L2_CID_AUDIO_BASS: - if (desc->flags & CHIP_HAS_BASSTREBLE) + if (!(desc->flags & CHIP_HAS_BASSTREBLE)) break; ctrl->value = chip->bass; return 0; case V4L2_CID_AUDIO_TREBLE: - if (desc->flags & CHIP_HAS_BASSTREBLE) - return -EINVAL; + if (!(desc->flags & CHIP_HAS_BASSTREBLE)) + break; ctrl->value = chip->treble; return 0; } @@ -1592,7 +1655,7 @@ static int tvaudio_get_ctrl(struct CHIPSTATE *chip, static int tvaudio_set_ctrl(struct CHIPSTATE *chip, struct v4l2_control *ctrl) { - struct CHIPDESC *desc = chiplist + chip->type; + struct CHIPDESC *desc = chip->desc; switch (ctrl->id) { case V4L2_CID_AUDIO_MUTE: @@ -1642,16 +1705,15 @@ static int tvaudio_set_ctrl(struct CHIPSTATE *chip, return 0; } case V4L2_CID_AUDIO_BASS: - if (desc->flags & CHIP_HAS_BASSTREBLE) + if (!(desc->flags & CHIP_HAS_BASSTREBLE)) break; chip->bass = ctrl->value; chip_write(chip,desc->bassreg,desc->bassfunc(chip->bass)); return 0; case V4L2_CID_AUDIO_TREBLE: - if (desc->flags & CHIP_HAS_BASSTREBLE) - return -EINVAL; - + if (!(desc->flags & CHIP_HAS_BASSTREBLE)) + break; chip->treble = ctrl->value; chip_write(chip,desc->treblereg,desc->treblefunc(chip->treble)); @@ -1668,9 +1730,12 @@ static int chip_command(struct i2c_client *client, unsigned int cmd, void *arg) { struct CHIPSTATE *chip = i2c_get_clientdata(client); - struct CHIPDESC *desc = chiplist + chip->type; + struct CHIPDESC *desc = chip->desc; - v4l_dbg(1, debug, chip->c, "%s: chip_command 0x%x\n", chip->c->name, cmd); + if (debug > 0) { + v4l_i2c_print_ioctl(chip->c, cmd); + printk("\n"); + } switch (cmd) { case AUDC_SET_RADIO: @@ -1695,7 +1760,7 @@ static int chip_command(struct i2c_client *client, break; case V4L2_CID_AUDIO_BASS: case V4L2_CID_AUDIO_TREBLE: - if (desc->flags & CHIP_HAS_BASSTREBLE) + if (!(desc->flags & CHIP_HAS_BASSTREBLE)) return -EINVAL; break; default: @@ -1792,12 +1857,20 @@ static int chip_command(struct i2c_client *client, break; case VIDIOC_S_FREQUENCY: chip->mode = 0; /* automatic */ - if (desc->checkmode && desc->setmode) { + + /* For chips that provide getmode and setmode, and doesn't + automatically follows the stereo carrier, a kthread is + created to set the audio standard. In this case, when then + the video channel is changed, tvaudio starts on MONO mode. + After waiting for 2 seconds, the kernel thread is called, + to follow whatever audio standard is pointed by the + audio carrier. + */ + if (chip->thread) { desc->setmode(chip,V4L2_TUNER_MODE_MONO); if (chip->prevmode != V4L2_TUNER_MODE_MONO) chip->prevmode = -1; /* reset previous mode */ mod_timer(&chip->wt, jiffies+msecs_to_jiffies(2000)); - /* the thread will call checkmode() later */ } break; @@ -1836,9 +1909,3 @@ static struct v4l2_i2c_driver_data v4l2_i2c_data = { .legacy_probe = chip_legacy_probe, .id_table = chip_id, }; - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig index 9494400e8fd..fee7304102a 100644 --- a/drivers/misc/Kconfig +++ b/drivers/misc/Kconfig @@ -227,10 +227,20 @@ config HP_WMI To compile this driver as a module, choose M here: the module will be called hp-wmi. +config ICS932S401 + tristate "Integrated Circuits ICS932S401" + depends on I2C && EXPERIMENTAL + help + If you say yes here you get support for the Integrated Circuits + ICS932S401 clock control chips. + + This driver can also be built as a module. If so, the module + will be called ics932s401. + config MSI_LAPTOP tristate "MSI Laptop Extras" depends on X86 - depends on ACPI_EC + depends on ACPI depends on BACKLIGHT_CLASS_DEVICE ---help--- This is a driver for laptops built by MSI (MICRO-STAR @@ -260,7 +270,7 @@ config PANASONIC_LAPTOP config COMPAL_LAPTOP tristate "Compal Laptop Extras" depends on X86 - depends on ACPI_EC + depends on ACPI depends on BACKLIGHT_CLASS_DEVICE ---help--- This is a driver for laptops built by Compal: @@ -488,4 +498,6 @@ config SGI_GRU_DEBUG This option enables addition debugging code for the SGI GRU driver. If you are unsure, say N. +source "drivers/misc/c2port/Kconfig" + endif # MISC_DEVICES diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile index 909e2468cdc..817f7f5ab3b 100644 --- a/drivers/misc/Makefile +++ b/drivers/misc/Makefile @@ -14,6 +14,7 @@ obj-$(CONFIG_ATMEL_PWM) += atmel_pwm.o obj-$(CONFIG_ATMEL_SSC) += atmel-ssc.o obj-$(CONFIG_ATMEL_TCLIB) += atmel_tclib.o obj-$(CONFIG_HP_WMI) += hp-wmi.o +obj-$(CONFIG_ICS932S401) += ics932s401.o obj-$(CONFIG_TC1100_WMI) += tc1100-wmi.o obj-$(CONFIG_LKDTM) += lkdtm.o obj-$(CONFIG_TIFM_CORE) += tifm_core.o @@ -31,3 +32,4 @@ obj-$(CONFIG_KGDB_TESTS) += kgdbts.o obj-$(CONFIG_SGI_XP) += sgi-xp/ obj-$(CONFIG_SGI_GRU) += sgi-gru/ obj-$(CONFIG_HP_ILO) += hpilo.o +obj-$(CONFIG_C2PORT) += c2port/ diff --git a/drivers/misc/acer-wmi.c b/drivers/misc/acer-wmi.c index 0532a2de2ce..94c9f911824 100644 --- a/drivers/misc/acer-wmi.c +++ b/drivers/misc/acer-wmi.c @@ -1297,6 +1297,12 @@ static int __init acer_wmi_init(void) set_quirks(); + if (!acpi_video_backlight_support() && has_cap(ACER_CAP_BRIGHTNESS)) { + interface->capability &= ~ACER_CAP_BRIGHTNESS; + printk(ACER_INFO "Brightness must be controlled by " + "generic video driver\n"); + } + if (platform_driver_register(&acer_platform_driver)) { printk(ACER_ERR "Unable to register platform driver.\n"); goto error_platform_register; diff --git a/drivers/misc/asus-laptop.c b/drivers/misc/asus-laptop.c index a9d5228724a..8fb8b359104 100644 --- a/drivers/misc/asus-laptop.c +++ b/drivers/misc/asus-laptop.c @@ -1208,9 +1208,13 @@ static int __init asus_laptop_init(void) dev = acpi_get_physical_device(hotk->device->handle); - result = asus_backlight_init(dev); - if (result) - goto fail_backlight; + if (!acpi_video_backlight_support()) { + result = asus_backlight_init(dev); + if (result) + goto fail_backlight; + } else + printk(ASUS_INFO "Brightness ignored, must be controlled by " + "ACPI video driver\n"); result = asus_led_init(dev); if (result) diff --git a/drivers/misc/c2port/Kconfig b/drivers/misc/c2port/Kconfig new file mode 100644 index 00000000000..e46af9a5810 --- /dev/null +++ b/drivers/misc/c2port/Kconfig @@ -0,0 +1,35 @@ +# +# C2 port devices +# + +menuconfig C2PORT + tristate "Silicon Labs C2 port support (EXPERIMENTAL)" + depends on EXPERIMENTAL + default no + help + This option enables support for Silicon Labs C2 port used to + program Silicon micro controller chips (and other 8051 compatible). + + If your board have no such micro controllers you don't need this + interface at all. + + To compile this driver as a module, choose M here: the module will + be called c2port_core. Note that you also need a client module + usually called c2port-*. + + If you are not sure, say N here. + +if C2PORT + +config C2PORT_DURAMAR_2150 + tristate "C2 port support for Eurotech's Duramar 2150 (EXPERIMENTAL)" + depends on X86 && C2PORT + default no + help + This option enables C2 support for the Eurotech's Duramar 2150 + on board micro controller. + + To compile this driver as a module, choose M here: the module will + be called c2port-duramar2150. + +endif # C2PORT diff --git a/drivers/misc/c2port/Makefile b/drivers/misc/c2port/Makefile new file mode 100644 index 00000000000..3b2cf43d60f --- /dev/null +++ b/drivers/misc/c2port/Makefile @@ -0,0 +1,3 @@ +obj-$(CONFIG_C2PORT) += core.o + +obj-$(CONFIG_C2PORT_DURAMAR_2150) += c2port-duramar2150.o diff --git a/drivers/misc/c2port/c2port-duramar2150.c b/drivers/misc/c2port/c2port-duramar2150.c new file mode 100644 index 00000000000..338dcc12150 --- /dev/null +++ b/drivers/misc/c2port/c2port-duramar2150.c @@ -0,0 +1,158 @@ +/* + * Silicon Labs C2 port Linux support for Eurotech Duramar 2150 + * + * Copyright (c) 2008 Rodolfo Giometti <giometti@linux.it> + * Copyright (c) 2008 Eurotech S.p.A. <info@eurotech.it> + * + * 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/errno.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/io.h> +#include <linux/c2port.h> + +#define DATA_PORT 0x325 +#define DIR_PORT 0x326 +#define C2D (1 << 0) +#define C2CK (1 << 1) + +static DEFINE_MUTEX(update_lock); + +/* + * C2 port operations + */ + +static void duramar2150_c2port_access(struct c2port_device *dev, int status) +{ + u8 v; + + mutex_lock(&update_lock); + + v = inb(DIR_PORT); + + /* 0 = input, 1 = output */ + if (status) + outb(v | (C2D | C2CK), DIR_PORT); + else + /* When access is "off" is important that both lines are set + * as inputs or hi-impedence */ + outb(v & ~(C2D | C2CK), DIR_PORT); + + mutex_unlock(&update_lock); +} + +static void duramar2150_c2port_c2d_dir(struct c2port_device *dev, int dir) +{ + u8 v; + + mutex_lock(&update_lock); + + v = inb(DIR_PORT); + + if (dir) + outb(v & ~C2D, DIR_PORT); + else + outb(v | C2D, DIR_PORT); + + mutex_unlock(&update_lock); +} + +static int duramar2150_c2port_c2d_get(struct c2port_device *dev) +{ + return inb(DATA_PORT) & C2D; +} + +static void duramar2150_c2port_c2d_set(struct c2port_device *dev, int status) +{ + u8 v; + + mutex_lock(&update_lock); + + v = inb(DATA_PORT); + + if (status) + outb(v | C2D, DATA_PORT); + else + outb(v & ~C2D, DATA_PORT); + + mutex_unlock(&update_lock); +} + +static void duramar2150_c2port_c2ck_set(struct c2port_device *dev, int status) +{ + u8 v; + + mutex_lock(&update_lock); + + v = inb(DATA_PORT); + + if (status) + outb(v | C2CK, DATA_PORT); + else + outb(v & ~C2CK, DATA_PORT); + + mutex_unlock(&update_lock); +} + +static struct c2port_ops duramar2150_c2port_ops = { + .block_size = 512, /* bytes */ + .blocks_num = 30, /* total flash size: 15360 bytes */ + + .access = duramar2150_c2port_access, + .c2d_dir = duramar2150_c2port_c2d_dir, + .c2d_get = duramar2150_c2port_c2d_get, + .c2d_set = duramar2150_c2port_c2d_set, + .c2ck_set = duramar2150_c2port_c2ck_set, +}; + +static struct c2port_device *duramar2150_c2port_dev; + +/* + * Module stuff + */ + +static int __init duramar2150_c2port_init(void) +{ + struct resource *res; + int ret = 0; + + res = request_region(0x325, 2, "c2port"); + if (!res) + return -EBUSY; + + duramar2150_c2port_dev = c2port_device_register("uc", + &duramar2150_c2port_ops, NULL); + if (!duramar2150_c2port_dev) { + ret = -ENODEV; + goto free_region; + } + + return 0; + +free_region: + release_region(0x325, 2); + return ret; +} + +static void __exit duramar2150_c2port_exit(void) +{ + /* Setup the GPIOs as input by default (access = 0) */ + duramar2150_c2port_access(duramar2150_c2port_dev, 0); + + c2port_device_unregister(duramar2150_c2port_dev); + + release_region(0x325, 2); +} + +module_init(duramar2150_c2port_init); +module_exit(duramar2150_c2port_exit); + +MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>"); +MODULE_DESCRIPTION("Silicon Labs C2 port Linux support for Duramar 2150"); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/c2port/core.c b/drivers/misc/c2port/core.c new file mode 100644 index 00000000000..0207dd59090 --- /dev/null +++ b/drivers/misc/c2port/core.c @@ -0,0 +1,1003 @@ +/* + * Silicon Labs C2 port core Linux support + * + * Copyright (c) 2007 Rodolfo Giometti <giometti@linux.it> + * Copyright (c) 2007 Eurotech S.p.A. <info@eurotech.it> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/device.h> +#include <linux/errno.h> +#include <linux/err.h> +#include <linux/kernel.h> +#include <linux/ctype.h> +#include <linux/delay.h> +#include <linux/idr.h> +#include <linux/sched.h> + +#include <linux/c2port.h> + +#define DRIVER_NAME "c2port" +#define DRIVER_VERSION "0.51.0" + +static DEFINE_SPINLOCK(c2port_idr_lock); +static DEFINE_IDR(c2port_idr); + +/* + * Local variables + */ + +static struct class *c2port_class; + +/* + * C2 registers & commands defines + */ + +/* C2 registers */ +#define C2PORT_DEVICEID 0x00 +#define C2PORT_REVID 0x01 +#define C2PORT_FPCTL 0x02 +#define C2PORT_FPDAT 0xB4 + +/* C2 interface commands */ +#define C2PORT_GET_VERSION 0x01 +#define C2PORT_DEVICE_ERASE 0x03 +#define C2PORT_BLOCK_READ 0x06 +#define C2PORT_BLOCK_WRITE 0x07 +#define C2PORT_PAGE_ERASE 0x08 + +/* C2 status return codes */ +#define C2PORT_INVALID_COMMAND 0x00 +#define C2PORT_COMMAND_FAILED 0x02 +#define C2PORT_COMMAND_OK 0x0d + +/* + * C2 port low level signal managements + */ + +static void c2port_reset(struct c2port_device *dev) +{ + struct c2port_ops *ops = dev->ops; + + /* To reset the device we have to keep clock line low for at least + * 20us. + */ + local_irq_disable(); + ops->c2ck_set(dev, 0); + udelay(25); + ops->c2ck_set(dev, 1); + local_irq_enable(); + + udelay(1); +} + +static void c2port_strobe_ck(struct c2port_device *dev) +{ + struct c2port_ops *ops = dev->ops; + + /* During hi-low-hi transition we disable local IRQs to avoid + * interructions since C2 port specification says that it must be + * shorter than 5us, otherwise the microcontroller may consider + * it as a reset signal! + */ + local_irq_disable(); + ops->c2ck_set(dev, 0); + udelay(1); + ops->c2ck_set(dev, 1); + local_irq_enable(); + + udelay(1); +} + +/* + * C2 port basic functions + */ + +static void c2port_write_ar(struct c2port_device *dev, u8 addr) +{ + struct c2port_ops *ops = dev->ops; + int i; + + /* START field */ + c2port_strobe_ck(dev); + + /* INS field (11b, LSB first) */ + ops->c2d_dir(dev, 0); + ops->c2d_set(dev, 1); + c2port_strobe_ck(dev); + ops->c2d_set(dev, 1); + c2port_strobe_ck(dev); + + /* ADDRESS field */ + for (i = 0; i < 8; i++) { + ops->c2d_set(dev, addr & 0x01); + c2port_strobe_ck(dev); + + addr >>= 1; + } + + /* STOP field */ + ops->c2d_dir(dev, 1); + c2port_strobe_ck(dev); +} + +static int c2port_read_ar(struct c2port_device *dev, u8 *addr) +{ + struct c2port_ops *ops = dev->ops; + int i; + + /* START field */ + c2port_strobe_ck(dev); + + /* INS field (10b, LSB first) */ + ops->c2d_dir(dev, 0); + ops->c2d_set(dev, 0); + c2port_strobe_ck(dev); + ops->c2d_set(dev, 1); + c2port_strobe_ck(dev); + + /* ADDRESS field */ + ops->c2d_dir(dev, 1); + *addr = 0; + for (i = 0; i < 8; i++) { + *addr >>= 1; /* shift in 8-bit ADDRESS field LSB first */ + + c2port_strobe_ck(dev); + if (ops->c2d_get(dev)) + *addr |= 0x80; + } + + /* STOP field */ + c2port_strobe_ck(dev); + + return 0; +} + +static int c2port_write_dr(struct c2port_device *dev, u8 data) +{ + struct c2port_ops *ops = dev->ops; + int timeout, i; + + /* START field */ + c2port_strobe_ck(dev); + + /* INS field (01b, LSB first) */ + ops->c2d_dir(dev, 0); + ops->c2d_set(dev, 1); + c2port_strobe_ck(dev); + ops->c2d_set(dev, 0); + c2port_strobe_ck(dev); + + /* LENGTH field (00b, LSB first -> 1 byte) */ + ops->c2d_set(dev, 0); + c2port_strobe_ck(dev); + ops->c2d_set(dev, 0); + c2port_strobe_ck(dev); + + /* DATA field */ + for (i = 0; i < 8; i++) { + ops->c2d_set(dev, data & 0x01); + c2port_strobe_ck(dev); + + data >>= 1; + } + + /* WAIT field */ + ops->c2d_dir(dev, 1); + timeout = 20; + do { + c2port_strobe_ck(dev); + if (ops->c2d_get(dev)) + break; + + udelay(1); + } while (--timeout > 0); + if (timeout == 0) + return -EIO; + + /* STOP field */ + c2port_strobe_ck(dev); + + return 0; +} + +static int c2port_read_dr(struct c2port_device *dev, u8 *data) +{ + struct c2port_ops *ops = dev->ops; + int timeout, i; + + /* START field */ + c2port_strobe_ck(dev); + + /* INS field (00b, LSB first) */ + ops->c2d_dir(dev, 0); + ops->c2d_set(dev, 0); + c2port_strobe_ck(dev); + ops->c2d_set(dev, 0); + c2port_strobe_ck(dev); + + /* LENGTH field (00b, LSB first -> 1 byte) */ + ops->c2d_set(dev, 0); + c2port_strobe_ck(dev); + ops->c2d_set(dev, 0); + c2port_strobe_ck(dev); + + /* WAIT field */ + ops->c2d_dir(dev, 1); + timeout = 20; + do { + c2port_strobe_ck(dev); + if (ops->c2d_get(dev)) + break; + + udelay(1); + } while (--timeout > 0); + if (timeout == 0) + return -EIO; + + /* DATA field */ + *data = 0; + for (i = 0; i < 8; i++) { + *data >>= 1; /* shift in 8-bit DATA field LSB first */ + + c2port_strobe_ck(dev); + if (ops->c2d_get(dev)) + *data |= 0x80; + } + + /* STOP field */ + c2port_strobe_ck(dev); + + return 0; +} + +static int c2port_poll_in_busy(struct c2port_device *dev) +{ + u8 addr; + int ret, timeout = 20; + + do { + ret = (c2port_read_ar(dev, &addr)); + if (ret < 0) + return -EIO; + + if (!(addr & 0x02)) + break; + + udelay(1); + } while (--timeout > 0); + if (timeout == 0) + return -EIO; + + return 0; +} + +static int c2port_poll_out_ready(struct c2port_device *dev) +{ + u8 addr; + int ret, timeout = 10000; /* erase flash needs long time... */ + + do { + ret = (c2port_read_ar(dev, &addr)); + if (ret < 0) + return -EIO; + + if (addr & 0x01) + break; + + udelay(1); + } while (--timeout > 0); + if (timeout == 0) + return -EIO; + + return 0; +} + +/* + * sysfs methods + */ + +static ssize_t c2port_show_name(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct c2port_device *c2dev = dev_get_drvdata(dev); + + return sprintf(buf, "%s\n", c2dev->name); +} + +static ssize_t c2port_show_flash_blocks_num(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct c2port_device *c2dev = dev_get_drvdata(dev); + struct c2port_ops *ops = c2dev->ops; + + return sprintf(buf, "%d\n", ops->blocks_num); +} + +static ssize_t c2port_show_flash_block_size(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct c2port_device *c2dev = dev_get_drvdata(dev); + struct c2port_ops *ops = c2dev->ops; + + return sprintf(buf, "%d\n", ops->block_size); +} + +static ssize_t c2port_show_flash_size(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct c2port_device *c2dev = dev_get_drvdata(dev); + struct c2port_ops *ops = c2dev->ops; + + return sprintf(buf, "%d\n", ops->blocks_num * ops->block_size); +} + +static ssize_t c2port_show_access(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct c2port_device *c2dev = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", c2dev->access); +} + +static ssize_t c2port_store_access(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct c2port_device *c2dev = dev_get_drvdata(dev); + struct c2port_ops *ops = c2dev->ops; + int status, ret; + + ret = sscanf(buf, "%d", &status); + if (ret != 1) + return -EINVAL; + + mutex_lock(&c2dev->mutex); + + c2dev->access = !!status; + + /* If access is "on" clock should be HIGH _before_ setting the line + * as output and data line should be set as INPUT anyway */ + if (c2dev->access) + ops->c2ck_set(c2dev, 1); + ops->access(c2dev, c2dev->access); + if (c2dev->access) + ops->c2d_dir(c2dev, 1); + + mutex_unlock(&c2dev->mutex); + + return count; +} + +static ssize_t c2port_store_reset(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct c2port_device *c2dev = dev_get_drvdata(dev); + + /* Check the device access status */ + if (!c2dev->access) + return -EBUSY; + + mutex_lock(&c2dev->mutex); + + c2port_reset(c2dev); + c2dev->flash_access = 0; + + mutex_unlock(&c2dev->mutex); + + return count; +} + +static ssize_t __c2port_show_dev_id(struct c2port_device *dev, char *buf) +{ + u8 data; + int ret; + + /* Select DEVICEID register for C2 data register accesses */ + c2port_write_ar(dev, C2PORT_DEVICEID); + + /* Read and return the device ID register */ + ret = c2port_read_dr(dev, &data); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", data); +} + +static ssize_t c2port_show_dev_id(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct c2port_device *c2dev = dev_get_drvdata(dev); + ssize_t ret; + + /* Check the device access status */ + if (!c2dev->access) + return -EBUSY; + + mutex_lock(&c2dev->mutex); + ret = __c2port_show_dev_id(c2dev, buf); + mutex_unlock(&c2dev->mutex); + + if (ret < 0) + dev_err(dev, "cannot read from %s\n", c2dev->name); + + return ret; +} + +static ssize_t __c2port_show_rev_id(struct c2port_device *dev, char *buf) +{ + u8 data; + int ret; + + /* Select REVID register for C2 data register accesses */ + c2port_write_ar(dev, C2PORT_REVID); + + /* Read and return the revision ID register */ + ret = c2port_read_dr(dev, &data); + if (ret < 0) + return ret; + + return sprintf(buf, "%d\n", data); +} + +static ssize_t c2port_show_rev_id(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct c2port_device *c2dev = dev_get_drvdata(dev); + ssize_t ret; + + /* Check the device access status */ + if (!c2dev->access) + return -EBUSY; + + mutex_lock(&c2dev->mutex); + ret = __c2port_show_rev_id(c2dev, buf); + mutex_unlock(&c2dev->mutex); + + if (ret < 0) + dev_err(c2dev->dev, "cannot read from %s\n", c2dev->name); + + return ret; +} + +static ssize_t c2port_show_flash_access(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct c2port_device *c2dev = dev_get_drvdata(dev); + + return sprintf(buf, "%d\n", c2dev->flash_access); +} + +static ssize_t __c2port_store_flash_access(struct c2port_device *dev, + int status) +{ + int ret; + + /* Check the device access status */ + if (!dev->access) + return -EBUSY; + + dev->flash_access = !!status; + + /* If flash_access is off we have nothing to do... */ + if (dev->flash_access == 0) + return 0; + + /* Target the C2 flash programming control register for C2 data + * register access */ + c2port_write_ar(dev, C2PORT_FPCTL); + + /* Write the first keycode to enable C2 Flash programming */ + ret = c2port_write_dr(dev, 0x02); + if (ret < 0) + return ret; + + /* Write the second keycode to enable C2 Flash programming */ + ret = c2port_write_dr(dev, 0x01); + if (ret < 0) + return ret; + + /* Delay for at least 20ms to ensure the target is ready for + * C2 flash programming */ + mdelay(25); + + return 0; +} + +static ssize_t c2port_store_flash_access(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct c2port_device *c2dev = dev_get_drvdata(dev); + int status; + ssize_t ret; + + ret = sscanf(buf, "%d", &status); + if (ret != 1) + return -EINVAL; + + mutex_lock(&c2dev->mutex); + ret = __c2port_store_flash_access(c2dev, status); + mutex_unlock(&c2dev->mutex); + + if (ret < 0) { + dev_err(c2dev->dev, "cannot enable %s flash programming\n", + c2dev->name); + return ret; + } + + return count; +} + +static ssize_t __c2port_write_flash_erase(struct c2port_device *dev) +{ + u8 status; + int ret; + + /* Target the C2 flash programming data register for C2 data register + * access. + */ + c2port_write_ar(dev, C2PORT_FPDAT); + + /* Send device erase command */ + c2port_write_dr(dev, C2PORT_DEVICE_ERASE); + + /* Wait for input acknowledge */ + ret = c2port_poll_in_busy(dev); + if (ret < 0) + return ret; + + /* Should check status before starting FLASH access sequence */ + + /* Wait for status information */ + ret = c2port_poll_out_ready(dev); + if (ret < 0) + return ret; + + /* Read flash programming interface status */ + ret = c2port_read_dr(dev, &status); + if (ret < 0) + return ret; + if (status != C2PORT_COMMAND_OK) + return -EBUSY; + + /* Send a three-byte arming sequence to enable the device erase. + * If the sequence is not received correctly, the command will be + * ignored. + * Sequence is: 0xde, 0xad, 0xa5. + */ + c2port_write_dr(dev, 0xde); + ret = c2port_poll_in_busy(dev); + if (ret < 0) + return ret; + c2port_write_dr(dev, 0xad); + ret = c2port_poll_in_busy(dev); + if (ret < 0) + return ret; + c2port_write_dr(dev, 0xa5); + ret = c2port_poll_in_busy(dev); + if (ret < 0) + return ret; + + ret = c2port_poll_out_ready(dev); + if (ret < 0) + return ret; + + return 0; +} + +static ssize_t c2port_store_flash_erase(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct c2port_device *c2dev = dev_get_drvdata(dev); + int ret; + + /* Check the device and flash access status */ + if (!c2dev->access || !c2dev->flash_access) + return -EBUSY; + + mutex_lock(&c2dev->mutex); + ret = __c2port_write_flash_erase(c2dev); + mutex_unlock(&c2dev->mutex); + + if (ret < 0) { + dev_err(c2dev->dev, "cannot erase %s flash\n", c2dev->name); + return ret; + } + + return count; +} + +static ssize_t __c2port_read_flash_data(struct c2port_device *dev, + char *buffer, loff_t offset, size_t count) +{ + struct c2port_ops *ops = dev->ops; + u8 status, nread = 128; + int i, ret; + + /* Check for flash end */ + if (offset >= ops->block_size * ops->blocks_num) + return 0; + + if (ops->block_size * ops->blocks_num - offset < nread) + nread = ops->block_size * ops->blocks_num - offset; + if (count < nread) + nread = count; + if (nread == 0) + return nread; + + /* Target the C2 flash programming data register for C2 data register + * access */ + c2port_write_ar(dev, C2PORT_FPDAT); + + /* Send flash block read command */ + c2port_write_dr(dev, C2PORT_BLOCK_READ); + + /* Wait for input acknowledge */ + ret = c2port_poll_in_busy(dev); + if (ret < 0) + return ret; + + /* Should check status before starting FLASH access sequence */ + + /* Wait for status information */ + ret = c2port_poll_out_ready(dev); + if (ret < 0) + return ret; + + /* Read flash programming interface status */ + ret = c2port_read_dr(dev, &status); + if (ret < 0) + return ret; + if (status != C2PORT_COMMAND_OK) + return -EBUSY; + + /* Send address high byte */ + c2port_write_dr(dev, offset >> 8); + ret = c2port_poll_in_busy(dev); + if (ret < 0) + return ret; + + /* Send address low byte */ + c2port_write_dr(dev, offset & 0x00ff); + ret = c2port_poll_in_busy(dev); + if (ret < 0) + return ret; + + /* Send address block size */ + c2port_write_dr(dev, nread); + ret = c2port_poll_in_busy(dev); + if (ret < 0) + return ret; + + /* Should check status before reading FLASH block */ + + /* Wait for status information */ + ret = c2port_poll_out_ready(dev); + if (ret < 0) + return ret; + + /* Read flash programming interface status */ + ret = c2port_read_dr(dev, &status); + if (ret < 0) + return ret; + if (status != C2PORT_COMMAND_OK) + return -EBUSY; + + /* Read flash block */ + for (i = 0; i < nread; i++) { + ret = c2port_poll_out_ready(dev); + if (ret < 0) + return ret; + + ret = c2port_read_dr(dev, buffer+i); + if (ret < 0) + return ret; + } + + return nread; +} + +static ssize_t c2port_read_flash_data(struct kobject *kobj, + struct bin_attribute *attr, + char *buffer, loff_t offset, size_t count) +{ + struct c2port_device *c2dev = + dev_get_drvdata(container_of(kobj, + struct device, kobj)); + ssize_t ret; + + /* Check the device and flash access status */ + if (!c2dev->access || !c2dev->flash_access) + return -EBUSY; + + mutex_lock(&c2dev->mutex); + ret = __c2port_read_flash_data(c2dev, buffer, offset, count); + mutex_unlock(&c2dev->mutex); + + if (ret < 0) + dev_err(c2dev->dev, "cannot read %s flash\n", c2dev->name); + + return ret; +} + +static ssize_t __c2port_write_flash_data(struct c2port_device *dev, + char *buffer, loff_t offset, size_t count) +{ + struct c2port_ops *ops = dev->ops; + u8 status, nwrite = 128; + int i, ret; + + if (nwrite > count) + nwrite = count; + if (ops->block_size * ops->blocks_num - offset < nwrite) + nwrite = ops->block_size * ops->blocks_num - offset; + + /* Check for flash end */ + if (offset >= ops->block_size * ops->blocks_num) + return -EINVAL; + + /* Target the C2 flash programming data register for C2 data register + * access */ + c2port_write_ar(dev, C2PORT_FPDAT); + + /* Send flash block write command */ + c2port_write_dr(dev, C2PORT_BLOCK_WRITE); + + /* Wait for input acknowledge */ + ret = c2port_poll_in_busy(dev); + if (ret < 0) + return ret; + + /* Should check status before starting FLASH access sequence */ + + /* Wait for status information */ + ret = c2port_poll_out_ready(dev); + if (ret < 0) + return ret; + + /* Read flash programming interface status */ + ret = c2port_read_dr(dev, &status); + if (ret < 0) + return ret; + if (status != C2PORT_COMMAND_OK) + return -EBUSY; + + /* Send address high byte */ + c2port_write_dr(dev, offset >> 8); + ret = c2port_poll_in_busy(dev); + if (ret < 0) + return ret; + + /* Send address low byte */ + c2port_write_dr(dev, offset & 0x00ff); + ret = c2port_poll_in_busy(dev); + if (ret < 0) + return ret; + + /* Send address block size */ + c2port_write_dr(dev, nwrite); + ret = c2port_poll_in_busy(dev); + if (ret < 0) + return ret; + + /* Should check status before writing FLASH block */ + + /* Wait for status information */ + ret = c2port_poll_out_ready(dev); + if (ret < 0) + return ret; + + /* Read flash programming interface status */ + ret = c2port_read_dr(dev, &status); + if (ret < 0) + return ret; + if (status != C2PORT_COMMAND_OK) + return -EBUSY; + + /* Write flash block */ + for (i = 0; i < nwrite; i++) { + ret = c2port_write_dr(dev, *(buffer+i)); + if (ret < 0) + return ret; + + ret = c2port_poll_in_busy(dev); + if (ret < 0) + return ret; + + } + + /* Wait for last flash write to complete */ + ret = c2port_poll_out_ready(dev); + if (ret < 0) + return ret; + + return nwrite; +} + +static ssize_t c2port_write_flash_data(struct kobject *kobj, + struct bin_attribute *attr, + char *buffer, loff_t offset, size_t count) +{ + struct c2port_device *c2dev = + dev_get_drvdata(container_of(kobj, + struct device, kobj)); + int ret; + + /* Check the device access status */ + if (!c2dev->access || !c2dev->flash_access) + return -EBUSY; + + mutex_lock(&c2dev->mutex); + ret = __c2port_write_flash_data(c2dev, buffer, offset, count); + mutex_unlock(&c2dev->mutex); + + if (ret < 0) + dev_err(c2dev->dev, "cannot write %s flash\n", c2dev->name); + + return ret; +} + +/* + * Class attributes + */ + +static struct device_attribute c2port_attrs[] = { + __ATTR(name, 0444, c2port_show_name, NULL), + __ATTR(flash_blocks_num, 0444, c2port_show_flash_blocks_num, NULL), + __ATTR(flash_block_size, 0444, c2port_show_flash_block_size, NULL), + __ATTR(flash_size, 0444, c2port_show_flash_size, NULL), + __ATTR(access, 0644, c2port_show_access, c2port_store_access), + __ATTR(reset, 0200, NULL, c2port_store_reset), + __ATTR(dev_id, 0444, c2port_show_dev_id, NULL), + __ATTR(rev_id, 0444, c2port_show_rev_id, NULL), + + __ATTR(flash_access, 0644, c2port_show_flash_access, + c2port_store_flash_access), + __ATTR(flash_erase, 0200, NULL, c2port_store_flash_erase), + __ATTR_NULL, +}; + +static struct bin_attribute c2port_bin_attrs = { + .attr = { + .name = "flash_data", + .mode = 0644 + }, + .read = c2port_read_flash_data, + .write = c2port_write_flash_data, + /* .size is computed at run-time */ +}; + +/* + * Exported functions + */ + +struct c2port_device *c2port_device_register(char *name, + struct c2port_ops *ops, void *devdata) +{ + struct c2port_device *c2dev; + int id, ret; + + if (unlikely(!ops) || unlikely(!ops->access) || \ + unlikely(!ops->c2d_dir) || unlikely(!ops->c2ck_set) || \ + unlikely(!ops->c2d_get) || unlikely(!ops->c2d_set)) + return ERR_PTR(-EINVAL); + + c2dev = kmalloc(sizeof(struct c2port_device), GFP_KERNEL); + if (unlikely(!c2dev)) + return ERR_PTR(-ENOMEM); + + ret = idr_pre_get(&c2port_idr, GFP_KERNEL); + if (!ret) { + ret = -ENOMEM; + goto error_idr_get_new; + } + + spin_lock_irq(&c2port_idr_lock); + ret = idr_get_new(&c2port_idr, c2dev, &id); + spin_unlock_irq(&c2port_idr_lock); + + if (ret < 0) + goto error_idr_get_new; + c2dev->id = id; + + c2dev->dev = device_create(c2port_class, NULL, 0, c2dev, + "c2port%d", id); + if (unlikely(!c2dev->dev)) { + ret = -ENOMEM; + goto error_device_create; + } + dev_set_drvdata(c2dev->dev, c2dev); + + strncpy(c2dev->name, name, C2PORT_NAME_LEN); + c2dev->ops = ops; + mutex_init(&c2dev->mutex); + + /* Create binary file */ + c2port_bin_attrs.size = ops->blocks_num * ops->block_size; + ret = device_create_bin_file(c2dev->dev, &c2port_bin_attrs); + if (unlikely(ret)) + goto error_device_create_bin_file; + + /* By default C2 port access is off */ + c2dev->access = c2dev->flash_access = 0; + ops->access(c2dev, 0); + + dev_info(c2dev->dev, "C2 port %s added\n", name); + dev_info(c2dev->dev, "%s flash has %d blocks x %d bytes " + "(%d bytes total)\n", + name, ops->blocks_num, ops->block_size, + ops->blocks_num * ops->block_size); + + return c2dev; + +error_device_create_bin_file: + device_destroy(c2port_class, 0); + +error_device_create: + spin_lock_irq(&c2port_idr_lock); + idr_remove(&c2port_idr, id); + spin_unlock_irq(&c2port_idr_lock); + +error_idr_get_new: + kfree(c2dev); + + return ERR_PTR(ret); +} +EXPORT_SYMBOL(c2port_device_register); + +void c2port_device_unregister(struct c2port_device *c2dev) +{ + if (!c2dev) + return; + + dev_info(c2dev->dev, "C2 port %s removed\n", c2dev->name); + + device_remove_bin_file(c2dev->dev, &c2port_bin_attrs); + spin_lock_irq(&c2port_idr_lock); + idr_remove(&c2port_idr, c2dev->id); + spin_unlock_irq(&c2port_idr_lock); + + device_destroy(c2port_class, c2dev->id); + + kfree(c2dev); +} +EXPORT_SYMBOL(c2port_device_unregister); + +/* + * Module stuff + */ + +static int __init c2port_init(void) +{ + printk(KERN_INFO "Silicon Labs C2 port support v. " DRIVER_VERSION + " - (C) 2007 Rodolfo Giometti\n"); + + c2port_class = class_create(THIS_MODULE, "c2port"); + if (!c2port_class) { + printk(KERN_ERR "c2port: failed to allocate class\n"); + return -ENOMEM; + } + c2port_class->dev_attrs = c2port_attrs; + + return 0; +} + +static void __exit c2port_exit(void) +{ + class_destroy(c2port_class); +} + +module_init(c2port_init); +module_exit(c2port_exit); + +MODULE_AUTHOR("Rodolfo Giometti <giometti@linux.it>"); +MODULE_DESCRIPTION("Silicon Labs C2 port support v. " DRIVER_VERSION); +MODULE_LICENSE("GPL"); diff --git a/drivers/misc/compal-laptop.c b/drivers/misc/compal-laptop.c index 344b790a625..11003bba10d 100644 --- a/drivers/misc/compal-laptop.c +++ b/drivers/misc/compal-laptop.c @@ -326,12 +326,14 @@ static int __init compal_init(void) /* Register backlight stuff */ - compalbl_device = backlight_device_register("compal-laptop", NULL, NULL, - &compalbl_ops); - if (IS_ERR(compalbl_device)) - return PTR_ERR(compalbl_device); + if (!acpi_video_backlight_support()) { + compalbl_device = backlight_device_register("compal-laptop", NULL, NULL, + &compalbl_ops); + if (IS_ERR(compalbl_device)) + return PTR_ERR(compalbl_device); - compalbl_device->props.max_brightness = COMPAL_LCD_LEVEL_MAX-1; + compalbl_device->props.max_brightness = COMPAL_LCD_LEVEL_MAX-1; + } ret = platform_driver_register(&compal_driver); if (ret) diff --git a/drivers/misc/eeepc-laptop.c b/drivers/misc/eeepc-laptop.c index 9ef98b2d503..02fe2b8b893 100644 --- a/drivers/misc/eeepc-laptop.c +++ b/drivers/misc/eeepc-laptop.c @@ -825,9 +825,15 @@ static int __init eeepc_laptop_init(void) return -ENODEV; } dev = acpi_get_physical_device(ehotk->device->handle); - result = eeepc_backlight_init(dev); - if (result) - goto fail_backlight; + + if (!acpi_video_backlight_support()) { + result = eeepc_backlight_init(dev); + if (result) + goto fail_backlight; + } else + printk(EEEPC_INFO "Backlight controlled by ACPI video " + "driver\n"); + result = eeepc_hwmon_init(dev); if (result) goto fail_hwmon; diff --git a/drivers/misc/fujitsu-laptop.c b/drivers/misc/fujitsu-laptop.c index d2cf0bfe316..a7dd3e9fb79 100644 --- a/drivers/misc/fujitsu-laptop.c +++ b/drivers/misc/fujitsu-laptop.c @@ -464,6 +464,14 @@ static int dmi_check_cb_s6410(const struct dmi_system_id *id) return 0; } +static int dmi_check_cb_s6420(const struct dmi_system_id *id) +{ + dmi_check_cb_common(id); + fujitsu->keycode1 = KEY_SCREENLOCK; /* "Lock" */ + fujitsu->keycode2 = KEY_HELP; /* "Mobility Center" */ + return 0; +} + static int dmi_check_cb_p8010(const struct dmi_system_id *id) { dmi_check_cb_common(id); @@ -473,7 +481,7 @@ static int dmi_check_cb_p8010(const struct dmi_system_id *id) return 0; } -static struct dmi_system_id __initdata fujitsu_dmi_table[] = { +static struct dmi_system_id fujitsu_dmi_table[] = { { .ident = "Fujitsu Siemens S6410", .matches = { @@ -482,6 +490,13 @@ static struct dmi_system_id __initdata fujitsu_dmi_table[] = { }, .callback = dmi_check_cb_s6410}, { + .ident = "Fujitsu Siemens S6420", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU SIEMENS"), + DMI_MATCH(DMI_PRODUCT_NAME, "LIFEBOOK S6420"), + }, + .callback = dmi_check_cb_s6420}, + { .ident = "Fujitsu LifeBook P8010", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "FUJITSU"), @@ -990,16 +1005,16 @@ static int __init fujitsu_init(void) /* Register backlight stuff */ - fujitsu->bl_device = - backlight_device_register("fujitsu-laptop", NULL, NULL, - &fujitsubl_ops); - if (IS_ERR(fujitsu->bl_device)) - return PTR_ERR(fujitsu->bl_device); - - max_brightness = fujitsu->max_brightness; - - fujitsu->bl_device->props.max_brightness = max_brightness - 1; - fujitsu->bl_device->props.brightness = fujitsu->brightness_level; + if (!acpi_video_backlight_support()) { + fujitsu->bl_device = + backlight_device_register("fujitsu-laptop", NULL, NULL, + &fujitsubl_ops); + if (IS_ERR(fujitsu->bl_device)) + return PTR_ERR(fujitsu->bl_device); + max_brightness = fujitsu->max_brightness; + fujitsu->bl_device->props.max_brightness = max_brightness - 1; + fujitsu->bl_device->props.brightness = fujitsu->brightness_level; + } ret = platform_driver_register(&fujitsupf_driver); if (ret) @@ -1035,7 +1050,8 @@ fail_hotkey: fail_backlight: - backlight_device_unregister(fujitsu->bl_device); + if (fujitsu->bl_device) + backlight_device_unregister(fujitsu->bl_device); fail_platform_device2: @@ -1062,7 +1078,8 @@ static void __exit fujitsu_cleanup(void) &fujitsupf_attribute_group); platform_device_unregister(fujitsu->pf_device); platform_driver_unregister(&fujitsupf_driver); - backlight_device_unregister(fujitsu->bl_device); + if (fujitsu->bl_device) + backlight_device_unregister(fujitsu->bl_device); acpi_bus_unregister_driver(&acpi_fujitsu_driver); diff --git a/drivers/misc/ics932s401.c b/drivers/misc/ics932s401.c new file mode 100644 index 00000000000..6e43ab4231a --- /dev/null +++ b/drivers/misc/ics932s401.c @@ -0,0 +1,515 @@ +/* + * A driver for the Integrated Circuits ICS932S401 + * Copyright (C) 2008 IBM + * + * Author: Darrick J. Wong <djwong@us.ibm.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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/module.h> +#include <linux/jiffies.h> +#include <linux/i2c.h> +#include <linux/err.h> +#include <linux/mutex.h> +#include <linux/delay.h> +#include <linux/log2.h> + +/* Addresses to scan */ +static const unsigned short normal_i2c[] = { 0x69, I2C_CLIENT_END }; + +/* Insmod parameters */ +I2C_CLIENT_INSMOD_1(ics932s401); + +/* ICS932S401 registers */ +#define ICS932S401_REG_CFG2 0x01 +#define ICS932S401_CFG1_SPREAD 0x01 +#define ICS932S401_REG_CFG7 0x06 +#define ICS932S401_FS_MASK 0x07 +#define ICS932S401_REG_VENDOR_REV 0x07 +#define ICS932S401_VENDOR 1 +#define ICS932S401_VENDOR_MASK 0x0F +#define ICS932S401_REV 4 +#define ICS932S401_REV_SHIFT 4 +#define ICS932S401_REG_DEVICE 0x09 +#define ICS932S401_DEVICE 11 +#define ICS932S401_REG_CTRL 0x0A +#define ICS932S401_MN_ENABLED 0x80 +#define ICS932S401_CPU_ALT 0x04 +#define ICS932S401_SRC_ALT 0x08 +#define ICS932S401_REG_CPU_M_CTRL 0x0B +#define ICS932S401_M_MASK 0x3F +#define ICS932S401_REG_CPU_N_CTRL 0x0C +#define ICS932S401_REG_CPU_SPREAD1 0x0D +#define ICS932S401_REG_CPU_SPREAD2 0x0E +#define ICS932S401_SPREAD_MASK 0x7FFF +#define ICS932S401_REG_SRC_M_CTRL 0x0F +#define ICS932S401_REG_SRC_N_CTRL 0x10 +#define ICS932S401_REG_SRC_SPREAD1 0x11 +#define ICS932S401_REG_SRC_SPREAD2 0x12 +#define ICS932S401_REG_CPU_DIVISOR 0x13 +#define ICS932S401_CPU_DIVISOR_SHIFT 4 +#define ICS932S401_REG_PCISRC_DIVISOR 0x14 +#define ICS932S401_SRC_DIVISOR_MASK 0x0F +#define ICS932S401_PCI_DIVISOR_SHIFT 4 + +/* Base clock is 14.318MHz */ +#define BASE_CLOCK 14318 + +#define NUM_REGS 21 +#define NUM_MIRRORED_REGS 15 + +static int regs_to_copy[NUM_MIRRORED_REGS] = { + ICS932S401_REG_CFG2, + ICS932S401_REG_CFG7, + ICS932S401_REG_VENDOR_REV, + ICS932S401_REG_DEVICE, + ICS932S401_REG_CTRL, + ICS932S401_REG_CPU_M_CTRL, + ICS932S401_REG_CPU_N_CTRL, + ICS932S401_REG_CPU_SPREAD1, + ICS932S401_REG_CPU_SPREAD2, + ICS932S401_REG_SRC_M_CTRL, + ICS932S401_REG_SRC_N_CTRL, + ICS932S401_REG_SRC_SPREAD1, + ICS932S401_REG_SRC_SPREAD2, + ICS932S401_REG_CPU_DIVISOR, + ICS932S401_REG_PCISRC_DIVISOR, +}; + +/* How often do we reread sensors values? (In jiffies) */ +#define SENSOR_REFRESH_INTERVAL (2 * HZ) + +/* How often do we reread sensor limit values? (In jiffies) */ +#define LIMIT_REFRESH_INTERVAL (60 * HZ) + +struct ics932s401_data { + struct attribute_group attrs; + struct mutex lock; + char sensors_valid; + unsigned long sensors_last_updated; /* In jiffies */ + + u8 regs[NUM_REGS]; +}; + +static int ics932s401_probe(struct i2c_client *client, + const struct i2c_device_id *id); +static int ics932s401_detect(struct i2c_client *client, int kind, + struct i2c_board_info *info); +static int ics932s401_remove(struct i2c_client *client); + +static const struct i2c_device_id ics932s401_id[] = { + { "ics932s401", ics932s401 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ics932s401_id); + +static struct i2c_driver ics932s401_driver = { + .class = I2C_CLASS_HWMON, + .driver = { + .name = "ics932s401", + }, + .probe = ics932s401_probe, + .remove = ics932s401_remove, + .id_table = ics932s401_id, + .detect = ics932s401_detect, + .address_data = &addr_data, +}; + +static struct ics932s401_data *ics932s401_update_device(struct device *dev) +{ + struct i2c_client *client = to_i2c_client(dev); + struct ics932s401_data *data = i2c_get_clientdata(client); + unsigned long local_jiffies = jiffies; + int i, temp; + + mutex_lock(&data->lock); + if (time_before(local_jiffies, data->sensors_last_updated + + SENSOR_REFRESH_INTERVAL) + && data->sensors_valid) + goto out; + + /* + * Each register must be read as a word and then right shifted 8 bits. + * Not really sure why this is; setting the "byte count programming" + * register to 1 does not fix this problem. + */ + for (i = 0; i < NUM_MIRRORED_REGS; i++) { + temp = i2c_smbus_read_word_data(client, regs_to_copy[i]); + data->regs[regs_to_copy[i]] = temp >> 8; + } + + data->sensors_last_updated = local_jiffies; + data->sensors_valid = 1; + +out: + mutex_unlock(&data->lock); + return data; +} + +static ssize_t show_spread_enabled(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct ics932s401_data *data = ics932s401_update_device(dev); + + if (data->regs[ICS932S401_REG_CFG2] & ICS932S401_CFG1_SPREAD) + return sprintf(buf, "1\n"); + + return sprintf(buf, "0\n"); +} + +/* bit to cpu khz map */ +static const int fs_speeds[] = { + 266666, + 133333, + 200000, + 166666, + 333333, + 100000, + 400000, + 0, +}; + +/* clock divisor map */ +static const int divisors[] = {2, 3, 5, 15, 4, 6, 10, 30, 8, 12, 20, 60, 16, + 24, 40, 120}; + +/* Calculate CPU frequency from the M/N registers. */ +static int calculate_cpu_freq(struct ics932s401_data *data) +{ + int m, n, freq; + + m = data->regs[ICS932S401_REG_CPU_M_CTRL] & ICS932S401_M_MASK; + n = data->regs[ICS932S401_REG_CPU_N_CTRL]; + + /* Pull in bits 8 & 9 from the M register */ + n |= ((int)data->regs[ICS932S401_REG_CPU_M_CTRL] & 0x80) << 1; + n |= ((int)data->regs[ICS932S401_REG_CPU_M_CTRL] & 0x40) << 3; + + freq = BASE_CLOCK * (n + 8) / (m + 2); + freq /= divisors[data->regs[ICS932S401_REG_CPU_DIVISOR] >> + ICS932S401_CPU_DIVISOR_SHIFT]; + + return freq; +} + +static ssize_t show_cpu_clock(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct ics932s401_data *data = ics932s401_update_device(dev); + + return sprintf(buf, "%d\n", calculate_cpu_freq(data)); +} + +static ssize_t show_cpu_clock_sel(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct ics932s401_data *data = ics932s401_update_device(dev); + int freq; + + if (data->regs[ICS932S401_REG_CTRL] & ICS932S401_MN_ENABLED) + freq = calculate_cpu_freq(data); + else { + /* Freq is neatly wrapped up for us */ + int fid = data->regs[ICS932S401_REG_CFG7] & ICS932S401_FS_MASK; + freq = fs_speeds[fid]; + if (data->regs[ICS932S401_REG_CTRL] & ICS932S401_CPU_ALT) { + switch (freq) { + case 166666: + freq = 160000; + break; + case 333333: + freq = 320000; + break; + } + } + } + + return sprintf(buf, "%d\n", freq); +} + +/* Calculate SRC frequency from the M/N registers. */ +static int calculate_src_freq(struct ics932s401_data *data) +{ + int m, n, freq; + + m = data->regs[ICS932S401_REG_SRC_M_CTRL] & ICS932S401_M_MASK; + n = data->regs[ICS932S401_REG_SRC_N_CTRL]; + + /* Pull in bits 8 & 9 from the M register */ + n |= ((int)data->regs[ICS932S401_REG_SRC_M_CTRL] & 0x80) << 1; + n |= ((int)data->regs[ICS932S401_REG_SRC_M_CTRL] & 0x40) << 3; + + freq = BASE_CLOCK * (n + 8) / (m + 2); + freq /= divisors[data->regs[ICS932S401_REG_PCISRC_DIVISOR] & + ICS932S401_SRC_DIVISOR_MASK]; + + return freq; +} + +static ssize_t show_src_clock(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct ics932s401_data *data = ics932s401_update_device(dev); + + return sprintf(buf, "%d\n", calculate_src_freq(data)); +} + +static ssize_t show_src_clock_sel(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct ics932s401_data *data = ics932s401_update_device(dev); + int freq; + + if (data->regs[ICS932S401_REG_CTRL] & ICS932S401_MN_ENABLED) + freq = calculate_src_freq(data); + else + /* Freq is neatly wrapped up for us */ + if (data->regs[ICS932S401_REG_CTRL] & ICS932S401_CPU_ALT && + data->regs[ICS932S401_REG_CTRL] & ICS932S401_SRC_ALT) + freq = 96000; + else + freq = 100000; + + return sprintf(buf, "%d\n", freq); +} + +/* Calculate PCI frequency from the SRC M/N registers. */ +static int calculate_pci_freq(struct ics932s401_data *data) +{ + int m, n, freq; + + m = data->regs[ICS932S401_REG_SRC_M_CTRL] & ICS932S401_M_MASK; + n = data->regs[ICS932S401_REG_SRC_N_CTRL]; + + /* Pull in bits 8 & 9 from the M register */ + n |= ((int)data->regs[ICS932S401_REG_SRC_M_CTRL] & 0x80) << 1; + n |= ((int)data->regs[ICS932S401_REG_SRC_M_CTRL] & 0x40) << 3; + + freq = BASE_CLOCK * (n + 8) / (m + 2); + freq /= divisors[data->regs[ICS932S401_REG_PCISRC_DIVISOR] >> + ICS932S401_PCI_DIVISOR_SHIFT]; + + return freq; +} + +static ssize_t show_pci_clock(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct ics932s401_data *data = ics932s401_update_device(dev); + + return sprintf(buf, "%d\n", calculate_pci_freq(data)); +} + +static ssize_t show_pci_clock_sel(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct ics932s401_data *data = ics932s401_update_device(dev); + int freq; + + if (data->regs[ICS932S401_REG_CTRL] & ICS932S401_MN_ENABLED) + freq = calculate_pci_freq(data); + else + freq = 33333; + + return sprintf(buf, "%d\n", freq); +} + +static ssize_t show_value(struct device *dev, + struct device_attribute *devattr, + char *buf); + +static ssize_t show_spread(struct device *dev, + struct device_attribute *devattr, + char *buf); + +static DEVICE_ATTR(spread_enabled, S_IRUGO, show_spread_enabled, NULL); +static DEVICE_ATTR(cpu_clock_selection, S_IRUGO, show_cpu_clock_sel, NULL); +static DEVICE_ATTR(cpu_clock, S_IRUGO, show_cpu_clock, NULL); +static DEVICE_ATTR(src_clock_selection, S_IRUGO, show_src_clock_sel, NULL); +static DEVICE_ATTR(src_clock, S_IRUGO, show_src_clock, NULL); +static DEVICE_ATTR(pci_clock_selection, S_IRUGO, show_pci_clock_sel, NULL); +static DEVICE_ATTR(pci_clock, S_IRUGO, show_pci_clock, NULL); +static DEVICE_ATTR(usb_clock, S_IRUGO, show_value, NULL); +static DEVICE_ATTR(ref_clock, S_IRUGO, show_value, NULL); +static DEVICE_ATTR(cpu_spread, S_IRUGO, show_spread, NULL); +static DEVICE_ATTR(src_spread, S_IRUGO, show_spread, NULL); + +static struct attribute *ics932s401_attr[] = +{ + &dev_attr_spread_enabled.attr, + &dev_attr_cpu_clock_selection.attr, + &dev_attr_cpu_clock.attr, + &dev_attr_src_clock_selection.attr, + &dev_attr_src_clock.attr, + &dev_attr_pci_clock_selection.attr, + &dev_attr_pci_clock.attr, + &dev_attr_usb_clock.attr, + &dev_attr_ref_clock.attr, + &dev_attr_cpu_spread.attr, + &dev_attr_src_spread.attr, + NULL +}; + +static ssize_t show_value(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + int x; + + if (devattr == &dev_attr_usb_clock) + x = 48000; + else if (devattr == &dev_attr_ref_clock) + x = BASE_CLOCK; + else + BUG(); + + return sprintf(buf, "%d\n", x); +} + +static ssize_t show_spread(struct device *dev, + struct device_attribute *devattr, + char *buf) +{ + struct ics932s401_data *data = ics932s401_update_device(dev); + int reg; + unsigned long val; + + if (!(data->regs[ICS932S401_REG_CFG2] & ICS932S401_CFG1_SPREAD)) + return sprintf(buf, "0%%\n"); + + if (devattr == &dev_attr_src_spread) + reg = ICS932S401_REG_SRC_SPREAD1; + else if (devattr == &dev_attr_cpu_spread) + reg = ICS932S401_REG_CPU_SPREAD1; + else + BUG(); + + val = data->regs[reg] | (data->regs[reg + 1] << 8); + val &= ICS932S401_SPREAD_MASK; + + /* Scale 0..2^14 to -0.5. */ + val = 500000 * val / 16384; + return sprintf(buf, "-0.%lu%%\n", val); +} + +/* Return 0 if detection is successful, -ENODEV otherwise */ +static int ics932s401_detect(struct i2c_client *client, int kind, + struct i2c_board_info *info) +{ + struct i2c_adapter *adapter = client->adapter; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; + + if (kind <= 0) { + int vendor, device, revision; + + vendor = i2c_smbus_read_word_data(client, + ICS932S401_REG_VENDOR_REV); + vendor >>= 8; + revision = vendor >> ICS932S401_REV_SHIFT; + vendor &= ICS932S401_VENDOR_MASK; + if (vendor != ICS932S401_VENDOR) + return -ENODEV; + + device = i2c_smbus_read_word_data(client, + ICS932S401_REG_DEVICE); + device >>= 8; + if (device != ICS932S401_DEVICE) + return -ENODEV; + + if (revision != ICS932S401_REV) + dev_info(&adapter->dev, "Unknown revision %d\n", + revision); + } else + dev_dbg(&adapter->dev, "detection forced\n"); + + strlcpy(info->type, "ics932s401", I2C_NAME_SIZE); + + return 0; +} + +static int ics932s401_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct ics932s401_data *data; + int err; + + data = kzalloc(sizeof(struct ics932s401_data), GFP_KERNEL); + if (!data) { + err = -ENOMEM; + goto exit; + } + + i2c_set_clientdata(client, data); + mutex_init(&data->lock); + + dev_info(&client->dev, "%s chip found\n", client->name); + + /* Register sysfs hooks */ + data->attrs.attrs = ics932s401_attr; + err = sysfs_create_group(&client->dev.kobj, &data->attrs); + if (err) + goto exit_free; + + return 0; + +exit_free: + kfree(data); +exit: + return err; +} + +static int ics932s401_remove(struct i2c_client *client) +{ + struct ics932s401_data *data = i2c_get_clientdata(client); + + sysfs_remove_group(&client->dev.kobj, &data->attrs); + kfree(data); + return 0; +} + +static int __init ics932s401_init(void) +{ + return i2c_add_driver(&ics932s401_driver); +} + +static void __exit ics932s401_exit(void) +{ + i2c_del_driver(&ics932s401_driver); +} + +MODULE_AUTHOR("Darrick J. Wong <djwong@us.ibm.com>"); +MODULE_DESCRIPTION("ICS932S401 driver"); +MODULE_LICENSE("GPL"); + +module_init(ics932s401_init); +module_exit(ics932s401_exit); + +/* IBM IntelliStation Z30 */ +MODULE_ALIAS("dmi:bvnIBM:*:rn9228:*"); +MODULE_ALIAS("dmi:bvnIBM:*:rn9232:*"); + +/* IBM x3650/x3550 */ +MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3650*"); +MODULE_ALIAS("dmi:bvnIBM:*:pnIBMSystemx3550*"); diff --git a/drivers/misc/intel_menlow.c b/drivers/misc/intel_menlow.c index e00a2756e97..27b7662955b 100644 --- a/drivers/misc/intel_menlow.c +++ b/drivers/misc/intel_menlow.c @@ -52,6 +52,11 @@ MODULE_LICENSE("GPL"); #define MEMORY_ARG_CUR_BANDWIDTH 1 #define MEMORY_ARG_MAX_BANDWIDTH 0 +/* + * GTHS returning 'n' would mean that [0,n-1] states are supported + * In that case max_cstate would be n-1 + * GTHS returning '0' would mean that no bandwidth control states are supported + */ static int memory_get_int_max_bandwidth(struct thermal_cooling_device *cdev, unsigned long *max_state) { @@ -71,6 +76,9 @@ static int memory_get_int_max_bandwidth(struct thermal_cooling_device *cdev, if (ACPI_FAILURE(status)) return -EFAULT; + if (!value) + return -EINVAL; + *max_state = value - 1; return 0; } @@ -121,7 +129,7 @@ static int memory_set_cur_bandwidth(struct thermal_cooling_device *cdev, if (memory_get_int_max_bandwidth(cdev, &max_state)) return -EFAULT; - if (max_state < 0 || state > max_state) + if (state > max_state) return -EINVAL; arg_list.count = 1; diff --git a/drivers/misc/msi-laptop.c b/drivers/misc/msi-laptop.c index de898c6938f..759763d18e4 100644 --- a/drivers/misc/msi-laptop.c +++ b/drivers/misc/msi-laptop.c @@ -347,12 +347,16 @@ static int __init msi_init(void) /* Register backlight stuff */ - msibl_device = backlight_device_register("msi-laptop-bl", NULL, NULL, - &msibl_ops); - if (IS_ERR(msibl_device)) - return PTR_ERR(msibl_device); - - msibl_device->props.max_brightness = MSI_LCD_LEVEL_MAX-1; + if (acpi_video_backlight_support()) { + printk(KERN_INFO "MSI: Brightness ignored, must be controlled " + "by ACPI video driver\n"); + } else { + msibl_device = backlight_device_register("msi-laptop-bl", NULL, + NULL, &msibl_ops); + if (IS_ERR(msibl_device)) + return PTR_ERR(msibl_device); + msibl_device->props.max_brightness = MSI_LCD_LEVEL_MAX-1; + } ret = platform_driver_register(&msipf_driver); if (ret) diff --git a/drivers/misc/sony-laptop.c b/drivers/misc/sony-laptop.c index 06f07e19dc7..7bcb81002dc 100644 --- a/drivers/misc/sony-laptop.c +++ b/drivers/misc/sony-laptop.c @@ -1038,7 +1038,11 @@ static int sony_nc_add(struct acpi_device *device) goto outinput; } - if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT", &handle))) { + if (!acpi_video_backlight_support()) { + printk(KERN_INFO DRV_PFX "Sony: Brightness ignored, must be " + "controlled by ACPI video driver\n"); + } else if (ACPI_SUCCESS(acpi_get_handle(sony_nc_acpi_handle, "GBRT", + &handle))) { sony_backlight_device = backlight_device_register("sony", NULL, NULL, &sony_backlight_ops); diff --git a/drivers/misc/thinkpad_acpi.c b/drivers/misc/thinkpad_acpi.c index 4db1cf9078d..7a4a26b0edd 100644 --- a/drivers/misc/thinkpad_acpi.c +++ b/drivers/misc/thinkpad_acpi.c @@ -4932,16 +4932,25 @@ static int __init brightness_init(struct ibm_init_struct *iibm) */ b = tpacpi_check_std_acpi_brightness_support(); if (b > 0) { - if (thinkpad_id.vendor == PCI_VENDOR_ID_LENOVO) { - printk(TPACPI_NOTICE - "Lenovo BIOS switched to ACPI backlight " - "control mode\n"); - } - if (brightness_enable > 1) { - printk(TPACPI_NOTICE - "standard ACPI backlight interface " - "available, not loading native one...\n"); - return 1; + + if (acpi_video_backlight_support()) { + if (brightness_enable > 1) { + printk(TPACPI_NOTICE + "Standard ACPI backlight interface " + "available, not loading native one.\n"); + return 1; + } else if (brightness_enable == 1) { + printk(TPACPI_NOTICE + "Backlight control force enabled, even if standard " + "ACPI backlight interface is available\n"); + } + } else { + if (brightness_enable > 1) { + printk(TPACPI_NOTICE + "Standard ACPI backlight interface not " + "available, thinkpad_acpi native " + "brightness control enabled\n"); + } } } diff --git a/drivers/mtd/maps/cdb89712.c b/drivers/mtd/maps/cdb89712.c index e5059aa3c72..8d92d8db9a9 100644 --- a/drivers/mtd/maps/cdb89712.c +++ b/drivers/mtd/maps/cdb89712.c @@ -14,7 +14,18 @@ #include <linux/mtd/map.h> #include <linux/mtd/partitions.h> - +/* dynamic ioremap() areas */ +#define FLASH_START 0x00000000 +#define FLASH_SIZE 0x800000 +#define FLASH_WIDTH 4 + +#define SRAM_START 0x60000000 +#define SRAM_SIZE 0xc000 +#define SRAM_WIDTH 4 + +#define BOOTROM_START 0x70000000 +#define BOOTROM_SIZE 0x80 +#define BOOTROM_WIDTH 4 static struct mtd_info *flash_mtd; diff --git a/drivers/mtd/maps/h720x-flash.c b/drivers/mtd/maps/h720x-flash.c index 35fef655ccc..3b959fad1c4 100644 --- a/drivers/mtd/maps/h720x-flash.c +++ b/drivers/mtd/maps/h720x-flash.c @@ -24,8 +24,8 @@ static struct mtd_info *mymtd; static struct map_info h720x_map = { .name = "H720X", .bankwidth = 4, - .size = FLASH_SIZE, - .phys = FLASH_PHYS, + .size = H720X_FLASH_SIZE, + .phys = H720X_FLASH_PHYS, }; static struct mtd_partition h720x_partitions[] = { @@ -70,7 +70,7 @@ int __init h720x_mtd_init(void) char *part_type = NULL; - h720x_map.virt = ioremap(FLASH_PHYS, FLASH_SIZE); + h720x_map.virt = ioremap(h720x_map.phys, h720x_map.size); if (!h720x_map.virt) { printk(KERN_ERR "H720x-MTD: ioremap failed\n"); diff --git a/drivers/net/bnx2.c b/drivers/net/bnx2.c index 430d430bce2..d07e3f14895 100644 --- a/drivers/net/bnx2.c +++ b/drivers/net/bnx2.c @@ -7204,10 +7204,13 @@ static void poll_bnx2(struct net_device *dev) { struct bnx2 *bp = netdev_priv(dev); + int i; - disable_irq(bp->pdev->irq); - bnx2_interrupt(bp->pdev->irq, dev); - enable_irq(bp->pdev->irq); + for (i = 0; i < bp->irq_nvecs; i++) { + disable_irq(bp->irq_tbl[i].vector); + bnx2_interrupt(bp->irq_tbl[i].vector, &bp->bnx2_napi[i]); + enable_irq(bp->irq_tbl[i].vector); + } } #endif diff --git a/drivers/net/cxgb3/cxgb3_main.c b/drivers/net/cxgb3/cxgb3_main.c index 1ace41a13ac..2c341f83d32 100644 --- a/drivers/net/cxgb3/cxgb3_main.c +++ b/drivers/net/cxgb3/cxgb3_main.c @@ -1307,8 +1307,10 @@ static void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) u32 fw_vers = 0; u32 tp_vers = 0; + spin_lock(&adapter->stats_lock); t3_get_fw_version(adapter, &fw_vers); t3_get_tp_version(adapter, &tp_vers); + spin_unlock(&adapter->stats_lock); strcpy(info->driver, DRV_NAME); strcpy(info->version, DRV_VERSION); @@ -2699,7 +2701,7 @@ static void set_nqsets(struct adapter *adap) int hwports = adap->params.nports; int nqsets = SGE_QSETS; - if (adap->params.rev > 0) { + if (adap->params.rev > 0 && adap->flags & USING_MSIX) { if (hwports == 2 && (hwports * nqsets > SGE_QSETS || num_cpus >= nqsets / hwports)) diff --git a/drivers/net/cxgb3/t3_hw.c b/drivers/net/cxgb3/t3_hw.c index 968f64be374..9a0898b0dbc 100644 --- a/drivers/net/cxgb3/t3_hw.c +++ b/drivers/net/cxgb3/t3_hw.c @@ -572,7 +572,7 @@ struct t3_vpd { u32 pad; /* for multiple-of-4 sizing and alignment */ }; -#define EEPROM_MAX_POLL 4 +#define EEPROM_MAX_POLL 40 #define EEPROM_STAT_ADDR 0x4000 #define VPD_BASE 0xc00 @@ -3690,6 +3690,12 @@ int t3_prep_adapter(struct adapter *adapter, const struct adapter_info *ai, ; pti = &port_types[adapter->params.vpd.port_type[j]]; + if (!pti->phy_prep) { + CH_ALERT(adapter, "Invalid port type index %d\n", + adapter->params.vpd.port_type[j]); + return -EINVAL; + } + ret = pti->phy_prep(&p->phy, adapter, ai->phy_base_addr + j, ai->mdio_ops); if (ret) diff --git a/drivers/net/mlx4/mlx4.h b/drivers/net/mlx4/mlx4.h index fa431fad0ee..56a2e213fe6 100644 --- a/drivers/net/mlx4/mlx4.h +++ b/drivers/net/mlx4/mlx4.h @@ -87,6 +87,9 @@ enum { #ifdef CONFIG_MLX4_DEBUG extern int mlx4_debug_level; +#else /* CONFIG_MLX4_DEBUG */ +#define mlx4_debug_level (0) +#endif /* CONFIG_MLX4_DEBUG */ #define mlx4_dbg(mdev, format, arg...) \ do { \ @@ -94,12 +97,6 @@ extern int mlx4_debug_level; dev_printk(KERN_DEBUG, &mdev->pdev->dev, format, ## arg); \ } while (0) -#else /* CONFIG_MLX4_DEBUG */ - -#define mlx4_dbg(mdev, format, arg...) do { (void) mdev; } while (0) - -#endif /* CONFIG_MLX4_DEBUG */ - #define mlx4_err(mdev, format, arg...) \ dev_err(&mdev->pdev->dev, format, ## arg) #define mlx4_info(mdev, format, arg...) \ diff --git a/drivers/net/myri10ge/myri10ge.c b/drivers/net/myri10ge/myri10ge.c index a5f428bcc0e..b3786709730 100644 --- a/drivers/net/myri10ge/myri10ge.c +++ b/drivers/net/myri10ge/myri10ge.c @@ -75,7 +75,7 @@ #include "myri10ge_mcp.h" #include "myri10ge_mcp_gen_header.h" -#define MYRI10GE_VERSION_STR "1.4.3-1.375" +#define MYRI10GE_VERSION_STR "1.4.3-1.378" MODULE_DESCRIPTION("Myricom 10G driver (10GbE)"); MODULE_AUTHOR("Maintainer: help@myri.com"); @@ -1393,6 +1393,7 @@ myri10ge_tx_done(struct myri10ge_slice_state *ss, int mcp_index) if (tx->req == tx->done) { tx->queue_active = 0; put_be32(htonl(1), tx->send_stop); + mb(); mmiowb(); } __netif_tx_unlock(dev_queue); @@ -2865,6 +2866,7 @@ again: if ((mgp->dev->real_num_tx_queues > 1) && tx->queue_active == 0) { tx->queue_active = 1; put_be32(htonl(1), tx->send_go); + mb(); mmiowb(); } tx->pkt_start++; diff --git a/drivers/net/niu.c b/drivers/net/niu.c index 9acb5d70a3a..d8463b1c3df 100644 --- a/drivers/net/niu.c +++ b/drivers/net/niu.c @@ -51,8 +51,7 @@ MODULE_VERSION(DRV_MODULE_VERSION); #ifndef readq static u64 readq(void __iomem *reg) { - return (((u64)readl(reg + 0x4UL) << 32) | - (u64)readl(reg)); + return ((u64) readl(reg)) | (((u64) readl(reg + 4UL)) << 32); } static void writeq(u64 val, void __iomem *reg) diff --git a/drivers/net/smc911x.c b/drivers/net/smc911x.c index 5051554ff05..1f26ab0e798 100644 --- a/drivers/net/smc911x.c +++ b/drivers/net/smc911x.c @@ -2050,7 +2050,9 @@ err_out: */ static int smc911x_drv_probe(struct platform_device *pdev) { +#ifdef SMC_DYNAMIC_BUS_CONFIG struct smc911x_platdata *pd = pdev->dev.platform_data; +#endif struct net_device *ndev; struct resource *res; struct smc911x_local *lp; @@ -2182,9 +2184,9 @@ static int smc911x_drv_resume(struct platform_device *dev) if (netif_running(ndev)) { smc911x_reset(ndev); - smc911x_enable(ndev); if (lp->phy_type != 0) smc911x_phy_configure(&lp->phy_configure); + smc911x_enable(ndev); netif_device_attach(ndev); } } diff --git a/drivers/net/usb/asix.c b/drivers/net/usb/asix.c index 37ecf845edf..e12cdb4543b 100644 --- a/drivers/net/usb/asix.c +++ b/drivers/net/usb/asix.c @@ -1444,6 +1444,10 @@ static const struct usb_device_id products [] = { // Apple USB Ethernet Adapter USB_DEVICE(0x05ac, 0x1402), .driver_info = (unsigned long) &ax88772_info, +}, { + // Cables-to-Go USB Ethernet Adapter + USB_DEVICE(0x0b95, 0x772a), + .driver_info = (unsigned long) &ax88772_info, }, { }, // END }; diff --git a/drivers/net/wireless/hostap/hostap_wlan.h b/drivers/net/wireless/hostap/hostap_wlan.h index ffdf4876121..a68f97c3935 100644 --- a/drivers/net/wireless/hostap/hostap_wlan.h +++ b/drivers/net/wireless/hostap/hostap_wlan.h @@ -918,9 +918,12 @@ struct hostap_interface { /* * TX meta data - stored in skb->cb buffer, so this must not be increased over - * the 40-byte limit + * the 48-byte limit. + * THE PADDING THIS STARTS WITH IS A HORRIBLE HACK THAT SHOULD NOT LIVE + * TO SEE THE DAY. */ struct hostap_skb_tx_data { + unsigned int __padding_for_default_qdiscs; u32 magic; /* HOSTAP_SKB_TX_DATA_MAGIC */ u8 rate; /* transmit rate */ #define HOSTAP_TX_FLAGS_WDS BIT(0) diff --git a/drivers/net/wireless/rtl8187_dev.c b/drivers/net/wireless/rtl8187_dev.c index 431e3c78bf2..69eb0132593 100644 --- a/drivers/net/wireless/rtl8187_dev.c +++ b/drivers/net/wireless/rtl8187_dev.c @@ -48,6 +48,9 @@ static struct usb_device_id rtl8187_table[] __devinitdata = { {USB_DEVICE(0x03f0, 0xca02), .driver_info = DEVICE_RTL8187}, /* Sitecom */ {USB_DEVICE(0x0df6, 0x000d), .driver_info = DEVICE_RTL8187}, + {USB_DEVICE(0x0df6, 0x0028), .driver_info = DEVICE_RTL8187B}, + /* Abocom */ + {USB_DEVICE(0x13d1, 0xabe6), .driver_info = DEVICE_RTL8187}, {} }; diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index b3a63edb690..ae5ec76dca7 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -63,7 +63,7 @@ static acpi_status acpi_run_osc(acpi_handle handle, union acpi_object in_params[4]; struct acpi_buffer output = {ACPI_ALLOCATE_BUFFER, NULL}; union acpi_object *out_obj; - u32 osc_dw0, flags = osc_args->capbuf[OSC_QUERY_TYPE]; + u32 errors, flags = osc_args->capbuf[OSC_QUERY_TYPE]; /* Setting up input parameters */ input.count = 4; @@ -92,15 +92,16 @@ static acpi_status acpi_run_osc(acpi_handle handle, status = AE_TYPE; goto out_kfree; } - osc_dw0 = *((u32 *)out_obj->buffer.pointer); - if (osc_dw0) { - if (osc_dw0 & OSC_REQUEST_ERROR) + /* Need to ignore the bit0 in result code */ + errors = *((u32 *)out_obj->buffer.pointer) & ~(1 << 0); + if (errors) { + if (errors & OSC_REQUEST_ERROR) printk(KERN_DEBUG "_OSC request fails\n"); - if (osc_dw0 & OSC_INVALID_UUID_ERROR) + if (errors & OSC_INVALID_UUID_ERROR) printk(KERN_DEBUG "_OSC invalid UUID\n"); - if (osc_dw0 & OSC_INVALID_REVISION_ERROR) + if (errors & OSC_INVALID_REVISION_ERROR) printk(KERN_DEBUG "_OSC invalid revision\n"); - if (osc_dw0 & OSC_CAPABILITIES_MASK_ERROR) { + if (errors & OSC_CAPABILITIES_MASK_ERROR) { if (flags & OSC_QUERY_ENABLE) goto out_success; printk(KERN_DEBUG "_OSC FW not grant req. control\n"); diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 8abbb2020af..123092d8a98 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -277,6 +277,14 @@ config RTC_DRV_FM3130 This driver can also be built as a module. If so the module will be called rtc-fm3130. +config RTC_DRV_RX8581 + tristate "Epson RX-8581" + help + If you say yes here you will get support for the Epson RX-8581. + + This driver can also be built as a module. If so the module + will be called rtc-rx8581. + endif # I2C comment "SPI RTC drivers" @@ -302,6 +310,17 @@ config RTC_DRV_DS1305 This driver can also be built as a module. If so, the module will be called rtc-ds1305. +config RTC_DRV_DS1390 + tristate "Dallas/Maxim DS1390/93/94" + help + If you say yes here you get support for the DS1390/93/94 chips. + + This driver only supports the RTC feature, and not other chip + features such as alarms and trickle charging. + + This driver can also be built as a module. If so, the module + will be called rtc-ds1390. + config RTC_DRV_MAX6902 tristate "Maxim MAX6902" help @@ -468,6 +487,16 @@ config RTC_DRV_V3020 This driver can also be built as a module. If so, the module will be called rtc-v3020. +config RTC_DRV_WM8350 + tristate "Wolfson Microelectronics WM8350 RTC" + depends on MFD_WM8350 + help + If you say yes here you will get support for the RTC subsystem + of the Wolfson Microelectronics WM8350. + + This driver can also be built as a module. If so, the module + will be called "rtc-wm8350". + comment "on-CPU RTC drivers" config RTC_DRV_OMAP diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index e9e8474cc8f..6e79c912bf9 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -28,6 +28,7 @@ obj-$(CONFIG_RTC_DRV_DS1302) += rtc-ds1302.o obj-$(CONFIG_RTC_DRV_DS1305) += rtc-ds1305.o obj-$(CONFIG_RTC_DRV_DS1307) += rtc-ds1307.o obj-$(CONFIG_RTC_DRV_DS1374) += rtc-ds1374.o +obj-$(CONFIG_RTC_DRV_DS1390) += rtc-ds1390.o obj-$(CONFIG_RTC_DRV_DS1511) += rtc-ds1511.o obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o @@ -57,6 +58,7 @@ obj-$(CONFIG_RTC_DRV_R9701) += rtc-r9701.o obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o obj-$(CONFIG_RTC_DRV_RS5C372) += rtc-rs5c372.o +obj-$(CONFIG_RTC_DRV_RX8581) += rtc-rx8581.o obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o @@ -66,4 +68,5 @@ obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o obj-$(CONFIG_RTC_DRV_TWL4030) += rtc-twl4030.o obj-$(CONFIG_RTC_DRV_V3020) += rtc-v3020.o obj-$(CONFIG_RTC_DRV_VR41XX) += rtc-vr41xx.o +obj-$(CONFIG_RTC_DRV_WM8350) += rtc-wm8350.o obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o diff --git a/drivers/rtc/rtc-ds1390.c b/drivers/rtc/rtc-ds1390.c new file mode 100644 index 00000000000..599e976bf01 --- /dev/null +++ b/drivers/rtc/rtc-ds1390.c @@ -0,0 +1,220 @@ +/* + * rtc-ds1390.c -- driver for DS1390/93/94 + * + * Copyright (C) 2008 Mercury IMC Ltd + * Written by Mark Jackson <mpfj@mimc.co.uk> + * + * 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. + * + * NOTE : Currently this driver only supports the bare minimum for read + * and write the RTC. The extra features provided by the chip family + * (alarms, trickle charger, different control registers) are unavailable. + */ + +#include <linux/platform_device.h> +#include <linux/rtc.h> +#include <linux/spi/spi.h> +#include <linux/bcd.h> + +#define DS1390_REG_100THS 0x00 +#define DS1390_REG_SECONDS 0x01 +#define DS1390_REG_MINUTES 0x02 +#define DS1390_REG_HOURS 0x03 +#define DS1390_REG_DAY 0x04 +#define DS1390_REG_DATE 0x05 +#define DS1390_REG_MONTH_CENT 0x06 +#define DS1390_REG_YEAR 0x07 + +#define DS1390_REG_ALARM_100THS 0x08 +#define DS1390_REG_ALARM_SECONDS 0x09 +#define DS1390_REG_ALARM_MINUTES 0x0A +#define DS1390_REG_ALARM_HOURS 0x0B +#define DS1390_REG_ALARM_DAY_DATE 0x0C + +#define DS1390_REG_CONTROL 0x0D +#define DS1390_REG_STATUS 0x0E +#define DS1390_REG_TRICKLE 0x0F + +struct ds1390 { + struct rtc_device *rtc; + u8 txrx_buf[9]; /* cmd + 8 registers */ +}; + +static void ds1390_set_reg(struct device *dev, unsigned char address, + unsigned char data) +{ + struct spi_device *spi = to_spi_device(dev); + struct ds1390 *chip = dev_get_drvdata(dev); + + /* Set MSB to indicate write */ + chip->txrx_buf[0] = address | 0x80; + chip->txrx_buf[1] = data; + + /* do the i/o */ + spi_write_then_read(spi, chip->txrx_buf, 2, NULL, 0); +} + +static int ds1390_get_reg(struct device *dev, unsigned char address, + unsigned char *data) +{ + struct spi_device *spi = to_spi_device(dev); + struct ds1390 *chip = dev_get_drvdata(dev); + int status; + + if (!data) + return -EINVAL; + + /* Clear MSB to indicate read */ + chip->txrx_buf[0] = address & 0x7f; + /* do the i/o */ + status = spi_write_then_read(spi, chip->txrx_buf, 1, chip->txrx_buf, 1); + if (status != 0) + return status; + + *data = chip->txrx_buf[1]; + + return 0; +} + +static int ds1390_get_datetime(struct device *dev, struct rtc_time *dt) +{ + struct spi_device *spi = to_spi_device(dev); + struct ds1390 *chip = dev_get_drvdata(dev); + int status; + + /* build the message */ + chip->txrx_buf[0] = DS1390_REG_SECONDS; + + /* do the i/o */ + status = spi_write_then_read(spi, chip->txrx_buf, 1, chip->txrx_buf, 8); + if (status != 0) + return status; + + /* The chip sends data in this order: + * Seconds, Minutes, Hours, Day, Date, Month / Century, Year */ + dt->tm_sec = bcd2bin(chip->txrx_buf[0]); + dt->tm_min = bcd2bin(chip->txrx_buf[1]); + dt->tm_hour = bcd2bin(chip->txrx_buf[2]); + dt->tm_wday = bcd2bin(chip->txrx_buf[3]); + dt->tm_mday = bcd2bin(chip->txrx_buf[4]); + /* mask off century bit */ + dt->tm_mon = bcd2bin(chip->txrx_buf[5] & 0x7f) - 1; + /* adjust for century bit */ + dt->tm_year = bcd2bin(chip->txrx_buf[6]) + ((chip->txrx_buf[5] & 0x80) ? 100 : 0); + + return rtc_valid_tm(dt); +} + +static int ds1390_set_datetime(struct device *dev, struct rtc_time *dt) +{ + struct spi_device *spi = to_spi_device(dev); + struct ds1390 *chip = dev_get_drvdata(dev); + + /* build the message */ + chip->txrx_buf[0] = DS1390_REG_SECONDS | 0x80; + chip->txrx_buf[1] = bin2bcd(dt->tm_sec); + chip->txrx_buf[2] = bin2bcd(dt->tm_min); + chip->txrx_buf[3] = bin2bcd(dt->tm_hour); + chip->txrx_buf[4] = bin2bcd(dt->tm_wday); + chip->txrx_buf[5] = bin2bcd(dt->tm_mday); + chip->txrx_buf[6] = bin2bcd(dt->tm_mon + 1) | + ((dt->tm_year > 99) ? 0x80 : 0x00); + chip->txrx_buf[7] = bin2bcd(dt->tm_year % 100); + + /* do the i/o */ + return spi_write_then_read(spi, chip->txrx_buf, 8, NULL, 0); +} + +static int ds1390_read_time(struct device *dev, struct rtc_time *tm) +{ + return ds1390_get_datetime(dev, tm); +} + +static int ds1390_set_time(struct device *dev, struct rtc_time *tm) +{ + return ds1390_set_datetime(dev, tm); +} + +static const struct rtc_class_ops ds1390_rtc_ops = { + .read_time = ds1390_read_time, + .set_time = ds1390_set_time, +}; + +static int __devinit ds1390_probe(struct spi_device *spi) +{ + struct rtc_device *rtc; + unsigned char tmp; + struct ds1390 *chip; + int res; + + printk(KERN_DEBUG "DS1390 SPI RTC driver\n"); + + rtc = rtc_device_register("ds1390", + &spi->dev, &ds1390_rtc_ops, THIS_MODULE); + if (IS_ERR(rtc)) { + printk(KERN_ALERT "RTC : unable to register device\n"); + return PTR_ERR(rtc); + } + + spi->mode = SPI_MODE_3; + spi->bits_per_word = 8; + spi_setup(spi); + + chip = kzalloc(sizeof *chip, GFP_KERNEL); + if (!chip) { + printk(KERN_ALERT "RTC : unable to allocate device memory\n"); + rtc_device_unregister(rtc); + return -ENOMEM; + } + chip->rtc = rtc; + dev_set_drvdata(&spi->dev, chip); + + res = ds1390_get_reg(&spi->dev, DS1390_REG_SECONDS, &tmp); + if (res) { + printk(KERN_ALERT "RTC : unable to read device\n"); + rtc_device_unregister(rtc); + return res; + } + + return 0; +} + +static int __devexit ds1390_remove(struct spi_device *spi) +{ + struct ds1390 *chip = platform_get_drvdata(spi); + struct rtc_device *rtc = chip->rtc; + + if (rtc) + rtc_device_unregister(rtc); + + kfree(chip); + + return 0; +} + +static struct spi_driver ds1390_driver = { + .driver = { + .name = "rtc-ds1390", + .owner = THIS_MODULE, + }, + .probe = ds1390_probe, + .remove = __devexit_p(ds1390_remove), +}; + +static __init int ds1390_init(void) +{ + return spi_register_driver(&ds1390_driver); +} +module_init(ds1390_init); + +static __exit void ds1390_exit(void) +{ + spi_unregister_driver(&ds1390_driver); +} +module_exit(ds1390_exit); + +MODULE_DESCRIPTION("DS1390/93/94 SPI RTC driver"); +MODULE_AUTHOR("Mark Jackson <mpfj@mimc.co.uk>"); +MODULE_LICENSE("GPL"); diff --git a/drivers/rtc/rtc-rx8581.c b/drivers/rtc/rtc-rx8581.c new file mode 100644 index 00000000000..c9522f3bc21 --- /dev/null +++ b/drivers/rtc/rtc-rx8581.c @@ -0,0 +1,281 @@ +/* + * An I2C driver for the Epson RX8581 RTC + * + * Author: Martyn Welch <martyn.welch@gefanuc.com> + * Copyright 2008 GE Fanuc Intelligent Platforms Embedded Systems, 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. + * + * Based on: rtc-pcf8563.c (An I2C driver for the Philips PCF8563 RTC) + * Copyright 2005-06 Tower Technologies + */ + +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/bcd.h> +#include <linux/rtc.h> +#include <linux/log2.h> + +#define DRV_VERSION "0.1" + +#define RX8581_REG_SC 0x00 /* Second in BCD */ +#define RX8581_REG_MN 0x01 /* Minute in BCD */ +#define RX8581_REG_HR 0x02 /* Hour in BCD */ +#define RX8581_REG_DW 0x03 /* Day of Week */ +#define RX8581_REG_DM 0x04 /* Day of Month in BCD */ +#define RX8581_REG_MO 0x05 /* Month in BCD */ +#define RX8581_REG_YR 0x06 /* Year in BCD */ +#define RX8581_REG_RAM 0x07 /* RAM */ +#define RX8581_REG_AMN 0x08 /* Alarm Min in BCD*/ +#define RX8581_REG_AHR 0x09 /* Alarm Hour in BCD */ +#define RX8581_REG_ADM 0x0A +#define RX8581_REG_ADW 0x0A +#define RX8581_REG_TMR0 0x0B +#define RX8581_REG_TMR1 0x0C +#define RX8581_REG_EXT 0x0D /* Extension Register */ +#define RX8581_REG_FLAG 0x0E /* Flag Register */ +#define RX8581_REG_CTRL 0x0F /* Control Register */ + + +/* Flag Register bit definitions */ +#define RX8581_FLAG_UF 0x20 /* Update */ +#define RX8581_FLAG_TF 0x10 /* Timer */ +#define RX8581_FLAG_AF 0x08 /* Alarm */ +#define RX8581_FLAG_VLF 0x02 /* Voltage Low */ + +/* Control Register bit definitions */ +#define RX8581_CTRL_UIE 0x20 /* Update Interrupt Enable */ +#define RX8581_CTRL_TIE 0x10 /* Timer Interrupt Enable */ +#define RX8581_CTRL_AIE 0x08 /* Alarm Interrupt Enable */ +#define RX8581_CTRL_STOP 0x02 /* STOP bit */ +#define RX8581_CTRL_RESET 0x01 /* RESET bit */ + +static struct i2c_driver rx8581_driver; + +/* + * In the routines that deal directly with the rx8581 hardware, we use + * rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch. + */ +static int rx8581_get_datetime(struct i2c_client *client, struct rtc_time *tm) +{ + unsigned char date[7]; + int data, err; + + /* First we ensure that the "update flag" is not set, we read the + * time and date then re-read the "update flag". If the update flag + * has been set, we know that the time has changed during the read so + * we repeat the whole process again. + */ + data = i2c_smbus_read_byte_data(client, RX8581_REG_FLAG); + if (data < 0) { + dev_err(&client->dev, "Unable to read device flags\n"); + return -EIO; + } + + do { + /* If update flag set, clear it */ + if (data & RX8581_FLAG_UF) { + err = i2c_smbus_write_byte_data(client, + RX8581_REG_FLAG, (data & ~RX8581_FLAG_UF)); + if (err != 0) { + dev_err(&client->dev, "Unable to write device " + "flags\n"); + return -EIO; + } + } + + /* Now read time and date */ + err = i2c_smbus_read_i2c_block_data(client, RX8581_REG_SC, + 7, date); + if (err < 0) { + dev_err(&client->dev, "Unable to read date\n"); + return -EIO; + } + + /* Check flag register */ + data = i2c_smbus_read_byte_data(client, RX8581_REG_FLAG); + if (data < 0) { + dev_err(&client->dev, "Unable to read device flags\n"); + return -EIO; + } + } while (data & RX8581_FLAG_UF); + + if (data & RX8581_FLAG_VLF) + dev_info(&client->dev, + "low voltage detected, date/time is not reliable.\n"); + + dev_dbg(&client->dev, + "%s: raw data is sec=%02x, min=%02x, hr=%02x, " + "wday=%02x, mday=%02x, mon=%02x, year=%02x\n", + __func__, + date[0], date[1], date[2], date[3], date[4], date[5], date[6]); + + tm->tm_sec = bcd2bin(date[RX8581_REG_SC] & 0x7F); + tm->tm_min = bcd2bin(date[RX8581_REG_MN] & 0x7F); + tm->tm_hour = bcd2bin(date[RX8581_REG_HR] & 0x3F); /* rtc hr 0-23 */ + tm->tm_wday = ilog2(date[RX8581_REG_DW] & 0x7F); + tm->tm_mday = bcd2bin(date[RX8581_REG_DM] & 0x3F); + tm->tm_mon = bcd2bin(date[RX8581_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */ + tm->tm_year = bcd2bin(date[RX8581_REG_YR]); + if (tm->tm_year < 70) + tm->tm_year += 100; /* assume we are in 1970...2069 */ + + + dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, " + "mday=%d, mon=%d, year=%d, wday=%d\n", + __func__, + tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + + err = rtc_valid_tm(tm); + if (err < 0) + dev_err(&client->dev, "retrieved date/time is not valid.\n"); + + return err; +} + +static int rx8581_set_datetime(struct i2c_client *client, struct rtc_time *tm) +{ + int data, err; + unsigned char buf[7]; + + dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, " + "mday=%d, mon=%d, year=%d, wday=%d\n", + __func__, + tm->tm_sec, tm->tm_min, tm->tm_hour, + tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); + + /* hours, minutes and seconds */ + buf[RX8581_REG_SC] = bin2bcd(tm->tm_sec); + buf[RX8581_REG_MN] = bin2bcd(tm->tm_min); + buf[RX8581_REG_HR] = bin2bcd(tm->tm_hour); + + buf[RX8581_REG_DM] = bin2bcd(tm->tm_mday); + + /* month, 1 - 12 */ + buf[RX8581_REG_MO] = bin2bcd(tm->tm_mon + 1); + + /* year and century */ + buf[RX8581_REG_YR] = bin2bcd(tm->tm_year % 100); + buf[RX8581_REG_DW] = (0x1 << tm->tm_wday); + + /* Stop the clock */ + data = i2c_smbus_read_byte_data(client, RX8581_REG_CTRL); + if (data < 0) { + dev_err(&client->dev, "Unable to read control register\n"); + return -EIO; + } + + err = i2c_smbus_write_byte_data(client, RX8581_REG_FLAG, + (data | RX8581_CTRL_STOP)); + if (err < 0) { + dev_err(&client->dev, "Unable to write control register\n"); + return -EIO; + } + + /* write register's data */ + err = i2c_smbus_write_i2c_block_data(client, RX8581_REG_SC, 7, buf); + if (err < 0) { + dev_err(&client->dev, "Unable to write to date registers\n"); + return -EIO; + } + + /* Restart the clock */ + data = i2c_smbus_read_byte_data(client, RX8581_REG_CTRL); + if (data < 0) { + dev_err(&client->dev, "Unable to read control register\n"); + return -EIO; + } + + err = i2c_smbus_write_byte_data(client, RX8581_REG_FLAG, + (data | ~(RX8581_CTRL_STOP))); + if (err != 0) { + dev_err(&client->dev, "Unable to write control register\n"); + return -EIO; + } + + return 0; +} + +static int rx8581_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + return rx8581_get_datetime(to_i2c_client(dev), tm); +} + +static int rx8581_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + return rx8581_set_datetime(to_i2c_client(dev), tm); +} + +static const struct rtc_class_ops rx8581_rtc_ops = { + .read_time = rx8581_rtc_read_time, + .set_time = rx8581_rtc_set_time, +}; + +static int __devinit rx8581_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct rtc_device *rtc; + + dev_dbg(&client->dev, "%s\n", __func__); + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) + return -ENODEV; + + dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n"); + + rtc = rtc_device_register(rx8581_driver.driver.name, + &client->dev, &rx8581_rtc_ops, THIS_MODULE); + + if (IS_ERR(rtc)) + return PTR_ERR(rtc); + + i2c_set_clientdata(client, rtc); + + return 0; +} + +static int __devexit rx8581_remove(struct i2c_client *client) +{ + struct rtc_device *rtc = i2c_get_clientdata(client); + + rtc_device_unregister(rtc); + + return 0; +} + +static const struct i2c_device_id rx8581_id[] = { + { "rx8581", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rx8581_id); + +static struct i2c_driver rx8581_driver = { + .driver = { + .name = "rtc-rx8581", + .owner = THIS_MODULE, + }, + .probe = rx8581_probe, + .remove = __devexit_p(rx8581_remove), + .id_table = rx8581_id, +}; + +static int __init rx8581_init(void) +{ + return i2c_add_driver(&rx8581_driver); +} + +static void __exit rx8581_exit(void) +{ + i2c_del_driver(&rx8581_driver); +} + +MODULE_AUTHOR("Martyn Welch <martyn.welch@gefanuc.com>"); +MODULE_DESCRIPTION("Epson RX-8581 RTC driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRV_VERSION); + +module_init(rx8581_init); +module_exit(rx8581_exit); diff --git a/drivers/rtc/rtc-wm8350.c b/drivers/rtc/rtc-wm8350.c new file mode 100644 index 00000000000..5c5e3aa9138 --- /dev/null +++ b/drivers/rtc/rtc-wm8350.c @@ -0,0 +1,514 @@ +/* + * Real Time Clock driver for Wolfson Microelectronics WM8350 + * + * Copyright (C) 2007, 2008 Wolfson Microelectronics PLC. + * + * Author: Liam Girdwood + * 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. + * + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/time.h> +#include <linux/rtc.h> +#include <linux/bcd.h> +#include <linux/interrupt.h> +#include <linux/ioctl.h> +#include <linux/completion.h> +#include <linux/mfd/wm8350/rtc.h> +#include <linux/mfd/wm8350/core.h> +#include <linux/delay.h> +#include <linux/platform_device.h> + +#define WM8350_SET_ALM_RETRIES 5 +#define WM8350_SET_TIME_RETRIES 5 +#define WM8350_GET_TIME_RETRIES 5 + +#define to_wm8350_from_rtc_dev(d) container_of(d, struct wm8350, rtc.pdev.dev) + +/* + * Read current time and date in RTC + */ +static int wm8350_rtc_readtime(struct device *dev, struct rtc_time *tm) +{ + struct wm8350 *wm8350 = dev_get_drvdata(dev); + u16 time1[4], time2[4]; + int retries = WM8350_GET_TIME_RETRIES, ret; + + /* + * Read the time twice and compare. + * If time1 == time2, then time is valid else retry. + */ + do { + ret = wm8350_block_read(wm8350, WM8350_RTC_SECONDS_MINUTES, + 4, time1); + if (ret < 0) + return ret; + ret = wm8350_block_read(wm8350, WM8350_RTC_SECONDS_MINUTES, + 4, time2); + if (ret < 0) + return ret; + + if (memcmp(time1, time2, sizeof(time1)) == 0) { + tm->tm_sec = time1[0] & WM8350_RTC_SECS_MASK; + + tm->tm_min = (time1[0] & WM8350_RTC_MINS_MASK) + >> WM8350_RTC_MINS_SHIFT; + + tm->tm_hour = time1[1] & WM8350_RTC_HRS_MASK; + + tm->tm_wday = ((time1[1] >> WM8350_RTC_DAY_SHIFT) + & 0x7) - 1; + + tm->tm_mon = ((time1[2] & WM8350_RTC_MTH_MASK) + >> WM8350_RTC_MTH_SHIFT) - 1; + + tm->tm_mday = (time1[2] & WM8350_RTC_DATE_MASK); + + tm->tm_year = ((time1[3] & WM8350_RTC_YHUNDREDS_MASK) + >> WM8350_RTC_YHUNDREDS_SHIFT) * 100; + tm->tm_year += time1[3] & WM8350_RTC_YUNITS_MASK; + + tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, + tm->tm_year); + tm->tm_year -= 1900; + + dev_dbg(dev, "Read (%d left): %04x %04x %04x %04x\n", + retries, + time1[0], time1[1], time1[2], time1[3]); + + return 0; + } + } while (retries--); + + dev_err(dev, "timed out reading RTC time\n"); + return -EIO; +} + +/* + * Set current time and date in RTC + */ +static int wm8350_rtc_settime(struct device *dev, struct rtc_time *tm) +{ + struct wm8350 *wm8350 = dev_get_drvdata(dev); + u16 time[4]; + u16 rtc_ctrl; + int ret, retries = WM8350_SET_TIME_RETRIES; + + time[0] = tm->tm_sec; + time[0] |= tm->tm_min << WM8350_RTC_MINS_SHIFT; + time[1] = tm->tm_hour; + time[1] |= (tm->tm_wday + 1) << WM8350_RTC_DAY_SHIFT; + time[2] = tm->tm_mday; + time[2] |= (tm->tm_mon + 1) << WM8350_RTC_MTH_SHIFT; + time[3] = ((tm->tm_year + 1900) / 100) << WM8350_RTC_YHUNDREDS_SHIFT; + time[3] |= (tm->tm_year + 1900) % 100; + + dev_dbg(dev, "Setting: %04x %04x %04x %04x\n", + time[0], time[1], time[2], time[3]); + + /* Set RTC_SET to stop the clock */ + ret = wm8350_set_bits(wm8350, WM8350_RTC_TIME_CONTROL, WM8350_RTC_SET); + if (ret < 0) + return ret; + + /* Wait until confirmation of stopping */ + do { + rtc_ctrl = wm8350_reg_read(wm8350, WM8350_RTC_TIME_CONTROL); + schedule_timeout_uninterruptible(msecs_to_jiffies(1)); + } while (retries-- && !(rtc_ctrl & WM8350_RTC_STS)); + + if (!retries) { + dev_err(dev, "timed out on set confirmation\n"); + return -EIO; + } + + /* Write time to RTC */ + ret = wm8350_block_write(wm8350, WM8350_RTC_SECONDS_MINUTES, 4, time); + if (ret < 0) + return ret; + + /* Clear RTC_SET to start the clock */ + ret = wm8350_clear_bits(wm8350, WM8350_RTC_TIME_CONTROL, + WM8350_RTC_SET); + return ret; +} + +/* + * Read alarm time and date in RTC + */ +static int wm8350_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct wm8350 *wm8350 = dev_get_drvdata(dev); + struct rtc_time *tm = &alrm->time; + u16 time[4]; + int ret; + + ret = wm8350_block_read(wm8350, WM8350_ALARM_SECONDS_MINUTES, 4, time); + if (ret < 0) + return ret; + + tm->tm_sec = time[0] & WM8350_RTC_ALMSECS_MASK; + if (tm->tm_sec == WM8350_RTC_ALMSECS_MASK) + tm->tm_sec = -1; + + tm->tm_min = time[0] & WM8350_RTC_ALMMINS_MASK; + if (tm->tm_min == WM8350_RTC_ALMMINS_MASK) + tm->tm_min = -1; + else + tm->tm_min >>= WM8350_RTC_ALMMINS_SHIFT; + + tm->tm_hour = time[1] & WM8350_RTC_ALMHRS_MASK; + if (tm->tm_hour == WM8350_RTC_ALMHRS_MASK) + tm->tm_hour = -1; + + tm->tm_wday = ((time[1] >> WM8350_RTC_ALMDAY_SHIFT) & 0x7) - 1; + if (tm->tm_wday > 7) + tm->tm_wday = -1; + + tm->tm_mon = time[2] & WM8350_RTC_ALMMTH_MASK; + if (tm->tm_mon == WM8350_RTC_ALMMTH_MASK) + tm->tm_mon = -1; + else + tm->tm_mon = (tm->tm_mon >> WM8350_RTC_ALMMTH_SHIFT) - 1; + + tm->tm_mday = (time[2] & WM8350_RTC_ALMDATE_MASK); + if (tm->tm_mday == WM8350_RTC_ALMDATE_MASK) + tm->tm_mday = -1; + + tm->tm_year = -1; + + alrm->enabled = !(time[3] & WM8350_RTC_ALMSTS); + + return 0; +} + +static int wm8350_rtc_stop_alarm(struct wm8350 *wm8350) +{ + int retries = WM8350_SET_ALM_RETRIES; + u16 rtc_ctrl; + int ret; + + /* Set RTC_SET to stop the clock */ + ret = wm8350_set_bits(wm8350, WM8350_RTC_TIME_CONTROL, + WM8350_RTC_ALMSET); + if (ret < 0) + return ret; + + /* Wait until confirmation of stopping */ + do { + rtc_ctrl = wm8350_reg_read(wm8350, WM8350_RTC_TIME_CONTROL); + schedule_timeout_uninterruptible(msecs_to_jiffies(1)); + } while (retries-- && !(rtc_ctrl & WM8350_RTC_ALMSTS)); + + if (!(rtc_ctrl & WM8350_RTC_ALMSTS)) + return -ETIMEDOUT; + + return 0; +} + +static int wm8350_rtc_start_alarm(struct wm8350 *wm8350) +{ + int ret; + int retries = WM8350_SET_ALM_RETRIES; + u16 rtc_ctrl; + + ret = wm8350_clear_bits(wm8350, WM8350_RTC_TIME_CONTROL, + WM8350_RTC_ALMSET); + if (ret < 0) + return ret; + + /* Wait until confirmation */ + do { + rtc_ctrl = wm8350_reg_read(wm8350, WM8350_RTC_TIME_CONTROL); + schedule_timeout_uninterruptible(msecs_to_jiffies(1)); + } while (retries-- && rtc_ctrl & WM8350_RTC_ALMSTS); + + if (rtc_ctrl & WM8350_RTC_ALMSTS) + return -ETIMEDOUT; + + return 0; +} + +static int wm8350_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct wm8350 *wm8350 = dev_get_drvdata(dev); + struct rtc_time *tm = &alrm->time; + u16 time[3]; + int ret; + + memset(time, 0, sizeof(time)); + + if (tm->tm_sec != -1) + time[0] |= tm->tm_sec; + else + time[0] |= WM8350_RTC_ALMSECS_MASK; + + if (tm->tm_min != -1) + time[0] |= tm->tm_min << WM8350_RTC_ALMMINS_SHIFT; + else + time[0] |= WM8350_RTC_ALMMINS_MASK; + + if (tm->tm_hour != -1) + time[1] |= tm->tm_hour; + else + time[1] |= WM8350_RTC_ALMHRS_MASK; + + if (tm->tm_wday != -1) + time[1] |= (tm->tm_wday + 1) << WM8350_RTC_ALMDAY_SHIFT; + else + time[1] |= WM8350_RTC_ALMDAY_MASK; + + if (tm->tm_mday != -1) + time[2] |= tm->tm_mday; + else + time[2] |= WM8350_RTC_ALMDATE_MASK; + + if (tm->tm_mon != -1) + time[2] |= (tm->tm_mon + 1) << WM8350_RTC_ALMMTH_SHIFT; + else + time[2] |= WM8350_RTC_ALMMTH_MASK; + + ret = wm8350_rtc_stop_alarm(wm8350); + if (ret < 0) + return ret; + + /* Write time to RTC */ + ret = wm8350_block_write(wm8350, WM8350_ALARM_SECONDS_MINUTES, + 3, time); + if (ret < 0) + return ret; + + if (alrm->enabled) + ret = wm8350_rtc_start_alarm(wm8350); + + return ret; +} + +/* + * Handle commands from user-space + */ +static int wm8350_rtc_ioctl(struct device *dev, unsigned int cmd, + unsigned long arg) +{ + struct wm8350 *wm8350 = dev_get_drvdata(dev); + + switch (cmd) { + case RTC_AIE_OFF: + return wm8350_rtc_stop_alarm(wm8350); + case RTC_AIE_ON: + return wm8350_rtc_start_alarm(wm8350); + + case RTC_UIE_OFF: + wm8350_mask_irq(wm8350, WM8350_IRQ_RTC_SEC); + break; + case RTC_UIE_ON: + wm8350_unmask_irq(wm8350, WM8350_IRQ_RTC_SEC); + break; + + default: + return -ENOIOCTLCMD; + } + + return 0; +} + +static void wm8350_rtc_alarm_handler(struct wm8350 *wm8350, int irq, + void *data) +{ + struct rtc_device *rtc = wm8350->rtc.rtc; + int ret; + + rtc_update_irq(rtc, 1, RTC_IRQF | RTC_AF); + + /* Make it one shot */ + ret = wm8350_set_bits(wm8350, WM8350_RTC_TIME_CONTROL, + WM8350_RTC_ALMSET); + if (ret != 0) { + dev_err(&(wm8350->rtc.pdev->dev), + "Failed to disable alarm: %d\n", ret); + } +} + +static void wm8350_rtc_update_handler(struct wm8350 *wm8350, int irq, + void *data) +{ + struct rtc_device *rtc = wm8350->rtc.rtc; + + rtc_update_irq(rtc, 1, RTC_IRQF | RTC_UF); +} + +static const struct rtc_class_ops wm8350_rtc_ops = { + .ioctl = wm8350_rtc_ioctl, + .read_time = wm8350_rtc_readtime, + .set_time = wm8350_rtc_settime, + .read_alarm = wm8350_rtc_readalarm, + .set_alarm = wm8350_rtc_setalarm, +}; + +#ifdef CONFIG_PM +static int wm8350_rtc_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct wm8350 *wm8350 = dev_get_drvdata(&pdev->dev); + int ret = 0; + u16 reg; + + reg = wm8350_reg_read(wm8350, WM8350_RTC_TIME_CONTROL); + + if (device_may_wakeup(&wm8350->rtc.pdev->dev) && + reg & WM8350_RTC_ALMSTS) { + ret = wm8350_rtc_stop_alarm(wm8350); + if (ret != 0) + dev_err(&pdev->dev, "Failed to stop RTC alarm: %d\n", + ret); + } + + return ret; +} + +static int wm8350_rtc_resume(struct platform_device *pdev) +{ + struct wm8350 *wm8350 = dev_get_drvdata(&pdev->dev); + int ret; + + if (wm8350->rtc.alarm_enabled) { + ret = wm8350_rtc_start_alarm(wm8350); + if (ret != 0) + dev_err(&pdev->dev, + "Failed to restart RTC alarm: %d\n", ret); + } + + return 0; +} + +#else +#define wm8350_rtc_suspend NULL +#define wm8350_rtc_resume NULL +#endif + +static int wm8350_rtc_probe(struct platform_device *pdev) +{ + struct wm8350 *wm8350 = platform_get_drvdata(pdev); + struct wm8350_rtc *wm_rtc = &wm8350->rtc; + int ret = 0; + u16 timectl, power5; + + timectl = wm8350_reg_read(wm8350, WM8350_RTC_TIME_CONTROL); + if (timectl & WM8350_RTC_BCD) { + dev_err(&pdev->dev, "RTC BCD mode not supported\n"); + return -EINVAL; + } + if (timectl & WM8350_RTC_12HR) { + dev_err(&pdev->dev, "RTC 12 hour mode not supported\n"); + return -EINVAL; + } + + /* enable the RTC if it's not already enabled */ + power5 = wm8350_reg_read(wm8350, WM8350_POWER_MGMT_5); + if (!(power5 & WM8350_RTC_TICK_ENA)) { + dev_info(wm8350->dev, "Starting RTC\n"); + + wm8350_reg_unlock(wm8350); + + ret = wm8350_set_bits(wm8350, WM8350_POWER_MGMT_5, + WM8350_RTC_TICK_ENA); + if (ret < 0) { + dev_err(&pdev->dev, "failed to enable RTC: %d\n", ret); + return ret; + } + + wm8350_reg_lock(wm8350); + } + + if (timectl & WM8350_RTC_STS) { + int retries; + + ret = wm8350_clear_bits(wm8350, WM8350_RTC_TIME_CONTROL, + WM8350_RTC_SET); + if (ret < 0) { + dev_err(&pdev->dev, "failed to start: %d\n", ret); + return ret; + } + + retries = WM8350_SET_TIME_RETRIES; + do { + timectl = wm8350_reg_read(wm8350, + WM8350_RTC_TIME_CONTROL); + } while (timectl & WM8350_RTC_STS && retries--); + + if (retries == 0) { + dev_err(&pdev->dev, "failed to start: timeout\n"); + return -ENODEV; + } + } + + device_init_wakeup(&pdev->dev, 1); + + wm_rtc->rtc = rtc_device_register("wm8350", &pdev->dev, + &wm8350_rtc_ops, THIS_MODULE); + if (IS_ERR(wm_rtc->rtc)) { + ret = PTR_ERR(wm_rtc->rtc); + dev_err(&pdev->dev, "failed to register RTC: %d\n", ret); + return ret; + } + + wm8350_mask_irq(wm8350, WM8350_IRQ_RTC_SEC); + wm8350_mask_irq(wm8350, WM8350_IRQ_RTC_PER); + + wm8350_register_irq(wm8350, WM8350_IRQ_RTC_SEC, + wm8350_rtc_update_handler, NULL); + + wm8350_register_irq(wm8350, WM8350_IRQ_RTC_ALM, + wm8350_rtc_alarm_handler, NULL); + wm8350_unmask_irq(wm8350, WM8350_IRQ_RTC_ALM); + + return 0; +} + +static int __devexit wm8350_rtc_remove(struct platform_device *pdev) +{ + struct wm8350 *wm8350 = platform_get_drvdata(pdev); + struct wm8350_rtc *wm_rtc = &wm8350->rtc; + + wm8350_mask_irq(wm8350, WM8350_IRQ_RTC_SEC); + + wm8350_free_irq(wm8350, WM8350_IRQ_RTC_SEC); + wm8350_free_irq(wm8350, WM8350_IRQ_RTC_ALM); + + rtc_device_unregister(wm_rtc->rtc); + + return 0; +} + +static struct platform_driver wm8350_rtc_driver = { + .probe = wm8350_rtc_probe, + .remove = __devexit_p(wm8350_rtc_remove), + .suspend = wm8350_rtc_suspend, + .resume = wm8350_rtc_resume, + .driver = { + .name = "wm8350-rtc", + }, +}; + +static int __init wm8350_rtc_init(void) +{ + return platform_driver_register(&wm8350_rtc_driver); +} +module_init(wm8350_rtc_init); + +static void __exit wm8350_rtc_exit(void) +{ + platform_driver_unregister(&wm8350_rtc_driver); +} +module_exit(wm8350_rtc_exit); + +MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); +MODULE_DESCRIPTION("RTC driver for the WM8350"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:wm8350-rtc"); diff --git a/drivers/s390/block/dasd.c b/drivers/s390/block/dasd.c index 4b76fca64a6..363bd1303d2 100644 --- a/drivers/s390/block/dasd.c +++ b/drivers/s390/block/dasd.c @@ -1746,6 +1746,11 @@ restart: goto restart; } + /* log sense for fatal error */ + if (cqr->status == DASD_CQR_FAILED) { + dasd_log_sense(cqr, &cqr->irb); + } + /* First of all call extended error reporting. */ if (dasd_eer_enabled(base) && cqr->status == DASD_CQR_FAILED) { diff --git a/drivers/s390/char/sclp_cmd.c b/drivers/s390/char/sclp_cmd.c index eb5f1b8bc57..ec9c0bcf66e 100644 --- a/drivers/s390/char/sclp_cmd.c +++ b/drivers/s390/char/sclp_cmd.c @@ -324,6 +324,9 @@ static int do_assign_storage(sclp_cmdw_t cmd, u16 rn) case 0x0120: break; default: + pr_warning("assign storage failed (cmd=0x%08x, " + "response=0x%04x, rn=0x%04x)\n", cmd, + sccb->header.response_code, rn); rc = -EIO; break; } diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index 4e78c82194b..4e4008325e2 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -874,11 +874,15 @@ void ccw_device_move_to_orphanage(struct work_struct *work) replacing_cdev = get_disc_ccwdev_by_dev_id(&dev_id, cdev); if (replacing_cdev) { sch_attach_disconnected_device(sch, replacing_cdev); + /* Release reference from get_disc_ccwdev_by_dev_id() */ + put_device(&cdev->dev); return; } replacing_cdev = get_orphaned_ccwdev_by_dev_id(css, &dev_id); if (replacing_cdev) { sch_attach_orphaned_device(sch, replacing_cdev); + /* Release reference from get_orphaned_ccwdev_by_dev_id() */ + put_device(&cdev->dev); return; } sch_create_and_recog_new_device(sch); diff --git a/drivers/s390/kvm/kvm_virtio.c b/drivers/s390/kvm/kvm_virtio.c index ff4a6931bb8..3d442444c61 100644 --- a/drivers/s390/kvm/kvm_virtio.c +++ b/drivers/s390/kvm/kvm_virtio.c @@ -322,13 +322,13 @@ static int __init kvm_devices_init(void) return rc; } - rc = vmem_add_mapping(PFN_PHYS(max_pfn), PAGE_SIZE); + rc = vmem_add_mapping(real_memory_size, PAGE_SIZE); if (rc) { s390_root_dev_unregister(kvm_root); return rc; } - kvm_devices = (void *) PFN_PHYS(max_pfn); + kvm_devices = (void *) real_memory_size; ctl_set_bit(0, 9); register_external_interrupt(0x2603, kvm_extint_handler); diff --git a/drivers/s390/scsi/zfcp_aux.c b/drivers/s390/scsi/zfcp_aux.c index 3b56220fb90..3d4e3e3f3fc 100644 --- a/drivers/s390/scsi/zfcp_aux.c +++ b/drivers/s390/scsi/zfcp_aux.c @@ -610,7 +610,8 @@ struct zfcp_port *zfcp_port_enqueue(struct zfcp_adapter *adapter, u64 wwpn, atomic_set_mask(status | ZFCP_STATUS_COMMON_REMOVE, &port->status); atomic_set(&port->refcount, 0); - dev_set_name(&port->sysfs_device, "0x%016llx", wwpn); + dev_set_name(&port->sysfs_device, "0x%016llx", + (unsigned long long)wwpn); port->sysfs_device.parent = &adapter->ccw_device->dev; port->sysfs_device.release = zfcp_sysfs_port_release; diff --git a/drivers/s390/scsi/zfcp_ccw.c b/drivers/s390/scsi/zfcp_ccw.c index b04038c7478..951a8d409d1 100644 --- a/drivers/s390/scsi/zfcp_ccw.c +++ b/drivers/s390/scsi/zfcp_ccw.c @@ -116,7 +116,9 @@ static int zfcp_ccw_set_online(struct ccw_device *ccw_device) zfcp_erp_adapter_reopen(adapter, ZFCP_STATUS_COMMON_ERP_FAILED, 85, NULL); zfcp_erp_wait(adapter); - goto out; + up(&zfcp_data.config_sema); + flush_work(&adapter->scan_work); + return 0; out_scsi_register: zfcp_erp_thread_kill(adapter); diff --git a/drivers/s390/scsi/zfcp_dbf.c b/drivers/s390/scsi/zfcp_dbf.c index 060f5f2352e..31012d58cfb 100644 --- a/drivers/s390/scsi/zfcp_dbf.c +++ b/drivers/s390/scsi/zfcp_dbf.c @@ -30,7 +30,7 @@ static void zfcp_dbf_hexdump(debug_info_t *dbf, void *to, int to_len, dump->offset = offset; dump->size = min(from_len - offset, room); memcpy(dump->data, from + offset, dump->size); - debug_event(dbf, level, dump, dump->size); + debug_event(dbf, level, dump, dump->size + sizeof(*dump)); } } @@ -108,7 +108,7 @@ static int zfcp_dbf_view_header(debug_info_t *id, struct debug_view *view, t.tv_sec, t.tv_nsec); zfcp_dbf_out(&p, "cpu", "%02i", entry->id.fields.cpuid); } else { - zfcp_dbf_outd(&p, NULL, dump->data, dump->size, dump->offset, + zfcp_dbf_outd(&p, "", dump->data, dump->size, dump->offset, dump->total_size); if ((dump->offset + dump->size) == dump->total_size) p += sprintf(p, "\n"); @@ -366,6 +366,7 @@ static void zfcp_hba_dbf_view_response(char **p, break; zfcp_dbf_out(p, "scsi_cmnd", "0x%0Lx", r->u.fcp.cmnd); zfcp_dbf_out(p, "scsi_serial", "0x%016Lx", r->u.fcp.serial); + p += sprintf(*p, "\n"); break; case FSF_QTCB_OPEN_PORT_WITH_DID: @@ -465,7 +466,8 @@ static int zfcp_hba_dbf_view_format(debug_info_t *id, struct debug_view *view, else if (strncmp(r->tag, "berr", ZFCP_DBF_TAG_SIZE) == 0) zfcp_hba_dbf_view_berr(&p, &r->u.berr); - p += sprintf(p, "\n"); + if (strncmp(r->tag, "resp", ZFCP_DBF_TAG_SIZE) != 0) + p += sprintf(p, "\n"); return p - out_buf; } @@ -880,6 +882,7 @@ void zfcp_san_dbf_event_ct_request(struct zfcp_fsf_req *fsf_req) struct ct_hdr *hdr = sg_virt(ct->req); struct zfcp_san_dbf_record *r = &adapter->san_dbf_buf; struct zfcp_san_dbf_record_ct_request *oct = &r->u.ct_req; + int level = 3; unsigned long flags; spin_lock_irqsave(&adapter->san_dbf_lock, flags); @@ -896,9 +899,10 @@ void zfcp_san_dbf_event_ct_request(struct zfcp_fsf_req *fsf_req) oct->options = hdr->options; oct->max_res_size = hdr->max_res_size; oct->len = min((int)ct->req->length - (int)sizeof(struct ct_hdr), - ZFCP_DBF_CT_PAYLOAD); - memcpy(oct->payload, (void *)hdr + sizeof(struct ct_hdr), oct->len); - debug_event(adapter->san_dbf, 3, r, sizeof(*r)); + ZFCP_DBF_SAN_MAX_PAYLOAD); + debug_event(adapter->san_dbf, level, r, sizeof(*r)); + zfcp_dbf_hexdump(adapter->san_dbf, r, sizeof(*r), level, + (void *)hdr + sizeof(struct ct_hdr), oct->len); spin_unlock_irqrestore(&adapter->san_dbf_lock, flags); } @@ -914,6 +918,7 @@ void zfcp_san_dbf_event_ct_response(struct zfcp_fsf_req *fsf_req) struct ct_hdr *hdr = sg_virt(ct->resp); struct zfcp_san_dbf_record *r = &adapter->san_dbf_buf; struct zfcp_san_dbf_record_ct_response *rct = &r->u.ct_resp; + int level = 3; unsigned long flags; spin_lock_irqsave(&adapter->san_dbf_lock, flags); @@ -929,9 +934,10 @@ void zfcp_san_dbf_event_ct_response(struct zfcp_fsf_req *fsf_req) rct->expl = hdr->reason_code_expl; rct->vendor_unique = hdr->vendor_unique; rct->len = min((int)ct->resp->length - (int)sizeof(struct ct_hdr), - ZFCP_DBF_CT_PAYLOAD); - memcpy(rct->payload, (void *)hdr + sizeof(struct ct_hdr), rct->len); - debug_event(adapter->san_dbf, 3, r, sizeof(*r)); + ZFCP_DBF_SAN_MAX_PAYLOAD); + debug_event(adapter->san_dbf, level, r, sizeof(*r)); + zfcp_dbf_hexdump(adapter->san_dbf, r, sizeof(*r), level, + (void *)hdr + sizeof(struct ct_hdr), rct->len); spin_unlock_irqrestore(&adapter->san_dbf_lock, flags); } @@ -954,7 +960,7 @@ static void zfcp_san_dbf_event_els(const char *tag, int level, rec->u.els.ls_code = ls_code; debug_event(adapter->san_dbf, level, rec, sizeof(*rec)); zfcp_dbf_hexdump(adapter->san_dbf, rec, sizeof(*rec), level, - buffer, min(buflen, ZFCP_DBF_ELS_MAX_PAYLOAD)); + buffer, min(buflen, ZFCP_DBF_SAN_MAX_PAYLOAD)); spin_unlock_irqrestore(&adapter->san_dbf_lock, flags); } @@ -1008,8 +1014,6 @@ static int zfcp_san_dbf_view_format(debug_info_t *id, struct debug_view *view, char *out_buf, const char *in_buf) { struct zfcp_san_dbf_record *r = (struct zfcp_san_dbf_record *)in_buf; - char *buffer = NULL; - int buflen = 0, total = 0; char *p = out_buf; if (strncmp(r->tag, "dump", ZFCP_DBF_TAG_SIZE) == 0) @@ -1029,9 +1033,6 @@ static int zfcp_san_dbf_view_format(debug_info_t *id, struct debug_view *view, zfcp_dbf_out(&p, "gs_subtype", "0x%02x", ct->gs_subtype); zfcp_dbf_out(&p, "options", "0x%02x", ct->options); zfcp_dbf_out(&p, "max_res_size", "0x%04x", ct->max_res_size); - total = ct->len; - buffer = ct->payload; - buflen = min(total, ZFCP_DBF_CT_PAYLOAD); } else if (strncmp(r->tag, "rctc", ZFCP_DBF_TAG_SIZE) == 0) { struct zfcp_san_dbf_record_ct_response *ct = &r->u.ct_resp; zfcp_dbf_out(&p, "cmd_rsp_code", "0x%04x", ct->cmd_rsp_code); @@ -1039,23 +1040,12 @@ static int zfcp_san_dbf_view_format(debug_info_t *id, struct debug_view *view, zfcp_dbf_out(&p, "reason_code", "0x%02x", ct->reason_code); zfcp_dbf_out(&p, "reason_code_expl", "0x%02x", ct->expl); zfcp_dbf_out(&p, "vendor_unique", "0x%02x", ct->vendor_unique); - total = ct->len; - buffer = ct->payload; - buflen = min(total, ZFCP_DBF_CT_PAYLOAD); } else if (strncmp(r->tag, "oels", ZFCP_DBF_TAG_SIZE) == 0 || strncmp(r->tag, "rels", ZFCP_DBF_TAG_SIZE) == 0 || strncmp(r->tag, "iels", ZFCP_DBF_TAG_SIZE) == 0) { struct zfcp_san_dbf_record_els *els = &r->u.els; zfcp_dbf_out(&p, "ls_code", "0x%02x", els->ls_code); - total = els->len; - buffer = els->payload; - buflen = min(total, ZFCP_DBF_ELS_PAYLOAD); } - - zfcp_dbf_outd(&p, "payload", buffer, buflen, 0, total); - if (buflen == total) - p += sprintf(p, "\n"); - return p - out_buf; } diff --git a/drivers/s390/scsi/zfcp_dbf.h b/drivers/s390/scsi/zfcp_dbf.h index e8f450801fe..5d6b2dff855 100644 --- a/drivers/s390/scsi/zfcp_dbf.h +++ b/drivers/s390/scsi/zfcp_dbf.h @@ -163,8 +163,6 @@ struct zfcp_san_dbf_record_ct_request { u8 options; u16 max_res_size; u32 len; -#define ZFCP_DBF_CT_PAYLOAD 24 - u8 payload[ZFCP_DBF_CT_PAYLOAD]; } __attribute__ ((packed)); struct zfcp_san_dbf_record_ct_response { @@ -174,15 +172,11 @@ struct zfcp_san_dbf_record_ct_response { u8 expl; u8 vendor_unique; u32 len; - u8 payload[ZFCP_DBF_CT_PAYLOAD]; } __attribute__ ((packed)); struct zfcp_san_dbf_record_els { u8 ls_code; u32 len; -#define ZFCP_DBF_ELS_PAYLOAD 32 -#define ZFCP_DBF_ELS_MAX_PAYLOAD 1024 - u8 payload[ZFCP_DBF_ELS_PAYLOAD]; } __attribute__ ((packed)); struct zfcp_san_dbf_record { @@ -196,6 +190,8 @@ struct zfcp_san_dbf_record { struct zfcp_san_dbf_record_ct_response ct_resp; struct zfcp_san_dbf_record_els els; } u; +#define ZFCP_DBF_SAN_MAX_PAYLOAD 1024 + u8 payload[32]; } __attribute__ ((packed)); struct zfcp_scsi_dbf_record { diff --git a/drivers/s390/scsi/zfcp_erp.c b/drivers/s390/scsi/zfcp_erp.c index 9040f738ff3..35364f64da7 100644 --- a/drivers/s390/scsi/zfcp_erp.c +++ b/drivers/s390/scsi/zfcp_erp.c @@ -472,6 +472,7 @@ static void zfcp_erp_strategy_check_fsfreq(struct zfcp_erp_action *act) ZFCP_STATUS_ERP_TIMEDOUT)) { act->fsf_req->status |= ZFCP_STATUS_FSFREQ_DISMISSED; zfcp_rec_dbf_event_action(142, act); + act->fsf_req->erp_action = NULL; } if (act->status & ZFCP_STATUS_ERP_TIMEDOUT) zfcp_rec_dbf_event_action(143, act); diff --git a/drivers/s390/scsi/zfcp_fsf.c b/drivers/s390/scsi/zfcp_fsf.c index 5ae1d497e5e..d024442ee12 100644 --- a/drivers/s390/scsi/zfcp_fsf.c +++ b/drivers/s390/scsi/zfcp_fsf.c @@ -683,6 +683,7 @@ static struct zfcp_fsf_req *zfcp_fsf_alloc_noqtcb(mempool_t *pool) if (!req) return NULL; memset(req, 0, sizeof(*req)); + req->pool = pool; return req; } @@ -769,28 +770,24 @@ static struct zfcp_fsf_req *zfcp_fsf_req_create(struct zfcp_adapter *adapter, static int zfcp_fsf_req_send(struct zfcp_fsf_req *req) { struct zfcp_adapter *adapter = req->adapter; - struct zfcp_qdio_queue *req_q = &adapter->req_q; + unsigned long flags; int idx; /* put allocated FSF request into hash table */ - spin_lock(&adapter->req_list_lock); + spin_lock_irqsave(&adapter->req_list_lock, flags); idx = zfcp_reqlist_hash(req->req_id); list_add_tail(&req->list, &adapter->req_list[idx]); - spin_unlock(&adapter->req_list_lock); + spin_unlock_irqrestore(&adapter->req_list_lock, flags); - req->qdio_outb_usage = atomic_read(&req_q->count); + req->qdio_outb_usage = atomic_read(&adapter->req_q.count); req->issued = get_clock(); if (zfcp_qdio_send(req)) { - /* Queues are down..... */ del_timer(&req->timer); - spin_lock(&adapter->req_list_lock); - zfcp_reqlist_remove(adapter, req); - spin_unlock(&adapter->req_list_lock); - /* undo changes in request queue made for this request */ - atomic_add(req->sbal_number, &req_q->count); - req_q->first -= req->sbal_number; - req_q->first += QDIO_MAX_BUFFERS_PER_Q; - req_q->first %= QDIO_MAX_BUFFERS_PER_Q; /* wrap */ + spin_lock_irqsave(&adapter->req_list_lock, flags); + /* lookup request again, list might have changed */ + if (zfcp_reqlist_find_safe(adapter, req)) + zfcp_reqlist_remove(adapter, req); + spin_unlock_irqrestore(&adapter->req_list_lock, flags); zfcp_erp_adapter_reopen(adapter, 0, 116, req); return -EIO; } diff --git a/drivers/s390/scsi/zfcp_scsi.c b/drivers/s390/scsi/zfcp_scsi.c index ca8f85f3dad..e46fd3e9f68 100644 --- a/drivers/s390/scsi/zfcp_scsi.c +++ b/drivers/s390/scsi/zfcp_scsi.c @@ -24,14 +24,10 @@ char *zfcp_get_fcp_sns_info_ptr(struct fcp_rsp_iu *fcp_rsp_iu) static void zfcp_scsi_slave_destroy(struct scsi_device *sdpnt) { struct zfcp_unit *unit = (struct zfcp_unit *) sdpnt->hostdata; - WARN_ON(!unit); - if (unit) { - atomic_clear_mask(ZFCP_STATUS_UNIT_REGISTERED, &unit->status); - sdpnt->hostdata = NULL; - unit->device = NULL; - zfcp_erp_unit_failed(unit, 12, NULL); - zfcp_unit_put(unit); - } + atomic_clear_mask(ZFCP_STATUS_UNIT_REGISTERED, &unit->status); + unit->device = NULL; + zfcp_erp_unit_failed(unit, 12, NULL); + zfcp_unit_put(unit); } static int zfcp_scsi_slave_configure(struct scsi_device *sdp) diff --git a/drivers/scsi/dpt_i2o.c b/drivers/scsi/dpt_i2o.c index 8aba4fdfb52..6194ed5d02c 100644 --- a/drivers/scsi/dpt_i2o.c +++ b/drivers/scsi/dpt_i2o.c @@ -2445,7 +2445,7 @@ static s32 adpt_i2o_to_scsi(void __iomem *reply, struct scsi_cmnd* cmd) hba_status = detailed_status >> 8; // calculate resid for sg - scsi_set_resid(cmd, scsi_bufflen(cmd) - readl(reply+5)); + scsi_set_resid(cmd, scsi_bufflen(cmd) - readl(reply+20)); pHba = (adpt_hba*) cmd->device->host->hostdata[0]; @@ -2456,7 +2456,7 @@ static s32 adpt_i2o_to_scsi(void __iomem *reply, struct scsi_cmnd* cmd) case I2O_SCSI_DSC_SUCCESS: cmd->result = (DID_OK << 16); // handle underflow - if(readl(reply+5) < cmd->underflow ) { + if (readl(reply+20) < cmd->underflow) { cmd->result = (DID_ERROR <<16); printk(KERN_WARNING"%s: SCSI CMD underflow\n",pHba->name); } diff --git a/drivers/scsi/megaraid.c b/drivers/scsi/megaraid.c index 28c9da7d4a5..7dc62deb408 100644 --- a/drivers/scsi/megaraid.c +++ b/drivers/scsi/megaraid.c @@ -4402,6 +4402,10 @@ mega_internal_command(adapter_t *adapter, megacmd_t *mc, mega_passthru *pthru) scb_t *scb; int rval; + scmd = scsi_allocate_command(GFP_KERNEL); + if (!scmd) + return -ENOMEM; + /* * The internal commands share one command id and hence are * serialized. This is so because we want to reserve maximum number of @@ -4412,12 +4416,11 @@ mega_internal_command(adapter_t *adapter, megacmd_t *mc, mega_passthru *pthru) scb = &adapter->int_scb; memset(scb, 0, sizeof(scb_t)); - scmd = &adapter->int_scmd; - memset(scmd, 0, sizeof(Scsi_Cmnd)); - sdev = kzalloc(sizeof(struct scsi_device), GFP_KERNEL); scmd->device = sdev; + memset(adapter->int_cdb, 0, sizeof(adapter->int_cdb)); + scmd->cmnd = adapter->int_cdb; scmd->device->host = adapter->host; scmd->host_scribble = (void *)scb; scmd->cmnd[0] = MEGA_INTERNAL_CMD; @@ -4456,6 +4459,8 @@ mega_internal_command(adapter_t *adapter, megacmd_t *mc, mega_passthru *pthru) mutex_unlock(&adapter->int_mtx); + scsi_free_command(GFP_KERNEL, scmd); + return rval; } diff --git a/drivers/scsi/megaraid.h b/drivers/scsi/megaraid.h index ee70bd4ae4b..795201fa0b4 100644 --- a/drivers/scsi/megaraid.h +++ b/drivers/scsi/megaraid.h @@ -888,8 +888,8 @@ typedef struct { u8 sglen; /* f/w supported scatter-gather list length */ + unsigned char int_cdb[MAX_COMMAND_SIZE]; scb_t int_scb; - Scsi_Cmnd int_scmd; struct mutex int_mtx; /* To synchronize the internal commands */ struct completion int_waitq; /* wait queue for internal diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index f25f41a499e..b97194096d8 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -2547,7 +2547,6 @@ typedef struct scsi_qla_host { uint8_t fcode_revision[16]; uint32_t fw_revision[4]; - uint16_t fdt_odd_index; uint32_t fdt_wrt_disable; uint32_t fdt_erase_cmd; uint32_t fdt_block_size; diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index a470f2d3270..4218f20f5ed 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -140,7 +140,6 @@ int qla2100_pci_config(scsi_qla_host_t *ha) { uint16_t w; - uint32_t d; unsigned long flags; struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; @@ -151,10 +150,7 @@ qla2100_pci_config(scsi_qla_host_t *ha) w |= (PCI_COMMAND_PARITY | PCI_COMMAND_SERR); pci_write_config_word(ha->pdev, PCI_COMMAND, w); - /* Reset expansion ROM address decode enable */ - pci_read_config_dword(ha->pdev, PCI_ROM_ADDRESS, &d); - d &= ~PCI_ROM_ADDRESS_ENABLE; - pci_write_config_dword(ha->pdev, PCI_ROM_ADDRESS, d); + pci_disable_rom(ha->pdev); /* Get PCI bus information. */ spin_lock_irqsave(&ha->hardware_lock, flags); @@ -174,7 +170,6 @@ int qla2300_pci_config(scsi_qla_host_t *ha) { uint16_t w; - uint32_t d; unsigned long flags = 0; uint32_t cnt; struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; @@ -236,10 +231,7 @@ qla2300_pci_config(scsi_qla_host_t *ha) pci_write_config_byte(ha->pdev, PCI_LATENCY_TIMER, 0x80); - /* Reset expansion ROM address decode enable */ - pci_read_config_dword(ha->pdev, PCI_ROM_ADDRESS, &d); - d &= ~PCI_ROM_ADDRESS_ENABLE; - pci_write_config_dword(ha->pdev, PCI_ROM_ADDRESS, d); + pci_disable_rom(ha->pdev); /* Get PCI bus information. */ spin_lock_irqsave(&ha->hardware_lock, flags); @@ -259,7 +251,6 @@ int qla24xx_pci_config(scsi_qla_host_t *ha) { uint16_t w; - uint32_t d; unsigned long flags = 0; struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; @@ -281,10 +272,7 @@ qla24xx_pci_config(scsi_qla_host_t *ha) if (pci_find_capability(ha->pdev, PCI_CAP_ID_EXP)) pcie_set_readrq(ha->pdev, 2048); - /* Reset expansion ROM address decode enable */ - pci_read_config_dword(ha->pdev, PCI_ROM_ADDRESS, &d); - d &= ~PCI_ROM_ADDRESS_ENABLE; - pci_write_config_dword(ha->pdev, PCI_ROM_ADDRESS, d); + pci_disable_rom(ha->pdev); ha->chip_revision = ha->pdev->revision; @@ -306,7 +294,6 @@ int qla25xx_pci_config(scsi_qla_host_t *ha) { uint16_t w; - uint32_t d; pci_set_master(ha->pdev); pci_try_set_mwi(ha->pdev); @@ -320,10 +307,7 @@ qla25xx_pci_config(scsi_qla_host_t *ha) if (pci_find_capability(ha->pdev, PCI_CAP_ID_EXP)) pcie_set_readrq(ha->pdev, 2048); - /* Reset expansion ROM address decode enable */ - pci_read_config_dword(ha->pdev, PCI_ROM_ADDRESS, &d); - d &= ~PCI_ROM_ADDRESS_ENABLE; - pci_write_config_dword(ha->pdev, PCI_ROM_ADDRESS, d); + pci_disable_rom(ha->pdev); ha->chip_revision = ha->pdev->revision; @@ -980,7 +964,6 @@ qla2x00_setup_chip(scsi_qla_host_t *ha) &ha->fw_minor_version, &ha->fw_subminor_version, &ha->fw_attributes, &ha->fw_memory_size); - qla2x00_resize_request_q(ha); ha->flags.npiv_supported = 0; if ((IS_QLA24XX(ha) || IS_QLA25XX(ha) || IS_QLA84XX(ha)) && @@ -992,6 +975,7 @@ qla2x00_setup_chip(scsi_qla_host_t *ha) ha->max_npiv_vports = MIN_MULTI_ID_FABRIC - 1; } + qla2x00_resize_request_q(ha); if (ql2xallocfwdump) qla2x00_alloc_fw_dump(ha); diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c index 36bc6851e23..3402746ec12 100644 --- a/drivers/scsi/qla2xxx/qla_mbx.c +++ b/drivers/scsi/qla2xxx/qla_mbx.c @@ -1964,7 +1964,7 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *ha, uint16_t *cur_xchg_cnt, *cur_iocb_cnt = mcp->mb[7]; if (orig_iocb_cnt) *orig_iocb_cnt = mcp->mb[10]; - if (max_npiv_vports) + if (ha->flags.npiv_supported && max_npiv_vports) *max_npiv_vports = mcp->mb[11]; } diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 21dd182ad51..35567203ef6 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -728,6 +728,7 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd) if (ha->isp_ops->abort_command(ha, sp)) { DEBUG2(printk("%s(%ld): abort_command " "mbx failed.\n", __func__, ha->host_no)); + ret = FAILED; } else { DEBUG3(printk("%s(%ld): abort_command " "mbx success.\n", __func__, ha->host_no)); diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c index 90a13211717..e4af678eb2d 100644 --- a/drivers/scsi/qla2xxx/qla_sup.c +++ b/drivers/scsi/qla2xxx/qla_sup.c @@ -722,6 +722,7 @@ done: static void qla2xxx_get_fdt_info(scsi_qla_host_t *ha) { +#define FLASH_BLK_SIZE_4K 0x1000 #define FLASH_BLK_SIZE_32K 0x8000 #define FLASH_BLK_SIZE_64K 0x10000 const char *loc, *locations[] = { "MID", "FDT" }; @@ -755,7 +756,6 @@ qla2xxx_get_fdt_info(scsi_qla_host_t *ha) loc = locations[1]; mid = le16_to_cpu(fdt->man_id); fid = le16_to_cpu(fdt->id); - ha->fdt_odd_index = mid == 0x1f; ha->fdt_wrt_disable = fdt->wrt_disable_bits; ha->fdt_erase_cmd = flash_conf_to_access_addr(0x0300 | fdt->erase_cmd); ha->fdt_block_size = le32_to_cpu(fdt->block_size); @@ -788,8 +788,7 @@ no_flash_data: ha->fdt_block_size = FLASH_BLK_SIZE_64K; break; case 0x1f: /* Atmel 26DF081A. */ - ha->fdt_odd_index = 1; - ha->fdt_block_size = FLASH_BLK_SIZE_64K; + ha->fdt_block_size = FLASH_BLK_SIZE_4K; ha->fdt_erase_cmd = flash_conf_to_access_addr(0x0320); ha->fdt_unprotect_sec_cmd = flash_conf_to_access_addr(0x0339); ha->fdt_protect_sec_cmd = flash_conf_to_access_addr(0x0336); @@ -801,9 +800,9 @@ no_flash_data: } done: DEBUG2(qla_printk(KERN_DEBUG, ha, "FDT[%s]: (0x%x/0x%x) erase=0x%x " - "pro=%x upro=%x idx=%d wrtd=0x%x blk=0x%x.\n", loc, mid, fid, + "pro=%x upro=%x wrtd=0x%x blk=0x%x.\n", loc, mid, fid, ha->fdt_erase_cmd, ha->fdt_protect_sec_cmd, - ha->fdt_unprotect_sec_cmd, ha->fdt_odd_index, ha->fdt_wrt_disable, + ha->fdt_unprotect_sec_cmd, ha->fdt_wrt_disable, ha->fdt_block_size)); } @@ -987,13 +986,9 @@ qla24xx_write_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr, qla24xx_unprotect_flash(ha); for (liter = 0; liter < dwords; liter++, faddr++, dwptr++) { - if (ha->fdt_odd_index) { - findex = faddr << 2; - fdata = findex & sec_mask; - } else { - findex = faddr; - fdata = (findex & sec_mask) << 2; - } + + findex = faddr; + fdata = (findex & sec_mask) << 2; /* Are we at the beginning of a sector? */ if ((findex & rest_addr) == 0) { diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h index be5e299df52..eea6720adf1 100644 --- a/drivers/scsi/qla2xxx/qla_version.h +++ b/drivers/scsi/qla2xxx/qla_version.h @@ -7,7 +7,7 @@ /* * Driver version */ -#define QLA2XXX_VERSION "8.02.01-k8" +#define QLA2XXX_VERSION "8.02.01-k9" #define QLA_DRIVER_MAJOR_VER 8 #define QLA_DRIVER_MINOR_VER 2 diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 94ed262bdf0..386361778eb 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -1340,9 +1340,10 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd) * LLD/transport was disrupted during processing of the IO. * The transport class is now blocked/blocking, * and the transport will decide what to do with the IO - * based on its timers and recovery capablilities. + * based on its timers and recovery capablilities if + * there are enough retries. */ - return ADD_TO_MLQUEUE; + goto maybe_retry; case DID_TRANSPORT_FAILFAST: /* * The transport decided to failfast the IO (most likely diff --git a/drivers/serial/sh-sci.c b/drivers/serial/sh-sci.c index 5c0f32c7fbf..165fc010978 100644 --- a/drivers/serial/sh-sci.c +++ b/drivers/serial/sh-sci.c @@ -144,9 +144,9 @@ static void put_char(struct uart_port *port, char c) status = sci_in(port, SCxSR); } while (!(status & SCxSR_TDxE(port))); - sci_out(port, SCxTDR, c); sci_in(port, SCxSR); /* Dummy read */ sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port)); + sci_out(port, SCxTDR, c); spin_unlock_irqrestore(&port->lock, flags); } @@ -478,10 +478,10 @@ static void sci_transmit_chars(struct uart_port *port) return; } - if (port->type == PORT_SCIF) - count = scif_txroom(port); - else + if (port->type == PORT_SCI) count = sci_txroom(port); + else + count = scif_txroom(port); do { unsigned char c; @@ -510,7 +510,7 @@ static void sci_transmit_chars(struct uart_port *port) } else { ctrl = sci_in(port, SCSCR); - if (port->type == PORT_SCIF) { + if (port->type != PORT_SCI) { sci_in(port, SCxSR); /* Dummy read */ sci_out(port, SCxSR, SCxSR_TDxE_CLEAR(port)); } @@ -536,10 +536,10 @@ static inline void sci_receive_chars(struct uart_port *port) return; while (1) { - if (port->type == PORT_SCIF) - count = scif_rxroom(port); - else + if (port->type == PORT_SCI) count = sci_rxroom(port); + else + count = scif_rxroom(port); /* Don't copy more bytes than there is room for in the buffer */ count = tty_buffer_request_room(tty, count); @@ -714,7 +714,7 @@ static inline int sci_handle_breaks(struct uart_port *port) #if defined(SCIF_ORER) /* XXX: Handle SCIF overrun error */ - if (port->type == PORT_SCIF && (sci_in(port, SCLSR) & SCIF_ORER) != 0) { + if (port->type != PORT_SCI && (sci_in(port, SCLSR) & SCIF_ORER) != 0) { sci_out(port, SCLSR, 0); if (tty_insert_flip_char(tty, 0, TTY_OVERRUN)) { copied++; @@ -1042,7 +1042,7 @@ static void sci_set_termios(struct uart_port *port, struct ktermios *termios, sci_out(port, SCSCR, 0x00); /* TE=0, RE=0, CKE1=0 */ - if (port->type == PORT_SCIF) + if (port->type != PORT_SCI) sci_out(port, SCFCR, SCFCR_RFRST | SCFCR_TFRST); smr_val = sci_in(port, SCSMR) & 3; @@ -1085,6 +1085,7 @@ static const char *sci_type(struct uart_port *port) case PORT_SCI: return "sci"; case PORT_SCIF: return "scif"; case PORT_IRDA: return "irda"; + case PORT_SCIFA: return "scifa"; } return NULL; @@ -1112,6 +1113,7 @@ static void sci_config_port(struct uart_port *port, int flags) s->init_pins = sci_init_pins_sci; break; case PORT_SCIF: + case PORT_SCIFA: s->init_pins = sci_init_pins_scif; break; case PORT_IRDA: diff --git a/drivers/serial/sh-sci.h b/drivers/serial/sh-sci.h index 6163a45f968..9f33b064172 100644 --- a/drivers/serial/sh-sci.h +++ b/drivers/serial/sh-sci.h @@ -289,18 +289,18 @@ #define CPU_SCIx_FNS(name, sci_offset, sci_size, scif_offset, scif_size)\ static inline unsigned int sci_##name##_in(struct uart_port *port) \ { \ - if (port->type == PORT_SCI) { \ - SCI_IN(sci_size, sci_offset) \ - } else { \ - SCI_IN(scif_size, scif_offset); \ + if (port->type == PORT_SCIF) { \ + SCI_IN(scif_size, scif_offset) \ + } else { /* PORT_SCI or PORT_SCIFA */ \ + SCI_IN(sci_size, sci_offset); \ } \ } \ static inline void sci_##name##_out(struct uart_port *port, unsigned int value) \ { \ - if (port->type == PORT_SCI) { \ - SCI_OUT(sci_size, sci_offset, value) \ - } else { \ - SCI_OUT(scif_size, scif_offset, value); \ + if (port->type == PORT_SCIF) { \ + SCI_OUT(scif_size, scif_offset, value) \ + } else { /* PORT_SCI or PORT_SCIFA */ \ + SCI_OUT(sci_size, sci_offset, value); \ } \ } diff --git a/drivers/spi/atmel_spi.c b/drivers/spi/atmel_spi.c index 02f9320f3ef..8abae4ad0fa 100644 --- a/drivers/spi/atmel_spi.c +++ b/drivers/spi/atmel_spi.c @@ -766,6 +766,7 @@ static int __init atmel_spi_probe(struct platform_device *pdev) /* Initialize the hardware */ clk_enable(clk); spi_writel(as, CR, SPI_BIT(SWRST)); + spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */ spi_writel(as, MR, SPI_BIT(MSTR) | SPI_BIT(MODFDIS)); spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS)); spi_writel(as, CR, SPI_BIT(SPIEN)); @@ -782,6 +783,7 @@ static int __init atmel_spi_probe(struct platform_device *pdev) out_reset_hw: spi_writel(as, CR, SPI_BIT(SWRST)); + spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */ clk_disable(clk); free_irq(irq, master); out_unmap_regs: @@ -805,6 +807,7 @@ static int __exit atmel_spi_remove(struct platform_device *pdev) spin_lock_irq(&as->lock); as->stopping = 1; spi_writel(as, CR, SPI_BIT(SWRST)); + spi_writel(as, CR, SPI_BIT(SWRST)); /* AT91SAM9263 Rev B workaround */ spi_readl(as, SR); spin_unlock_irq(&as->lock); diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 20104443081..d50a99f70ae 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c @@ -158,16 +158,12 @@ static int acm_wb_is_avail(struct acm *acm) } /* - * Finish write. + * Finish write. Caller must hold acm->write_lock */ static void acm_write_done(struct acm *acm, struct acm_wb *wb) { - unsigned long flags; - - spin_lock_irqsave(&acm->write_lock, flags); wb->use = 0; acm->transmitting--; - spin_unlock_irqrestore(&acm->write_lock, flags); } /* @@ -482,6 +478,7 @@ static void acm_write_bulk(struct urb *urb) { struct acm_wb *wb = urb->context; struct acm *acm = wb->instance; + unsigned long flags; if (verbose || urb->status || (urb->actual_length != urb->transfer_buffer_length)) @@ -490,7 +487,9 @@ static void acm_write_bulk(struct urb *urb) urb->transfer_buffer_length, urb->status); + spin_lock_irqsave(&acm->write_lock, flags); acm_write_done(acm, wb); + spin_unlock_irqrestore(&acm->write_lock, flags); if (ACM_READY(acm)) schedule_work(&acm->work); else diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c index 887738577b2..6d1048faf08 100644 --- a/drivers/usb/core/message.c +++ b/drivers/usb/core/message.c @@ -1091,6 +1091,7 @@ void usb_disable_device(struct usb_device *dev, int skip_ep0) continue; dev_dbg(&dev->dev, "unregistering interface %s\n", dev_name(&interface->dev)); + interface->unregistering = 1; usb_remove_sysfs_intf_files(interface); device_del(&interface->dev); } diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c index f66fba11fbd..4fb65fdc9dc 100644 --- a/drivers/usb/core/sysfs.c +++ b/drivers/usb/core/sysfs.c @@ -840,7 +840,7 @@ int usb_create_sysfs_intf_files(struct usb_interface *intf) struct usb_host_interface *alt = intf->cur_altsetting; int retval; - if (intf->sysfs_files_created) + if (intf->sysfs_files_created || intf->unregistering) return 0; /* The interface string may be present in some altsettings diff --git a/drivers/usb/core/urb.c b/drivers/usb/core/urb.c index 4342bd9c3bb..1f68af9db3f 100644 --- a/drivers/usb/core/urb.c +++ b/drivers/usb/core/urb.c @@ -85,8 +85,8 @@ EXPORT_SYMBOL_GPL(usb_alloc_urb); * Must be called when a user of a urb is finished with it. When the last user * of the urb calls this function, the memory of the urb is freed. * - * Note: The transfer buffer associated with the urb is not freed, that must be - * done elsewhere. + * Note: The transfer buffer associated with the urb is not freed unless the + * URB_FREE_BUFFER transfer flag is set. */ void usb_free_urb(struct urb *urb) { diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/f_acm.c index 5ee1590b8e9..c1d34df0b15 100644 --- a/drivers/usb/gadget/f_acm.c +++ b/drivers/usb/gadget/f_acm.c @@ -463,7 +463,11 @@ static int acm_cdc_notify(struct f_acm *acm, u8 type, u16 value, notify->wLength = cpu_to_le16(length); memcpy(buf, data, length); + /* ep_queue() can complete immediately if it fills the fifo... */ + spin_unlock(&acm->lock); status = usb_ep_queue(ep, req, GFP_ATOMIC); + spin_lock(&acm->lock); + if (status < 0) { ERROR(acm->port.func.config->cdev, "acm ttyGS%d can't notify serial state, %d\n", diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 56f592dc0b3..f3a75a929e0 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -110,29 +110,18 @@ config USB_ISP116X_HCD config USB_ISP1760_HCD tristate "ISP 1760 HCD support" - depends on USB && EXPERIMENTAL + depends on USB && EXPERIMENTAL && (PCI || PPC_OF) ---help--- The ISP1760 chip is a USB 2.0 host controller. This driver does not support isochronous transfers or OTG. + This USB controller is usually attached to a non-DMA-Master + capable bus. NXP's eval kit brings this chip on PCI card + where the chip itself is behind a PLB to simulate such + a bus. To compile this driver as a module, choose M here: the - module will be called isp1760-hcd. - -config USB_ISP1760_PCI - bool "Support for the PCI bus" - depends on USB_ISP1760_HCD && PCI - ---help--- - Enables support for the device present on the PCI bus. - This should only be required if you happen to have the eval kit from - NXP and you are going to test it. - -config USB_ISP1760_OF - bool "Support for the OF platform bus" - depends on USB_ISP1760_HCD && PPC_OF - ---help--- - Enables support for the device present on the PowerPC - OpenFirmware platform bus. + module will be called isp1760. config USB_OHCI_HCD tristate "OHCI HCD support" diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 15a803b206b..4725d15d096 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -643,7 +643,7 @@ static int ehci_run (struct usb_hcd *hcd) static irqreturn_t ehci_irq (struct usb_hcd *hcd) { struct ehci_hcd *ehci = hcd_to_ehci (hcd); - u32 status, pcd_status = 0, cmd; + u32 status, masked_status, pcd_status = 0, cmd; int bh; spin_lock (&ehci->lock); @@ -656,14 +656,14 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) goto dead; } - status &= INTR_MASK; - if (!status) { /* irq sharing? */ + masked_status = status & INTR_MASK; + if (!masked_status) { /* irq sharing? */ spin_unlock(&ehci->lock); return IRQ_NONE; } /* clear (just) interrupts */ - ehci_writel(ehci, status, &ehci->regs->status); + ehci_writel(ehci, masked_status, &ehci->regs->status); cmd = ehci_readl(ehci, &ehci->regs->command); bh = 0; @@ -734,18 +734,17 @@ static irqreturn_t ehci_irq (struct usb_hcd *hcd) /* PCI errors [4.15.2.4] */ if (unlikely ((status & STS_FATAL) != 0)) { + ehci_err(ehci, "fatal error\n"); dbg_cmd(ehci, "fatal", cmd); dbg_status(ehci, "fatal", status); - if (status & STS_HALT) { - ehci_err (ehci, "fatal error\n"); + ehci_halt(ehci); dead: - ehci_reset (ehci); - ehci_writel(ehci, 0, &ehci->regs->configured_flag); - /* generic layer kills/unlinks all urbs, then - * uses ehci_stop to clean up the rest - */ - bh = 1; - } + ehci_reset(ehci); + ehci_writel(ehci, 0, &ehci->regs->configured_flag); + /* generic layer kills/unlinks all urbs, then + * uses ehci_stop to clean up the rest + */ + bh = 1; } if (bh) diff --git a/drivers/usb/host/ehci-ps3.c b/drivers/usb/host/ehci-ps3.c index 0eba894bcb0..9c9da35abc6 100644 --- a/drivers/usb/host/ehci-ps3.c +++ b/drivers/usb/host/ehci-ps3.c @@ -205,6 +205,7 @@ static int ps3_ehci_remove(struct ps3_system_bus_device *dev) tmp = hcd->irq; + ehci_shutdown(hcd); usb_remove_hcd(hcd); ps3_system_bus_set_driver_data(dev, NULL); diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index 4a0c5a78b2e..a081ee65bde 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -918,7 +918,7 @@ iso_stream_init ( */ stream->usecs = HS_USECS_ISO (maxp); bandwidth = stream->usecs * 8; - bandwidth /= 1 << (interval - 1); + bandwidth /= interval; } else { u32 addr; @@ -951,7 +951,7 @@ iso_stream_init ( } else stream->raw_mask = smask_out [hs_transfers - 1]; bandwidth = stream->usecs + stream->c_usecs; - bandwidth /= 1 << (interval + 2); + bandwidth /= interval << 3; /* stream->splits gets created from raw_mask later */ stream->address = cpu_to_hc32(ehci, addr); diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/host/isp1760-if.c index af849f59613..b87ca7cf4b3 100644 --- a/drivers/usb/host/isp1760-if.c +++ b/drivers/usb/host/isp1760-if.c @@ -14,16 +14,16 @@ #include "../core/hcd.h" #include "isp1760-hcd.h" -#ifdef CONFIG_USB_ISP1760_OF +#ifdef CONFIG_PPC_OF #include <linux/of.h> #include <linux/of_platform.h> #endif -#ifdef CONFIG_USB_ISP1760_PCI +#ifdef CONFIG_PCI #include <linux/pci.h> #endif -#ifdef CONFIG_USB_ISP1760_OF +#ifdef CONFIG_PPC_OF static int of_isp1760_probe(struct of_device *dev, const struct of_device_id *match) { @@ -128,7 +128,7 @@ static struct of_platform_driver isp1760_of_driver = { }; #endif -#ifdef CONFIG_USB_ISP1760_PCI +#ifdef CONFIG_PCI static u32 nxp_pci_io_base; static u32 iolength; static u32 pci_mem_phy0; @@ -288,28 +288,28 @@ static struct pci_driver isp1761_pci_driver = { static int __init isp1760_init(void) { - int ret = -ENODEV; + int ret; init_kmem_once(); -#ifdef CONFIG_USB_ISP1760_OF +#ifdef CONFIG_PPC_OF ret = of_register_platform_driver(&isp1760_of_driver); if (ret) { deinit_kmem_cache(); return ret; } #endif -#ifdef CONFIG_USB_ISP1760_PCI +#ifdef CONFIG_PCI ret = pci_register_driver(&isp1761_pci_driver); if (ret) goto unreg_of; #endif return ret; -#ifdef CONFIG_USB_ISP1760_PCI +#ifdef CONFIG_PCI unreg_of: #endif -#ifdef CONFIG_USB_ISP1760_OF +#ifdef CONFIG_PPC_OF of_unregister_platform_driver(&isp1760_of_driver); #endif deinit_kmem_cache(); @@ -319,10 +319,10 @@ module_init(isp1760_init); static void __exit isp1760_exit(void) { -#ifdef CONFIG_USB_ISP1760_OF +#ifdef CONFIG_PPC_OF of_unregister_platform_driver(&isp1760_of_driver); #endif -#ifdef CONFIG_USB_ISP1760_PCI +#ifdef CONFIG_PCI pci_unregister_driver(&isp1761_pci_driver); #endif deinit_kmem_cache(); diff --git a/drivers/usb/host/ohci-ps3.c b/drivers/usb/host/ohci-ps3.c index 2089d8a46c4..3c1a3b5f89f 100644 --- a/drivers/usb/host/ohci-ps3.c +++ b/drivers/usb/host/ohci-ps3.c @@ -192,7 +192,7 @@ fail_start: return result; } -static int ps3_ohci_remove (struct ps3_system_bus_device *dev) +static int ps3_ohci_remove(struct ps3_system_bus_device *dev) { unsigned int tmp; struct usb_hcd *hcd = @@ -205,6 +205,7 @@ static int ps3_ohci_remove (struct ps3_system_bus_device *dev) tmp = hcd->irq; + ohci_shutdown(hcd); usb_remove_hcd(hcd); ps3_system_bus_set_driver_data(dev, NULL); diff --git a/drivers/usb/host/r8a66597-hcd.c b/drivers/usb/host/r8a66597-hcd.c index c18d8790c41..2376f24f3c8 100644 --- a/drivers/usb/host/r8a66597-hcd.c +++ b/drivers/usb/host/r8a66597-hcd.c @@ -1763,11 +1763,12 @@ static void r8a66597_timer(unsigned long _r8a66597) { struct r8a66597 *r8a66597 = (struct r8a66597 *)_r8a66597; unsigned long flags; + int port; spin_lock_irqsave(&r8a66597->lock, flags); - r8a66597_root_hub_control(r8a66597, 0); - r8a66597_root_hub_control(r8a66597, 1); + for (port = 0; port < R8A66597_MAX_ROOT_HUB; port++) + r8a66597_root_hub_control(r8a66597, port); spin_unlock_irqrestore(&r8a66597->lock, flags); } diff --git a/drivers/usb/misc/sisusbvga/sisusb.c b/drivers/usb/misc/sisusbvga/sisusb.c index 69c34a58e20..b4ec716de7d 100644 --- a/drivers/usb/misc/sisusbvga/sisusb.c +++ b/drivers/usb/misc/sisusbvga/sisusb.c @@ -3270,6 +3270,7 @@ static struct usb_device_id sisusb_table [] = { { USB_DEVICE(0x0711, 0x0900) }, { USB_DEVICE(0x0711, 0x0901) }, { USB_DEVICE(0x0711, 0x0902) }, + { USB_DEVICE(0x0711, 0x0903) }, { USB_DEVICE(0x0711, 0x0918) }, { USB_DEVICE(0x182d, 0x021c) }, { USB_DEVICE(0x182d, 0x0269) }, diff --git a/drivers/usb/misc/vstusb.c b/drivers/usb/misc/vstusb.c index 8648470c81c..63dff9ba73c 100644 --- a/drivers/usb/misc/vstusb.c +++ b/drivers/usb/misc/vstusb.c @@ -620,7 +620,7 @@ static long vstusb_ioctl(struct file *file, unsigned int cmd, unsigned long arg) __func__); retval = -EFAULT; } else { - dev_dbg(&dev->dev, "%s: recv %d bytes from pipe %d\n", + dev_dbg(&dev->dev, "%s: recv %zd bytes from pipe %d\n", __func__, usb_data.count, usb_data.pipe); } diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 4a35745b30b..5280dba9b1f 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -114,8 +114,8 @@ -unsigned debug; -module_param(debug, uint, S_IRUGO | S_IWUSR); +unsigned musb_debug; +module_param(musb_debug, uint, S_IRUGO | S_IWUSR); MODULE_PARM_DESC(debug, "Debug message level. Default = 0"); #define DRIVER_AUTHOR "Mentor Graphics, Texas Instruments, Nokia" @@ -2248,7 +2248,7 @@ static int __init musb_init(void) "host" #endif ", debug=%d\n", - musb_driver_name, debug); + musb_driver_name, musb_debug); return platform_driver_probe(&musb_driver, musb_probe); } diff --git a/drivers/usb/musb/musb_debug.h b/drivers/usb/musb/musb_debug.h index 4d2794441b1..9fc1db44c72 100644 --- a/drivers/usb/musb/musb_debug.h +++ b/drivers/usb/musb/musb_debug.h @@ -48,11 +48,11 @@ __func__, __LINE__ , ## args); \ } } while (0) -extern unsigned debug; +extern unsigned musb_debug; static inline int _dbg_level(unsigned l) { - return debug >= l; + return musb_debug >= l; } #define DBG(level, fmt, args...) xprintk(level, KERN_DEBUG, fmt, ## args) diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index 3133990f04e..e45e70bcc5e 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -378,6 +378,19 @@ musb_giveback(struct musb_qh *qh, struct urb *urb, int status) switch (qh->type) { + case USB_ENDPOINT_XFER_CONTROL: + case USB_ENDPOINT_XFER_BULK: + /* fifo policy for these lists, except that NAKing + * should rotate a qh to the end (for fairness). + */ + if (qh->mux == 1) { + head = qh->ring.prev; + list_del(&qh->ring); + kfree(qh); + qh = first_qh(head); + break; + } + case USB_ENDPOINT_XFER_ISOC: case USB_ENDPOINT_XFER_INT: /* this is where periodic bandwidth should be @@ -388,17 +401,6 @@ musb_giveback(struct musb_qh *qh, struct urb *urb, int status) kfree(qh); qh = NULL; break; - - case USB_ENDPOINT_XFER_CONTROL: - case USB_ENDPOINT_XFER_BULK: - /* fifo policy for these lists, except that NAKing - * should rotate a qh to the end (for fairness). - */ - head = qh->ring.prev; - list_del(&qh->ring); - kfree(qh); - qh = first_qh(head); - break; } } return qh; @@ -1507,10 +1509,29 @@ void musb_host_rx(struct musb *musb, u8 epnum) musb_writew(hw_ep->regs, MUSB_RXCSR, val); #ifdef CONFIG_USB_INVENTRA_DMA + if (usb_pipeisoc(pipe)) { + struct usb_iso_packet_descriptor *d; + + d = urb->iso_frame_desc + qh->iso_idx; + d->actual_length = xfer_len; + + /* even if there was an error, we did the dma + * for iso_frame_desc->length + */ + if (d->status != EILSEQ && d->status != -EOVERFLOW) + d->status = 0; + + if (++qh->iso_idx >= urb->number_of_packets) + done = true; + else + done = false; + + } else { /* done if urb buffer is full or short packet is recd */ done = (urb->actual_length + xfer_len >= urb->transfer_buffer_length || dma->actual_len < qh->maxpacket); + } /* send IN token for next packet, without AUTOREQ */ if (!done) { @@ -1547,7 +1568,8 @@ void musb_host_rx(struct musb *musb, u8 epnum) if (dma) { struct dma_controller *c; u16 rx_count; - int ret; + int ret, length; + dma_addr_t buf; rx_count = musb_readw(epio, MUSB_RXCOUNT); @@ -1560,6 +1582,35 @@ void musb_host_rx(struct musb *musb, u8 epnum) c = musb->dma_controller; + if (usb_pipeisoc(pipe)) { + int status = 0; + struct usb_iso_packet_descriptor *d; + + d = urb->iso_frame_desc + qh->iso_idx; + + if (iso_err) { + status = -EILSEQ; + urb->error_count++; + } + if (rx_count > d->length) { + if (status == 0) { + status = -EOVERFLOW; + urb->error_count++; + } + DBG(2, "** OVERFLOW %d into %d\n",\ + rx_count, d->length); + + length = d->length; + } else + length = rx_count; + d->status = status; + buf = urb->transfer_dma + d->offset; + } else { + length = rx_count; + buf = urb->transfer_dma + + urb->actual_length; + } + dma->desired_mode = 0; #ifdef USE_MODE1 /* because of the issue below, mode 1 will @@ -1571,6 +1622,12 @@ void musb_host_rx(struct musb *musb, u8 epnum) urb->actual_length) > qh->maxpacket) dma->desired_mode = 1; + if (rx_count < hw_ep->max_packet_sz_rx) { + length = rx_count; + dma->bDesiredMode = 0; + } else { + length = urb->transfer_buffer_length; + } #endif /* Disadvantage of using mode 1: @@ -1608,12 +1665,7 @@ void musb_host_rx(struct musb *musb, u8 epnum) */ ret = c->channel_program( dma, qh->maxpacket, - dma->desired_mode, - urb->transfer_dma - + urb->actual_length, - (dma->desired_mode == 0) - ? rx_count - : urb->transfer_buffer_length); + dma->desired_mode, buf, length); if (!ret) { c->channel_release(dma); @@ -1631,19 +1683,6 @@ void musb_host_rx(struct musb *musb, u8 epnum) } } - if (dma && usb_pipeisoc(pipe)) { - struct usb_iso_packet_descriptor *d; - int iso_stat = status; - - d = urb->iso_frame_desc + qh->iso_idx; - d->actual_length += xfer_len; - if (iso_err) { - iso_stat = -EILSEQ; - urb->error_count++; - } - d->status = iso_stat; - } - finish: urb->actual_length += xfer_len; qh->offset += xfer_len; @@ -1671,22 +1710,9 @@ static int musb_schedule( struct list_head *head = NULL; /* use fixed hardware for control and bulk */ - switch (qh->type) { - case USB_ENDPOINT_XFER_CONTROL: + if (qh->type == USB_ENDPOINT_XFER_CONTROL) { head = &musb->control; hw_ep = musb->control_ep; - break; - case USB_ENDPOINT_XFER_BULK: - hw_ep = musb->bulk_ep; - if (is_in) - head = &musb->in_bulk; - else - head = &musb->out_bulk; - break; - } - if (head) { - idle = list_empty(head); - list_add_tail(&qh->ring, head); goto success; } @@ -1725,19 +1751,34 @@ static int musb_schedule( else diff = hw_ep->max_packet_sz_tx - qh->maxpacket; - if (diff > 0 && best_diff > diff) { + if (diff >= 0 && best_diff > diff) { best_diff = diff; best_end = epnum; } } - if (best_end < 0) + /* use bulk reserved ep1 if no other ep is free */ + if (best_end > 0 && qh->type == USB_ENDPOINT_XFER_BULK) { + hw_ep = musb->bulk_ep; + if (is_in) + head = &musb->in_bulk; + else + head = &musb->out_bulk; + goto success; + } else if (best_end < 0) { return -ENOSPC; + } idle = 1; + qh->mux = 0; hw_ep = musb->endpoints + best_end; musb->periodic[best_end] = qh; DBG(4, "qh %p periodic slot %d\n", qh, best_end); success: + if (head) { + idle = list_empty(head); + list_add_tail(&qh->ring, head); + qh->mux = 1; + } qh->hw_ep = hw_ep; qh->hep->hcpriv = qh; if (idle) @@ -2015,11 +2056,13 @@ static int musb_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) sched = &musb->control; break; case USB_ENDPOINT_XFER_BULK: - if (usb_pipein(urb->pipe)) - sched = &musb->in_bulk; - else - sched = &musb->out_bulk; - break; + if (qh->mux == 1) { + if (usb_pipein(urb->pipe)) + sched = &musb->in_bulk; + else + sched = &musb->out_bulk; + break; + } default: /* REVISIT when we get a schedule tree, periodic * transfers won't always be at the head of a @@ -2067,11 +2110,13 @@ musb_h_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep) sched = &musb->control; break; case USB_ENDPOINT_XFER_BULK: - if (is_in) - sched = &musb->in_bulk; - else - sched = &musb->out_bulk; - break; + if (qh->mux == 1) { + if (is_in) + sched = &musb->in_bulk; + else + sched = &musb->out_bulk; + break; + } default: /* REVISIT when we get a schedule tree, periodic transfers * won't always be at the head of a singleton queue... diff --git a/drivers/usb/musb/musb_host.h b/drivers/usb/musb/musb_host.h index 77bcdb9d5b3..0b7fbcd2196 100644 --- a/drivers/usb/musb/musb_host.h +++ b/drivers/usb/musb/musb_host.h @@ -53,6 +53,7 @@ struct musb_qh { struct list_head ring; /* of musb_qh */ /* struct musb_qh *next; */ /* for periodic tree */ + u8 mux; /* qh multiplexed to hw_ep */ unsigned offset; /* in urb->transfer_buffer */ unsigned segsize; /* current xfer fragment */ diff --git a/drivers/usb/musb/omap2430.c b/drivers/usb/musb/omap2430.c index 9d2dcb121c5..ce6c162920f 100644 --- a/drivers/usb/musb/omap2430.c +++ b/drivers/usb/musb/omap2430.c @@ -53,7 +53,9 @@ static void musb_do_idle(unsigned long _musb) { struct musb *musb = (void *)_musb; unsigned long flags; +#ifdef CONFIG_USB_MUSB_HDRC_HCD u8 power; +#endif u8 devctl; devctl = musb_readb(musb->mregs, MUSB_DEVCTL); diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c index b73b036f3d7..ee8fca92a4a 100644 --- a/drivers/usb/musb/tusb6010.c +++ b/drivers/usb/musb/tusb6010.c @@ -605,7 +605,7 @@ void musb_platform_set_mode(struct musb *musb, u8 musb_mode) if (musb->board_mode != MUSB_OTG) { ERR("Changing mode currently only supported in OTG mode\n"); - return; + return -EINVAL; } otg_stat = musb_readl(tbase, TUSB_DEV_OTG_STAT); diff --git a/drivers/usb/serial/cp2101.c b/drivers/usb/serial/cp2101.c index 8008d0bc80a..9035d7256b0 100644 --- a/drivers/usb/serial/cp2101.c +++ b/drivers/usb/serial/cp2101.c @@ -67,6 +67,7 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(0x10C4, 0x800A) }, /* SPORTident BSM7-D-USB main station */ { USB_DEVICE(0x10C4, 0x803B) }, /* Pololu USB-serial converter */ { USB_DEVICE(0x10C4, 0x8053) }, /* Enfora EDG1228 */ + { USB_DEVICE(0x10C4, 0x8054) }, /* Enfora GSM2228 */ { USB_DEVICE(0x10C4, 0x8066) }, /* Argussoft In-System Programmer */ { USB_DEVICE(0x10C4, 0x807A) }, /* Crumb128 board */ { USB_DEVICE(0x10C4, 0x80CA) }, /* Degree Controls Inc */ @@ -85,6 +86,7 @@ static struct usb_device_id id_table [] = { { USB_DEVICE(0x10C4, 0x8218) }, /* Lipowsky Industrie Elektronik GmbH, HARP-1 */ { USB_DEVICE(0x10c4, 0x8293) }, /* Telegesys ETRX2USB */ { USB_DEVICE(0x10C4, 0x8341) }, /* Siemens MC35PU GPRS Modem */ + { USB_DEVICE(0x10C4, 0x83A8) }, /* Amber Wireless AMB2560 */ { USB_DEVICE(0x10C4, 0xEA60) }, /* Silicon Labs factory default */ { USB_DEVICE(0x10C4, 0xEA61) }, /* Silicon Labs factory default */ { USB_DEVICE(0x10C4, 0xF001) }, /* Elan Digital Systems USBscope50 */ diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index bd07eaa300b..6fa1ec441b6 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -160,6 +160,11 @@ static int option_send_setup(struct tty_struct *tty, struct usb_serial_port *po #define NOVATELWIRELESS_VENDOR_ID 0x1410 +/* YISO PRODUCTS */ + +#define YISO_VENDOR_ID 0x0EAB +#define YISO_PRODUCT_U893 0xC893 + /* MERLIN EVDO PRODUCTS */ #define NOVATELWIRELESS_PRODUCT_V640 0x1100 #define NOVATELWIRELESS_PRODUCT_V620 0x1110 @@ -408,6 +413,7 @@ static struct usb_device_id option_ids[] = { { USB_DEVICE(AXESSTEL_VENDOR_ID, AXESSTEL_PRODUCT_MV110H) }, { USB_DEVICE(ONDA_VENDOR_ID, ONDA_PRODUCT_MSA501HS) }, { USB_DEVICE(ONDA_VENDOR_ID, ONDA_PRODUCT_ET502HS) }, + { USB_DEVICE(YISO_VENDOR_ID, YISO_PRODUCT_U893) }, { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_1) }, { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_2) }, { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_1004) }, diff --git a/drivers/usb/storage/Kconfig b/drivers/usb/storage/Kconfig index 3d9249632ae..c68b738900b 100644 --- a/drivers/usb/storage/Kconfig +++ b/drivers/usb/storage/Kconfig @@ -2,8 +2,8 @@ # USB Storage driver configuration # -comment "NOTE: USB_STORAGE enables SCSI, and 'SCSI disk support'" -comment "may also be needed; see USB_STORAGE Help for more information" +comment "NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may also be needed;" +comment "see USB_STORAGE Help for more information" depends on USB config USB_STORAGE diff --git a/drivers/usb/storage/unusual_devs.h b/drivers/usb/storage/unusual_devs.h index fb9e20e624c..d4e5fc86e43 100644 --- a/drivers/usb/storage/unusual_devs.h +++ b/drivers/usb/storage/unusual_devs.h @@ -253,6 +253,14 @@ UNUSUAL_DEV( 0x0421, 0x006a, 0x0000, 0x0591, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_CAPACITY ), +/* Submitted by Ricky Wong Yung Fei <evilbladewarrior@gmail.com> */ +/* Nokia 7610 Supernova - Too many sectors reported in usb storage mode */ +UNUSUAL_DEV( 0x0421, 0x00f5, 0x0000, 0x0470, + "Nokia", + "7610 Supernova", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY ), + /* Reported by Olaf Hering <olh@suse.de> from novell bug #105878 */ UNUSUAL_DEV( 0x0424, 0x0fdc, 0x0210, 0x0210, "SMSC", @@ -418,6 +426,13 @@ UNUSUAL_DEV( 0x04b0, 0x0417, 0x0100, 0x0100, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_CAPACITY), +/* Reported by paul ready <lxtwin@homecall.co.uk> */ +UNUSUAL_DEV( 0x04b0, 0x0419, 0x0100, 0x0200, + "NIKON", + "NIKON DSC D300", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY), + /* Reported by Doug Maxey (dwm@austin.ibm.com) */ UNUSUAL_DEV( 0x04b3, 0x4001, 0x0110, 0x0110, "IBM", @@ -1258,6 +1273,13 @@ UNUSUAL_DEV( 0x0839, 0x000a, 0x0001, 0x0001, US_SC_DEVICE, US_PR_DEVICE, NULL, US_FL_FIX_INQUIRY), +/* Reported by Luciano Rocha <luciano@eurotux.com> */ +UNUSUAL_DEV( 0x0840, 0x0082, 0x0001, 0x0001, + "Argosy", + "Storage", + US_SC_DEVICE, US_PR_DEVICE, NULL, + US_FL_FIX_CAPACITY), + /* Entry and supporting patch by Theodore Kilgore <kilgota@auburn.edu>. * Flag will support Bulk devices which use a standards-violating 32-byte * Command Block Wrapper. Here, the "DC2MEGA" cameras (several brands) with diff --git a/drivers/w1/masters/Kconfig b/drivers/w1/masters/Kconfig index c4493091c65..a14d5b6e4c7 100644 --- a/drivers/w1/masters/Kconfig +++ b/drivers/w1/masters/Kconfig @@ -52,5 +52,12 @@ config W1_MASTER_GPIO This support is also available as a module. If so, the module will be called w1-gpio.ko. +config HDQ_MASTER_OMAP + tristate "OMAP HDQ driver" + depends on ARCH_OMAP2430 || ARCH_OMAP34XX + help + Say Y here if you want support for the 1-wire or HDQ Interface + on an OMAP processor. + endmenu diff --git a/drivers/w1/masters/Makefile b/drivers/w1/masters/Makefile index 1420b5bbdda..bc4714a75f3 100644 --- a/drivers/w1/masters/Makefile +++ b/drivers/w1/masters/Makefile @@ -7,3 +7,4 @@ obj-$(CONFIG_W1_MASTER_DS2490) += ds2490.o obj-$(CONFIG_W1_MASTER_DS2482) += ds2482.o obj-$(CONFIG_W1_MASTER_DS1WM) += ds1wm.o obj-$(CONFIG_W1_MASTER_GPIO) += w1-gpio.o +obj-$(CONFIG_HDQ_MASTER_OMAP) += omap_hdq.o diff --git a/drivers/w1/masters/omap_hdq.c b/drivers/w1/masters/omap_hdq.c new file mode 100644 index 00000000000..1295625c482 --- /dev/null +++ b/drivers/w1/masters/omap_hdq.c @@ -0,0 +1,725 @@ +/* + * drivers/w1/masters/omap_hdq.c + * + * Copyright (C) 2007 Texas Instruments, Inc. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + */ +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/interrupt.h> +#include <linux/err.h> +#include <linux/clk.h> +#include <linux/io.h> + +#include <asm/irq.h> +#include <mach/hardware.h> + +#include "../w1.h" +#include "../w1_int.h" + +#define MOD_NAME "OMAP_HDQ:" + +#define OMAP_HDQ_REVISION 0x00 +#define OMAP_HDQ_TX_DATA 0x04 +#define OMAP_HDQ_RX_DATA 0x08 +#define OMAP_HDQ_CTRL_STATUS 0x0c +#define OMAP_HDQ_CTRL_STATUS_INTERRUPTMASK (1<<6) +#define OMAP_HDQ_CTRL_STATUS_CLOCKENABLE (1<<5) +#define OMAP_HDQ_CTRL_STATUS_GO (1<<4) +#define OMAP_HDQ_CTRL_STATUS_INITIALIZATION (1<<2) +#define OMAP_HDQ_CTRL_STATUS_DIR (1<<1) +#define OMAP_HDQ_CTRL_STATUS_MODE (1<<0) +#define OMAP_HDQ_INT_STATUS 0x10 +#define OMAP_HDQ_INT_STATUS_TXCOMPLETE (1<<2) +#define OMAP_HDQ_INT_STATUS_RXCOMPLETE (1<<1) +#define OMAP_HDQ_INT_STATUS_TIMEOUT (1<<0) +#define OMAP_HDQ_SYSCONFIG 0x14 +#define OMAP_HDQ_SYSCONFIG_SOFTRESET (1<<1) +#define OMAP_HDQ_SYSCONFIG_AUTOIDLE (1<<0) +#define OMAP_HDQ_SYSSTATUS 0x18 +#define OMAP_HDQ_SYSSTATUS_RESETDONE (1<<0) + +#define OMAP_HDQ_FLAG_CLEAR 0 +#define OMAP_HDQ_FLAG_SET 1 +#define OMAP_HDQ_TIMEOUT (HZ/5) + +#define OMAP_HDQ_MAX_USER 4 + +static DECLARE_WAIT_QUEUE_HEAD(hdq_wait_queue); +static int w1_id; + +struct hdq_data { + struct device *dev; + void __iomem *hdq_base; + /* lock status update */ + struct mutex hdq_mutex; + int hdq_usecount; + struct clk *hdq_ick; + struct clk *hdq_fck; + u8 hdq_irqstatus; + /* device lock */ + spinlock_t hdq_spinlock; + /* + * Used to control the call to omap_hdq_get and omap_hdq_put. + * HDQ Protocol: Write the CMD|REG_address first, followed by + * the data wrire or read. + */ + int init_trans; +}; + +static int __init omap_hdq_probe(struct platform_device *pdev); +static int omap_hdq_remove(struct platform_device *pdev); + +static struct platform_driver omap_hdq_driver = { + .probe = omap_hdq_probe, + .remove = omap_hdq_remove, + .driver = { + .name = "omap_hdq", + }, +}; + +static u8 omap_w1_read_byte(void *_hdq); +static void omap_w1_write_byte(void *_hdq, u8 byte); +static u8 omap_w1_reset_bus(void *_hdq); +static void omap_w1_search_bus(void *_hdq, u8 search_type, + w1_slave_found_callback slave_found); + + +static struct w1_bus_master omap_w1_master = { + .read_byte = omap_w1_read_byte, + .write_byte = omap_w1_write_byte, + .reset_bus = omap_w1_reset_bus, + .search = omap_w1_search_bus, +}; + +/* HDQ register I/O routines */ +static inline u8 hdq_reg_in(struct hdq_data *hdq_data, u32 offset) +{ + return __raw_readb(hdq_data->hdq_base + offset); +} + +static inline void hdq_reg_out(struct hdq_data *hdq_data, u32 offset, u8 val) +{ + __raw_writeb(val, hdq_data->hdq_base + offset); +} + +static inline u8 hdq_reg_merge(struct hdq_data *hdq_data, u32 offset, + u8 val, u8 mask) +{ + u8 new_val = (__raw_readb(hdq_data->hdq_base + offset) & ~mask) + | (val & mask); + __raw_writeb(new_val, hdq_data->hdq_base + offset); + + return new_val; +} + +/* + * Wait for one or more bits in flag change. + * HDQ_FLAG_SET: wait until any bit in the flag is set. + * HDQ_FLAG_CLEAR: wait until all bits in the flag are cleared. + * return 0 on success and -ETIMEDOUT in the case of timeout. + */ +static int hdq_wait_for_flag(struct hdq_data *hdq_data, u32 offset, + u8 flag, u8 flag_set, u8 *status) +{ + int ret = 0; + unsigned long timeout = jiffies + OMAP_HDQ_TIMEOUT; + + if (flag_set == OMAP_HDQ_FLAG_CLEAR) { + /* wait for the flag clear */ + while (((*status = hdq_reg_in(hdq_data, offset)) & flag) + && time_before(jiffies, timeout)) { + schedule_timeout_uninterruptible(1); + } + if (*status & flag) + ret = -ETIMEDOUT; + } else if (flag_set == OMAP_HDQ_FLAG_SET) { + /* wait for the flag set */ + while (!((*status = hdq_reg_in(hdq_data, offset)) & flag) + && time_before(jiffies, timeout)) { + schedule_timeout_uninterruptible(1); + } + if (!(*status & flag)) + ret = -ETIMEDOUT; + } else + return -EINVAL; + + return ret; +} + +/* write out a byte and fill *status with HDQ_INT_STATUS */ +static int hdq_write_byte(struct hdq_data *hdq_data, u8 val, u8 *status) +{ + int ret; + u8 tmp_status; + unsigned long irqflags; + + *status = 0; + + spin_lock_irqsave(&hdq_data->hdq_spinlock, irqflags); + /* clear interrupt flags via a dummy read */ + hdq_reg_in(hdq_data, OMAP_HDQ_INT_STATUS); + /* ISR loads it with new INT_STATUS */ + hdq_data->hdq_irqstatus = 0; + spin_unlock_irqrestore(&hdq_data->hdq_spinlock, irqflags); + + hdq_reg_out(hdq_data, OMAP_HDQ_TX_DATA, val); + + /* set the GO bit */ + hdq_reg_merge(hdq_data, OMAP_HDQ_CTRL_STATUS, OMAP_HDQ_CTRL_STATUS_GO, + OMAP_HDQ_CTRL_STATUS_DIR | OMAP_HDQ_CTRL_STATUS_GO); + /* wait for the TXCOMPLETE bit */ + ret = wait_event_timeout(hdq_wait_queue, + hdq_data->hdq_irqstatus, OMAP_HDQ_TIMEOUT); + if (ret == 0) { + dev_dbg(hdq_data->dev, "TX wait elapsed\n"); + goto out; + } + + *status = hdq_data->hdq_irqstatus; + /* check irqstatus */ + if (!(*status & OMAP_HDQ_INT_STATUS_TXCOMPLETE)) { + dev_dbg(hdq_data->dev, "timeout waiting for" + "TXCOMPLETE/RXCOMPLETE, %x", *status); + ret = -ETIMEDOUT; + goto out; + } + + /* wait for the GO bit return to zero */ + ret = hdq_wait_for_flag(hdq_data, OMAP_HDQ_CTRL_STATUS, + OMAP_HDQ_CTRL_STATUS_GO, + OMAP_HDQ_FLAG_CLEAR, &tmp_status); + if (ret) { + dev_dbg(hdq_data->dev, "timeout waiting GO bit" + "return to zero, %x", tmp_status); + } + +out: + return ret; +} + +/* HDQ Interrupt service routine */ +static irqreturn_t hdq_isr(int irq, void *_hdq) +{ + struct hdq_data *hdq_data = _hdq; + unsigned long irqflags; + + spin_lock_irqsave(&hdq_data->hdq_spinlock, irqflags); + hdq_data->hdq_irqstatus = hdq_reg_in(hdq_data, OMAP_HDQ_INT_STATUS); + spin_unlock_irqrestore(&hdq_data->hdq_spinlock, irqflags); + dev_dbg(hdq_data->dev, "hdq_isr: %x", hdq_data->hdq_irqstatus); + + if (hdq_data->hdq_irqstatus & + (OMAP_HDQ_INT_STATUS_TXCOMPLETE | OMAP_HDQ_INT_STATUS_RXCOMPLETE + | OMAP_HDQ_INT_STATUS_TIMEOUT)) { + /* wake up sleeping process */ + wake_up(&hdq_wait_queue); + } + + return IRQ_HANDLED; +} + +/* HDQ Mode: always return success */ +static u8 omap_w1_reset_bus(void *_hdq) +{ + return 0; +} + +/* W1 search callback function */ +static void omap_w1_search_bus(void *_hdq, u8 search_type, + w1_slave_found_callback slave_found) +{ + u64 module_id, rn_le, cs, id; + + if (w1_id) + module_id = w1_id; + else + module_id = 0x1; + + rn_le = cpu_to_le64(module_id); + /* + * HDQ might not obey truly the 1-wire spec. + * So calculate CRC based on module parameter. + */ + cs = w1_calc_crc8((u8 *)&rn_le, 7); + id = (cs << 56) | module_id; + + slave_found(_hdq, id); +} + +static int _omap_hdq_reset(struct hdq_data *hdq_data) +{ + int ret; + u8 tmp_status; + + hdq_reg_out(hdq_data, OMAP_HDQ_SYSCONFIG, OMAP_HDQ_SYSCONFIG_SOFTRESET); + /* + * Select HDQ mode & enable clocks. + * It is observed that INT flags can't be cleared via a read and GO/INIT + * won't return to zero if interrupt is disabled. So we always enable + * interrupt. + */ + hdq_reg_out(hdq_data, OMAP_HDQ_CTRL_STATUS, + OMAP_HDQ_CTRL_STATUS_CLOCKENABLE | + OMAP_HDQ_CTRL_STATUS_INTERRUPTMASK); + + /* wait for reset to complete */ + ret = hdq_wait_for_flag(hdq_data, OMAP_HDQ_SYSSTATUS, + OMAP_HDQ_SYSSTATUS_RESETDONE, OMAP_HDQ_FLAG_SET, &tmp_status); + if (ret) + dev_dbg(hdq_data->dev, "timeout waiting HDQ reset, %x", + tmp_status); + else { + hdq_reg_out(hdq_data, OMAP_HDQ_CTRL_STATUS, + OMAP_HDQ_CTRL_STATUS_CLOCKENABLE | + OMAP_HDQ_CTRL_STATUS_INTERRUPTMASK); + hdq_reg_out(hdq_data, OMAP_HDQ_SYSCONFIG, + OMAP_HDQ_SYSCONFIG_AUTOIDLE); + } + + return ret; +} + +/* Issue break pulse to the device */ +static int omap_hdq_break(struct hdq_data *hdq_data) +{ + int ret = 0; + u8 tmp_status; + unsigned long irqflags; + + ret = mutex_lock_interruptible(&hdq_data->hdq_mutex); + if (ret < 0) { + dev_dbg(hdq_data->dev, "Could not acquire mutex\n"); + ret = -EINTR; + goto rtn; + } + + spin_lock_irqsave(&hdq_data->hdq_spinlock, irqflags); + /* clear interrupt flags via a dummy read */ + hdq_reg_in(hdq_data, OMAP_HDQ_INT_STATUS); + /* ISR loads it with new INT_STATUS */ + hdq_data->hdq_irqstatus = 0; + spin_unlock_irqrestore(&hdq_data->hdq_spinlock, irqflags); + + /* set the INIT and GO bit */ + hdq_reg_merge(hdq_data, OMAP_HDQ_CTRL_STATUS, + OMAP_HDQ_CTRL_STATUS_INITIALIZATION | OMAP_HDQ_CTRL_STATUS_GO, + OMAP_HDQ_CTRL_STATUS_DIR | OMAP_HDQ_CTRL_STATUS_INITIALIZATION | + OMAP_HDQ_CTRL_STATUS_GO); + + /* wait for the TIMEOUT bit */ + ret = wait_event_timeout(hdq_wait_queue, + hdq_data->hdq_irqstatus, OMAP_HDQ_TIMEOUT); + if (ret == 0) { + dev_dbg(hdq_data->dev, "break wait elapsed\n"); + ret = -EINTR; + goto out; + } + + tmp_status = hdq_data->hdq_irqstatus; + /* check irqstatus */ + if (!(tmp_status & OMAP_HDQ_INT_STATUS_TIMEOUT)) { + dev_dbg(hdq_data->dev, "timeout waiting for TIMEOUT, %x", + tmp_status); + ret = -ETIMEDOUT; + goto out; + } + /* + * wait for both INIT and GO bits rerurn to zero. + * zero wait time expected for interrupt mode. + */ + ret = hdq_wait_for_flag(hdq_data, OMAP_HDQ_CTRL_STATUS, + OMAP_HDQ_CTRL_STATUS_INITIALIZATION | + OMAP_HDQ_CTRL_STATUS_GO, OMAP_HDQ_FLAG_CLEAR, + &tmp_status); + if (ret) + dev_dbg(hdq_data->dev, "timeout waiting INIT&GO bits" + "return to zero, %x", tmp_status); + +out: + mutex_unlock(&hdq_data->hdq_mutex); +rtn: + return ret; +} + +static int hdq_read_byte(struct hdq_data *hdq_data, u8 *val) +{ + int ret = 0; + u8 status; + unsigned long timeout = jiffies + OMAP_HDQ_TIMEOUT; + + ret = mutex_lock_interruptible(&hdq_data->hdq_mutex); + if (ret < 0) { + ret = -EINTR; + goto rtn; + } + + if (!hdq_data->hdq_usecount) { + ret = -EINVAL; + goto out; + } + + if (!(hdq_data->hdq_irqstatus & OMAP_HDQ_INT_STATUS_RXCOMPLETE)) { + hdq_reg_merge(hdq_data, OMAP_HDQ_CTRL_STATUS, + OMAP_HDQ_CTRL_STATUS_DIR | OMAP_HDQ_CTRL_STATUS_GO, + OMAP_HDQ_CTRL_STATUS_DIR | OMAP_HDQ_CTRL_STATUS_GO); + /* + * The RX comes immediately after TX. It + * triggers another interrupt before we + * sleep. So we have to wait for RXCOMPLETE bit. + */ + while (!(hdq_data->hdq_irqstatus + & OMAP_HDQ_INT_STATUS_RXCOMPLETE) + && time_before(jiffies, timeout)) { + schedule_timeout_uninterruptible(1); + } + hdq_reg_merge(hdq_data, OMAP_HDQ_CTRL_STATUS, 0, + OMAP_HDQ_CTRL_STATUS_DIR); + status = hdq_data->hdq_irqstatus; + /* check irqstatus */ + if (!(status & OMAP_HDQ_INT_STATUS_RXCOMPLETE)) { + dev_dbg(hdq_data->dev, "timeout waiting for" + "RXCOMPLETE, %x", status); + ret = -ETIMEDOUT; + goto out; + } + } + /* the data is ready. Read it in! */ + *val = hdq_reg_in(hdq_data, OMAP_HDQ_RX_DATA); +out: + mutex_unlock(&hdq_data->hdq_mutex); +rtn: + return 0; + +} + +/* Enable clocks and set the controller to HDQ mode */ +static int omap_hdq_get(struct hdq_data *hdq_data) +{ + int ret = 0; + + ret = mutex_lock_interruptible(&hdq_data->hdq_mutex); + if (ret < 0) { + ret = -EINTR; + goto rtn; + } + + if (OMAP_HDQ_MAX_USER == hdq_data->hdq_usecount) { + dev_dbg(hdq_data->dev, "attempt to exceed the max use count"); + ret = -EINVAL; + goto out; + } else { + hdq_data->hdq_usecount++; + try_module_get(THIS_MODULE); + if (1 == hdq_data->hdq_usecount) { + if (clk_enable(hdq_data->hdq_ick)) { + dev_dbg(hdq_data->dev, "Can not enable ick\n"); + ret = -ENODEV; + goto clk_err; + } + if (clk_enable(hdq_data->hdq_fck)) { + dev_dbg(hdq_data->dev, "Can not enable fck\n"); + clk_disable(hdq_data->hdq_ick); + ret = -ENODEV; + goto clk_err; + } + + /* make sure HDQ is out of reset */ + if (!(hdq_reg_in(hdq_data, OMAP_HDQ_SYSSTATUS) & + OMAP_HDQ_SYSSTATUS_RESETDONE)) { + ret = _omap_hdq_reset(hdq_data); + if (ret) + /* back up the count */ + hdq_data->hdq_usecount--; + } else { + /* select HDQ mode & enable clocks */ + hdq_reg_out(hdq_data, OMAP_HDQ_CTRL_STATUS, + OMAP_HDQ_CTRL_STATUS_CLOCKENABLE | + OMAP_HDQ_CTRL_STATUS_INTERRUPTMASK); + hdq_reg_out(hdq_data, OMAP_HDQ_SYSCONFIG, + OMAP_HDQ_SYSCONFIG_AUTOIDLE); + hdq_reg_in(hdq_data, OMAP_HDQ_INT_STATUS); + } + } + } + +clk_err: + clk_put(hdq_data->hdq_ick); + clk_put(hdq_data->hdq_fck); +out: + mutex_unlock(&hdq_data->hdq_mutex); +rtn: + return ret; +} + +/* Disable clocks to the module */ +static int omap_hdq_put(struct hdq_data *hdq_data) +{ + int ret = 0; + + ret = mutex_lock_interruptible(&hdq_data->hdq_mutex); + if (ret < 0) + return -EINTR; + + if (0 == hdq_data->hdq_usecount) { + dev_dbg(hdq_data->dev, "attempt to decrement use count" + "when it is zero"); + ret = -EINVAL; + } else { + hdq_data->hdq_usecount--; + module_put(THIS_MODULE); + if (0 == hdq_data->hdq_usecount) { + clk_disable(hdq_data->hdq_ick); + clk_disable(hdq_data->hdq_fck); + } + } + mutex_unlock(&hdq_data->hdq_mutex); + + return ret; +} + +/* Read a byte of data from the device */ +static u8 omap_w1_read_byte(void *_hdq) +{ + struct hdq_data *hdq_data = _hdq; + u8 val = 0; + int ret; + + ret = hdq_read_byte(hdq_data, &val); + if (ret) { + ret = mutex_lock_interruptible(&hdq_data->hdq_mutex); + if (ret < 0) { + dev_dbg(hdq_data->dev, "Could not acquire mutex\n"); + return -EINTR; + } + hdq_data->init_trans = 0; + mutex_unlock(&hdq_data->hdq_mutex); + omap_hdq_put(hdq_data); + return -1; + } + + /* Write followed by a read, release the module */ + if (hdq_data->init_trans) { + ret = mutex_lock_interruptible(&hdq_data->hdq_mutex); + if (ret < 0) { + dev_dbg(hdq_data->dev, "Could not acquire mutex\n"); + return -EINTR; + } + hdq_data->init_trans = 0; + mutex_unlock(&hdq_data->hdq_mutex); + omap_hdq_put(hdq_data); + } + + return val; +} + +/* Write a byte of data to the device */ +static void omap_w1_write_byte(void *_hdq, u8 byte) +{ + struct hdq_data *hdq_data = _hdq; + int ret; + u8 status; + + /* First write to initialize the transfer */ + if (hdq_data->init_trans == 0) + omap_hdq_get(hdq_data); + + ret = mutex_lock_interruptible(&hdq_data->hdq_mutex); + if (ret < 0) { + dev_dbg(hdq_data->dev, "Could not acquire mutex\n"); + return; + } + hdq_data->init_trans++; + mutex_unlock(&hdq_data->hdq_mutex); + + ret = hdq_write_byte(hdq_data, byte, &status); + if (ret == 0) { + dev_dbg(hdq_data->dev, "TX failure:Ctrl status %x\n", status); + return; + } + + /* Second write, data transfered. Release the module */ + if (hdq_data->init_trans > 1) { + omap_hdq_put(hdq_data); + ret = mutex_lock_interruptible(&hdq_data->hdq_mutex); + if (ret < 0) { + dev_dbg(hdq_data->dev, "Could not acquire mutex\n"); + return; + } + hdq_data->init_trans = 0; + mutex_unlock(&hdq_data->hdq_mutex); + } + + return; +} + +static int __init omap_hdq_probe(struct platform_device *pdev) +{ + struct hdq_data *hdq_data; + struct resource *res; + int ret, irq; + u8 rev; + + hdq_data = kmalloc(sizeof(*hdq_data), GFP_KERNEL); + if (!hdq_data) { + dev_dbg(&pdev->dev, "unable to allocate memory\n"); + ret = -ENOMEM; + goto err_kmalloc; + } + + hdq_data->dev = &pdev->dev; + platform_set_drvdata(pdev, hdq_data); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_dbg(&pdev->dev, "unable to get resource\n"); + ret = -ENXIO; + goto err_resource; + } + + hdq_data->hdq_base = ioremap(res->start, SZ_4K); + if (!hdq_data->hdq_base) { + dev_dbg(&pdev->dev, "ioremap failed\n"); + ret = -EINVAL; + goto err_ioremap; + } + + /* get interface & functional clock objects */ + hdq_data->hdq_ick = clk_get(&pdev->dev, "hdq_ick"); + hdq_data->hdq_fck = clk_get(&pdev->dev, "hdq_fck"); + + if (IS_ERR(hdq_data->hdq_ick) || IS_ERR(hdq_data->hdq_fck)) { + dev_dbg(&pdev->dev, "Can't get HDQ clock objects\n"); + if (IS_ERR(hdq_data->hdq_ick)) { + ret = PTR_ERR(hdq_data->hdq_ick); + goto err_clk; + } + if (IS_ERR(hdq_data->hdq_fck)) { + ret = PTR_ERR(hdq_data->hdq_fck); + clk_put(hdq_data->hdq_ick); + goto err_clk; + } + } + + hdq_data->hdq_usecount = 0; + mutex_init(&hdq_data->hdq_mutex); + + if (clk_enable(hdq_data->hdq_ick)) { + dev_dbg(&pdev->dev, "Can not enable ick\n"); + ret = -ENODEV; + goto err_intfclk; + } + + if (clk_enable(hdq_data->hdq_fck)) { + dev_dbg(&pdev->dev, "Can not enable fck\n"); + ret = -ENODEV; + goto err_fnclk; + } + + rev = hdq_reg_in(hdq_data, OMAP_HDQ_REVISION); + dev_info(&pdev->dev, "OMAP HDQ Hardware Rev %c.%c. Driver in %s mode\n", + (rev >> 4) + '0', (rev & 0x0f) + '0', "Interrupt"); + + spin_lock_init(&hdq_data->hdq_spinlock); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) { + ret = -ENXIO; + goto err_irq; + } + + ret = request_irq(irq, hdq_isr, IRQF_DISABLED, "omap_hdq", hdq_data); + if (ret < 0) { + dev_dbg(&pdev->dev, "could not request irq\n"); + goto err_irq; + } + + omap_hdq_break(hdq_data); + + /* don't clock the HDQ until it is needed */ + clk_disable(hdq_data->hdq_ick); + clk_disable(hdq_data->hdq_fck); + + omap_w1_master.data = hdq_data; + + ret = w1_add_master_device(&omap_w1_master); + if (ret) { + dev_dbg(&pdev->dev, "Failure in registering w1 master\n"); + goto err_w1; + } + + return 0; + +err_w1: +err_irq: + clk_disable(hdq_data->hdq_fck); + +err_fnclk: + clk_disable(hdq_data->hdq_ick); + +err_intfclk: + clk_put(hdq_data->hdq_ick); + clk_put(hdq_data->hdq_fck); + +err_clk: + iounmap(hdq_data->hdq_base); + +err_ioremap: +err_resource: + platform_set_drvdata(pdev, NULL); + kfree(hdq_data); + +err_kmalloc: + return ret; + +} + +static int omap_hdq_remove(struct platform_device *pdev) +{ + struct hdq_data *hdq_data = platform_get_drvdata(pdev); + + mutex_lock(&hdq_data->hdq_mutex); + + if (hdq_data->hdq_usecount) { + dev_dbg(&pdev->dev, "removed when use count is not zero\n"); + return -EBUSY; + } + + mutex_unlock(&hdq_data->hdq_mutex); + + /* remove module dependency */ + clk_put(hdq_data->hdq_ick); + clk_put(hdq_data->hdq_fck); + free_irq(INT_24XX_HDQ_IRQ, hdq_data); + platform_set_drvdata(pdev, NULL); + iounmap(hdq_data->hdq_base); + kfree(hdq_data); + + return 0; +} + +static int __init +omap_hdq_init(void) +{ + return platform_driver_register(&omap_hdq_driver); +} +module_init(omap_hdq_init); + +static void __exit +omap_hdq_exit(void) +{ + platform_driver_unregister(&omap_hdq_driver); +} +module_exit(omap_hdq_exit); + +module_param(w1_id, int, S_IRUSR); +MODULE_PARM_DESC(w1_id, "1-wire id for the slave detection"); + +MODULE_AUTHOR("Texas Instruments"); +MODULE_DESCRIPTION("HDQ driver Library"); +MODULE_LICENSE("GPL"); diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig index 3df29a122f8..8d0b1fb1e52 100644 --- a/drivers/w1/slaves/Kconfig +++ b/drivers/w1/slaves/Kconfig @@ -44,4 +44,11 @@ config W1_SLAVE_DS2760 If you are unsure, say N. +config W1_SLAVE_BQ27000 + tristate "BQ27000 slave support" + depends on W1 + help + Say Y here if you want to use a hdq + bq27000 slave support. + endmenu diff --git a/drivers/w1/slaves/Makefile b/drivers/w1/slaves/Makefile index a8eb7524df1..990f400b6d2 100644 --- a/drivers/w1/slaves/Makefile +++ b/drivers/w1/slaves/Makefile @@ -6,4 +6,4 @@ obj-$(CONFIG_W1_SLAVE_THERM) += w1_therm.o obj-$(CONFIG_W1_SLAVE_SMEM) += w1_smem.o obj-$(CONFIG_W1_SLAVE_DS2433) += w1_ds2433.o obj-$(CONFIG_W1_SLAVE_DS2760) += w1_ds2760.o - +obj-$(CONFIG_W1_SLAVE_BQ27000) += w1_bq27000.o diff --git a/drivers/w1/slaves/w1_bq27000.c b/drivers/w1/slaves/w1_bq27000.c new file mode 100644 index 00000000000..8f4c91f6c68 --- /dev/null +++ b/drivers/w1/slaves/w1_bq27000.c @@ -0,0 +1,123 @@ +/* + * drivers/w1/slaves/w1_bq27000.c + * + * Copyright (C) 2007 Texas Instruments, Inc. + * + * This file is licensed under the terms of the GNU General Public License + * version 2. This program is licensed "as is" without any warranty of any + * kind, whether express or implied. + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/device.h> +#include <linux/types.h> +#include <linux/platform_device.h> +#include <linux/mutex.h> + +#include "../w1.h" +#include "../w1_int.h" +#include "../w1_family.h" + +#define HDQ_CMD_READ (0) +#define HDQ_CMD_WRITE (1<<7) + +static int F_ID; + +void w1_bq27000_write(struct device *dev, u8 buf, u8 reg) +{ + struct w1_slave *sl = container_of(dev, struct w1_slave, dev); + + if (!dev) { + pr_info("Could not obtain slave dev ptr\n"); + return; + } + + w1_write_8(sl->master, HDQ_CMD_WRITE | reg); + w1_write_8(sl->master, buf); +} +EXPORT_SYMBOL(w1_bq27000_write); + +int w1_bq27000_read(struct device *dev, u8 reg) +{ + u8 val; + struct w1_slave *sl = container_of(dev, struct w1_slave, dev); + + if (!dev) + return 0; + + w1_write_8(sl->master, HDQ_CMD_READ | reg); + val = w1_read_8(sl->master); + + return val; +} +EXPORT_SYMBOL(w1_bq27000_read); + +static int w1_bq27000_add_slave(struct w1_slave *sl) +{ + int ret; + int id = 1; + struct platform_device *pdev; + + pdev = platform_device_alloc("bq27000-battery", id); + if (!pdev) { + ret = -ENOMEM; + return ret; + } + pdev->dev.parent = &sl->dev; + + ret = platform_device_add(pdev); + if (ret) + goto pdev_add_failed; + + dev_set_drvdata(&sl->dev, pdev); + + goto success; + +pdev_add_failed: + platform_device_unregister(pdev); +success: + return ret; +} + +static void w1_bq27000_remove_slave(struct w1_slave *sl) +{ + struct platform_device *pdev = dev_get_drvdata(&sl->dev); + + platform_device_unregister(pdev); +} + +static struct w1_family_ops w1_bq27000_fops = { + .add_slave = w1_bq27000_add_slave, + .remove_slave = w1_bq27000_remove_slave, +}; + +static struct w1_family w1_bq27000_family = { + .fid = 1, + .fops = &w1_bq27000_fops, +}; + +static int __init w1_bq27000_init(void) +{ + if (F_ID) + w1_bq27000_family.fid = F_ID; + + return w1_register_family(&w1_bq27000_family); +} + +static void __exit w1_bq27000_exit(void) +{ + w1_unregister_family(&w1_bq27000_family); +} + + +module_init(w1_bq27000_init); +module_exit(w1_bq27000_exit); + +module_param(F_ID, int, S_IRUSR); +MODULE_PARM_DESC(F_ID, "1-wire slave FID for BQ device"); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Texas Instruments Ltd"); +MODULE_DESCRIPTION("HDQ/1-wire slave driver bq27000 battery monitor chip"); diff --git a/drivers/w1/w1.h b/drivers/w1/w1.h index cdaa6fffbfc..97304bd83ec 100644 --- a/drivers/w1/w1.h +++ b/drivers/w1/w1.h @@ -206,6 +206,7 @@ void w1_slave_detach(struct w1_slave *sl); u8 w1_triplet(struct w1_master *dev, int bdir); void w1_write_8(struct w1_master *, u8); +u8 w1_read_8(struct w1_master *); int w1_reset_bus(struct w1_master *); u8 w1_calc_crc8(u8 *, int); void w1_write_block(struct w1_master *, const u8 *, int); diff --git a/drivers/w1/w1_io.c b/drivers/w1/w1_io.c index f4f82f1f486..0d15b0eaf79 100644 --- a/drivers/w1/w1_io.c +++ b/drivers/w1/w1_io.c @@ -217,7 +217,7 @@ u8 w1_triplet(struct w1_master *dev, int bdir) * @param dev the master device * @return the byte read */ -static u8 w1_read_8(struct w1_master * dev) +u8 w1_read_8(struct w1_master *dev) { int i; u8 res = 0; @@ -230,6 +230,7 @@ static u8 w1_read_8(struct w1_master * dev) return res; } +EXPORT_SYMBOL_GPL(w1_read_8); /** * Writes a series of bytes. diff --git a/drivers/xen/balloon.c b/drivers/xen/balloon.c index a0fb5eac407..526c191e84e 100644 --- a/drivers/xen/balloon.c +++ b/drivers/xen/balloon.c @@ -122,14 +122,7 @@ static struct timer_list balloon_timer; static void scrub_page(struct page *page) { #ifdef CONFIG_XEN_SCRUB_PAGES - if (PageHighMem(page)) { - void *v = kmap(page); - clear_page(v); - kunmap(v); - } else { - void *v = page_address(page); - clear_page(v); - } + clear_highpage(page); #endif } diff --git a/fs/dlm/lockspace.c b/fs/dlm/lockspace.c index d910501de6d..8d86b7960f0 100644 --- a/fs/dlm/lockspace.c +++ b/fs/dlm/lockspace.c @@ -812,7 +812,7 @@ int dlm_release_lockspace(void *lockspace, int force) error = release_lockspace(ls, force); if (!error) ls_count--; - else if (!ls_count) + if (!ls_count) threads_stop(); mutex_unlock(&ls_lock); diff --git a/fs/ext3/super.c b/fs/ext3/super.c index 5dec6d1356c..f6c94f232ec 100644 --- a/fs/ext3/super.c +++ b/fs/ext3/super.c @@ -2375,12 +2375,9 @@ int ext3_force_commit(struct super_block *sb) /* * Ext3 always journals updates to the superblock itself, so we don't * have to propagate any other updates to the superblock on disk at this - * point. Just start an async writeback to get the buffers on their way - * to the disk. - * - * This implicitly triggers the writebehind on sync(). + * point. (We can probably nuke this function altogether, and remove + * any mention to sb->s_dirt in all of fs/ext3; eventual cleanup...) */ - static void ext3_write_super (struct super_block * sb) { if (mutex_trylock(&sb->s_lock) != 0) diff --git a/fs/inotify.c b/fs/inotify.c index 690e72595e6..7bbed1b8982 100644 --- a/fs/inotify.c +++ b/fs/inotify.c @@ -106,6 +106,20 @@ void get_inotify_watch(struct inotify_watch *watch) } EXPORT_SYMBOL_GPL(get_inotify_watch); +int pin_inotify_watch(struct inotify_watch *watch) +{ + struct super_block *sb = watch->inode->i_sb; + spin_lock(&sb_lock); + if (sb->s_count >= S_BIAS) { + atomic_inc(&sb->s_active); + spin_unlock(&sb_lock); + atomic_inc(&watch->count); + return 1; + } + spin_unlock(&sb_lock); + return 0; +} + /** * put_inotify_watch - decrements the ref count on a given watch. cleans up * watch references if the count reaches zero. inotify_watch is freed by @@ -124,6 +138,13 @@ void put_inotify_watch(struct inotify_watch *watch) } EXPORT_SYMBOL_GPL(put_inotify_watch); +void unpin_inotify_watch(struct inotify_watch *watch) +{ + struct super_block *sb = watch->inode->i_sb; + put_inotify_watch(watch); + deactivate_super(sb); +} + /* * inotify_handle_get_wd - returns the next WD for use by the given handle * @@ -479,6 +500,112 @@ void inotify_init_watch(struct inotify_watch *watch) } EXPORT_SYMBOL_GPL(inotify_init_watch); +/* + * Watch removals suck violently. To kick the watch out we need (in this + * order) inode->inotify_mutex and ih->mutex. That's fine if we have + * a hold on inode; however, for all other cases we need to make damn sure + * we don't race with umount. We can *NOT* just grab a reference to a + * watch - inotify_unmount_inodes() will happily sail past it and we'll end + * with reference to inode potentially outliving its superblock. Ideally + * we just want to grab an active reference to superblock if we can; that + * will make sure we won't go into inotify_umount_inodes() until we are + * done. Cleanup is just deactivate_super(). However, that leaves a messy + * case - what if we *are* racing with umount() and active references to + * superblock can't be acquired anymore? We can bump ->s_count, grab + * ->s_umount, which will almost certainly wait until the superblock is shut + * down and the watch in question is pining for fjords. That's fine, but + * there is a problem - we might have hit the window between ->s_active + * getting to 0 / ->s_count - below S_BIAS (i.e. the moment when superblock + * is past the point of no return and is heading for shutdown) and the + * moment when deactivate_super() acquires ->s_umount. We could just do + * drop_super() yield() and retry, but that's rather antisocial and this + * stuff is luser-triggerable. OTOH, having grabbed ->s_umount and having + * found that we'd got there first (i.e. that ->s_root is non-NULL) we know + * that we won't race with inotify_umount_inodes(). So we could grab a + * reference to watch and do the rest as above, just with drop_super() instead + * of deactivate_super(), right? Wrong. We had to drop ih->mutex before we + * could grab ->s_umount. So the watch could've been gone already. + * + * That still can be dealt with - we need to save watch->wd, do idr_find() + * and compare its result with our pointer. If they match, we either have + * the damn thing still alive or we'd lost not one but two races at once, + * the watch had been killed and a new one got created with the same ->wd + * at the same address. That couldn't have happened in inotify_destroy(), + * but inotify_rm_wd() could run into that. Still, "new one got created" + * is not a problem - we have every right to kill it or leave it alone, + * whatever's more convenient. + * + * So we can use idr_find(...) == watch && watch->inode->i_sb == sb as + * "grab it and kill it" check. If it's been our original watch, we are + * fine, if it's a newcomer - nevermind, just pretend that we'd won the + * race and kill the fscker anyway; we are safe since we know that its + * superblock won't be going away. + * + * And yes, this is far beyond mere "not very pretty"; so's the entire + * concept of inotify to start with. + */ + +/** + * pin_to_kill - pin the watch down for removal + * @ih: inotify handle + * @watch: watch to kill + * + * Called with ih->mutex held, drops it. Possible return values: + * 0 - nothing to do, it has died + * 1 - remove it, drop the reference and deactivate_super() + * 2 - remove it, drop the reference and drop_super(); we tried hard to avoid + * that variant, since it involved a lot of PITA, but that's the best that + * could've been done. + */ +static int pin_to_kill(struct inotify_handle *ih, struct inotify_watch *watch) +{ + struct super_block *sb = watch->inode->i_sb; + s32 wd = watch->wd; + + spin_lock(&sb_lock); + if (sb->s_count >= S_BIAS) { + atomic_inc(&sb->s_active); + spin_unlock(&sb_lock); + get_inotify_watch(watch); + mutex_unlock(&ih->mutex); + return 1; /* the best outcome */ + } + sb->s_count++; + spin_unlock(&sb_lock); + mutex_unlock(&ih->mutex); /* can't grab ->s_umount under it */ + down_read(&sb->s_umount); + if (likely(!sb->s_root)) { + /* fs is already shut down; the watch is dead */ + drop_super(sb); + return 0; + } + /* raced with the final deactivate_super() */ + mutex_lock(&ih->mutex); + if (idr_find(&ih->idr, wd) != watch || watch->inode->i_sb != sb) { + /* the watch is dead */ + mutex_unlock(&ih->mutex); + drop_super(sb); + return 0; + } + /* still alive or freed and reused with the same sb and wd; kill */ + get_inotify_watch(watch); + mutex_unlock(&ih->mutex); + return 2; +} + +static void unpin_and_kill(struct inotify_watch *watch, int how) +{ + struct super_block *sb = watch->inode->i_sb; + put_inotify_watch(watch); + switch (how) { + case 1: + deactivate_super(sb); + break; + case 2: + drop_super(sb); + } +} + /** * inotify_destroy - clean up and destroy an inotify instance * @ih: inotify handle @@ -490,11 +617,15 @@ void inotify_destroy(struct inotify_handle *ih) * pretty. We cannot do a simple iteration over the list, because we * do not know the inode until we iterate to the watch. But we need to * hold inode->inotify_mutex before ih->mutex. The following works. + * + * AV: it had to become even uglier to start working ;-/ */ while (1) { struct inotify_watch *watch; struct list_head *watches; + struct super_block *sb; struct inode *inode; + int how; mutex_lock(&ih->mutex); watches = &ih->watches; @@ -503,8 +634,10 @@ void inotify_destroy(struct inotify_handle *ih) break; } watch = list_first_entry(watches, struct inotify_watch, h_list); - get_inotify_watch(watch); - mutex_unlock(&ih->mutex); + sb = watch->inode->i_sb; + how = pin_to_kill(ih, watch); + if (!how) + continue; inode = watch->inode; mutex_lock(&inode->inotify_mutex); @@ -518,7 +651,7 @@ void inotify_destroy(struct inotify_handle *ih) mutex_unlock(&ih->mutex); mutex_unlock(&inode->inotify_mutex); - put_inotify_watch(watch); + unpin_and_kill(watch, how); } /* free this handle: the put matching the get in inotify_init() */ @@ -719,7 +852,9 @@ void inotify_evict_watch(struct inotify_watch *watch) int inotify_rm_wd(struct inotify_handle *ih, u32 wd) { struct inotify_watch *watch; + struct super_block *sb; struct inode *inode; + int how; mutex_lock(&ih->mutex); watch = idr_find(&ih->idr, wd); @@ -727,9 +862,12 @@ int inotify_rm_wd(struct inotify_handle *ih, u32 wd) mutex_unlock(&ih->mutex); return -EINVAL; } - get_inotify_watch(watch); + sb = watch->inode->i_sb; + how = pin_to_kill(ih, watch); + if (!how) + return 0; + inode = watch->inode; - mutex_unlock(&ih->mutex); mutex_lock(&inode->inotify_mutex); mutex_lock(&ih->mutex); @@ -740,7 +878,7 @@ int inotify_rm_wd(struct inotify_handle *ih, u32 wd) mutex_unlock(&ih->mutex); mutex_unlock(&inode->inotify_mutex); - put_inotify_watch(watch); + unpin_and_kill(watch, how); return 0; } diff --git a/fs/namespace.c b/fs/namespace.c index cce46702d33..65b3dc844c8 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1815,8 +1815,8 @@ static void shrink_submounts(struct vfsmount *mnt, struct list_head *umounts) while (!list_empty(&graveyard)) { m = list_first_entry(&graveyard, struct vfsmount, mnt_expire); - touch_mnt_namespace(mnt->mnt_ns); - umount_tree(mnt, 1, umounts); + touch_mnt_namespace(m->mnt_ns); + umount_tree(m, 1, umounts); } } } diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 54a279e44c9..e9f6574930e 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -380,6 +380,8 @@ struct acpi_bus_type { int register_acpi_bus_type(struct acpi_bus_type *); int unregister_acpi_bus_type(struct acpi_bus_type *); struct device *acpi_get_physical_device(acpi_handle); +struct device *acpi_get_physical_pci_device(acpi_handle); + /* helper */ acpi_handle acpi_get_child(acpi_handle, acpi_integer); acpi_handle acpi_get_pci_rootbridge_handle(unsigned int, unsigned int); diff --git a/include/acpi/acpi_drivers.h b/include/acpi/acpi_drivers.h index cf04c6011c2..5fc1bb0f4a9 100644 --- a/include/acpi/acpi_drivers.h +++ b/include/acpi/acpi_drivers.h @@ -31,8 +31,24 @@ #define ACPI_MAX_STRING 80 +/* + * Please update drivers/acpi/debug.c and Documentation/acpi/debug.txt + * if you add to this list. + */ #define ACPI_BUS_COMPONENT 0x00010000 +#define ACPI_AC_COMPONENT 0x00020000 +#define ACPI_BATTERY_COMPONENT 0x00040000 +#define ACPI_BUTTON_COMPONENT 0x00080000 +#define ACPI_SBS_COMPONENT 0x00100000 +#define ACPI_FAN_COMPONENT 0x00200000 +#define ACPI_PCI_COMPONENT 0x00400000 +#define ACPI_POWER_COMPONENT 0x00800000 +#define ACPI_CONTAINER_COMPONENT 0x01000000 #define ACPI_SYSTEM_COMPONENT 0x02000000 +#define ACPI_THERMAL_COMPONENT 0x04000000 +#define ACPI_MEMORY_DEVICE_COMPONENT 0x08000000 +#define ACPI_VIDEO_COMPONENT 0x10000000 +#define ACPI_PROCESSOR_COMPONENT 0x20000000 /* * _HID definitions @@ -41,6 +57,7 @@ */ #define ACPI_POWER_HID "LNXPOWER" +#define ACPI_PROCESSOR_OBJECT_HID "ACPI_CPU" #define ACPI_PROCESSOR_HID "ACPI0007" #define ACPI_SYSTEM_HID "LNXSYSTM" #define ACPI_THERMAL_HID "LNXTHERM" @@ -54,7 +71,6 @@ PCI -------------------------------------------------------------------------- */ -#define ACPI_PCI_COMPONENT 0x00400000 /* ACPI PCI Interrupt Link (pci_link.c) */ @@ -86,7 +102,6 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_device *device, int domain, Power Resource -------------------------------------------------------------------------- */ -#ifdef CONFIG_ACPI_POWER int acpi_device_sleep_wake(struct acpi_device *dev, int enable, int sleep_state, int dev_state); int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state); @@ -94,15 +109,12 @@ int acpi_disable_wakeup_device_power(struct acpi_device *dev); int acpi_power_get_inferred_state(struct acpi_device *device); int acpi_power_transition(struct acpi_device *device, int state); extern int acpi_power_nocheck; -#endif /* -------------------------------------------------------------------------- Embedded Controller -------------------------------------------------------------------------- */ -#ifdef CONFIG_ACPI_EC int acpi_ec_ecdt_probe(void); int acpi_boot_ec_enable(void); -#endif /* -------------------------------------------------------------------------- Processor diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 94d94e126e9..33bc0e3b195 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -252,9 +252,9 @@ acpi_status acpi_get_event_status(u32 event, acpi_event_status * event_status); acpi_status acpi_set_gpe_type(acpi_handle gpe_device, u32 gpe_number, u8 type); -acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number, u32 flags); +acpi_status acpi_enable_gpe(acpi_handle gpe_device, u32 gpe_number); -acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number, u32 flags); +acpi_status acpi_disable_gpe(acpi_handle gpe_device, u32 gpe_number); acpi_status acpi_clear_gpe(acpi_handle gpe_device, u32 gpe_number, u32 flags); diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h index e8936ab5962..7220361790b 100644 --- a/include/acpi/actypes.h +++ b/include/acpi/actypes.h @@ -525,6 +525,7 @@ typedef u32 acpi_event_status; #define ACPI_EVENT_FLAG_ENABLED (acpi_event_status) 0x01 #define ACPI_EVENT_FLAG_WAKE_ENABLED (acpi_event_status) 0x02 #define ACPI_EVENT_FLAG_SET (acpi_event_status) 0x04 +#define ACPI_EVENT_FLAG_HANDLE (acpi_event_status) 0x08 /* * General Purpose Events (GPE) diff --git a/include/linux/acpi.h b/include/linux/acpi.h index fd6a452b0ce..fba8051fb29 100644 --- a/include/linux/acpi.h +++ b/include/linux/acpi.h @@ -163,8 +163,6 @@ struct acpi_pci_driver { int acpi_pci_register_driver(struct acpi_pci_driver *driver); void acpi_pci_unregister_driver(struct acpi_pci_driver *driver); -#ifdef CONFIG_ACPI_EC - extern int ec_read(u8 addr, u8 *val); extern int ec_write(u8 addr, u8 val); extern int ec_transaction(u8 command, @@ -172,8 +170,6 @@ extern int ec_transaction(u8 command, u8 *rdata, unsigned rdata_len, int force_poll); -#endif /*CONFIG_ACPI_EC*/ - #if defined(CONFIG_ACPI_WMI) || defined(CONFIG_ACPI_WMI_MODULE) typedef void (*wmi_notify_handler) (u32 value, void *context); @@ -194,6 +190,50 @@ extern bool wmi_has_guid(const char *guid); #endif /* CONFIG_ACPI_WMI */ +#define ACPI_VIDEO_OUTPUT_SWITCHING 0x0001 +#define ACPI_VIDEO_DEVICE_POSTING 0x0002 +#define ACPI_VIDEO_ROM_AVAILABLE 0x0004 +#define ACPI_VIDEO_BACKLIGHT 0x0008 +#define ACPI_VIDEO_BACKLIGHT_FORCE_VENDOR 0x0010 +#define ACPI_VIDEO_BACKLIGHT_FORCE_VIDEO 0x0020 +#define ACPI_VIDEO_OUTPUT_SWITCHING_FORCE_VENDOR 0x0040 +#define ACPI_VIDEO_OUTPUT_SWITCHING_FORCE_VIDEO 0x0080 +#define ACPI_VIDEO_BACKLIGHT_DMI_VENDOR 0x0100 +#define ACPI_VIDEO_BACKLIGHT_DMI_VIDEO 0x0200 +#define ACPI_VIDEO_OUTPUT_SWITCHING_DMI_VENDOR 0x0400 +#define ACPI_VIDEO_OUTPUT_SWITCHING_DMI_VIDEO 0x0800 + +#if defined(CONFIG_ACPI_VIDEO) || defined(CONFIG_ACPI_VIDEO_MODULE) + +extern long acpi_video_get_capabilities(acpi_handle graphics_dev_handle); +extern long acpi_is_video_device(struct acpi_device *device); +extern int acpi_video_backlight_support(void); +extern int acpi_video_display_switch_support(void); + +#else + +static inline long acpi_video_get_capabilities(acpi_handle graphics_dev_handle) +{ + return 0; +} + +static inline long acpi_is_video_device(struct acpi_device *device) +{ + return 0; +} + +static inline int acpi_video_backlight_support(void) +{ + return 0; +} + +static inline int acpi_video_display_switch_support(void) +{ + return 0; +} + +#endif /* defined(CONFIG_ACPI_VIDEO) || defined(CONFIG_ACPI_VIDEO_MODULE) */ + extern int acpi_blacklisted(void); #ifdef CONFIG_DMI extern void acpi_dmi_osi_linux(int enable, const struct dmi_system_id *d); diff --git a/include/linux/c2port.h b/include/linux/c2port.h new file mode 100644 index 00000000000..7b5a2388ba6 --- /dev/null +++ b/include/linux/c2port.h @@ -0,0 +1,65 @@ +/* + * Silicon Labs C2 port Linux support + * + * Copyright (c) 2007 Rodolfo Giometti <giometti@linux.it> + * Copyright (c) 2007 Eurotech S.p.A. <info@eurotech.it> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 as published by + * the Free Software Foundation + */ + +#include <linux/device.h> + +#define C2PORT_NAME_LEN 32 + +/* + * C2 port basic structs + */ + +/* Main struct */ +struct c2port_ops; +struct c2port_device { + unsigned int access:1; + unsigned int flash_access:1; + + int id; + char name[C2PORT_NAME_LEN]; + struct c2port_ops *ops; + struct mutex mutex; /* prevent races during read/write */ + + struct device *dev; + + void *private_data; +}; + +/* Basic operations */ +struct c2port_ops { + /* Flash layout */ + unsigned short block_size; /* flash block size in bytes */ + unsigned short blocks_num; /* flash blocks number */ + + /* Enable or disable the access to C2 port */ + void (*access)(struct c2port_device *dev, int status); + + /* Set C2D data line as input/output */ + void (*c2d_dir)(struct c2port_device *dev, int dir); + + /* Read/write C2D data line */ + int (*c2d_get)(struct c2port_device *dev); + void (*c2d_set)(struct c2port_device *dev, int status); + + /* Write C2CK clock line */ + void (*c2ck_set)(struct c2port_device *dev, int status); +}; + +/* + * Exported functions + */ + +#define to_class_dev(obj) container_of((obj), struct class_device, kobj) +#define to_c2port_device(obj) container_of((obj), struct c2port_device, class) + +extern struct c2port_device *c2port_device_register(char *name, + struct c2port_ops *ops, void *devdata); +extern void c2port_device_unregister(struct c2port_device *dev); diff --git a/include/linux/hrtimer.h b/include/linux/hrtimer.h index 07e510a3b00..3eba43878dc 100644 --- a/include/linux/hrtimer.h +++ b/include/linux/hrtimer.h @@ -46,9 +46,6 @@ enum hrtimer_restart { * hrtimer callback modes: * * HRTIMER_CB_SOFTIRQ: Callback must run in softirq context - * HRTIMER_CB_IRQSAFE: Callback may run in hardirq context - * HRTIMER_CB_IRQSAFE_NO_RESTART: Callback may run in hardirq context and - * does not restart the timer * HRTIMER_CB_IRQSAFE_PERCPU: Callback must run in hardirq context * Special mode for tick emulation and * scheduler timer. Such timers are per @@ -61,8 +58,6 @@ enum hrtimer_restart { */ enum hrtimer_cb_mode { HRTIMER_CB_SOFTIRQ, - HRTIMER_CB_IRQSAFE, - HRTIMER_CB_IRQSAFE_NO_RESTART, HRTIMER_CB_IRQSAFE_PERCPU, HRTIMER_CB_IRQSAFE_UNLOCKED, }; diff --git a/include/linux/inotify.h b/include/linux/inotify.h index bd578578a8b..37ea2894b3c 100644 --- a/include/linux/inotify.h +++ b/include/linux/inotify.h @@ -134,6 +134,8 @@ extern void inotify_remove_watch_locked(struct inotify_handle *, struct inotify_watch *); extern void get_inotify_watch(struct inotify_watch *); extern void put_inotify_watch(struct inotify_watch *); +extern int pin_inotify_watch(struct inotify_watch *); +extern void unpin_inotify_watch(struct inotify_watch *); #else @@ -228,6 +230,15 @@ static inline void put_inotify_watch(struct inotify_watch *watch) { } +extern inline int pin_inotify_watch(struct inotify_watch *watch) +{ + return 0; +} + +extern inline void unpin_inotify_watch(struct inotify_watch *watch) +{ +} + #endif /* CONFIG_INOTIFY */ #endif /* __KERNEL __ */ diff --git a/include/linux/kernel.h b/include/linux/kernel.h index fba141d3ca0..dc7e0d0a647 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -318,32 +318,36 @@ static inline char *pack_hex_byte(char *buf, u8 byte) return buf; } -#define pr_emerg(fmt, arg...) \ - printk(KERN_EMERG fmt, ##arg) -#define pr_alert(fmt, arg...) \ - printk(KERN_ALERT fmt, ##arg) -#define pr_crit(fmt, arg...) \ - printk(KERN_CRIT fmt, ##arg) -#define pr_err(fmt, arg...) \ - printk(KERN_ERR fmt, ##arg) -#define pr_warning(fmt, arg...) \ - printk(KERN_WARNING fmt, ##arg) -#define pr_notice(fmt, arg...) \ - printk(KERN_NOTICE fmt, ##arg) -#define pr_info(fmt, arg...) \ - printk(KERN_INFO fmt, ##arg) +#ifndef pr_fmt +#define pr_fmt(fmt) fmt +#endif + +#define pr_emerg(fmt, ...) \ + printk(KERN_EMERG pr_fmt(fmt), ##__VA_ARGS__) +#define pr_alert(fmt, ...) \ + printk(KERN_ALERT pr_fmt(fmt), ##__VA_ARGS__) +#define pr_crit(fmt, ...) \ + printk(KERN_CRIT pr_fmt(fmt), ##__VA_ARGS__) +#define pr_err(fmt, ...) \ + printk(KERN_ERR pr_fmt(fmt), ##__VA_ARGS__) +#define pr_warning(fmt, ...) \ + printk(KERN_WARNING pr_fmt(fmt), ##__VA_ARGS__) +#define pr_notice(fmt, ...) \ + printk(KERN_NOTICE pr_fmt(fmt), ##__VA_ARGS__) +#define pr_info(fmt, ...) \ + printk(KERN_INFO pr_fmt(fmt), ##__VA_ARGS__) /* If you are writing a driver, please use dev_dbg instead */ #if defined(CONFIG_DYNAMIC_PRINTK_DEBUG) #define pr_debug(fmt, ...) do { \ - dynamic_pr_debug(fmt, ##__VA_ARGS__); \ + dynamic_pr_debug(pr_fmt(fmt), ##__VA_ARGS__); \ } while (0) #elif defined(DEBUG) -#define pr_debug(fmt, arg...) \ - printk(KERN_DEBUG fmt, ##arg) +#define pr_debug(fmt, ...) \ + printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__) #else -#define pr_debug(fmt, arg...) \ - ({ if (0) printk(KERN_DEBUG fmt, ##arg); 0; }) +#define pr_debug(fmt, ...) \ + ({ if (0) printk(KERN_DEBUG pr_fmt(fmt), ##__VA_ARGS__); 0; }) #endif /* diff --git a/include/linux/mfd/wm8350/rtc.h b/include/linux/mfd/wm8350/rtc.h index dfda69e9f44..24add2bef6c 100644 --- a/include/linux/mfd/wm8350/rtc.h +++ b/include/linux/mfd/wm8350/rtc.h @@ -261,6 +261,8 @@ struct wm8350_rtc { struct platform_device *pdev; + struct rtc_device *rtc; + int alarm_enabled; /* used over suspend/resume */ }; #endif diff --git a/include/linux/ratelimit.h b/include/linux/ratelimit.h index 18a5b9ba9d4..00044b85645 100644 --- a/include/linux/ratelimit.h +++ b/include/linux/ratelimit.h @@ -17,11 +17,4 @@ struct ratelimit_state { struct ratelimit_state name = {interval, burst,} extern int __ratelimit(struct ratelimit_state *rs); - -static inline int ratelimit(void) -{ - static DEFINE_RATELIMIT_STATE(rs, DEFAULT_RATELIMIT_INTERVAL, - DEFAULT_RATELIMIT_BURST); - return __ratelimit(&rs); -} #endif diff --git a/include/linux/serial_core.h b/include/linux/serial_core.h index e27f216361f..4e4f1277f3b 100644 --- a/include/linux/serial_core.h +++ b/include/linux/serial_core.h @@ -155,6 +155,9 @@ #define PORT_SC26XX 82 +/* SH-SCI */ +#define PORT_SCIFA 83 + #ifdef __KERNEL__ #include <linux/compiler.h> diff --git a/include/linux/slab.h b/include/linux/slab.h index ba965c84ae0..000da12b5cf 100644 --- a/include/linux/slab.h +++ b/include/linux/slab.h @@ -23,6 +23,34 @@ #define SLAB_CACHE_DMA 0x00004000UL /* Use GFP_DMA memory */ #define SLAB_STORE_USER 0x00010000UL /* DEBUG: Store the last owner for bug hunting */ #define SLAB_PANIC 0x00040000UL /* Panic if kmem_cache_create() fails */ +/* + * SLAB_DESTROY_BY_RCU - **WARNING** READ THIS! + * + * This delays freeing the SLAB page by a grace period, it does _NOT_ + * delay object freeing. This means that if you do kmem_cache_free() + * that memory location is free to be reused at any time. Thus it may + * be possible to see another object there in the same RCU grace period. + * + * This feature only ensures the memory location backing the object + * stays valid, the trick to using this is relying on an independent + * object validation pass. Something like: + * + * rcu_read_lock() + * again: + * obj = lockless_lookup(key); + * if (obj) { + * if (!try_get_ref(obj)) // might fail for free objects + * goto again; + * + * if (obj->key != key) { // not the object we expected + * put_ref(obj); + * goto again; + * } + * } + * rcu_read_unlock(); + * + * See also the comment on struct slab_rcu in mm/slab.c. + */ #define SLAB_DESTROY_BY_RCU 0x00080000UL /* Defer freeing slabs to RCU */ #define SLAB_MEM_SPREAD 0x00100000UL /* Spread some memory over cpuset */ #define SLAB_TRACE 0x00200000UL /* Trace allocations and frees */ diff --git a/include/linux/usb.h b/include/linux/usb.h index 8fa973bede5..f72aa51f7bc 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -108,6 +108,7 @@ enum usb_interface_condition { * (in probe()), bound to a driver, or unbinding (in disconnect()) * @is_active: flag set when the interface is bound and not suspended. * @sysfs_files_created: sysfs attributes exist + * @unregistering: flag set when the interface is being unregistered * @needs_remote_wakeup: flag set when the driver requires remote-wakeup * capability during autosuspend. * @needs_altsetting0: flag set when a set-interface request for altsetting 0 @@ -163,6 +164,7 @@ struct usb_interface { enum usb_interface_condition condition; /* state of binding */ unsigned is_active:1; /* the interface is not suspended */ unsigned sysfs_files_created:1; /* the sysfs attributes exist */ + unsigned unregistering:1; /* unregistration is in progress */ unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */ unsigned needs_altsetting0:1; /* switch to altsetting 0 is pending */ unsigned needs_binding:1; /* needs delayed unbind/rebind */ diff --git a/include/video/atmel_lcdc.h b/include/video/atmel_lcdc.h index 6ad87f48599..0c864db1a46 100644 --- a/include/video/atmel_lcdc.h +++ b/include/video/atmel_lcdc.h @@ -38,7 +38,7 @@ struct atmel_lcdfb_info { spinlock_t lock; struct fb_info *info; void __iomem *mmio; - unsigned long irq_base; + int irq_base; struct work_struct task; unsigned int guard_time; diff --git a/init/Kconfig b/init/Kconfig index 86b00c53fad..226da2733c1 100644 --- a/init/Kconfig +++ b/init/Kconfig @@ -771,8 +771,7 @@ config SLAB help The regular slab allocator that is established and known to work well in all environments. It organizes cache hot objects in - per cpu and per node queues. SLAB is the default choice for - a slab allocator. + per cpu and per node queues. config SLUB bool "SLUB (Unqueued Allocator)" @@ -781,7 +780,8 @@ config SLUB instead of managing queues of cached objects (SLAB approach). Per cpu caching is realized using slabs of objects instead of queues of objects. SLUB can use memory efficiently - and has enhanced diagnostics. + and has enhanced diagnostics. SLUB is the default choice for + a slab allocator. config SLOB depends on EMBEDDED diff --git a/kernel/audit_tree.c b/kernel/audit_tree.c index 8ba0e0d934f..8b509441f49 100644 --- a/kernel/audit_tree.c +++ b/kernel/audit_tree.c @@ -24,6 +24,7 @@ struct audit_chunk { struct list_head trees; /* with root here */ int dead; int count; + atomic_long_t refs; struct rcu_head head; struct node { struct list_head list; @@ -56,7 +57,8 @@ static LIST_HEAD(prune_list); * tree is refcounted; one reference for "some rules on rules_list refer to * it", one for each chunk with pointer to it. * - * chunk is refcounted by embedded inotify_watch. + * chunk is refcounted by embedded inotify_watch + .refs (non-zero refcount + * of watch contributes 1 to .refs). * * node.index allows to get from node.list to containing chunk. * MSB of that sucker is stolen to mark taggings that we might have to @@ -121,6 +123,7 @@ static struct audit_chunk *alloc_chunk(int count) INIT_LIST_HEAD(&chunk->hash); INIT_LIST_HEAD(&chunk->trees); chunk->count = count; + atomic_long_set(&chunk->refs, 1); for (i = 0; i < count; i++) { INIT_LIST_HEAD(&chunk->owners[i].list); chunk->owners[i].index = i; @@ -129,9 +132,8 @@ static struct audit_chunk *alloc_chunk(int count) return chunk; } -static void __free_chunk(struct rcu_head *rcu) +static void free_chunk(struct audit_chunk *chunk) { - struct audit_chunk *chunk = container_of(rcu, struct audit_chunk, head); int i; for (i = 0; i < chunk->count; i++) { @@ -141,14 +143,16 @@ static void __free_chunk(struct rcu_head *rcu) kfree(chunk); } -static inline void free_chunk(struct audit_chunk *chunk) +void audit_put_chunk(struct audit_chunk *chunk) { - call_rcu(&chunk->head, __free_chunk); + if (atomic_long_dec_and_test(&chunk->refs)) + free_chunk(chunk); } -void audit_put_chunk(struct audit_chunk *chunk) +static void __put_chunk(struct rcu_head *rcu) { - put_inotify_watch(&chunk->watch); + struct audit_chunk *chunk = container_of(rcu, struct audit_chunk, head); + audit_put_chunk(chunk); } enum {HASH_SIZE = 128}; @@ -176,7 +180,7 @@ struct audit_chunk *audit_tree_lookup(const struct inode *inode) list_for_each_entry_rcu(p, list, hash) { if (p->watch.inode == inode) { - get_inotify_watch(&p->watch); + atomic_long_inc(&p->refs); return p; } } @@ -194,17 +198,49 @@ int audit_tree_match(struct audit_chunk *chunk, struct audit_tree *tree) /* tagging and untagging inodes with trees */ -static void untag_chunk(struct audit_chunk *chunk, struct node *p) +static struct audit_chunk *find_chunk(struct node *p) +{ + int index = p->index & ~(1U<<31); + p -= index; + return container_of(p, struct audit_chunk, owners[0]); +} + +static void untag_chunk(struct node *p) { + struct audit_chunk *chunk = find_chunk(p); struct audit_chunk *new; struct audit_tree *owner; int size = chunk->count - 1; int i, j; + if (!pin_inotify_watch(&chunk->watch)) { + /* + * Filesystem is shutting down; all watches are getting + * evicted, just take it off the node list for this + * tree and let the eviction logics take care of the + * rest. + */ + owner = p->owner; + if (owner->root == chunk) { + list_del_init(&owner->same_root); + owner->root = NULL; + } + list_del_init(&p->list); + p->owner = NULL; + put_tree(owner); + return; + } + + spin_unlock(&hash_lock); + + /* + * pin_inotify_watch() succeeded, so the watch won't go away + * from under us. + */ mutex_lock(&chunk->watch.inode->inotify_mutex); if (chunk->dead) { mutex_unlock(&chunk->watch.inode->inotify_mutex); - return; + goto out; } owner = p->owner; @@ -221,7 +257,7 @@ static void untag_chunk(struct audit_chunk *chunk, struct node *p) inotify_evict_watch(&chunk->watch); mutex_unlock(&chunk->watch.inode->inotify_mutex); put_inotify_watch(&chunk->watch); - return; + goto out; } new = alloc_chunk(size); @@ -263,7 +299,7 @@ static void untag_chunk(struct audit_chunk *chunk, struct node *p) inotify_evict_watch(&chunk->watch); mutex_unlock(&chunk->watch.inode->inotify_mutex); put_inotify_watch(&chunk->watch); - return; + goto out; Fallback: // do the best we can @@ -277,6 +313,9 @@ Fallback: put_tree(owner); spin_unlock(&hash_lock); mutex_unlock(&chunk->watch.inode->inotify_mutex); +out: + unpin_inotify_watch(&chunk->watch); + spin_lock(&hash_lock); } static int create_chunk(struct inode *inode, struct audit_tree *tree) @@ -387,13 +426,6 @@ static int tag_chunk(struct inode *inode, struct audit_tree *tree) return 0; } -static struct audit_chunk *find_chunk(struct node *p) -{ - int index = p->index & ~(1U<<31); - p -= index; - return container_of(p, struct audit_chunk, owners[0]); -} - static void kill_rules(struct audit_tree *tree) { struct audit_krule *rule, *next; @@ -431,17 +463,10 @@ static void prune_one(struct audit_tree *victim) spin_lock(&hash_lock); while (!list_empty(&victim->chunks)) { struct node *p; - struct audit_chunk *chunk; p = list_entry(victim->chunks.next, struct node, list); - chunk = find_chunk(p); - get_inotify_watch(&chunk->watch); - spin_unlock(&hash_lock); - - untag_chunk(chunk, p); - put_inotify_watch(&chunk->watch); - spin_lock(&hash_lock); + untag_chunk(p); } spin_unlock(&hash_lock); put_tree(victim); @@ -469,7 +494,6 @@ static void trim_marked(struct audit_tree *tree) while (!list_empty(&tree->chunks)) { struct node *node; - struct audit_chunk *chunk; node = list_entry(tree->chunks.next, struct node, list); @@ -477,14 +501,7 @@ static void trim_marked(struct audit_tree *tree) if (!(node->index & (1U<<31))) break; - chunk = find_chunk(node); - get_inotify_watch(&chunk->watch); - spin_unlock(&hash_lock); - - untag_chunk(chunk, node); - - put_inotify_watch(&chunk->watch); - spin_lock(&hash_lock); + untag_chunk(node); } if (!tree->root && !tree->goner) { tree->goner = 1; @@ -878,7 +895,7 @@ static void handle_event(struct inotify_watch *watch, u32 wd, u32 mask, static void destroy_watch(struct inotify_watch *watch) { struct audit_chunk *chunk = container_of(watch, struct audit_chunk, watch); - free_chunk(chunk); + call_rcu(&chunk->head, __put_chunk); } static const struct inotify_operations rtree_inotify_ops = { diff --git a/kernel/auditfilter.c b/kernel/auditfilter.c index b7d354e2b0e..9fd85a4640a 100644 --- a/kernel/auditfilter.c +++ b/kernel/auditfilter.c @@ -1094,8 +1094,8 @@ static void audit_inotify_unregister(struct list_head *in_list) list_for_each_entry_safe(p, n, in_list, ilist) { list_del(&p->ilist); inotify_rm_watch(audit_ih, &p->wdata); - /* the put matching the get in audit_do_del_rule() */ - put_inotify_watch(&p->wdata); + /* the unpin matching the pin in audit_do_del_rule() */ + unpin_inotify_watch(&p->wdata); } } @@ -1389,9 +1389,13 @@ static inline int audit_del_rule(struct audit_entry *entry, /* Put parent on the inotify un-registration * list. Grab a reference before releasing * audit_filter_mutex, to be released in - * audit_inotify_unregister(). */ - list_add(&parent->ilist, &inotify_list); - get_inotify_watch(&parent->wdata); + * audit_inotify_unregister(). + * If filesystem is going away, just leave + * the sucker alone, eviction will take + * care of it. + */ + if (pin_inotify_watch(&parent->wdata)) + list_add(&parent->ilist, &inotify_list); } } } diff --git a/kernel/cgroup_freezer.c b/kernel/cgroup_freezer.c index 7fa476f01d0..fb249e2bcad 100644 --- a/kernel/cgroup_freezer.c +++ b/kernel/cgroup_freezer.c @@ -184,9 +184,20 @@ static void freezer_fork(struct cgroup_subsys *ss, struct task_struct *task) { struct freezer *freezer; - task_lock(task); + /* + * No lock is needed, since the task isn't on tasklist yet, + * so it can't be moved to another cgroup, which means the + * freezer won't be removed and will be valid during this + * function call. + */ freezer = task_freezer(task); - task_unlock(task); + + /* + * The root cgroup is non-freezable, so we can skip the + * following check. + */ + if (!freezer->css.cgroup->parent) + return; spin_lock_irq(&freezer->lock); BUG_ON(freezer->state == CGROUP_FROZEN); @@ -331,7 +342,7 @@ static int freezer_write(struct cgroup *cgroup, else if (strcmp(buffer, freezer_state_strs[CGROUP_FROZEN]) == 0) goal_state = CGROUP_FROZEN; else - return -EIO; + return -EINVAL; if (!cgroup_lock_live_group(cgroup)) return -ENODEV; @@ -350,6 +361,8 @@ static struct cftype files[] = { static int freezer_populate(struct cgroup_subsys *ss, struct cgroup *cgroup) { + if (!cgroup->parent) + return 0; return cgroup_add_files(cgroup, ss, files, ARRAY_SIZE(files)); } diff --git a/kernel/exit.c b/kernel/exit.c index ae2b92be5fa..2d8be7ebb0f 100644 --- a/kernel/exit.c +++ b/kernel/exit.c @@ -40,7 +40,6 @@ #include <linux/cn_proc.h> #include <linux/mutex.h> #include <linux/futex.h> -#include <linux/compat.h> #include <linux/pipe_fs_i.h> #include <linux/audit.h> /* for audit_free() */ #include <linux/resource.h> @@ -1059,14 +1058,6 @@ NORET_TYPE void do_exit(long code) exit_itimers(tsk->signal); } acct_collect(code, group_dead); -#ifdef CONFIG_FUTEX - if (unlikely(tsk->robust_list)) - exit_robust_list(tsk); -#ifdef CONFIG_COMPAT - if (unlikely(tsk->compat_robust_list)) - compat_exit_robust_list(tsk); -#endif -#endif if (group_dead) tty_audit_exit(); if (unlikely(tsk->audit_context)) diff --git a/kernel/fork.c b/kernel/fork.c index f6083561dfe..2a372a0e206 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -40,6 +40,7 @@ #include <linux/jiffies.h> #include <linux/tracehook.h> #include <linux/futex.h> +#include <linux/compat.h> #include <linux/task_io_accounting_ops.h> #include <linux/rcupdate.h> #include <linux/ptrace.h> @@ -519,6 +520,16 @@ void mm_release(struct task_struct *tsk, struct mm_struct *mm) { struct completion *vfork_done = tsk->vfork_done; + /* Get rid of any futexes when releasing the mm */ +#ifdef CONFIG_FUTEX + if (unlikely(tsk->robust_list)) + exit_robust_list(tsk); +#ifdef CONFIG_COMPAT + if (unlikely(tsk->compat_robust_list)) + compat_exit_robust_list(tsk); +#endif +#endif + /* Get rid of any cached register state */ deactivate_mm(tsk, mm); diff --git a/kernel/hrtimer.c b/kernel/hrtimer.c index 95d3949f2ae..47e63349d1b 100644 --- a/kernel/hrtimer.c +++ b/kernel/hrtimer.c @@ -664,14 +664,6 @@ static inline int hrtimer_enqueue_reprogram(struct hrtimer *timer, /* Timer is expired, act upon the callback mode */ switch(timer->cb_mode) { - case HRTIMER_CB_IRQSAFE_NO_RESTART: - debug_hrtimer_deactivate(timer); - /* - * We can call the callback from here. No restart - * happens, so no danger of recursion - */ - BUG_ON(timer->function(timer) != HRTIMER_NORESTART); - return 1; case HRTIMER_CB_IRQSAFE_PERCPU: case HRTIMER_CB_IRQSAFE_UNLOCKED: /* @@ -683,7 +675,6 @@ static inline int hrtimer_enqueue_reprogram(struct hrtimer *timer, */ debug_hrtimer_deactivate(timer); return 1; - case HRTIMER_CB_IRQSAFE: case HRTIMER_CB_SOFTIRQ: /* * Move everything else into the softirq pending list ! diff --git a/kernel/kprobes.c b/kernel/kprobes.c index 8b57a2597f2..9f8a3f25259 100644 --- a/kernel/kprobes.c +++ b/kernel/kprobes.c @@ -72,7 +72,7 @@ static bool kprobe_enabled; DEFINE_MUTEX(kprobe_mutex); /* Protects kprobe_table */ static DEFINE_PER_CPU(struct kprobe *, kprobe_instance) = NULL; static struct { - spinlock_t lock ____cacheline_aligned; + spinlock_t lock ____cacheline_aligned_in_smp; } kretprobe_table_locks[KPROBE_TABLE_SIZE]; static spinlock_t *kretprobe_table_lock_ptr(unsigned long hash) @@ -613,30 +613,37 @@ static int __kprobes __register_kprobe(struct kprobe *p, return -EINVAL; p->addr = addr; - if (!kernel_text_address((unsigned long) p->addr) || - in_kprobes_functions((unsigned long) p->addr)) + preempt_disable(); + if (!__kernel_text_address((unsigned long) p->addr) || + in_kprobes_functions((unsigned long) p->addr)) { + preempt_enable(); return -EINVAL; + } p->mod_refcounted = 0; /* * Check if are we probing a module. */ - probed_mod = module_text_address((unsigned long) p->addr); + probed_mod = __module_text_address((unsigned long) p->addr); if (probed_mod) { - struct module *calling_mod = module_text_address(called_from); + struct module *calling_mod; + calling_mod = __module_text_address(called_from); /* * We must allow modules to probe themself and in this case * avoid incrementing the module refcount, so as to allow * unloading of self probing modules. */ if (calling_mod && calling_mod != probed_mod) { - if (unlikely(!try_module_get(probed_mod))) + if (unlikely(!try_module_get(probed_mod))) { + preempt_enable(); return -EINVAL; + } p->mod_refcounted = 1; } else probed_mod = NULL; } + preempt_enable(); p->nmissed = 0; INIT_LIST_HEAD(&p->list); @@ -718,6 +725,10 @@ static void __kprobes __unregister_kprobe_bottom(struct kprobe *p) struct kprobe *old_p; if (p->mod_refcounted) { + /* + * Since we've already incremented refcount, + * we don't need to disable preemption. + */ mod = module_text_address((unsigned long)p->addr); if (mod) module_put(mod); diff --git a/kernel/sched.c b/kernel/sched.c index 50a21f96467..c94baf2969e 100644 --- a/kernel/sched.c +++ b/kernel/sched.c @@ -1456,6 +1456,8 @@ static unsigned long cpu_avg_load_per_task(int cpu) if (rq->nr_running) rq->avg_load_per_task = rq->load.weight / rq->nr_running; + else + rq->avg_load_per_task = 0; return rq->avg_load_per_task; } @@ -5868,6 +5870,8 @@ void __cpuinit init_idle(struct task_struct *idle, int cpu) struct rq *rq = cpu_rq(cpu); unsigned long flags; + spin_lock_irqsave(&rq->lock, flags); + __sched_fork(idle); idle->se.exec_start = sched_clock(); @@ -5875,7 +5879,6 @@ void __cpuinit init_idle(struct task_struct *idle, int cpu) idle->cpus_allowed = cpumask_of_cpu(cpu); __set_task_cpu(idle, cpu); - spin_lock_irqsave(&rq->lock, flags); rq->curr = rq->idle = idle; #if defined(CONFIG_SMP) && defined(__ARCH_WANT_UNLOCKED_CTXSW) idle->oncpu = 1; diff --git a/mm/hugetlb.c b/mm/hugetlb.c index d143ab67be4..6058b53dcb8 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -1796,6 +1796,7 @@ void unmap_hugepage_range(struct vm_area_struct *vma, unsigned long start, static int unmap_ref_private(struct mm_struct *mm, struct vm_area_struct *vma, struct page *page, unsigned long address) { + struct hstate *h = hstate_vma(vma); struct vm_area_struct *iter_vma; struct address_space *mapping; struct prio_tree_iter iter; @@ -1805,7 +1806,7 @@ static int unmap_ref_private(struct mm_struct *mm, struct vm_area_struct *vma, * vm_pgoff is in PAGE_SIZE units, hence the different calculation * from page cache lookup which is in HPAGE_SIZE units. */ - address = address & huge_page_mask(hstate_vma(vma)); + address = address & huge_page_mask(h); pgoff = ((address - vma->vm_start) >> PAGE_SHIFT) + (vma->vm_pgoff >> PAGE_SHIFT); mapping = (struct address_space *)page_private(page); @@ -1824,7 +1825,7 @@ static int unmap_ref_private(struct mm_struct *mm, struct vm_area_struct *vma, */ if (!is_vma_resv_set(iter_vma, HPAGE_RESV_OWNER)) unmap_hugepage_range(iter_vma, - address, address + HPAGE_SIZE, + address, address + huge_page_size(h), page); } diff --git a/mm/mlock.c b/mm/mlock.c index 008ea70b7af..a6da2aee940 100644 --- a/mm/mlock.c +++ b/mm/mlock.c @@ -66,14 +66,10 @@ void __clear_page_mlock(struct page *page) putback_lru_page(page); } else { /* - * Page not on the LRU yet. Flush all pagevecs and retry. + * We lost the race. the page already moved to evictable list. */ - lru_add_drain_all(); - if (!isolate_lru_page(page)) - putback_lru_page(page); - else if (PageUnevictable(page)) + if (PageUnevictable(page)) count_vm_event(UNEVICTABLE_PGSTRANDED); - } } @@ -187,8 +183,6 @@ static long __mlock_vma_pages_range(struct vm_area_struct *vma, if (vma->vm_flags & VM_WRITE) gup_flags |= GUP_FLAGS_WRITE; - lru_add_drain_all(); /* push cached pages to LRU */ - while (nr_pages > 0) { int i; @@ -251,8 +245,6 @@ static long __mlock_vma_pages_range(struct vm_area_struct *vma, ret = 0; } - lru_add_drain_all(); /* to update stats */ - return ret; /* count entire vma as locked_vm */ } @@ -546,6 +538,8 @@ asmlinkage long sys_mlock(unsigned long start, size_t len) if (!can_do_mlock()) return -EPERM; + lru_add_drain_all(); /* flush pagevec */ + down_write(¤t->mm->mmap_sem); len = PAGE_ALIGN(len + (start & ~PAGE_MASK)); start &= PAGE_MASK; @@ -612,6 +606,8 @@ asmlinkage long sys_mlockall(int flags) if (!can_do_mlock()) goto out; + lru_add_drain_all(); /* flush pagevec */ + down_write(¤t->mm->mmap_sem); lock_limit = current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur; diff --git a/mm/mmap.c b/mm/mmap.c index de14ac21e5b..d4855a682ab 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -1704,7 +1704,7 @@ find_extend_vma(struct mm_struct *mm, unsigned long addr) vma = find_vma_prev(mm, addr, &prev); if (vma && (vma->vm_start <= addr)) return vma; - if (expand_stack(prev, addr)) + if (!prev || expand_stack(prev, addr)) return NULL; if (prev->vm_flags & VM_LOCKED) { if (mlock_vma_pages_range(prev, addr, prev->vm_end) < 0) diff --git a/mm/page_alloc.c b/mm/page_alloc.c index 54069e64e3a..d8ac0147456 100644 --- a/mm/page_alloc.c +++ b/mm/page_alloc.c @@ -1561,6 +1561,10 @@ nofail_alloc: /* We now go into synchronous reclaim */ cpuset_memory_pressure_bump(); + /* + * The task's cpuset might have expanded its set of allowable nodes + */ + cpuset_update_task_memory_state(); p->flags |= PF_MEMALLOC; reclaim_state.reclaimed_slab = 0; p->reclaim_state = &reclaim_state; diff --git a/mm/page_cgroup.c b/mm/page_cgroup.c index f59d797dc5a..1223d927904 100644 --- a/mm/page_cgroup.c +++ b/mm/page_cgroup.c @@ -165,7 +165,7 @@ int online_page_cgroup(unsigned long start_pfn, unsigned long start, end, pfn; int fail = 0; - start = start_pfn & (PAGES_PER_SECTION - 1); + start = start_pfn & ~(PAGES_PER_SECTION - 1); end = ALIGN(start_pfn + nr_pages, PAGES_PER_SECTION); for (pfn = start; !fail && pfn < end; pfn += PAGES_PER_SECTION) { @@ -188,7 +188,7 @@ int offline_page_cgroup(unsigned long start_pfn, { unsigned long start, end, pfn; - start = start_pfn & (PAGES_PER_SECTION - 1); + start = start_pfn & ~(PAGES_PER_SECTION - 1); end = ALIGN(start_pfn + nr_pages, PAGES_PER_SECTION); for (pfn = start; pfn < end; pfn += PAGES_PER_SECTION) diff --git a/mm/vmscan.c b/mm/vmscan.c index 3b5860294bb..c141b3e7807 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -2368,39 +2368,6 @@ int page_evictable(struct page *page, struct vm_area_struct *vma) return 1; } -static void show_page_path(struct page *page) -{ - char buf[256]; - if (page_is_file_cache(page)) { - struct address_space *mapping = page->mapping; - struct dentry *dentry; - pgoff_t pgoff = page->index << (PAGE_CACHE_SHIFT - PAGE_SHIFT); - - spin_lock(&mapping->i_mmap_lock); - dentry = d_find_alias(mapping->host); - printk(KERN_INFO "rescued: %s %lu\n", - dentry_path(dentry, buf, 256), pgoff); - spin_unlock(&mapping->i_mmap_lock); - } else { -#if defined(CONFIG_MM_OWNER) && defined(CONFIG_MMU) - struct anon_vma *anon_vma; - struct vm_area_struct *vma; - - anon_vma = page_lock_anon_vma(page); - if (!anon_vma) - return; - - list_for_each_entry(vma, &anon_vma->head, anon_vma_node) { - printk(KERN_INFO "rescued: anon %s\n", - vma->vm_mm->owner->comm); - break; - } - page_unlock_anon_vma(anon_vma); -#endif - } -} - - /** * check_move_unevictable_page - check page for evictability and move to appropriate zone lru list * @page: page to check evictability and move to appropriate lru list @@ -2421,8 +2388,6 @@ retry: if (page_evictable(page, NULL)) { enum lru_list l = LRU_INACTIVE_ANON + page_is_file_cache(page); - show_page_path(page); - __dec_zone_state(zone, NR_UNEVICTABLE); list_move(&page->lru, &zone->lru[l].list); __inc_zone_state(zone, NR_INACTIVE_ANON + l); diff --git a/net/9p/Kconfig b/net/9p/Kconfig index c42c0c400bf..0663f99e977 100644 --- a/net/9p/Kconfig +++ b/net/9p/Kconfig @@ -13,22 +13,24 @@ menuconfig NET_9P If unsure, say N. +if NET_9P + config NET_9P_VIRTIO - depends on NET_9P && EXPERIMENTAL && VIRTIO + depends on EXPERIMENTAL && VIRTIO tristate "9P Virtio Transport (Experimental)" help This builds support for a transports between guest partitions and a host partition. config NET_9P_RDMA - depends on NET_9P && INFINIBAND && EXPERIMENTAL + depends on INET && INFINIBAND && EXPERIMENTAL tristate "9P RDMA Transport (Experimental)" help - This builds support for a RDMA transport. + This builds support for an RDMA transport. config NET_9P_DEBUG bool "Debug information" - depends on NET_9P help Say Y if you want the 9P subsystem to log debug information. +endif diff --git a/net/compat.c b/net/compat.c index 67fb6a3834a..6ce1a1cadcc 100644 --- a/net/compat.c +++ b/net/compat.c @@ -226,14 +226,14 @@ int put_cmsg_compat(struct msghdr *kmsg, int level, int type, int len, void *dat return 0; /* XXX: return error? check spec. */ } - if (level == SOL_SOCKET && type == SO_TIMESTAMP) { + if (level == SOL_SOCKET && type == SCM_TIMESTAMP) { struct timeval *tv = (struct timeval *)data; ctv.tv_sec = tv->tv_sec; ctv.tv_usec = tv->tv_usec; data = &ctv; len = sizeof(ctv); } - if (level == SOL_SOCKET && type == SO_TIMESTAMPNS) { + if (level == SOL_SOCKET && type == SCM_TIMESTAMPNS) { struct timespec *ts = (struct timespec *)data; cts.tv_sec = ts->tv_sec; cts.tv_nsec = ts->tv_nsec; diff --git a/net/ipv4/tcp_htcp.c b/net/ipv4/tcp_htcp.c index af99776146f..937549b8a92 100644 --- a/net/ipv4/tcp_htcp.c +++ b/net/ipv4/tcp_htcp.c @@ -69,9 +69,12 @@ static u32 htcp_cwnd_undo(struct sock *sk) const struct tcp_sock *tp = tcp_sk(sk); struct htcp *ca = inet_csk_ca(sk); - ca->last_cong = ca->undo_last_cong; - ca->maxRTT = ca->undo_maxRTT; - ca->old_maxB = ca->undo_old_maxB; + if (ca->undo_last_cong) { + ca->last_cong = ca->undo_last_cong; + ca->maxRTT = ca->undo_maxRTT; + ca->old_maxB = ca->undo_old_maxB; + ca->undo_last_cong = 0; + } return max(tp->snd_cwnd, (tp->snd_ssthresh << 7) / ca->beta); } @@ -268,7 +271,10 @@ static void htcp_state(struct sock *sk, u8 new_state) case TCP_CA_Open: { struct htcp *ca = inet_csk_ca(sk); - ca->last_cong = jiffies; + if (ca->undo_last_cong) { + ca->last_cong = jiffies; + ca->undo_last_cong = 0; + } } break; case TCP_CA_CWR: diff --git a/net/mac80211/mlme.c b/net/mac80211/mlme.c index 87665d7bb4f..14d165f0df7 100644 --- a/net/mac80211/mlme.c +++ b/net/mac80211/mlme.c @@ -2570,14 +2570,14 @@ void ieee80211_notify_mac(struct ieee80211_hw *hw, switch (notif_type) { case IEEE80211_NOTIFY_RE_ASSOC: - rcu_read_lock(); - list_for_each_entry_rcu(sdata, &local->interfaces, list) { + rtnl_lock(); + list_for_each_entry(sdata, &local->interfaces, list) { if (sdata->vif.type != NL80211_IFTYPE_STATION) continue; ieee80211_sta_req_auth(sdata, &sdata->u.sta); } - rcu_read_unlock(); + rtnl_unlock(); break; } } diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c index e6085915d86..4300a679cd8 100644 --- a/sound/pci/hda/patch_sigmatel.c +++ b/sound/pci/hda/patch_sigmatel.c @@ -212,7 +212,7 @@ struct sigmatel_spec { /* i/o switches */ unsigned int io_switch[2]; unsigned int clfe_swap; - unsigned int hp_switch; + unsigned int hp_switch; /* NID of HP as line-out */ unsigned int aloopback; struct hda_pcm pcm_rec[2]; /* PCM information */ @@ -2443,7 +2443,7 @@ static int stac92xx_hp_switch_get(struct snd_kcontrol *kcontrol, struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct sigmatel_spec *spec = codec->spec; - ucontrol->value.integer.value[0] = spec->hp_switch; + ucontrol->value.integer.value[0] = !!spec->hp_switch; return 0; } @@ -2452,8 +2452,9 @@ static int stac92xx_hp_switch_put(struct snd_kcontrol *kcontrol, { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct sigmatel_spec *spec = codec->spec; - - spec->hp_switch = ucontrol->value.integer.value[0]; + int nid = kcontrol->private_value; + + spec->hp_switch = ucontrol->value.integer.value[0] ? nid : 0; /* check to be sure that the ports are upto date with * switch changes @@ -2862,7 +2863,8 @@ static int stac92xx_auto_create_multi_out_ctls(struct hda_codec *codec, if (cfg->hp_outs > 1) { err = stac92xx_add_control(spec, STAC_CTL_WIDGET_HP_SWITCH, - "Headphone as Line Out Switch", 0); + "Headphone as Line Out Switch", + cfg->hp_pins[cfg->hp_outs - 1]); if (err < 0) return err; } @@ -3530,6 +3532,12 @@ static int stac9200_parse_auto_config(struct hda_codec *codec) if ((err = stac9200_auto_create_lfe_ctls(codec, &spec->autocfg)) < 0) return err; + if (spec->num_muxes > 0) { + err = stac92xx_auto_create_mux_input_ctls(codec); + if (err < 0) + return err; + } + if (spec->autocfg.dig_out_pin) spec->multiout.dig_out_nid = 0x05; if (spec->autocfg.dig_in_pin) @@ -3647,14 +3655,18 @@ static int stac92xx_init(struct hda_codec *codec) for (i = 0; i < AUTO_PIN_LAST; i++) { hda_nid_t nid = cfg->input_pins[i]; if (nid) { - unsigned int pinctl = snd_hda_codec_read(codec, nid, - 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); - /* if PINCTL already set then skip */ - if (pinctl & AC_PINCAP_IN) - continue; - pinctl = AC_PINCTL_IN_EN; - if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC) - pinctl |= stac92xx_get_vref(codec, nid); + unsigned int pinctl; + if (i == AUTO_PIN_MIC || i == AUTO_PIN_FRONT_MIC) { + /* for mic pins, force to initialize */ + pinctl = stac92xx_get_vref(codec, nid); + } else { + pinctl = snd_hda_codec_read(codec, nid, 0, + AC_VERB_GET_PIN_WIDGET_CONTROL, 0); + /* if PINCTL already set then skip */ + if (pinctl & AC_PINCTL_IN_EN) + continue; + } + pinctl |= AC_PINCTL_IN_EN; stac92xx_auto_set_pinctl(codec, nid, pinctl); } } @@ -3776,11 +3788,30 @@ static int get_hp_pin_presence(struct hda_codec *codec, hda_nid_t nid) return 0; } +/* return non-zero if the hp-pin of the given array index isn't + * a jack-detection target + */ +static int no_hp_sensing(struct sigmatel_spec *spec, int i) +{ + struct auto_pin_cfg *cfg = &spec->autocfg; + + /* ignore sensing of shared line and mic jacks */ + if (spec->line_switch && + cfg->hp_pins[i] == cfg->input_pins[AUTO_PIN_LINE]) + return 1; + if (spec->mic_switch && + cfg->hp_pins[i] == cfg->input_pins[AUTO_PIN_MIC]) + return 1; + /* ignore if the pin is set as line-out */ + if (cfg->hp_pins[i] == spec->hp_switch) + return 1; + return 0; +} + static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res) { struct sigmatel_spec *spec = codec->spec; struct auto_pin_cfg *cfg = &spec->autocfg; - int nid = cfg->hp_pins[cfg->hp_outs - 1]; int i, presence; presence = 0; @@ -3791,15 +3822,16 @@ static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res) for (i = 0; i < cfg->hp_outs; i++) { if (presence) break; - if (spec->hp_switch && cfg->hp_pins[i] == nid) - break; + if (no_hp_sensing(spec, i)) + continue; presence = get_hp_pin_presence(codec, cfg->hp_pins[i]); } if (presence) { - /* disable lineouts, enable hp */ + /* disable lineouts */ if (spec->hp_switch) - stac92xx_reset_pinctl(codec, nid, AC_PINCTL_OUT_EN); + stac92xx_reset_pinctl(codec, spec->hp_switch, + AC_PINCTL_OUT_EN); for (i = 0; i < cfg->line_outs; i++) stac92xx_reset_pinctl(codec, cfg->line_out_pins[i], AC_PINCTL_OUT_EN); @@ -3811,9 +3843,10 @@ static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res) spec->gpio_dir, spec->gpio_data & ~spec->eapd_mask); } else { - /* enable lineouts, disable hp */ + /* enable lineouts */ if (spec->hp_switch) - stac92xx_set_pinctl(codec, nid, AC_PINCTL_OUT_EN); + stac92xx_set_pinctl(codec, spec->hp_switch, + AC_PINCTL_OUT_EN); for (i = 0; i < cfg->line_outs; i++) stac92xx_set_pinctl(codec, cfg->line_out_pins[i], AC_PINCTL_OUT_EN); @@ -3825,8 +3858,16 @@ static void stac92xx_hp_detect(struct hda_codec *codec, unsigned int res) spec->gpio_dir, spec->gpio_data | spec->eapd_mask); } - if (!spec->hp_switch && cfg->hp_outs > 1 && presence) - stac92xx_set_pinctl(codec, nid, AC_PINCTL_OUT_EN); + /* toggle hp outs */ + for (i = 0; i < cfg->hp_outs; i++) { + unsigned int val = AC_PINCTL_OUT_EN | AC_PINCTL_HP_EN; + if (no_hp_sensing(spec, i)) + continue; + if (presence) + stac92xx_set_pinctl(codec, cfg->hp_pins[i], val); + else + stac92xx_reset_pinctl(codec, cfg->hp_pins[i], val); + } } static void stac92xx_pin_sense(struct hda_codec *codec, int idx) |